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 }