Allow overlapping change in bridge's IPv6 network.
**- What I did**

The original ticket reports that the daemon won't start if the `fixed-cidr-v6` address is changed from `fe80::/64` to, for example, `fe80::/80` ... those are link local addresses, which turn out to be a special case. But, the same applies to any IPv6 subnet - if the daemon is started with IPv6 enabled on the default bridge, then the `fixed-cidr-v6` is changed to an overlapping subnet, the daemon fails to start. (For example, `2001:db8::/64` to `2001:db8::/80`.)

This change makes it possible to change the configured address for the default bridge to an overlapping subnet (without manually deleting the `docker0` bridge).

Link-local addresses were a special case because they were not released when a container stopped. So, for the default bridge where IP addresses are based on MAC addresses - and MAC addresses were recycled - with `fixed-cidr-v6=fe80::/64`, once a container was stopped new containers could not be started.  For non-default bridge networks with a link-local subnet, addresses leaked when containers stopped.

Linux always uses the `fe80::/64` network, the 'veth' devices get addresses in that subnet. So, overlapping LL subnets are not allowed by this change, other LL subnets are ok.

**- How I did it**

The code was doing this:
```
setupBridgeIPv6():
   - programIPv6Address(<default link local address>)
   - programIPv6Address(<configured address>)
if !config.InhibitIPv4 then setupVerifyAndReconcile():
  - some IPv4 stuff
  - remove global-unicast IPv6 addresses, apart from the configured on
```

So, it was trying to add the new configured address before deleting the old one. Nowhere in the code has a complete picture of the IPv6 addresses the bridge needs, so it's not able to reconcile existing/required config properly.

This change calculates the IPv6 addresses needed on the bridge, then reconciles them with the addresses on an existing bridge by deleting then adding addresses as required - all in `setupBridgeIPv6()`.

If the configured subnet is link-local, it replaces the default link local address/network.

Link local addresses are returned to their address pool when containers are stopped.

Also...

The only remaining calls of `bridgeInterface.addresses()` want either IPv4 or IPv6, but not both. So, made the address family a param.

Because the IPv6 addresses are now calculated and applied in one go, there's no need for `setupVerifyAndReconcile()` to check the set of IPv6 addresses on the bridge. And, that code was guarded by `!config.InhibitIPv4`, which can't have been right. So, removed its IPv6 parts, and added IPv4 to the function's name.

Fixes #46829

**- How to verify it**

New integration test, and updated unit tests.

Overlapping change to `--fixed-cidr-v6`:
```
# dockerd  --ipv6 --experimental --ip6tables --fixed-cidr-v6=fc00:1234::/64
...
# ip a
[...]
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:b8:f5:44:23 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fc00:1234::1/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever

# dockerd  --ipv6 --experimental --ip6tables --fixed-cidr-v6=fc00:1234::/80
...
# ip a
[...]
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:b8:f5:44:23 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fc00:1234::1/80 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever
```

Explicit errors if `--fixed-cidr-v6` is link local and overlaps with (but isn't equal to) `fe80::/64`:

```
# dockerd --ipv6 --experimental --ip6tables --fixed-cidr-v6=fe80::/65 --validate
unable to configure the Docker daemon with file /etc/docker/daemon.json: merged configuration validation from file and command line flags failed: invalid fixed-cidr-v6: clash with the Link-Local prefix 'fe80::/64'

# docker network create --subnet=fe80::/65 testnet
Error response from daemon: clash with the Link-Local prefix 'fe80::/64'
```

**- Description for the changelog**

Allow overlapping change in bridge's IPv6 subnet, support link-local subnets.
libnetwork/drivers/bridge/interface_linux.go
package bridge

import (
	"context"
	"fmt"
	"net"

	"github.com/containerd/log"
	"github.com/vishvananda/netlink"
)

const (
	// DefaultBridgeName is the default name for the bridge interface managed
	// by the driver when unspecified by the caller.
	DefaultBridgeName = "docker0"
)

// Interface models the bridge network device.
type bridgeInterface struct {
	Link        netlink.Link
	bridgeIPv4  *net.IPNet
	bridgeIPv6  *net.IPNet
	gatewayIPv4 net.IP
	gatewayIPv6 net.IP
	nlh         *netlink.Handle
}

// newInterface creates a new bridge interface structure. It attempts to find
// an already existing device identified by the configuration BridgeName field,
// or the default bridge name when unspecified, but doesn't attempt to create
// one when missing
func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) {
	var err error
	i := &bridgeInterface{nlh: nlh}

	// Initialize the bridge name to the default if unspecified.
	if config.BridgeName == "" {
		config.BridgeName = DefaultBridgeName
	}

	// Attempt to find an existing bridge named with the specified name.
	i.Link, err = nlh.LinkByName(config.BridgeName)
	if err != nil {
		log.G(context.TODO()).Debugf("Did not find any interface with name %s: %v", config.BridgeName, err)
	} else if _, ok := i.Link.(*netlink.Bridge); !ok {
		return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name)
	}
	return i, nil
}

