|
|
|
|
@ -274,33 +274,56 @@ func TestDialBlocks(t *testing.T) {
|
|
|
|
|
defer c.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestConn tests basic TCP connections between two tsnet Servers, s1 and s2:
|
|
|
|
|
//
|
|
|
|
|
// - s1, a subnet router, first listens on its TCP :8081.
|
|
|
|
|
// - s2 can connect to s1:8081
|
|
|
|
|
// - s2 cannot connect to s1:8082 (no listener)
|
|
|
|
|
// - s2 can dial through the subnet router functionality (getting a synthetic RST
|
|
|
|
|
// that we verify we generated & saw)
|
|
|
|
|
func TestConn(t *testing.T) {
|
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
|
t.Skip("slow on macOS: https://github.com/tailscale/tailscale/issues/17805")
|
|
|
|
|
}
|
|
|
|
|
tstest.ResourceCheck(t)
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
controlURL, c := startControl(t)
|
|
|
|
|
s1, s1ip, s1PubKey := startServer(t, ctx, controlURL, "s1")
|
|
|
|
|
s2, _, _ := startServer(t, ctx, controlURL, "s2")
|
|
|
|
|
|
|
|
|
|
s1.lb.EditPrefs(&ipn.MaskedPrefs{
|
|
|
|
|
// Track whether we saw an attempted dial to 192.0.2.1:8081.
|
|
|
|
|
var saw192DocNetDial atomic.Bool
|
|
|
|
|
s1.RegisterFallbackTCPHandler(func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
|
|
|
|
t.Logf("s1: fallback TCP handler called for %v -> %v", src, dst)
|
|
|
|
|
if dst.String() == "192.0.2.1:8081" {
|
|
|
|
|
saw192DocNetDial.Store(true)
|
|
|
|
|
}
|
|
|
|
|
return nil, true // nil handler but intercept=true means to send RST
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
lc1 := must.Get(s1.LocalClient())
|
|
|
|
|
|
|
|
|
|
must.Get(lc1.EditPrefs(ctx, &ipn.MaskedPrefs{
|
|
|
|
|
Prefs: ipn.Prefs{
|
|
|
|
|
AdvertiseRoutes: []netip.Prefix{netip.MustParsePrefix("192.0.2.0/24")},
|
|
|
|
|
},
|
|
|
|
|
AdvertiseRoutesSet: true,
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
c.SetSubnetRoutes(s1PubKey, []netip.Prefix{netip.MustParsePrefix("192.0.2.0/24")})
|
|
|
|
|
|
|
|
|
|
lc2, err := s2.LocalClient()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
// Start s2 after s1 is fully set up, including advertising its routes,
|
|
|
|
|
// otherwise the test is flaky if the test starts dialing through s2 before
|
|
|
|
|
// our test control server has told s2 about s1's routes.
|
|
|
|
|
s2, _, _ := startServer(t, ctx, controlURL, "s2")
|
|
|
|
|
lc2 := must.Get(s2.LocalClient())
|
|
|
|
|
|
|
|
|
|
must.Get(lc2.EditPrefs(ctx, &ipn.MaskedPrefs{
|
|
|
|
|
Prefs: ipn.Prefs{
|
|
|
|
|
RouteAll: true,
|
|
|
|
|
},
|
|
|
|
|
RouteAllSet: true,
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
// ping to make sure the connection is up.
|
|
|
|
|
res, err := lc2.Ping(ctx, s1ip, tailcfg.PingICMP)
|
|
|
|
|
res, err := lc2.Ping(ctx, s1ip, tailcfg.PingTSMP)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
@ -313,12 +336,26 @@ func TestConn(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
defer ln.Close()
|
|
|
|
|
|
|
|
|
|
w, err := s2.Dial(ctx, "tcp", fmt.Sprintf("%s:8081", s1ip))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
s1Conns := make(chan net.Conn)
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
c, err := ln.Accept()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if ctx.Err() != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("s1.Accept: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
select {
|
|
|
|
|
case s1Conns <- c:
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
c.Close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
r, err := ln.Accept()
|
|
|
|
|
w, err := s2.Dial(ctx, "tcp", fmt.Sprintf("%s:8081", s1ip))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
@ -328,28 +365,51 @@ func TestConn(t *testing.T) {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := make([]byte, len(want))
|
|
|
|
|
if _, err := io.ReadAtLeast(r, got, len(got)); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
t.Logf("got: %q", got)
|
|
|
|
|
if string(got) != want {
|
|
|
|
|
t.Errorf("got %q, want %q", got, want)
|
|
|
|
|
select {
|
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
|
t.Fatal("timeout waiting for connection")
|
|
|
|
|
case r := <-s1Conns:
|
|
|
|
|
got := make([]byte, len(want))
|
|
|
|
|
_, err := io.ReadAtLeast(r, got, len(got))
|
|
|
|
|
r.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
t.Logf("got: %q", got)
|
|
|
|
|
if string(got) != want {
|
|
|
|
|
t.Errorf("got %q, want %q", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dial a non-existent port on s1 and expect it to fail.
|
|
|
|
|
_, err = s2.Dial(ctx, "tcp", fmt.Sprintf("%s:8082", s1ip)) // some random port
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("unexpected success; should have seen a connection refused error")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// s1 is a subnet router for TEST-NET-1 (192.0.2.0/24). Lets dial to that
|
|
|
|
|
// subnet from s2 to ensure a listener without an IP address (i.e. ":8081")
|
|
|
|
|
// only matches destination IPs corresponding to the node's IP, and not
|
|
|
|
|
// to any random IP a subnet is routing.
|
|
|
|
|
_, err = s2.Dial(ctx, "tcp", fmt.Sprintf("%s:8081", "192.0.2.1"))
|
|
|
|
|
t.Logf("got expected failure: %v", err)
|
|
|
|
|
|
|
|
|
|
// s1 is a subnet router for TEST-NET-1 (192.0.2.0/24). Let's dial to that
|
|
|
|
|
// subnet from s2 to ensure a listener without an IP address (i.e. our
|
|
|
|
|
// ":8081" listen above) only matches destination IPs corresponding to the
|
|
|
|
|
// s1 node's IP addresses, and not to any random IP of a subnet it's routing.
|
|
|
|
|
//
|
|
|
|
|
// The RegisterFallbackTCPHandler on s1 above handles sending a RST when the
|
|
|
|
|
// TCP SYN arrives from s2. But we bound it to 5 seconds lest a regression
|
|
|
|
|
// like tailscale/tailscale#17805 recur.
|
|
|
|
|
s2dialer := s2.Sys().Dialer.Get()
|
|
|
|
|
s2dialer.SetSystemDialerForTest(func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
|
|
|
|
t.Logf("s2: unexpected system dial called for %s %s", netw, addr)
|
|
|
|
|
return nil, fmt.Errorf("system dialer called unexpectedly for %s %s", netw, addr)
|
|
|
|
|
})
|
|
|
|
|
docCtx, docCancel := context.WithTimeout(ctx, 5*time.Second)
|
|
|
|
|
defer docCancel()
|
|
|
|
|
_, err = s2.Dial(docCtx, "tcp", "192.0.2.1:8081")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("unexpected success; should have seen a connection refused error")
|
|
|
|
|
}
|
|
|
|
|
if !saw192DocNetDial.Load() {
|
|
|
|
|
t.Errorf("expected s1's fallback TCP handler to have been called for 192.0.2.1:8081")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLoopbackLocalAPI(t *testing.T) {
|
|
|
|
|
|