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