// exists indicates if the existing bridge interface exists on the system.
func (i *bridgeInterface) exists() bool {
	return i.Link != nil
}

// addresses returns all IPv4 addresses and all IPv6 addresses for the bridge interface.
func (i *bridgeInterface) addresses() ([]netlink.Addr, []netlink.Addr, error) {
	if !i.exists() {
		// A nonexistent interface, by definition, cannot have any addresses.
		return nil, nil, nil
	}
	v4addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V4)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to retrieve V4 addresses: %v", err)
	}

	v6addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V6)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to retrieve V6 addresses: %v", err)
	}

	if len(v4addr) == 0 {
		return nil, v6addr, nil
	}
	return v4addr, v6addr, nil
}

func (i *bridgeInterface) programIPv6Address() error {
	_, nlAddressList, err := i.addresses()
	if err != nil {
		return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)}
	}
	nlAddr := netlink.Addr{IPNet: i.bridgeIPv6}
	if findIPv6Address(nlAddr, nlAddressList) {
		return nil
	}
	if err := i.nlh.AddrAdd(i.Link, &nlAddr); err != nil {
		return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
	}
	return nil
}

package bridge

import (
	"context"
	"fmt"
	"net"
	"net/netip"

	"github.com/containerd/log"
	"github.com/docker/docker/errdefs"
	"github.com/docker/docker/libnetwork/internal/netiputil"
	"github.com/vishvananda/netlink"
)

const (
	// DefaultBridgeName is the default name for the bridge interface managed
	// by the driver when unspecified by the caller.
	DefaultBridgeName = "docker0"
)

// Interface models the bridge network device.
type bridgeInterface struct {
	Link        netlink.Link
	bridgeIPv4  *net.IPNet
	bridgeIPv6  *net.IPNet
	gatewayIPv4 net.IP
	gatewayIPv6 net.IP
	nlh         *netlink.Handle
}

// newInterface creates a new bridge interface structure. It attempts to find
// an already existing device identified by the configuration BridgeName field,
// or the default bridge name when unspecified, but doesn't attempt to create
// one when missing
func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) {
	var err error
	i := &bridgeInterface{nlh: nlh}

	// Initialize the bridge name to the default if unspecified.
	if config.BridgeName == "" {
		config.BridgeName = DefaultBridgeName
	}

	// Attempt to find an existing bridge named with the specified name.
	i.Link, err = nlh.LinkByName(config.BridgeName)
	if err != nil {
		log.G(context.TODO()).Debugf("Did not find any interface with name %s: %v", config.BridgeName, err)
	} else if _, ok := i.Link.(*netlink.Bridge); !ok {
		return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name)
	}
	return i, nil
}

// exists indicates if the existing bridge interface exists on the system.
func (i *bridgeInterface) exists() bool {
	return i.Link != nil
}

// addresses returns a bridge's addresses, IPv4 (with family=netlink.FAMILY_V4)
// or IPv6 (family=netlink.FAMILY_V6).
func (i *bridgeInterface) addresses(family int) ([]netlink.Addr, error) {
	if !i.exists() {
		// A nonexistent interface, by definition, cannot have any addresses.
		return nil, nil
	}
	addrs, err := i.nlh.AddrList(i.Link, family)
	if err != nil {
		return nil, fmt.Errorf("Failed to retrieve addresses: %v", err)
	}
	return addrs, nil
}

