github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/libnetwork/drivers/bridge/interface_linux.go (about)

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