netstack_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package netstack
  5. import (
  6. "fmt"
  7. "net/netip"
  8. "runtime"
  9. "sync/atomic"
  10. "testing"
  11. "gvisor.dev/gvisor/pkg/refs"
  12. "tailscale.com/ipn"
  13. "tailscale.com/ipn/ipnlocal"
  14. "tailscale.com/ipn/store/mem"
  15. "tailscale.com/net/packet"
  16. "tailscale.com/net/tsaddr"
  17. "tailscale.com/net/tsdial"
  18. "tailscale.com/net/tstun"
  19. "tailscale.com/tstest"
  20. "tailscale.com/types/ipproto"
  21. "tailscale.com/wgengine"
  22. "tailscale.com/wgengine/filter"
  23. )
  24. // TestInjectInboundLeak tests that injectInbound doesn't leak memory.
  25. // See https://github.com/tailscale/tailscale/issues/3762
  26. func TestInjectInboundLeak(t *testing.T) {
  27. tunDev := tstun.NewFake()
  28. dialer := new(tsdial.Dialer)
  29. logf := func(format string, args ...any) {
  30. if !t.Failed() {
  31. t.Logf(format, args...)
  32. }
  33. }
  34. eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
  35. Tun: tunDev,
  36. Dialer: dialer,
  37. })
  38. if err != nil {
  39. t.Fatal(err)
  40. }
  41. defer eng.Close()
  42. ig, ok := eng.(wgengine.InternalsGetter)
  43. if !ok {
  44. t.Fatal("not an InternalsGetter")
  45. }
  46. tunWrap, magicSock, dns, ok := ig.GetInternals()
  47. if !ok {
  48. t.Fatal("failed to get internals")
  49. }
  50. ns, err := Create(logf, tunWrap, eng, magicSock, dialer, dns)
  51. if err != nil {
  52. t.Fatal(err)
  53. }
  54. defer ns.Close()
  55. ns.ProcessLocalIPs = true
  56. if err := ns.Start(); err != nil {
  57. t.Fatalf("Start: %v", err)
  58. }
  59. ns.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return true })
  60. pkt := &packet.Parsed{}
  61. const N = 10_000
  62. ms0 := getMemStats()
  63. for i := 0; i < N; i++ {
  64. outcome := ns.injectInbound(pkt, tunWrap)
  65. if outcome != filter.DropSilently {
  66. t.Fatalf("got outcome %v; want DropSilently", outcome)
  67. }
  68. }
  69. ms1 := getMemStats()
  70. if grew := int64(ms1.HeapObjects) - int64(ms0.HeapObjects); grew >= N {
  71. t.Fatalf("grew by %v (which is too much and >= the %v packets we sent)", grew, N)
  72. }
  73. }
  74. func getMemStats() (ms runtime.MemStats) {
  75. runtime.GC()
  76. runtime.ReadMemStats(&ms)
  77. return
  78. }
  79. func TestNetstackLeakMode(t *testing.T) {
  80. // See the comments in init(), and/or in issue #4309.
  81. // Influenced by an envknob that may be useful in tests, so just check that
  82. // it's not the oddly behaving zero value.
  83. if refs.GetLeakMode() == 0 {
  84. t.Fatalf("refs.leakMode is 0, want a non-zero value")
  85. }
  86. }
  87. func makeNetstack(t *testing.T, config func(*Impl)) *Impl {
  88. tunDev := tstun.NewFake()
  89. dialer := new(tsdial.Dialer)
  90. logf := func(format string, args ...any) {
  91. if !t.Failed() {
  92. t.Helper()
  93. t.Logf(format, args...)
  94. }
  95. }
  96. eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
  97. Tun: tunDev,
  98. Dialer: dialer,
  99. })
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. t.Cleanup(func() { eng.Close() })
  104. ig, ok := eng.(wgengine.InternalsGetter)
  105. if !ok {
  106. t.Fatal("not an InternalsGetter")
  107. }
  108. tunWrap, magicSock, dns, ok := ig.GetInternals()
  109. if !ok {
  110. t.Fatal("failed to get internals")
  111. }
  112. ns, err := Create(logf, tunWrap, eng, magicSock, dialer, dns)
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. t.Cleanup(func() { ns.Close() })
  117. ns.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return true })
  118. config(ns)
  119. if err := ns.Start(); err != nil {
  120. t.Fatalf("Start: %v", err)
  121. }
  122. return ns
  123. }
  124. func TestShouldHandlePing(t *testing.T) {
  125. srcIP := netip.AddrFrom4([4]byte{1, 2, 3, 4})
  126. t.Run("ICMP4", func(t *testing.T) {
  127. dst := netip.MustParseAddr("5.6.7.8")
  128. icmph := packet.ICMP4Header{
  129. IP4Header: packet.IP4Header{
  130. IPProto: ipproto.ICMPv4,
  131. Src: srcIP,
  132. Dst: dst,
  133. },
  134. Type: packet.ICMP4EchoRequest,
  135. Code: packet.ICMP4NoCode,
  136. }
  137. _, payload := packet.ICMPEchoPayload(nil)
  138. icmpPing := packet.Generate(icmph, payload)
  139. pkt := &packet.Parsed{}
  140. pkt.Decode(icmpPing)
  141. impl := makeNetstack(t, func(impl *Impl) {
  142. impl.ProcessSubnets = true
  143. })
  144. pingDst, ok := impl.shouldHandlePing(pkt)
  145. if !ok {
  146. t.Errorf("expected shouldHandlePing==true")
  147. }
  148. if pingDst != dst {
  149. t.Errorf("got dst %s; want %s", pingDst, dst)
  150. }
  151. })
  152. t.Run("ICMP6-no-via", func(t *testing.T) {
  153. dst := netip.MustParseAddr("2a09:8280:1::4169")
  154. icmph := packet.ICMP6Header{
  155. IP6Header: packet.IP6Header{
  156. IPProto: ipproto.ICMPv6,
  157. Src: srcIP,
  158. Dst: dst,
  159. },
  160. Type: packet.ICMP6EchoRequest,
  161. Code: packet.ICMP6NoCode,
  162. }
  163. _, payload := packet.ICMPEchoPayload(nil)
  164. icmpPing := packet.Generate(icmph, payload)
  165. pkt := &packet.Parsed{}
  166. pkt.Decode(icmpPing)
  167. impl := makeNetstack(t, func(impl *Impl) {
  168. impl.ProcessSubnets = true
  169. })
  170. pingDst, ok := impl.shouldHandlePing(pkt)
  171. // Expect that we handle this since it's going out onto the
  172. // network.
  173. if !ok {
  174. t.Errorf("expected shouldHandlePing==true")
  175. }
  176. if pingDst != dst {
  177. t.Errorf("got dst %s; want %s", pingDst, dst)
  178. }
  179. })
  180. t.Run("ICMP6-tailscale-addr", func(t *testing.T) {
  181. dst := netip.MustParseAddr("fd7a:115c:a1e0:ab12::1")
  182. icmph := packet.ICMP6Header{
  183. IP6Header: packet.IP6Header{
  184. IPProto: ipproto.ICMPv6,
  185. Src: srcIP,
  186. Dst: dst,
  187. },
  188. Type: packet.ICMP6EchoRequest,
  189. Code: packet.ICMP6NoCode,
  190. }
  191. _, payload := packet.ICMPEchoPayload(nil)
  192. icmpPing := packet.Generate(icmph, payload)
  193. pkt := &packet.Parsed{}
  194. pkt.Decode(icmpPing)
  195. impl := makeNetstack(t, func(impl *Impl) {
  196. impl.ProcessSubnets = true
  197. })
  198. _, ok := impl.shouldHandlePing(pkt)
  199. // We don't handle this because it's a Tailscale IP and not 4via6
  200. if ok {
  201. t.Errorf("expected shouldHandlePing==false")
  202. }
  203. })
  204. // Handle pings for 4via6 addresses regardless of ProcessSubnets
  205. for _, subnets := range []bool{true, false} {
  206. t.Run("ICMP6-4via6-ProcessSubnets-"+fmt.Sprint(subnets), func(t *testing.T) {
  207. // The 4via6 route 10.1.1.0/24 siteid 7, and then the IP
  208. // 10.1.1.9 within that route.
  209. dst := netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:7:a01:109")
  210. expectedPingDst := netip.MustParseAddr("10.1.1.9")
  211. icmph := packet.ICMP6Header{
  212. IP6Header: packet.IP6Header{
  213. IPProto: ipproto.ICMPv6,
  214. Src: srcIP,
  215. Dst: dst,
  216. },
  217. Type: packet.ICMP6EchoRequest,
  218. Code: packet.ICMP6NoCode,
  219. }
  220. _, payload := packet.ICMPEchoPayload(nil)
  221. icmpPing := packet.Generate(icmph, payload)
  222. pkt := &packet.Parsed{}
  223. pkt.Decode(icmpPing)
  224. impl := makeNetstack(t, func(impl *Impl) {
  225. impl.ProcessSubnets = subnets
  226. })
  227. pingDst, ok := impl.shouldHandlePing(pkt)
  228. // Handled due to being 4via6
  229. if !ok {
  230. t.Errorf("expected shouldHandlePing==true")
  231. } else if pingDst != expectedPingDst {
  232. t.Errorf("got dst %s; want %s", pingDst, expectedPingDst)
  233. }
  234. })
  235. }
  236. }
  237. // looksLikeATailscaleSelfAddress reports whether addr looks like
  238. // a Tailscale self address, for tests.
  239. func looksLikeATailscaleSelfAddress(addr netip.Addr) bool {
  240. return addr.Is4() && tsaddr.IsTailscaleIP(addr) ||
  241. addr.Is6() && tsaddr.Tailscale4To6Range().Contains(addr)
  242. }
  243. func TestShouldProcessInbound(t *testing.T) {
  244. testCases := []struct {
  245. name string
  246. pkt *packet.Parsed
  247. setup func(*Impl)
  248. want bool
  249. runOnGOOS string
  250. }{
  251. {
  252. name: "ipv6-via",
  253. pkt: &packet.Parsed{
  254. IPVersion: 6,
  255. IPProto: ipproto.TCP,
  256. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  257. // $ tailscale debug via 7 10.1.1.9/24
  258. // fd7a:115c:a1e0:b1a:0:7:a01:109/120
  259. Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
  260. TCPFlags: packet.TCPSyn,
  261. },
  262. setup: func(i *Impl) {
  263. prefs := ipn.NewPrefs()
  264. prefs.AdvertiseRoutes = []netip.Prefix{
  265. // $ tailscale debug via 7 10.1.1.0/24
  266. // fd7a:115c:a1e0:b1a:0:7:a01:100/120
  267. netip.MustParsePrefix("fd7a:115c:a1e0:b1a:0:7:a01:100/120"),
  268. }
  269. i.lb.Start(ipn.Options{
  270. LegacyMigrationPrefs: prefs,
  271. })
  272. i.atomicIsLocalIPFunc.Store(looksLikeATailscaleSelfAddress)
  273. // This should be handled even if we're
  274. // otherwise not processing local IPs or
  275. // subnets.
  276. i.ProcessLocalIPs = false
  277. i.ProcessSubnets = false
  278. },
  279. want: true,
  280. },
  281. {
  282. name: "ipv6-via-not-advertised",
  283. pkt: &packet.Parsed{
  284. IPVersion: 6,
  285. IPProto: ipproto.TCP,
  286. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  287. // $ tailscale debug via 7 10.1.1.9/24
  288. // fd7a:115c:a1e0:b1a:0:7:a01:109/120
  289. Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
  290. TCPFlags: packet.TCPSyn,
  291. },
  292. setup: func(i *Impl) {
  293. prefs := ipn.NewPrefs()
  294. prefs.AdvertiseRoutes = []netip.Prefix{
  295. // tailscale debug via 7 10.1.2.0/24
  296. // fd7a:115c:a1e0:b1a:0:7:a01:200/120
  297. netip.MustParsePrefix("fd7a:115c:a1e0:b1a:0:7:a01:200/120"),
  298. }
  299. i.lb.Start(ipn.Options{
  300. LegacyMigrationPrefs: prefs,
  301. })
  302. },
  303. want: false,
  304. },
  305. {
  306. name: "tailscale-ssh-enabled",
  307. pkt: &packet.Parsed{
  308. IPVersion: 4,
  309. IPProto: ipproto.TCP,
  310. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  311. Dst: netip.MustParseAddrPort("100.101.102.104:22"),
  312. TCPFlags: packet.TCPSyn,
  313. },
  314. setup: func(i *Impl) {
  315. prefs := ipn.NewPrefs()
  316. prefs.RunSSH = true
  317. i.lb.Start(ipn.Options{
  318. LegacyMigrationPrefs: prefs,
  319. })
  320. i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
  321. return addr.String() == "100.101.102.104" // Dst, above
  322. })
  323. },
  324. want: true,
  325. runOnGOOS: "linux",
  326. },
  327. {
  328. name: "tailscale-ssh-disabled",
  329. pkt: &packet.Parsed{
  330. IPVersion: 4,
  331. IPProto: ipproto.TCP,
  332. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  333. Dst: netip.MustParseAddrPort("100.101.102.104:22"),
  334. TCPFlags: packet.TCPSyn,
  335. },
  336. setup: func(i *Impl) {
  337. prefs := ipn.NewPrefs()
  338. prefs.RunSSH = false // default, but to be explicit
  339. i.lb.Start(ipn.Options{
  340. LegacyMigrationPrefs: prefs,
  341. })
  342. i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
  343. return addr.String() == "100.101.102.104" // Dst, above
  344. })
  345. },
  346. want: false,
  347. },
  348. {
  349. name: "process-local-ips",
  350. pkt: &packet.Parsed{
  351. IPVersion: 4,
  352. IPProto: ipproto.TCP,
  353. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  354. Dst: netip.MustParseAddrPort("100.101.102.104:4567"),
  355. TCPFlags: packet.TCPSyn,
  356. },
  357. setup: func(i *Impl) {
  358. i.ProcessLocalIPs = true
  359. i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
  360. return addr.String() == "100.101.102.104" // Dst, above
  361. })
  362. },
  363. want: true,
  364. },
  365. {
  366. name: "process-subnets",
  367. pkt: &packet.Parsed{
  368. IPVersion: 4,
  369. IPProto: ipproto.TCP,
  370. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  371. Dst: netip.MustParseAddrPort("10.1.2.3:4567"),
  372. TCPFlags: packet.TCPSyn,
  373. },
  374. setup: func(i *Impl) {
  375. i.ProcessSubnets = true
  376. // For testing purposes, assume all Tailscale
  377. // IPs are local; the Dst above is something
  378. // not in that range.
  379. i.atomicIsLocalIPFunc.Store(looksLikeATailscaleSelfAddress)
  380. },
  381. want: true,
  382. },
  383. {
  384. name: "peerapi-port-subnet-router", // see #6235
  385. pkt: &packet.Parsed{
  386. IPVersion: 4,
  387. IPProto: ipproto.TCP,
  388. Src: netip.MustParseAddrPort("100.101.102.103:1234"),
  389. Dst: netip.MustParseAddrPort("10.0.0.23:5555"),
  390. TCPFlags: packet.TCPSyn,
  391. },
  392. setup: func(i *Impl) {
  393. prefs := ipn.NewPrefs()
  394. prefs.AdvertiseRoutes = []netip.Prefix{
  395. netip.MustParsePrefix("10.0.0.1/24"),
  396. }
  397. i.lb.Start(ipn.Options{
  398. LegacyMigrationPrefs: prefs,
  399. })
  400. // As if we were running on Linux where netstack isn't used.
  401. i.ProcessSubnets = false
  402. i.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return false })
  403. // Set the PeerAPI port to the Dst port above.
  404. atomic.StoreUint32(&i.peerapiPort4Atomic, 5555)
  405. atomic.StoreUint32(&i.peerapiPort6Atomic, 5555)
  406. },
  407. want: false,
  408. },
  409. // TODO(andrew): test PeerAPI
  410. // TODO(andrew): test TCP packets without the SYN flag set
  411. }
  412. for _, tc := range testCases {
  413. t.Run(tc.name, func(t *testing.T) {
  414. if tc.runOnGOOS != "" && runtime.GOOS != tc.runOnGOOS {
  415. t.Skipf("skipping on GOOS=%v", runtime.GOOS)
  416. }
  417. impl := makeNetstack(t, func(i *Impl) {
  418. defer t.Logf("netstack setup finished")
  419. logf := tstest.WhileTestRunningLogger(t)
  420. e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
  421. if err != nil {
  422. t.Fatalf("NewFakeUserspaceEngine: %v", err)
  423. }
  424. t.Cleanup(e.Close)
  425. lb, err := ipnlocal.NewLocalBackend(logf, "logid", new(mem.Store), "", new(tsdial.Dialer), e, 0)
  426. if err != nil {
  427. t.Fatalf("NewLocalBackend: %v", err)
  428. }
  429. t.Cleanup(lb.Shutdown)
  430. dir := t.TempDir()
  431. lb.SetVarRoot(dir)
  432. i.SetLocalBackend(lb)
  433. if tc.setup != nil {
  434. tc.setup(i)
  435. }
  436. })
  437. got := impl.shouldProcessInbound(tc.pkt, nil)
  438. if got != tc.want {
  439. t.Errorf("got shouldProcessInbound()=%v; want %v", got, tc.want)
  440. } else {
  441. t.Logf("OK: shouldProcessInbound() = %v", got)
  442. }
  443. })
  444. }
  445. }