|
|
@@ -0,0 +1,133 @@
|
|
|
+//go:build linux && !android && !e2e_testing
|
|
|
+
|
|
|
+package overlay
|
|
|
+
|
|
|
+import (
|
|
|
+ "net"
|
|
|
+ "net/netip"
|
|
|
+ "os"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/DefinedNet/netlink"
|
|
|
+ "github.com/slackhq/nebula/config"
|
|
|
+ "github.com/slackhq/nebula/test"
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+ "github.com/stretchr/testify/require"
|
|
|
+ "golang.org/x/sys/unix"
|
|
|
+)
|
|
|
+
|
|
|
+// An unsafe_route with the same destination and metric as a lan's route must not replace it. Ours waits behind theirs
|
|
|
+// and takes over when the lan goes away. Run as root, or unprivileged with `unshare -rn`.
|
|
|
+func TestTunRouteLeavesAnotherDevicesRoute(t *testing.T) {
|
|
|
+ if os.Geteuid() != 0 {
|
|
|
+ t.Skip("creating links and adding routes needs root")
|
|
|
+ }
|
|
|
+
|
|
|
+ lan := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: "nebrt0"}, PeerName: "nebrt1"}
|
|
|
+ require.NoError(t, netlink.LinkAdd(lan))
|
|
|
+ t.Cleanup(func() { _ = netlink.LinkDel(lan) })
|
|
|
+ peer, err := netlink.LinkByName("nebrt1")
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NoError(t, netlink.LinkSetUp(lan))
|
|
|
+ require.NoError(t, netlink.LinkSetUp(peer))
|
|
|
+
|
|
|
+ lanAddrs := []*netlink.Addr{
|
|
|
+ {IPNet: &net.IPNet{IP: net.ParseIP("198.51.100.5"), Mask: net.CIDRMask(24, 32)}},
|
|
|
+ {IPNet: &net.IPNet{IP: net.ParseIP("2001:db8:1::5"), Mask: net.CIDRMask(64, 128)}, Flags: unix.IFA_F_NODAD},
|
|
|
+ }
|
|
|
+ for _, a := range lanAddrs {
|
|
|
+ require.NoError(t, netlink.AddrAdd(lan, a))
|
|
|
+ }
|
|
|
+
|
|
|
+ l := test.NewLogger()
|
|
|
+ c := config.NewC(l)
|
|
|
+ // The kernel puts a v4 lan route at metric 0, our default, and a v6 one at 256
|
|
|
+ require.NoError(t, c.LoadString(`
|
|
|
+tun:
|
|
|
+ dev: nebrt2
|
|
|
+ unsafe_routes:
|
|
|
+ - route: 198.51.100.0/24
|
|
|
+ via: 10.250.0.2
|
|
|
+ - route: 2001:db8:1::/64
|
|
|
+ via: fd00:250::2
|
|
|
+ metric: 256
|
|
|
+`))
|
|
|
+ tn, err := newTun(c, l, []netip.Prefix{netip.MustParsePrefix("10.250.0.1/24"), netip.MustParsePrefix("fd00:250::1/64")}, false)
|
|
|
+ require.NoError(t, err)
|
|
|
+ t.Cleanup(func() { _ = tn.Close() })
|
|
|
+ require.NoError(t, tn.Activate())
|
|
|
+
|
|
|
+ via := func(dst string) int {
|
|
|
+ routes, err := netlink.RouteGet(net.ParseIP(dst))
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NotEmpty(t, routes)
|
|
|
+ return routes[0].LinkIndex
|
|
|
+ }
|
|
|
+ devices := func(dst string) []int {
|
|
|
+ _, n, err := net.ParseCIDR(dst)
|
|
|
+ require.NoError(t, err)
|
|
|
+ routes, err := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{Dst: n}, netlink.RT_FILTER_DST)
|
|
|
+ require.NoError(t, err)
|
|
|
+ var out []int
|
|
|
+ for _, r := range routes {
|
|
|
+ out = append(out, r.LinkIndex)
|
|
|
+ }
|
|
|
+ return out
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, dst := range []string{"198.51.100.0/24", "2001:db8:1::/64"} {
|
|
|
+ assert.ElementsMatch(t, []int{lan.Index, tn.deviceIndex}, devices(dst), dst)
|
|
|
+ }
|
|
|
+ assert.Equal(t, lan.Index, via("198.51.100.9"))
|
|
|
+ assert.Equal(t, lan.Index, via("2001:db8:1::9"))
|
|
|
+
|
|
|
+ // Leaving the lan
|
|
|
+ for _, a := range lanAddrs {
|
|
|
+ require.NoError(t, netlink.AddrDel(lan, a))
|
|
|
+ }
|
|
|
+ assert.Equal(t, tn.deviceIndex, via("198.51.100.9"))
|
|
|
+ assert.Equal(t, tn.deviceIndex, via("2001:db8:1::9"))
|
|
|
+
|
|
|
+ // Dropping the unsafe_routes removes only ours
|
|
|
+ for _, a := range lanAddrs {
|
|
|
+ require.NoError(t, netlink.AddrAdd(lan, a))
|
|
|
+ }
|
|
|
+ require.NoError(t, c.ReloadConfigString("tun:\n dev: nebrt2\n"))
|
|
|
+ for _, dst := range []string{"198.51.100.0/24", "2001:db8:1::/64"} {
|
|
|
+ assert.Equal(t, []int{lan.Index}, devices(dst), dst)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// The route for our own vpn network is replaced to set its mtu, which must not take a lan's route for the same network
|
|
|
+func TestTunVpnNetworkLeavesAnotherDevicesRoute(t *testing.T) {
|
|
|
+ if os.Geteuid() != 0 {
|
|
|
+ t.Skip("creating links and adding routes needs root")
|
|
|
+ }
|
|
|
+
|
|
|
+ lan := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: "nebrt0"}, PeerName: "nebrt1"}
|
|
|
+ require.NoError(t, netlink.LinkAdd(lan))
|
|
|
+ t.Cleanup(func() { _ = netlink.LinkDel(lan) })
|
|
|
+ require.NoError(t, netlink.LinkSetUp(lan))
|
|
|
+ require.NoError(t, netlink.AddrAdd(lan, &netlink.Addr{IPNet: &net.IPNet{IP: net.ParseIP("10.250.0.5"), Mask: net.CIDRMask(24, 32)}}))
|
|
|
+
|
|
|
+ l := test.NewLogger()
|
|
|
+ c := config.NewC(l)
|
|
|
+ require.NoError(t, c.LoadString("tun:\n dev: nebrt2\n"))
|
|
|
+ tn, err := newTun(c, l, []netip.Prefix{netip.MustParsePrefix("10.250.0.1/24")}, false)
|
|
|
+ require.NoError(t, err)
|
|
|
+ t.Cleanup(func() { _ = tn.Close() })
|
|
|
+ require.NoError(t, tn.Activate())
|
|
|
+
|
|
|
+ _, n, err := net.ParseCIDR("10.250.0.0/24")
|
|
|
+ require.NoError(t, err)
|
|
|
+ routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, &netlink.Route{Dst: n}, netlink.RT_FILTER_DST)
|
|
|
+ require.NoError(t, err)
|
|
|
+ var devices []int
|
|
|
+ for _, r := range routes {
|
|
|
+ devices = append(devices, r.LinkIndex)
|
|
|
+ if r.LinkIndex == tn.deviceIndex {
|
|
|
+ assert.Equal(t, tn.DefaultMTU, r.MTU, "ours still carries the mtu")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ assert.ElementsMatch(t, []int{lan.Index, tn.deviceIndex}, devices)
|
|
|
+}
|