github.com/moby/docker@v26.1.3+incompatible/libnetwork/osl/interface_linux.go (about) 1 package osl 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "syscall" 8 "time" 9 10 "github.com/containerd/log" 11 "github.com/docker/docker/libnetwork/ns" 12 "github.com/docker/docker/libnetwork/types" 13 "github.com/vishvananda/netlink" 14 "github.com/vishvananda/netns" 15 ) 16 17 // newInterface creates a new interface in the given namespace using the 18 // provided options. 19 func newInterface(ns *Namespace, srcName, dstPrefix string, options ...IfaceOption) (*Interface, error) { 20 i := &Interface{ 21 srcName: srcName, 22 dstName: dstPrefix, 23 ns: ns, 24 } 25 for _, opt := range options { 26 if opt != nil { 27 // TODO(thaJeztah): use multi-error instead of returning early. 28 if err := opt(i); err != nil { 29 return nil, err 30 } 31 } 32 } 33 if i.master != "" { 34 i.dstMaster = ns.findDst(i.master, true) 35 if i.dstMaster == "" { 36 return nil, fmt.Errorf("could not find an appropriate master %q for %q", i.master, i.srcName) 37 } 38 } 39 return i, nil 40 } 41 42 // Interface represents the settings and identity of a network device. 43 // It is used as a return type for Network.Link, and it is common practice 44 // for the caller to use this information when moving interface SrcName from 45 // host namespace to DstName in a different net namespace with the appropriate 46 // network settings. 47 type Interface struct { 48 srcName string 49 dstName string 50 master string 51 dstMaster string 52 mac net.HardwareAddr 53 address *net.IPNet 54 addressIPv6 *net.IPNet 55 llAddrs []*net.IPNet 56 routes []*net.IPNet 57 bridge bool 58 ns *Namespace 59 } 60 61 // SrcName returns the name of the interface in the origin network namespace. 62 func (i *Interface) SrcName() string { 63 return i.srcName 64 } 65 66 // DstName returns the name that will be assigned to the interface once 67 // moved inside a network namespace. When the caller passes in a DstName, 68 // it is only expected to pass a prefix. The name will be modified with an 69 // auto-generated suffix. 70 func (i *Interface) DstName() string { 71 return i.dstName 72 } 73 74 func (i *Interface) DstMaster() string { 75 return i.dstMaster 76 } 77 78 // Bridge returns true if the interface is a bridge. 79 func (i *Interface) Bridge() bool { 80 return i.bridge 81 } 82 83 func (i *Interface) MacAddress() net.HardwareAddr { 84 return types.GetMacCopy(i.mac) 85 } 86 87 // Address returns the IPv4 address for the interface. 88 func (i *Interface) Address() *net.IPNet { 89 return types.GetIPNetCopy(i.address) 90 } 91 92 // AddressIPv6 returns the IPv6 address for the interface. 93 func (i *Interface) AddressIPv6() *net.IPNet { 94 return types.GetIPNetCopy(i.addressIPv6) 95 } 96 97 // LinkLocalAddresses returns the link-local IP addresses assigned to the 98 // interface. 99 func (i *Interface) LinkLocalAddresses() []*net.IPNet { 100 return i.llAddrs 101 } 102 103 // Routes returns IP routes for the interface. 104 func (i *Interface) Routes() []*net.IPNet { 105 routes := make([]*net.IPNet, len(i.routes)) 106 for index, route := range i.routes { 107 routes[index] = types.GetIPNetCopy(route) 108 } 109 110 return routes 111 } 112 113 // Remove an interface from the sandbox by renaming to original name 114 // and moving it out of the sandbox. 115 func (i *Interface) Remove() error { 116 nameSpace := i.ns 117 return nameSpace.RemoveInterface(i) 118 } 119 120 // Statistics returns the sandbox's side veth interface statistics. 121 func (i *Interface) Statistics() (*types.InterfaceStatistics, error) { 122 l, err := i.ns.nlHandle.LinkByName(i.DstName()) 123 if err != nil { 124 return nil, fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName(), i.ns.path, err) 125 } 126 127 stats := l.Attrs().Statistics 128 if stats == nil { 129 return nil, fmt.Errorf("no statistics were returned") 130 } 131 132 return &types.InterfaceStatistics{ 133 RxBytes: stats.RxBytes, 134 TxBytes: stats.TxBytes, 135 RxPackets: stats.RxPackets, 136 TxPackets: stats.TxPackets, 137 RxDropped: stats.RxDropped, 138 TxDropped: stats.TxDropped, 139 }, nil 140 } 141 142 func (n *Namespace) findDst(srcName string, isBridge bool) string { 143 n.mu.Lock() 144 defer n.mu.Unlock() 145 146 for _, i := range n.iFaces { 147 // The master should match the srcname of the interface and the 148 // master interface should be of type bridge, if searching for a bridge type 149 if i.SrcName() == srcName && (!isBridge || i.Bridge()) { 150 return i.DstName() 151 } 152 } 153 154 return "" 155 } 156 157 // AddInterface adds an existing Interface to the sandbox. The operation will rename 158 // from the Interface SrcName to DstName as it moves, and reconfigure the 159 // interface according to the specified settings. The caller is expected 160 // to only provide a prefix for DstName. The AddInterface api will auto-generate 161 // an appropriate suffix for the DstName to disambiguate. 162 func (n *Namespace) AddInterface(srcName, dstPrefix string, options ...IfaceOption) error { 163 i, err := newInterface(n, srcName, dstPrefix, options...) 164 if err != nil { 165 return err 166 } 167 168 n.mu.Lock() 169 if n.isDefault { 170 i.dstName = i.srcName 171 } else { 172 i.dstName = fmt.Sprintf("%s%d", dstPrefix, n.nextIfIndex[dstPrefix]) 173 n.nextIfIndex[dstPrefix]++ 174 } 175 176 path := n.path 177 isDefault := n.isDefault 178 nlh := n.nlHandle 179 nlhHost := ns.NlHandle() 180 n.mu.Unlock() 181 182 // If it is a bridge interface we have to create the bridge inside 183 // the namespace so don't try to lookup the interface using srcName 184 if i.bridge { 185 if err := nlh.LinkAdd(&netlink.Bridge{ 186 LinkAttrs: netlink.LinkAttrs{ 187 Name: i.srcName, 188 }, 189 }); err != nil { 190 return fmt.Errorf("failed to create bridge %q: %v", i.srcName, err) 191 } 192 } else { 193 // Find the network interface identified by the SrcName attribute. 194 iface, err := nlhHost.LinkByName(i.srcName) 195 if err != nil { 196 return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err) 197 } 198 199 // Move the network interface to the destination 200 // namespace only if the namespace is not a default 201 // type 202 if !isDefault { 203 newNs, err := netns.GetFromPath(path) 204 if err != nil { 205 return fmt.Errorf("failed get network namespace %q: %v", path, err) 206 } 207 defer newNs.Close() 208 if err := nlhHost.LinkSetNsFd(iface, int(newNs)); err != nil { 209 return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err) 210 } 211 } 212 } 213 214 // Find the network interface identified by the SrcName attribute. 215 iface, err := nlh.LinkByName(i.srcName) 216 if err != nil { 217 return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err) 218 } 219 220 // Down the interface before configuring 221 if err := nlh.LinkSetDown(iface); err != nil { 222 return fmt.Errorf("failed to set link down: %v", err) 223 } 224 225 // Configure the interface now this is moved in the proper namespace. 226 if err := configureInterface(nlh, iface, i); err != nil { 227 // If configuring the device fails move it back to the host namespace 228 // and change the name back to the source name. This allows the caller 229 // to properly cleanup the interface. Its important especially for 230 // interfaces with global attributes, ex: vni id for vxlan interfaces. 231 if nerr := nlh.LinkSetName(iface, i.SrcName()); nerr != nil { 232 log.G(context.TODO()).Errorf("renaming interface (%s->%s) failed, %v after config error %v", i.DstName(), i.SrcName(), nerr, err) 233 } 234 if nerr := nlh.LinkSetNsFd(iface, ns.ParseHandlerInt()); nerr != nil { 235 log.G(context.TODO()).Errorf("moving interface %s to host ns failed, %v, after config error %v", i.SrcName(), nerr, err) 236 } 237 return err 238 } 239 240 // Up the interface. 241 cnt := 0 242 for err = nlh.LinkSetUp(iface); err != nil && cnt < 3; cnt++ { 243 log.G(context.TODO()).Debugf("retrying link setup because of: %v", err) 244 time.Sleep(10 * time.Millisecond) 245 err = nlh.LinkSetUp(iface) 246 } 247 if err != nil { 248 return fmt.Errorf("failed to set link up: %v", err) 249 } 250 251 // Set the routes on the interface. This can only be done when the interface is up. 252 if err := setInterfaceRoutes(nlh, iface, i); err != nil { 253 return fmt.Errorf("error setting interface %q routes to %q: %v", iface.Attrs().Name, i.Routes(), err) 254 } 255 256 n.mu.Lock() 257 n.iFaces = append(n.iFaces, i) 258 n.mu.Unlock() 259 260 return nil 261 } 262 263 // RemoveInterface removes an interface from the namespace by renaming to 264 // original name and moving it out of the sandbox. 265 func (n *Namespace) RemoveInterface(i *Interface) error { 266 n.mu.Lock() 267 isDefault := n.isDefault 268 nlh := n.nlHandle 269 n.mu.Unlock() 270 271 // Find the network interface identified by the DstName attribute. 272 iface, err := nlh.LinkByName(i.DstName()) 273 if err != nil { 274 return err 275 } 276 277 // Down the interface before configuring 278 if err := nlh.LinkSetDown(iface); err != nil { 279 return err 280 } 281 282 // TODO(aker): Why are we doing this? This would fail if the initial interface set up failed before the "dest interface" was moved into its own namespace; see https://github.com/moby/moby/pull/46315/commits/108595c2fe852a5264b78e96f9e63cda284990a6#r1331253578 283 err = nlh.LinkSetName(iface, i.SrcName()) 284 if err != nil { 285 log.G(context.TODO()).Debugf("LinkSetName failed for interface %s: %v", i.SrcName(), err) 286 return err 287 } 288 289 // if it is a bridge just delete it. 290 if i.Bridge() { 291 if err := nlh.LinkDel(iface); err != nil { 292 return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err) 293 } 294 } else if !isDefault { 295 // Move the network interface to caller namespace. 296 // TODO(aker): What's this really doing? There are no calls to LinkDel in this package: is this code really used? (Interface.Remove() has 3 callers); see https://github.com/moby/moby/pull/46315/commits/108595c2fe852a5264b78e96f9e63cda284990a6#r1331265335 297 if err := nlh.LinkSetNsFd(iface, ns.ParseHandlerInt()); err != nil { 298 log.G(context.TODO()).Debugf("LinkSetNsFd failed for interface %s: %v", i.SrcName(), err) 299 return err 300 } 301 } 302 303 n.mu.Lock() 304 for index, intf := range i.ns.iFaces { 305 if intf == i { 306 i.ns.iFaces = append(i.ns.iFaces[:index], i.ns.iFaces[index+1:]...) 307 break 308 } 309 } 310 n.mu.Unlock() 311 312 return nil 313 } 314 315 func configureInterface(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 316 ifaceName := iface.Attrs().Name 317 ifaceConfigurators := []struct { 318 Fn func(*netlink.Handle, netlink.Link, *Interface) error 319 ErrMessage string 320 }{ 321 {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())}, 322 {setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC to %q", ifaceName, i.MacAddress())}, 323 {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %v", ifaceName, i.Address())}, 324 {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %v", ifaceName, i.AddressIPv6())}, 325 {setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())}, 326 {setInterfaceLinkLocalIPs, fmt.Sprintf("error setting interface %q link local IPs to %v", ifaceName, i.LinkLocalAddresses())}, 327 } 328 329 for _, config := range ifaceConfigurators { 330 if err := config.Fn(nlh, iface, i); err != nil { 331 return fmt.Errorf("%s: %v", config.ErrMessage, err) 332 } 333 } 334 return nil 335 } 336 337 func setInterfaceMaster(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 338 if i.DstMaster() == "" { 339 return nil 340 } 341 342 return nlh.LinkSetMaster(iface, &netlink.Bridge{ 343 LinkAttrs: netlink.LinkAttrs{Name: i.DstMaster()}, 344 }) 345 } 346 347 func setInterfaceMAC(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 348 if i.MacAddress() == nil { 349 return nil 350 } 351 return nlh.LinkSetHardwareAddr(iface, i.MacAddress()) 352 } 353 354 func setInterfaceIP(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 355 if i.Address() == nil { 356 return nil 357 } 358 if err := checkRouteConflict(nlh, i.Address(), netlink.FAMILY_V4); err != nil { 359 return err 360 } 361 ipAddr := &netlink.Addr{IPNet: i.Address(), Label: ""} 362 return nlh.AddrAdd(iface, ipAddr) 363 } 364 365 func setInterfaceIPv6(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 366 addr := i.AddressIPv6() 367 // IPv6 must be enabled on the interface if and only if the network is 368 // IPv6-enabled. For an interface on an IPv4-only network, if IPv6 isn't 369 // disabled, the interface will be put into IPv6 multicast groups making 370 // it unexpectedly susceptible to NDP cache poisoning, route injection, etc. 371 // (At present, there will always be a pre-configured IPv6 address if the 372 // network is IPv6-enabled.) 373 if err := setIPv6(i.ns.path, i.DstName(), addr != nil); err != nil { 374 return fmt.Errorf("failed to configure ipv6: %v", err) 375 } 376 if addr == nil { 377 return nil 378 } 379 if err := checkRouteConflict(nlh, addr, netlink.FAMILY_V6); err != nil { 380 return err 381 } 382 nlAddr := &netlink.Addr{IPNet: addr, Label: "", Flags: syscall.IFA_F_NODAD} 383 return nlh.AddrAdd(iface, nlAddr) 384 } 385 386 func setInterfaceLinkLocalIPs(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 387 for _, llIP := range i.LinkLocalAddresses() { 388 ipAddr := &netlink.Addr{IPNet: llIP} 389 if err := nlh.AddrAdd(iface, ipAddr); err != nil { 390 return err 391 } 392 } 393 return nil 394 } 395 396 func setInterfaceName(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 397 return nlh.LinkSetName(iface, i.DstName()) 398 } 399 400 func setInterfaceRoutes(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { 401 for _, route := range i.Routes() { 402 err := nlh.RouteAdd(&netlink.Route{ 403 Scope: netlink.SCOPE_LINK, 404 LinkIndex: iface.Attrs().Index, 405 Dst: route, 406 }) 407 if err != nil { 408 return err 409 } 410 } 411 return nil 412 } 413 414 func checkRouteConflict(nlh *netlink.Handle, address *net.IPNet, family int) error { 415 routes, err := nlh.RouteList(nil, family) 416 if err != nil { 417 return err 418 } 419 for _, route := range routes { 420 if route.Dst != nil { 421 if route.Dst.Contains(address.IP) || address.Contains(route.Dst.IP) { 422 return fmt.Errorf("cannot program address %v in sandbox interface because it conflicts with existing route %s", 423 address, route) 424 } 425 } 426 } 427 return nil 428 }