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

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