mirror of
https://github.com/moby/moby.git
synced 2026-07-24 16:26:51 +00:00
If IPv6 is enabled for a bridge network, by the time configuration is applied, the bridge will always have an address. Assert that, by raising an error when the configuration is validated. Use that to simplify the logic used to calculate which addresses should be assigned to a bridge. Also remove a redundant check in setupGatewayIPv6() and the error associated with it. Fix unit tests that enabled IPv6, but didn't supply an IPv6 IPAM address/pool. Before this change, these tests passed but silently left the bridge without an IPv6 address. (The daemon already ensured there was an IPv6 address, this change does not add a new restriction on config at that level.) Signed-off-by: Rob Murray <rob.murray@docker.com>
189 lines
5.3 KiB
Go
189 lines
5.3 KiB
Go
package bridge
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/internal/testutils/netnsutils"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/vishvananda/netlink"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func cidrToIPNet(t *testing.T, cidr string) *net.IPNet {
|
|
t.Helper()
|
|
ip, ipNet, err := net.ParseCIDR(cidr)
|
|
assert.Assert(t, is.Nil(err))
|
|
return &net.IPNet{IP: ip, Mask: ipNet.Mask}
|
|
}
|
|
|
|
func addAddr(t *testing.T, link netlink.Link, addr string) {
|
|
t.Helper()
|
|
ipNet := cidrToIPNet(t, addr)
|
|
err := netlink.AddrAdd(link, &netlink.Addr{IPNet: ipNet})
|
|
assert.Assert(t, is.Nil(err))
|
|
}
|
|
|
|
func prepTestBridge(t *testing.T, nc *networkConfiguration) *bridgeInterface {
|
|
t.Helper()
|
|
nh, err := netlink.NewHandle()
|
|
assert.Assert(t, err)
|
|
i, err := newInterface(nh, nc)
|
|
assert.Assert(t, err)
|
|
err = setupDevice(nc, i)
|
|
assert.Assert(t, err)
|
|
return i
|
|
}
|
|
|
|
func TestInterfaceDefaultName(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
nh, err := netlink.NewHandle()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config := &networkConfiguration{}
|
|
_, err = newInterface(nh, config)
|
|
assert.Check(t, err)
|
|
assert.Equal(t, config.BridgeName, DefaultBridgeName)
|
|
}
|
|
|
|
func TestAddressesNoInterface(t *testing.T) {
|
|
i := bridgeInterface{}
|
|
addrs, err := i.addresses(netlink.FAMILY_V6)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(addrs, 0))
|
|
}
|
|
|
|
func TestAddressesEmptyInterface(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
nh, err := netlink.NewHandle()
|
|
assert.NilError(t, err)
|
|
|
|
inf, err := newInterface(nh, &networkConfiguration{})
|
|
assert.NilError(t, err)
|
|
|
|
addrsv4, err := inf.addresses(netlink.FAMILY_V4)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(addrsv4, 0))
|
|
|
|
addrsv6, err := inf.addresses(netlink.FAMILY_V6)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(addrsv6, 0))
|
|
}
|
|
|
|
func TestAddressesNonEmptyInterface(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
i := prepTestBridge(t, &networkConfiguration{})
|
|
|
|
const expAddrV4, expAddrV6 = "192.168.1.2/24", "fd00:1234::/64"
|
|
addAddr(t, i.Link, expAddrV4)
|
|
addAddr(t, i.Link, expAddrV6)
|
|
|
|
addrs, err := i.addresses(netlink.FAMILY_V4)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(addrs, 1))
|
|
assert.Equal(t, addrs[0].IPNet.String(), expAddrV4)
|
|
|
|
addrs, err = i.addresses(netlink.FAMILY_V6)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(addrs, 1))
|
|
assert.Equal(t, addrs[0].IPNet.String(), expAddrV6)
|
|
}
|
|
|
|
func TestGetRequiredIPv6Addrs(t *testing.T) {
|
|
testcases := []struct {
|
|
name string
|
|
addressIPv6 string
|
|
expReqdAddrs []string
|
|
}{
|
|
{
|
|
name: "Regular address, expect default link local",
|
|
addressIPv6: "2000:3000::1/80",
|
|
expReqdAddrs: []string{"fe80::1/64", "2000:3000::1/80"},
|
|
},
|
|
{
|
|
name: "Standard link local address only",
|
|
addressIPv6: "fe80::1/64",
|
|
expReqdAddrs: []string{"fe80::1/64"},
|
|
},
|
|
{
|
|
name: "Nonstandard link local address",
|
|
addressIPv6: "fe80:abcd::1/42",
|
|
expReqdAddrs: []string{"fe80:abcd::1/42", "fe80::1/64"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
config := &networkConfiguration{
|
|
AddressIPv6: cidrToIPNet(t, tc.addressIPv6),
|
|
}
|
|
|
|
expResult := map[netip.Addr]netip.Prefix{}
|
|
for _, addr := range tc.expReqdAddrs {
|
|
expResult[netip.MustParseAddr(strings.Split(addr, "/")[0])] = netip.MustParsePrefix(addr)
|
|
}
|
|
|
|
reqd, err := getRequiredIPv6Addrs(config)
|
|
assert.Check(t, is.Nil(err))
|
|
assert.Check(t, is.DeepEqual(reqd, expResult,
|
|
cmp.Comparer(func(a, b netip.Prefix) bool { return a == b })))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProgramIPv6Addresses(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
checkAddrs := func(i *bridgeInterface, nc *networkConfiguration, expAddrs []string) {
|
|
t.Helper()
|
|
exp := []netlink.Addr{}
|
|
for _, a := range expAddrs {
|
|
ipNet := cidrToIPNet(t, a)
|
|
exp = append(exp, netlink.Addr{IPNet: ipNet})
|
|
}
|
|
actual, err := i.addresses(netlink.FAMILY_V6)
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, exp, actual)
|
|
assert.Check(t, is.DeepEqual(i.bridgeIPv6, nc.AddressIPv6))
|
|
assert.Check(t, is.DeepEqual(i.gatewayIPv6, nc.AddressIPv6.IP))
|
|
}
|
|
|
|
nc := &networkConfiguration{}
|
|
i := prepTestBridge(t, nc)
|
|
|
|
// The bridge has no addresses, ask for a regular IPv6 network and expect it to
|
|
// be added to the bridge, with the default link local address.
|
|
nc.AddressIPv6 = cidrToIPNet(t, "2000:3000::1/64")
|
|
err := i.programIPv6Addresses(nc)
|
|
assert.NilError(t, err)
|
|
checkAddrs(i, nc, []string{"2000:3000::1/64", "fe80::1/64"})
|
|
|
|
// Shrink the subnet of that regular address, the prefix length of the address
|
|
// will not be modified - but it's informational-only, the address itself has
|
|
// not changed.
|
|
nc.AddressIPv6 = cidrToIPNet(t, "2000:3000::1/80")
|
|
err = i.programIPv6Addresses(nc)
|
|
assert.NilError(t, err)
|
|
checkAddrs(i, nc, []string{"2000:3000::1/64", "fe80::1/64"})
|
|
|
|
// Ask for link-local only, by specifying an address with the Link Local prefix.
|
|
// The regular address should be removed.
|
|
nc.AddressIPv6 = cidrToIPNet(t, "fe80::1/64")
|
|
err = i.programIPv6Addresses(nc)
|
|
assert.NilError(t, err)
|
|
checkAddrs(i, nc, []string{"fe80::1/64"})
|
|
|
|
// Swap the standard link local address for a nonstandard one.
|
|
nc.AddressIPv6 = cidrToIPNet(t, "fe80:5555::1/55")
|
|
err = i.programIPv6Addresses(nc)
|
|
assert.NilError(t, err)
|
|
checkAddrs(i, nc, []string{"fe80:5555::1/55", "fe80::1/64"})
|
|
}
|