github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/libnetwork/drivers/ipvlan/ipvlan_setup.go (about)

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