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