tun_darwin_route_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //go:build darwin && !ios && !e2e_testing
  2. package overlay
  3. import (
  4. "net"
  5. "net/netip"
  6. "os"
  7. "os/exec"
  8. "strings"
  9. "testing"
  10. "github.com/slackhq/nebula/config"
  11. "github.com/slackhq/nebula/routing"
  12. "github.com/slackhq/nebula/test"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. netroute "golang.org/x/net/route"
  16. "golang.org/x/sys/unix"
  17. )
  18. // Every mac routes loopback through lo0
  19. func TestRouteInterfaceIn(t *testing.T) {
  20. msgs, err := fetchRoutes()
  21. require.NoError(t, err)
  22. routeInterface := func(s string) int { return routeInterfaceIn(msgs, netip.MustParsePrefix(s)) }
  23. lo0, err := net.InterfaceByName("lo0")
  24. require.NoError(t, err)
  25. assert.Equal(t, lo0.Index, routeInterface("127.0.0.0/8"))
  26. assert.Equal(t, lo0.Index, routeInterface("127.0.0.1/32"), "a host route has no netmask")
  27. assert.Equal(t, lo0.Index, routeInterface("::1/128"))
  28. assert.Equal(t, 0, routeInterface("127.0.0.0/16"), "only an exact prefix counts")
  29. // Most macs also list the default route scoped to their other interfaces
  30. out, err := exec.Command("route", "-n", "get", "default").Output()
  31. require.NoError(t, err)
  32. var name string
  33. for _, line := range strings.Split(string(out), "\n") {
  34. if v, ok := strings.CutPrefix(strings.TrimSpace(line), "interface: "); ok {
  35. name = v
  36. }
  37. }
  38. if name == "" {
  39. t.Skip("no default route")
  40. }
  41. def, err := net.InterfaceByName(name)
  42. require.NoError(t, err)
  43. assert.Equal(t, def.Index, routeInterface("0.0.0.0/0"))
  44. }
  45. func TestRouteInterfaceIn_SkipsScopedCopies(t *testing.T) {
  46. route := func(index, flags int) netroute.Message {
  47. addrs := make([]netroute.Addr, unix.RTAX_MAX)
  48. addrs[unix.RTAX_DST] = &netroute.Inet4Addr{IP: [4]byte{192, 168, 1, 0}}
  49. addrs[unix.RTAX_NETMASK] = &netroute.Inet4Addr{IP: [4]byte{255, 255, 255, 0}}
  50. return &netroute.RouteMessage{Index: index, Flags: flags, Addrs: addrs}
  51. }
  52. p := netip.MustParsePrefix("192.168.1.0/24")
  53. assert.Equal(t, 7, routeInterfaceIn([]netroute.Message{route(4, unix.RTF_UP|unix.RTF_IFSCOPE), route(7, unix.RTF_UP)}, p))
  54. assert.Equal(t, 0, routeInterfaceIn([]netroute.Message{route(4, unix.RTF_UP|unix.RTF_IFSCOPE)}, p))
  55. }
  56. // A reload can happen before Activate
  57. func TestTunRoutesBeforeActivate(t *testing.T) {
  58. tn := &tun{l: test.NewLogger()}
  59. routes := []Route{{
  60. Cidr: netip.MustParsePrefix("10.253.1.0/24"),
  61. Via: routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.250.0.2"), 1)},
  62. Install: true,
  63. }}
  64. tn.Routes.Store(&routes)
  65. assert.NoError(t, tn.addRoutes(true, nil))
  66. assert.NoError(t, tn.removeRoutes(routes, nil))
  67. }
  68. // A tun.routes entry with no via is never installed, so removing it must not delete a route for the same cidr that
  69. // belongs to someone else
  70. func TestTunReloadLeavesRoutesItNeverInstalled(t *testing.T) {
  71. if os.Geteuid() != 0 {
  72. t.Skip("creating a utun and adding routes needs root")
  73. }
  74. lo0, err := net.InterfaceByName("lo0")
  75. require.NoError(t, err)
  76. cidr := "10.250.0.128/25"
  77. require.NoError(t, exec.Command("route", "-q", "-n", "add", "-net", cidr, "-interface", "lo0").Run())
  78. t.Cleanup(func() { _ = exec.Command("route", "-q", "-n", "delete", "-net", cidr).Run() })
  79. l := test.NewLogger()
  80. c := config.NewC(l)
  81. require.NoError(t, c.LoadString("tun:\n routes:\n - route: "+cidr+"\n mtu: 1300\n"))
  82. tn, err := newTun(c, l, []netip.Prefix{netip.MustParsePrefix("10.250.0.1/24")}, false)
  83. require.NoError(t, err)
  84. t.Cleanup(func() { _ = tn.Close() })
  85. require.NoError(t, tn.Activate())
  86. res := c.ReloadConfigString("tun: {}\n")
  87. require.NoError(t, res.Err)
  88. assert.False(t, res.Failed, "removing a route we never installed failed the reload")
  89. msgs, err := fetchRoutes()
  90. require.NoError(t, err)
  91. assert.Equal(t, lo0.Index, routeInterfaceIn(msgs, netip.MustParsePrefix(cidr)))
  92. }
  93. // Only naming a different utun asks for a restart. Empty, plain utun and names that aren't utunN take whichever is free
  94. func TestTunDevNeedsRestart(t *testing.T) {
  95. for name, want := range map[string]bool{
  96. "": false,
  97. "utun": false,
  98. "nope": false,
  99. "utun7": false,
  100. "utun3": true,
  101. } {
  102. assert.Equal(t, want, tunDevNeedsRestart(name, "utun7"), name)
  103. }
  104. }