166 lines
4.7 KiB
Go
166 lines
4.7 KiB
Go
//go:build integration
|
|
|
|
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)
|
|
})
|
|
}
|