multinet/dialer_test.go

165 lines
4.6 KiB
Go
Raw Normal View History

package multinet
import (
"net"
"net/netip"
"runtime"
"testing"
"github.com/stretchr/testify/require"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
func TestDialer(t *testing.T) {
runInNewNamespace(t, "2 interfaces with multiple routes in different subnets", func(t *testing.T, ns netns.NsHandle) {
setup(t, map[string][]string{
"testdev1": {"1.2.30.10/23", "4.4.4.4/8"},
"testdev2": {"1.2.30.11/23", "4.4.4.5/8"},
})
// Do not use `t.Run` because everything should be executed in a single OS thread.
{ // Restrict to a single subnet.
d, err := NewDialer(Config{
Subnets: []string{"1.2.30.0/23"},
})
require.NoError(t, err)
require.Equal(t, []Subnet{
{
Mask: netip.MustParsePrefix("1.2.30.0/23"),
Interfaces: []Source{
{Name: "testdev1", LocalAddr: &net.TCPAddr{IP: net.IP{1, 2, 30, 10}}},
{Name: "testdev2", LocalAddr: &net.TCPAddr{IP: net.IP{1, 2, 30, 11}}},
},
},
}, d.(*dialer).subnets)
}
{ // Restrict to two subnets.
d, err := NewDialer(Config{
Subnets: []string{"1.2.30.0/23", "4.0.0.0/8"},
})
require.NoError(t, err)
require.Equal(t, []Subnet{
{
Mask: netip.MustParsePrefix("1.2.30.0/23"),
Interfaces: []Source{
{Name: "testdev1", LocalAddr: &net.TCPAddr{IP: net.IP{1, 2, 30, 10}}},
{Name: "testdev2", LocalAddr: &net.TCPAddr{IP: net.IP{1, 2, 30, 11}}},
},
},
{
Mask: netip.MustParsePrefix("4.0.0.0/8"),
Interfaces: []Source{
{Name: "testdev1", LocalAddr: &net.TCPAddr{IP: net.IP{4, 4, 4, 4}}},
{Name: "testdev2", LocalAddr: &net.TCPAddr{IP: net.IP{4, 4, 4, 5}}},
},
},
}, d.(*dialer).subnets)
}
})
runInNewNamespace(t, "4 interfaces, 2 for data, 2 internal", func(t *testing.T, ns netns.NsHandle) {
setup(t, map[string][]string{
"internal1": {"192.168.0.1/16"},
"internal2": {"192.168.0.2/16"},
"data1": {"10.11.12.101/24"},
"data2": {"10.11.12.102/24"},
})
d, err := NewDialer(Config{
Subnets: []string{"10.11.12.0/24", "192.168.0.0/16"},
})
require.NoError(t, err)
require.Equal(t, []Subnet{
{
Mask: netip.MustParsePrefix("10.11.12.0/24"),
Interfaces: []Source{
{Name: "data1", LocalAddr: &net.TCPAddr{IP: net.IP{10, 11, 12, 101}}},
{Name: "data2", LocalAddr: &net.TCPAddr{IP: net.IP{10, 11, 12, 102}}},
},
},
{
Mask: netip.MustParsePrefix("192.168.0.0/16"),
Interfaces: []Source{
{Name: "internal1", LocalAddr: &net.TCPAddr{IP: net.IP{192, 168, 0, 1}}},
{Name: "internal2", LocalAddr: &net.TCPAddr{IP: net.IP{192, 168, 0, 2}}},
},
},
}, d.(*dialer).subnets)
})
runInNewNamespace(t, "with ipv6", func(t *testing.T, ns netns.NsHandle) {
addr1 := "2001:db8:85a3:8d3:1319:8a2e:370:7348/64"
addr2 := "2001:db8:85a3:8d3:1319:8a2e:370:8192/64"
setup(t, map[string][]string{
"testdev1": {addr1},
"testdev2": {addr2},
})
// Do not use `t.Run` because everything should be executed in a single OS thread.
{ // Restrict to a single subnet.
d, err := NewDialer(Config{
Subnets: []string{"2001:db8:85a3:8d3::/64"},
})
require.NoError(t, err)
require.Equal(t, []Subnet{
{
Mask: netip.MustParsePrefix("2001:db8:85a3:8d3::/64"),
Interfaces: []Source{
{Name: "testdev1", LocalAddr: mustParseIPv6(t, addr1)},
{Name: "testdev2", LocalAddr: mustParseIPv6(t, addr2)},
},
},
}, d.(*dialer).subnets)
}
})
}
func mustParseIPv6(t *testing.T, s string) *net.TCPAddr {
ip, _, err := net.ParseCIDR(s)
require.NoError(t, err)
return &net.TCPAddr{IP: ip}
}
func setup(t *testing.T, config map[string][]string) {
for name, ips := range config {
link := createLink(t, name)
for i := range ips {
ip, err := netlink.ParseIPNet(ips[i])
require.NoError(t, err)
require.NoError(t, netlink.AddrAdd(link, &netlink.Addr{IPNet: ip}))
}
}
}
func createLink(t *testing.T, name string) netlink.Link {
require.NoError(t, netlink.LinkAdd(&netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: name}}))
link, err := netlink.LinkByName(name)
require.NoError(t, err)
require.NoError(t, netlink.LinkSetUp(link))
return link
}
func runInNewNamespace(t *testing.T, name string, f func(t *testing.T, ns netns.NsHandle)) {
t.Run(name, func(t *testing.T) {
// To avoid messing with host network settings,
// we create a new names space and execute tests in it.
// Switching thread can move us to a different namespace, thus this line.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
require.NoError(t, err)
defer origns.Close()
defer netns.Set(origns)
newns, err := netns.New()
require.NoError(t, err)
defer newns.Close()
f(t, newns)
})
}