Skip to content

Commit a45c9f9

Browse files
Claire Wangbradfitz
andcommitted
wgengine/netstack: change netstack API to require LocalBackend
The macOS client was forgetting to call netstack.Impl.SetLocalBackend. Change the API so that it can't be started without one, eliminating this class of bug. Then update all the callers. Updates tailscale#6764 Change-Id: I2b3a4f31fdfd9fdbbbbfe25a42db0c505373562f Signed-off-by: Claire Wang <claire@tailscale.com> Co-authored-by: Brad Fitzpatrick <bradfitz@tailscale.com> Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
1 parent 84eaef0 commit a45c9f9

File tree

5 files changed

+54
-63
lines changed

5 files changed

+54
-63
lines changed

cmd/tailscaled/tailscaled.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,7 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logid string) (_ *ip
533533
return smallzstd.NewDecoder(nil)
534534
})
535535
configureTaildrop(logf, lb)
536-
ns.SetLocalBackend(lb)
537-
if err := ns.Start(); err != nil {
536+
if err := ns.Start(lb); err != nil {
538537
log.Fatalf("failed to start netstack: %v", err)
539538
}
540539
return lb, nil

cmd/tsconnect/wasm/wasm_js.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,7 @@ func newIPN(jsConfig js.Value) map[string]any {
115115
}
116116
ns.ProcessLocalIPs = true
117117
ns.ProcessSubnets = true
118-
if err := ns.Start(); err != nil {
119-
log.Fatalf("failed to start netstack: %v", err)
120-
}
118+
121119
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
122120
return true
123121
}
@@ -127,16 +125,17 @@ func newIPN(jsConfig js.Value) map[string]any {
127125

128126
logid := lpc.PublicID.String()
129127
srv := ipnserver.New(logf, logid)
130-
131128
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, "wasm", dialer, eng, controlclient.LoginEphemeral)
132129
if err != nil {
133130
log.Fatalf("ipnlocal.NewLocalBackend: %v", err)
134131
}
132+
if err := ns.Start(lb); err != nil {
133+
log.Fatalf("failed to start netstack: %v", err)
134+
}
135135
lb.SetDecompressor(func() (controlclient.Decompressor, error) {
136136
return smallzstd.NewDecoder(nil)
137137
})
138138
srv.SetLocalBackend(lb)
139-
ns.SetLocalBackend(lb)
140139

