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