func getRequiredIPv6Addrs(config *networkConfiguration) (requiredAddrs map[netip.Addr]netip.Prefix, addr *net.IPNet, gateway net.IP, err error) {
	requiredAddrs = make(map[netip.Addr]netip.Prefix)

	// TODO(robmry) - is config.AddressIPv6 always set at this point?
	//   The logic here is preserved from the original setupBridgeIPv6(), but
	//   can probably be simplified.

	// Always give the bridge 'fe80::1' - every interface is required to have an
	// address in 'fe80::/64'. Linux may assign an address, but we'll replace it with
	// 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool
	// assigned address will not be a second address in the LL subnet.
	addr = bridgeIPv6
	gateway = bridgeIPv6.IP
	ra, ok := netiputil.ToPrefix(bridgeIPv6)
	if !ok {
		err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix")
		return nil, nil, net.IP{}, err
	}
	requiredAddrs[ra.Addr()] = ra

	// Set up the user-specified bridge address and gateway.
	if config.AddressIPv6 != nil {
		addr = config.AddressIPv6
		gateway = config.AddressIPv6.IP
		ra, ok := netiputil.ToPrefix(config.AddressIPv6)
		if !ok {
			err = fmt.Errorf("failed to convert bridge IPv6 address '%s' to netip.Prefix", config.AddressIPv6.String())
			return nil, nil, net.IP{}, err
		}
		requiredAddrs[ra.Addr()] = ra
	}

	return requiredAddrs, addr, gateway, nil
}

func (i *bridgeInterface) programIPv6Addresses(config *networkConfiguration) error {
	// Get the IPv6 addresses currently assigned to the bridge, if any.
	existingAddrs, err := i.addresses(netlink.FAMILY_V6)
	if err != nil {
		return errdefs.System(err)
	}

	// Get the list of required IPv6 addresses for this bridge.
	var requiredAddrs map[netip.Addr]netip.Prefix
	requiredAddrs, i.bridgeIPv6, i.gatewayIPv6, err = getRequiredIPv6Addrs(config)
	if err != nil {
		return errdefs.System(err)
	}

	// Remove addresses that aren't required.
	for _, existingAddr := range existingAddrs {
		ea, ok := netip.AddrFromSlice(existingAddr.IP)
		if !ok {
			return errdefs.System(fmt.Errorf("Failed to convert IPv6 address '%s' to netip.Addr", config.AddressIPv6))
		}
		// Ignore the prefix length when comparing addresses, it's informational
		// (RFC-5942 section 4), and removing/re-adding an address that's still valid
		// would disrupt traffic on live-restore.
		if _, required := requiredAddrs[ea]; !required {
			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.
			if err != nil {
				log.G(context.TODO()).WithFields(log.Fields{"error": err, "address": existingAddr.IPNet}).Warnf("Failed to remove residual IPv6 address from bridge")
			}
		}
	}
	// Add or update required addresses.
	for _, addrPrefix := range requiredAddrs {
		// Using AddrReplace(), rather than AddrAdd(). When the subnet is changed for an
		// existing bridge in a way that doesn't affect the bridge's assigned address,
		// the old address has not been removed at this point - because that would be
		// service-affecting for a running container.
		//
		// But if, for example, 'fixed-cidr-v6' is changed from '2000:dbe::/64' to
		// '2000:dbe::/80', the default bridge will still be assigned address
		// '2000:dbe::1'. In the output of 'ip a', the prefix length is displayed - and
		// the user is likely to expect to see it updated from '64' to '80'.
		// Unfortunately, 'netlink.AddrReplace()' ('RTM_NEWADDR' with 'NLM_F_REPLACE')
		// doesn't update the prefix length. This is a cosmetic problem, the prefix
		// length of an assigned address is not used to determine whether an address is
		// "on-link" (RFC-5942).
		if err := i.nlh.AddrReplace(i.Link, &netlink.Addr{IPNet: netiputil.ToIPNet(addrPrefix)}); err != nil {
			return errdefs.System(fmt.Errorf("failed to add IPv6 address %s to bridge: %v", i.bridgeIPv6, err))
		}
	}
	return nil
}

- If possible, I'd rather avoid "naked returns", and be explicit what we're returning.  Naked returns can be a bit of a footgun (forgetting to assign to a return var); also avoid using common (`err` in this case) named returns; they can easily be considered "local" and there's a risk of shadowing (or inadvertently overwriting) the return variable because of that.
- As we're updating the error-message in this PR, also considerer fixing its format; errors should not be capitalised or end with punctuation marks (see https://go.dev/wiki/CodeReviewComments#error-strings)
- Finally; in most cases, returned values should not be consumed if an error occurs. Where possible, avoid returning values in those cases to prevent consumers from being tempted to ignore the error and consume the (faulty / invalid / partial) returned values (e.g. consider `return nil, fmt.Errorf("foo")` instead of `return faultyValue, fmt.Errorf("foo")`)
