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  }