wgengine/netstack: add tests for shouldProcessInbound
Inspired by #6235, let's explicitly test the behaviour of this function to ensure that we're not processing things we don't expect to. Change-Id: I158050a63be7410fb99452089ea607aaf89fe91a Signed-off-by: Andrew Dunham <andrew@tailscale.com>
This commit is contained in:
parent
e85613aa2d
commit
acf5839dd2
|
@ -8,12 +8,18 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/refs"
|
"gvisor.dev/gvisor/pkg/refs"
|
||||||
|
"tailscale.com/ipn"
|
||||||
|
"tailscale.com/ipn/ipnlocal"
|
||||||
|
"tailscale.com/ipn/store/mem"
|
||||||
"tailscale.com/net/packet"
|
"tailscale.com/net/packet"
|
||||||
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/net/tstun"
|
"tailscale.com/net/tstun"
|
||||||
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/ipproto"
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
|
@ -252,3 +258,218 @@ func TestShouldHandlePing(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShouldProcessInbound(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
pkt *packet.Parsed
|
||||||
|
setup func(*Impl)
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ipv6-via",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 6,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
|
||||||
|
// $ tailscale debug via 7 10.1.1.9/24
|
||||||
|
// fd7a:115c:a1e0:b1a:0:7:a01:109/120
|
||||||
|
Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
prefs := ipn.NewPrefs()
|
||||||
|
prefs.AdvertiseRoutes = []netip.Prefix{
|
||||||
|
// $ tailscale debug via 7 10.1.1.0/24
|
||||||
|
// fd7a:115c:a1e0:b1a:0:7:a01:100/120
|
||||||
|
netip.MustParsePrefix("fd7a:115c:a1e0:b1a:0:7:a01:100/120"),
|
||||||
|
}
|
||||||
|
i.lb.Start(ipn.Options{
|
||||||
|
StateKey: ipn.GlobalDaemonStateKey,
|
||||||
|
UpdatePrefs: prefs,
|
||||||
|
})
|
||||||
|
|
||||||
|
// This should be handled even if we're
|
||||||
|
// otherwise not processing local IPs or
|
||||||
|
// subnets.
|
||||||
|
i.ProcessLocalIPs = false
|
||||||
|
i.ProcessSubnets = false
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6-via-not-advertised",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 6,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
|
||||||
|
// $ tailscale debug via 7 10.1.1.9/24
|
||||||
|
// fd7a:115c:a1e0:b1a:0:7:a01:109/120
|
||||||
|
Dst: netip.MustParseAddrPort("[fd7a:115c:a1e0:b1a:0:7:a01:109]:5678"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
prefs := ipn.NewPrefs()
|
||||||
|
prefs.AdvertiseRoutes = []netip.Prefix{
|
||||||
|
// tailscale debug via 7 10.1.2.0/24
|
||||||
|
// fd7a:115c:a1e0:b1a:0:7:a01:200/120
|
||||||
|
netip.MustParsePrefix("fd7a:115c:a1e0:b1a:0:7:a01:200/120"),
|
||||||
|
}
|
||||||
|
i.lb.Start(ipn.Options{
|
||||||
|
StateKey: ipn.GlobalDaemonStateKey,
|
||||||
|
UpdatePrefs: prefs,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tailscale-ssh-enabled",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 4,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
Dst: netip.MustParseAddrPort("100.101.102.104:22"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
prefs := ipn.NewPrefs()
|
||||||
|
prefs.RunSSH = true
|
||||||
|
i.lb.Start(ipn.Options{
|
||||||
|
StateKey: ipn.GlobalDaemonStateKey,
|
||||||
|
UpdatePrefs: prefs,
|
||||||
|
})
|
||||||
|
i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
|
||||||
|
return addr.String() == "100.101.102.104" // Dst, above
|
||||||
|
})
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tailscale-ssh-disabled",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 4,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
Dst: netip.MustParseAddrPort("100.101.102.104:22"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
prefs := ipn.NewPrefs()
|
||||||
|
prefs.RunSSH = false // default, but to be explicit
|
||||||
|
i.lb.Start(ipn.Options{
|
||||||
|
StateKey: ipn.GlobalDaemonStateKey,
|
||||||
|
UpdatePrefs: prefs,
|
||||||
|
})
|
||||||
|
i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
|
||||||
|
return addr.String() == "100.101.102.104" // Dst, above
|
||||||
|
})
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "process-local-ips",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 4,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
Dst: netip.MustParseAddrPort("100.101.102.104:4567"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
i.ProcessLocalIPs = true
|
||||||
|
i.atomicIsLocalIPFunc.Store(func(addr netip.Addr) bool {
|
||||||
|
return addr.String() == "100.101.102.104" // Dst, above
|
||||||
|
})
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "process-subnets",
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 4,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
Dst: netip.MustParseAddrPort("10.1.2.3:4567"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
i.ProcessSubnets = true
|
||||||
|
|
||||||
|
// For testing purposes, assume all Tailscale
|
||||||
|
// IPs are local; the Dst above is something
|
||||||
|
// not in that range.
|
||||||
|
i.atomicIsLocalIPFunc.Store(tsaddr.IsTailscaleIP)
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "peerapi-port-subnet-router", // see #6235
|
||||||
|
pkt: &packet.Parsed{
|
||||||
|
IPVersion: 4,
|
||||||
|
IPProto: ipproto.TCP,
|
||||||
|
Src: netip.MustParseAddrPort("100.101.102.103:1234"),
|
||||||
|
Dst: netip.MustParseAddrPort("10.0.0.23:5555"),
|
||||||
|
TCPFlags: packet.TCPSyn,
|
||||||
|
},
|
||||||
|
setup: func(i *Impl) {
|
||||||
|
prefs := ipn.NewPrefs()
|
||||||
|
prefs.AdvertiseRoutes = []netip.Prefix{
|
||||||
|
netip.MustParsePrefix("10.0.0.1/24"),
|
||||||
|
}
|
||||||
|
i.lb.Start(ipn.Options{
|
||||||
|
StateKey: ipn.GlobalDaemonStateKey,
|
||||||
|
UpdatePrefs: prefs,
|
||||||
|
})
|
||||||
|
|
||||||
|
// As if we were running on Linux where netstack isn't used.
|
||||||
|
i.ProcessSubnets = false
|
||||||
|
i.atomicIsLocalIPFunc.Store(func(netip.Addr) bool { return false })
|
||||||
|
|
||||||
|
// Set the PeerAPI port to the Dst port above.
|
||||||
|
atomic.StoreUint32(&i.peerapiPort4Atomic, 5555)
|
||||||
|
atomic.StoreUint32(&i.peerapiPort6Atomic, 5555)
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO(andrew): test PeerAPI
|
||||||
|
// TODO(andrew): test TCP packets without the SYN flag set
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
impl := makeNetstack(t, func(i *Impl) {
|
||||||
|
defer t.Logf("netstack setup finished")
|
||||||
|
|
||||||
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
|
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(e.Close)
|
||||||
|
|
||||||
|
lb, err := ipnlocal.NewLocalBackend(logf, "logid", new(mem.Store), new(tsdial.Dialer), e, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(lb.Shutdown)
|
||||||
|
dir := t.TempDir()
|
||||||
|
lb.SetVarRoot(dir)
|
||||||
|
|
||||||
|
i.SetLocalBackend(lb)
|
||||||
|
|
||||||
|
if tc.setup != nil {
|
||||||
|
tc.setup(i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
got := impl.shouldProcessInbound(tc.pkt, nil)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("got shouldProcessInbound()=%v; want %v", got, tc.want)
|
||||||
|
} else {
|
||||||
|
t.Logf("OK: shouldProcessInbound() = %v", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue