github.com/moby/docker@v26.1.3+incompatible/libnetwork/drivers/bridge/interface_linux.go (about) 1 package bridge 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "net/netip" 8 "os" 9 10 "github.com/containerd/log" 11 "github.com/docker/docker/errdefs" 12 "github.com/docker/docker/libnetwork/internal/netiputil" 13 "github.com/vishvananda/netlink" 14 ) 15 16 const ( 17 // DefaultBridgeName is the default name for the bridge interface managed 18 // by the driver when unspecified by the caller. 19 DefaultBridgeName = "docker0" 20 ) 21 22 // Interface models the bridge network device. 23 type bridgeInterface struct { 24 Link netlink.Link 25 bridgeIPv4 *net.IPNet 26 bridgeIPv6 *net.IPNet 27 gatewayIPv4 net.IP 28 gatewayIPv6 net.IP 29 nlh *netlink.Handle 30 } 31 32 // newInterface creates a new bridge interface structure. It attempts to find 33 // an already existing device identified by the configuration BridgeName field, 34 // or the default bridge name when unspecified, but doesn't attempt to create 35 // one when missing 36 func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) { 37 var err error 38 i := &bridgeInterface{nlh: nlh} 39 40 // Initialize the bridge name to the default if unspecified. 41 if config.BridgeName == "" { 42 config.BridgeName = DefaultBridgeName 43 } 44 45 // Attempt to find an existing bridge named with the specified name. 46 i.Link, err = nlh.LinkByName(config.BridgeName) 47 if err != nil { 48 log.G(context.TODO()).Debugf("Did not find any interface with name %s: %v", config.BridgeName, err) 49 } else if _, ok := i.Link.(*netlink.Bridge); !ok { 50 return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name) 51 } 52 return i, nil 53 } 54 55 // exists indicates if the existing bridge interface exists on the system. 56 func (i *bridgeInterface) exists() bool { 57 return i.Link != nil 58 } 59 60 // addresses returns a bridge's addresses, IPv4 (with family=netlink.FAMILY_V4) 61 // or IPv6 (family=netlink.FAMILY_V6). 62 func (i *bridgeInterface) addresses(family int) ([]netlink.Addr, error) { 63 if !i.exists() { 64 // A nonexistent interface, by definition, cannot have any addresses. 65 return nil, nil 66 } 67 addrs, err := i.nlh.AddrList(i.Link, family) 68 if err != nil { 69 return nil, fmt.Errorf("Failed to retrieve addresses: %v", err) 70 } 71 return addrs, nil 72 } 73 74 func getRequiredIPv6Addrs(config *networkConfiguration) (requiredAddrs map[netip.Addr]netip.Prefix, err error) { 75 requiredAddrs = make(map[netip.Addr]netip.Prefix) 76 77 if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") != "1" { 78 // Always give the bridge 'fe80::1' - every interface is required to have an 79 // address in 'fe80::/64'. Linux may assign an address, but we'll replace it with 80 // 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool 81 // assigned address will not be a second address in the LL subnet. 82 ra, ok := netiputil.ToPrefix(bridgeIPv6) 83 if !ok { 84 err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix") 85 return nil, err 86 } 87 requiredAddrs[ra.Addr()] = ra 88 } 89 90 ra, ok := netiputil.ToPrefix(config.AddressIPv6) 91 if !ok { 92 err = fmt.Errorf("failed to convert bridge IPv6 address '%s' to netip.Prefix", config.AddressIPv6.String()) 93 return nil, err 94 } 95 requiredAddrs[ra.Addr()] = ra 96 97 return requiredAddrs, nil 98 } 99 100 func (i *bridgeInterface) programIPv6Addresses(config *networkConfiguration) error { 101 // Get the IPv6 addresses currently assigned to the bridge, if any. 102 existingAddrs, err := i.addresses(netlink.FAMILY_V6) 103 if err != nil { 104 return errdefs.System(err) 105 } 106 107 // Get the list of required IPv6 addresses for this bridge. 108 var requiredAddrs map[netip.Addr]netip.Prefix 109 requiredAddrs, err = getRequiredIPv6Addrs(config) 110 if err != nil { 111 return errdefs.System(err) 112 } 113 i.bridgeIPv6 = config.AddressIPv6 114 i.gatewayIPv6 = config.AddressIPv6.IP 115 116 // Remove addresses that aren't required. 117 for _, existingAddr := range existingAddrs { 118 ea, ok := netip.AddrFromSlice(existingAddr.IP) 119 if !ok { 120 return errdefs.System(fmt.Errorf("Failed to convert IPv6 address '%s' to netip.Addr", config.AddressIPv6)) 121 } 122 // Optionally, avoid deleting the kernel-assigned link local address. 123 // (Don't delete fe80::1 either - if it was previously assigned to the bridge, and the 124 // kernel_ll address was deleted, the bridge won't get a new kernel_ll address.) 125 if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") == "1" { 126 if p, _ := ea.Prefix(64); p == linkLocalPrefix { 127 continue 128 } 129 } 130 // Ignore the prefix length when comparing addresses, it's informational 131 // (RFC-5942 section 4), and removing/re-adding an address that's still valid 132 // would disrupt traffic on live-restore. 133 if _, required := requiredAddrs[ea]; !required { 134 err := i.nlh.AddrDel(i.Link, &existingAddr) //#nosec G601 -- Memory aliasing is not an issue in practice as the &existingAddr pointer is not retained by the callee after the AddrDel() call returns. 135 if err != nil { 136 log.G(context.TODO()).WithFields(log.Fields{"error": err, "address": existingAddr.IPNet}).Warnf("Failed to remove residual IPv6 address from bridge") 137 } 138 } 139 } 140 // Add or update required addresses. 141 for _, addrPrefix := range requiredAddrs { 142 // Using AddrReplace(), rather than AddrAdd(). When the subnet is changed for an 143 // existing bridge in a way that doesn't affect the bridge's assigned address, 144 // the old address has not been removed at this point - because that would be 145 // service-affecting for a running container. 146 // 147 // But if, for example, 'fixed-cidr-v6' is changed from '2000:dbe::/64' to 148 // '2000:dbe::/80', the default bridge will still be assigned address 149 // '2000:dbe::1'. In the output of 'ip a', the prefix length is displayed - and 150 // the user is likely to expect to see it updated from '64' to '80'. 151 // Unfortunately, 'netlink.AddrReplace()' ('RTM_NEWADDR' with 'NLM_F_REPLACE') 152 // doesn't update the prefix length. This is a cosmetic problem, the prefix 153 // length of an assigned address is not used to determine whether an address is 154 // "on-link" (RFC-5942). 155 if err := i.nlh.AddrReplace(i.Link, &netlink.Addr{IPNet: netiputil.ToIPNet(addrPrefix)}); err != nil { 156 return errdefs.System(fmt.Errorf("failed to add IPv6 address %s to bridge: %v", i.bridgeIPv6, err)) 157 } 158 } 159 return nil 160 }