netstack_test.go 12 KB

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