github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/libnetwork/drivers/ipvlan/ipvlan_setup.go (about)

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