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 }