123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- 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, addr, gw, err := getRequiredIPv6Addrs(config)
- assert.Check(t, is.Nil(err))
- assert.Check(t, is.DeepEqual(addr, config.AddressIPv6))
- assert.Check(t, is.DeepEqual(gw, config.AddressIPv6.IP))
- 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, 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)
- }
- 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, []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, []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, []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, []string{"fe80:5555::1/55", "fe80::1/64"})
- }
|