github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/libnetwork/drivers/ipvlan/ipvlan_setup.go (about)

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