141140
jsIPN := &jsIPN{
142141
dialer: dialer,

tsnet/tsnet.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,6 @@ func (s *Server) start() (reterr error) {
317317
}
318318
ns.ProcessLocalIPs = true
319319
ns.ForwardTCPIn = s.forwardTCP
320-
if err := ns.Start(); err != nil {
321-
return fmt.Errorf("failed to start netstack: %w", err)
322-
}
323320
s.netstack = ns
324321
s.dialer.UseNetstackForIP = func(ip netip.Addr) bool {
325322
_, ok := eng.PeerForIP(ip)
@@ -349,6 +346,9 @@ func (s *Server) start() (reterr error) {
349346
lb.SetVarRoot(s.rootPath)
350347
logf("tsnet starting with hostname %q, varRoot %q", s.hostname, s.rootPath)
351348
s.lb = lb
349+
if err := ns.Start(lb); err != nil {
350+
return fmt.Errorf("failed to start netstack: %w", err)
351+
}
352352
closePool.addFunc(func() { s.lb.Shutdown() })
353353
lb.SetDecompressor(func() (controlclient.Decompressor, error) {
354354
return smallzstd.NewDecoder(nil)

wgengine/netstack/netstack.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,6 @@ func (ns *Impl) Close() error {
204204
return nil
205205
}
206206

207-
// SetLocalBackend sets the LocalBackend; it should only be run before
208-
// the Start method is called.
209-
func (ns *Impl) SetLocalBackend(lb *ipnlocal.LocalBackend) {
210-
ns.lb = lb
211-
}
212-
213207
// wrapProtoHandler returns protocol handler h wrapped in a version
214208
// that dynamically reconfigures ns's subnet addresses as needed for
215209
// outbound traffic.
@@ -231,7 +225,11 @@ func (ns *Impl) wrapProtoHandler(h func(stack.TransportEndpointID, stack.PacketB
231225

232226
// Start sets up all the handlers so netstack can start working. Implements
233227
// wgengine.FakeImpl.
234-
func (ns *Impl) Start() error {
228+
func (ns *Impl) Start(lb *ipnlocal.LocalBackend) error {
229+
if lb == nil {
230+
panic("nil LocalBackend")
231+
}
232+
ns.lb = lb
235233
ns.e.AddNetworkMapCallback(ns.updateIPs)
236234
// size = 0 means use default buffer size
237235
const tcpReceiveBufferSize = 0

wgengine/netstack/netstack_test.go

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"tailscale.com/net/tsaddr"
1818
"tailscale.com/net/tsdial"
1919
"tailscale.com/net/tstun"
20-
"tailscale.com/tstest"
2120
"tailscale.com/types/ipproto"
2221
"tailscale.com/wgengine"
2322
"tailscale.com/wgengine/filter"
@@ -50,13 +49,18 @@ func TestInjectInboundLeak(t *testing.T) {
5049
t.Fatal("failed to get internals")
5150
}
5251

52+
lb, err := ipnlocal.NewLocalBackend(logf, "logid", new(mem.Store), "", dialer, eng, 0)
53+
if err != nil {
54+
t.Fatal(err)
55+
}
56+
5357
ns, err := Create(logf, tunWrap, eng, magicSock, dialer, dns)
5458
if err != nil {
5559
t.Fatal(err)
5660
}
5761
defer ns.Close()
5862
ns.ProcessLocalIPs = true
59-
if err := ns.Start(); err != nil {
63+
if err := ns.Start(lb); err != nil {
6064
t.Fatalf("Start: %v", err)
6165
}
6266
ns.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return true })
@@ -114,10 +118,16 @@ func makeNetstack(t *testing.T, config func(*Impl)) *Impl {
114118
}
115119
t.Cleanup(func() { ns.Close() })
116120

117-
ns.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return true })
118-
config(ns)
121+
lb, err := ipnlocal.NewLocalBackend(logf, "logid", new(mem.Store), "", dialer, eng, 0)
122+
if err != nil {
123+
t.Fatalf("NewLocalBackend: %v", err)
124+
}
119125

120-
if err := ns.Start(); err != nil {
126+
ns.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return true })
127+
if config != nil {
128+
config(ns)
129+
}
130+
if err := ns.Start(lb); err != nil {
121131
t.Fatalf("Start: %v", err)
122132
}
123133
return ns
@@ -257,11 +267,12 @@ func looksLikeATailscaleSelfAddress(addr netip.Addr) bool {
257267

258268
func TestShouldProcessInbound(t *testing.T) {
259269
testCases := []struct {
260-
name string
261-
pkt *packet.Parsed
262-
setup func(*Impl)
263-
want bool
264-
runOnGOOS string
270+
name string
271+
pkt *packet.Parsed
272+
afterStart func(*Impl) // optional; after Impl.Start is called
273+
beforeStart func(*Impl) // optional; before Impl.Start is called
274+
want bool
275+
runOnGOOS string
265276
}{
266277
{
267278
name: "ipv6-via",
@@ -275,7 +286,7 @@ func TestShouldProcessInbound(t *testing.T) {
275286
Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
276287
TCPFlags: packet.TCPSyn,
277288
},
278-
setup: func(i *Impl) {
289+
afterStart: func(i *Impl) {
279290
prefs := ipn.NewPrefs()
280291
prefs.AdvertiseRoutes = []netip.Prefix{
281292
// $ tailscale debug via 7 10.1.1.0/24
@@ -286,7 +297,8 @@ func TestShouldProcessInbound(t *testing.T) {
286297
LegacyMigrationPrefs: prefs,
287298
})
288299
i.atomicIsLocalIPFunc.Store(looksLikeATailscaleSelfAddress)
289-
300+
},
301+
beforeStart: func(i *Impl) {
290302
// This should be handled even if we're
291303
// otherwise not processing local IPs or
292304
// subnets.
@@ -307,7 +319,7 @@ func TestShouldProcessInbound(t *testing.T) {
307319
Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
308320
TCPFlags: packet.TCPSyn,
309321
},
310-
setup: func(i *Impl) {
322+
afterStart: func(i *Impl) {
311323
prefs := ipn.NewPrefs()
312324
prefs.AdvertiseRoutes = []netip.Prefix{
313325
// tailscale debug via 7 10.1.2.0/24
@@ -329,7 +341,7 @@ func TestShouldProcessInbound(t *testing.T) {
329341
Dst: netip.MustParseAddrPort("100.101.102.104:22"),
330342
TCPFlags: packet.TCPSyn,
331343
},
332-
setup: func(i *Impl) {
344+
afterStart: func(i *Impl) {
333345
prefs := ipn.NewPrefs()
334346
prefs.RunSSH = true
335347
i.lb.Start(ipn.Options{
@@ -351,7 +363,7 @@ func TestShouldProcessInbound(t *testing.T) {
351363
Dst: netip.MustParseAddrPort("100.101.102.104:22"),
352364
TCPFlags: packet.TCPSyn,
353365
},
354-
setup: func(i *Impl) {
366+
afterStart: func(i *Impl) {
355367
prefs := ipn.NewPrefs()
356368
prefs.RunSSH = false // default, but to be explicit
357369
i.lb.Start(ipn.Options{
@@ -372,7 +384,7 @@ func TestShouldProcessInbound(t *testing.T) {
372384
Dst: netip.MustParseAddrPort("100.101.102.104:4567"),
373385
TCPFlags: packet.TCPSyn,
374386
},
375-
setup: func(i *Impl) {
387+
afterStart: func(i *Impl) {
376388
i.ProcessLocalIPs = true
377389
i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
378390
return addr.String() == "100.101.102.104" // Dst, above
@@ -389,9 +401,10 @@ func TestShouldProcessInbound(t *testing.T) {
389401
Dst: netip.MustParseAddrPort("10.1.2.3:4567"),
390402
TCPFlags: packet.TCPSyn,
391403
},
392-
setup: func(i *Impl) {
404+
beforeStart: func(i *Impl) {
393405
i.ProcessSubnets = true
394-
406+
},
407+
afterStart: func(i *Impl) {
395408
// For testing purposes, assume all Tailscale
396409
// IPs are local; the Dst above is something
397410
// not in that range.
@@ -408,7 +421,12 @@ func TestShouldProcessInbound(t *testing.T) {
408421
Dst: netip.MustParseAddrPort("10.0.0.23:5555"),
409422
TCPFlags: packet.TCPSyn,
410423
},
411-
setup: func(i *Impl) {
424+
beforeStart: func(i *Impl) {
425+
// As if we were running on Linux where netstack isn't used.
426+
i.ProcessSubnets = false
427+
i.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return false })
428+
},
429+
afterStart: func(i *Impl) {
412430
prefs := ipn.NewPrefs()
413431
prefs.AdvertiseRoutes = []netip.Prefix{
414432
netip.MustParsePrefix("10.0.0.1/24"),
@@ -417,10 +435,6 @@ func TestShouldProcessInbound(t *testing.T) {
417435
LegacyMigrationPrefs: prefs,
418436
})
419437

420-
// As if we were running on Linux where netstack isn't used.
421-
i.ProcessSubnets = false
422-
i.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return false })
423-
424438
// Set the PeerAPI port to the Dst port above.
425439
i.peerapiPort4Atomic.Store(5555)
426440
i.peerapiPort6Atomic.Store(5555)
@@ -437,30 +451,11 @@ func TestShouldProcessInbound(t *testing.T) {
437451
if tc.runOnGOOS != "" && runtime.GOOS != tc.runOnGOOS {
438452
t.Skipf("skipping on GOOS=%v", runtime.GOOS)
439453
}
440-
impl := makeNetstack(t, func(i *Impl) {
441-
defer t.Logf("netstack setup finished")
442-
443-
logf := tstest.WhileTestRunningLogger(t)
444-
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
445-
if err != nil {
446-
t.Fatalf("NewFakeUserspaceEngine: %v", err)
447-
}
448-
t.Cleanup(e.Close)
449-
450-
lb, err := ipnlocal.NewLocalBackend(logf, "logid", new(mem.Store), "", new(tsdial.Dialer), e, 0)
451-
if err != nil {
452-
t.Fatalf("NewLocalBackend: %v", err)
453-
}
454-
t.Cleanup(lb.Shutdown)
455-
dir := t.TempDir()
456-
lb.SetVarRoot(dir)
457-
458-
i.SetLocalBackend(lb)
454+
impl := makeNetstack(t, tc.beforeStart)
455+
if tc.afterStart != nil {
456+
tc.afterStart(impl)
457+
}
459458

460-
if tc.setup != nil {
461-
tc.setup(i)
462-
}
463-
})
464459
got := impl.shouldProcessInbound(tc.pkt, nil)
465460
if got != tc.want {
466461
t.Errorf("got shouldProcessInbound()=%v; want %v", got, tc.want)

0 commit comments

Comments
 (0)