github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/drivers/macvlan/macvlan_setup.go (about)

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