github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/drivers/ipvlan/ipvlan_setup.go (about)

     1  //go:build linux
     2  
     3  package ipvlan
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/containerd/log"
    12  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/ns"
    13  	"github.com/vishvananda/netlink"
    14  )
    15  
    16  const (
    17  	dummyPrefix     = "di-" // ipvlan prefix for dummy parent interface
    18  	ipvlanKernelVer = 4     // minimum ipvlan kernel support
    19  	ipvlanMajorVer  = 2     // minimum ipvlan major kernel support
    20  )
    21  
    22  // createIPVlan Create the ipvlan slave specifying the source name
    23  func createIPVlan(containerIfName, parent, ipvlanMode, ipvlanFlag string) (string, error) {
    24  	// Set the ipvlan mode and flag. Default is L2 bridge
    25  	mode, err := setIPVlanMode(ipvlanMode)
    26  	if err != nil {
    27  		return "", fmt.Errorf("Unsupported %s ipvlan mode: %v", ipvlanMode, err)
    28  	}
    29  	// Set the ipvlan flag. Default is bridge
    30  	flag, err := setIPVlanFlag(ipvlanFlag)
    31  	if err != nil {
    32  		return "", fmt.Errorf("Unsupported %s ipvlan flag: %v", ipvlanFlag, err)
    33  	}
    34  	// verify the Docker host interface acting as the macvlan parent iface exists
    35  	if !parentExists(parent) {
    36  		return "", fmt.Errorf("the requested parent interface %s was not found on the Docker host", parent)
    37  	}
    38  	// Get the link for the master index (Example: the docker host eth iface)
    39  	parentLink, err := ns.NlHandle().LinkByName(parent)
    40  	if err != nil {
    41  		return "", fmt.Errorf("error occurred looking up the ipvlan parent iface %s error: %s", parent, err)
    42  	}
    43  	// Create an ipvlan link
    44  	ipvlan := &netlink.IPVlan{
    45  		LinkAttrs: netlink.LinkAttrs{
    46  			Name:        containerIfName,
    47  			ParentIndex: parentLink.Attrs().Index,
    48  		},
    49  		Mode: mode,
    50  		Flag: flag,
    51  	}
    52  	if err := ns.NlHandle().LinkAdd(ipvlan); err != nil {
    53  		// If a user creates a macvlan and ipvlan on same parent, only one slave iface can be active at a time.
    54  		return "", fmt.Errorf("failed to create the ipvlan port: %v", err)
    55  	}
    56  
    57  	return ipvlan.Attrs().Name, nil
    58  }
    59  
    60  // setIPVlanMode setter for one of the three ipvlan port types
    61  func setIPVlanMode(mode string) (netlink.IPVlanMode, error) {
    62  	switch mode {
    63  	case modeL2:
    64  		return netlink.IPVLAN_MODE_L2, nil
    65  	case modeL3:
    66  		return netlink.IPVLAN_MODE_L3, nil
    67  	case modeL3S:
    68  		return netlink.IPVLAN_MODE_L3S, nil
    69  	default:
    70  		return 0, fmt.Errorf("Unknown ipvlan mode: %s", mode)
    71  	}
    72  }
    73  
    74  // setIPVlanFlag setter for one of the three ipvlan port flags
    75  func setIPVlanFlag(flag string) (netlink.IPVlanFlag, error) {
    76  	switch flag {
    77  	case flagBridge:
    78  		return netlink.IPVLAN_FLAG_BRIDGE, nil
    79  	case flagPrivate:
    80  		return netlink.IPVLAN_FLAG_PRIVATE, nil
    81  	case flagVepa:
    82  		return netlink.IPVLAN_FLAG_VEPA, nil
    83  	default:
    84  		return 0, fmt.Errorf("unknown ipvlan flag: %s", flag)
    85  	}
    86  }
    87  
    88  // parentExists check if the specified interface exists in the default namespace
    89  func parentExists(ifaceStr string) bool {
    90  	_, err := ns.NlHandle().LinkByName(ifaceStr)
    91  	return err == nil
    92  }
    93  
    94  // createVlanLink parses sub-interfaces and vlan id for creation
    95  func createVlanLink(parentName string) error {
    96  	if strings.Contains(parentName, ".") {
    97  		parent, vidInt, err := parseVlan(parentName)
    98  		if err != nil {
    99  			return err
   100  		}
   101  		// VLAN identifier or VID is a 12-bit field specifying the VLAN to which the frame belongs
   102  		if vidInt > 4094 || vidInt < 1 {
   103  			return fmt.Errorf("vlan id must be between 1-4094, received: %d", vidInt)
   104  		}
   105  		// get the parent link to attach a vlan subinterface
   106  		parentLink, err := ns.NlHandle().LinkByName(parent)
   107  		if err != nil {
   108  			return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
   109  		}
   110  		vlanLink := &netlink.Vlan{
   111  			LinkAttrs: netlink.LinkAttrs{
   112  				Name:        parentName,
   113  				ParentIndex: parentLink.Attrs().Index,
   114  			},
   115  			VlanId: vidInt,
   116  		}
   117  		// create the subinterface
   118  		if err := ns.NlHandle().LinkAdd(vlanLink); err != nil {
   119  			return fmt.Errorf("failed to create %s vlan link: %v", vlanLink.Name, err)
   120  		}
   121  		// Bring the new netlink iface up
   122  		if err := ns.NlHandle().LinkSetUp(vlanLink); err != nil {
   123  			return fmt.Errorf("failed to enable %s the ipvlan parent link %v", vlanLink.Name, err)
   124  		}
   125  		log.G(context.TODO()).Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", parentName, vidInt)
   126  		return nil
   127  	}
   128  
   129  	return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", parentName)
   130  }
   131  
   132  // delVlanLink verifies only sub-interfaces with a vlan id get deleted
   133  func delVlanLink(linkName string) error {
   134  	if strings.Contains(linkName, ".") {
   135  		_, _, err := parseVlan(linkName)
   136  		if err != nil {
   137  			return err
   138  		}
   139  		// delete the vlan subinterface
   140  		vlanLink, err := ns.NlHandle().LinkByName(linkName)
   141  		if err != nil {
   142  			return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
   143  		}
   144  		// verify a parent interface isn't being deleted
   145  		if vlanLink.Attrs().ParentIndex == 0 {
   146  			return fmt.Errorf("interface %s does not appear to be a slave device: %v", linkName, err)
   147  		}
   148  		// delete the ipvlan slave device
   149  		if err := ns.NlHandle().LinkDel(vlanLink); err != nil {
   150  			return fmt.Errorf("failed to delete  %s link: %v", linkName, err)
   151  		}
   152  		log.G(context.TODO()).Debugf("Deleted a vlan tagged netlink subinterface: %s", linkName)
   153  	}
   154  	// if the subinterface doesn't parse to iface.vlan_id leave the interface in
   155  	// place since it could be a user specified name not created by the driver.
   156  	return nil
   157  }
   158  
   159  // parseVlan parses and verifies a slave interface name: -o parent=eth0.10
   160  func parseVlan(linkName string) (string, int, error) {
   161  	// parse -o parent=eth0.10
   162  	splitName := strings.Split(linkName, ".")
   163  	if len(splitName) != 2 {
   164  		return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", linkName)
   165  	}
   166  	parent, vidStr := splitName[0], splitName[1]
   167  	// validate type and convert vlan id to int
   168  	vidInt, err := strconv.Atoi(vidStr)
   169  	if err != nil {
   170  		return "", 0, fmt.Errorf("unable to parse a valid vlan id from: %s (ex. eth0.10 for vlan 10)", vidStr)
   171  	}
   172  	// Check if the interface exists
   173  	if !parentExists(parent) {
   174  		return "", 0, fmt.Errorf("-o parent interface was not found on the host: %s", parent)
   175  	}
   176  
   177  	return parent, vidInt, nil
   178  }
   179  
   180  // createDummyLink creates a dummy0 parent link
   181  func createDummyLink(dummyName, truncNetID string) error {
   182  	// create a parent interface since one was not specified
   183  	parent := &netlink.Dummy{
   184  		LinkAttrs: netlink.LinkAttrs{
   185  			Name: dummyName,
   186  		},
   187  	}
   188  	if err := ns.NlHandle().LinkAdd(parent); err != nil {
   189  		return err
   190  	}
   191  	parentDummyLink, err := ns.NlHandle().LinkByName(dummyName)
   192  	if err != nil {
   193  		return fmt.Errorf("error occurred looking up the ipvlan parent iface %s error: %s", dummyName, err)
   194  	}
   195  	// bring the new netlink iface up
   196  	if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil {
   197  		return fmt.Errorf("failed to enable %s the ipvlan parent link: %v", dummyName, err)
   198  	}
   199  
   200  	return nil
   201  }
   202  
   203  // delDummyLink deletes the link type dummy used when -o parent is not passed
   204  func delDummyLink(linkName string) error {
   205  	// delete the vlan subinterface
   206  	dummyLink, err := ns.NlHandle().LinkByName(linkName)
   207  	if err != nil {
   208  		return fmt.Errorf("failed to find link %s on the Docker host : %v", linkName, err)
   209  	}
   210  	// verify a parent interface is being deleted
   211  	if dummyLink.Attrs().ParentIndex != 0 {
   212  		return fmt.Errorf("link %s is not a parent dummy interface", linkName)
   213  	}
   214  	// delete the ipvlan dummy device
   215  	if err := ns.NlHandle().LinkDel(dummyLink); err != nil {
   216  		return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
   217  	}
   218  	log.G(context.TODO()).Debugf("Deleted a dummy parent link: %s", linkName)
   219  
   220  	return nil
   221  }
   222  
   223  // getDummyName returns the name of a dummy parent with truncated net ID and driver prefix
   224  func getDummyName(netID string) string {
   225  	return dummyPrefix + netID
   226  }