diff --git a/go.mod b/go.mod index df049876cb08c..57beaf8277170 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ replace github.com/tcnksm/go-httpstat => github.com/coder/go-httpstat v0.0.0-202 // There are a few minor changes we make to Tailscale that we're slowly upstreaming. Compare here: // https://github.com/tailscale/tailscale/compare/main...coder:tailscale:main -replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e +replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20250611020837-f14d20d23d8c // This is replaced to include // 1. a fix for a data race: c.f. https://github.com/tailscale/wireguard-go/pull/25 diff --git a/go.sum b/go.sum index acabb41e1dec8..d6e5e5adf4ec3 100644 --- a/go.sum +++ b/go.sum @@ -920,8 +920,8 @@ github.com/coder/serpent v0.10.0 h1:ofVk9FJXSek+SmL3yVE3GoArP83M+1tX+H7S4t8BSuM= github.com/coder/serpent v0.10.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788 h1:YoUSJ19E8AtuUFVYBpXuOD6a/zVP3rcxezNsoDseTUw= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788/go.mod h1:aGQbuCLyhRLMzZF067xc84Lh7JDs1FKwCmF1Crl9dxQ= -github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e h1:nope/SZfoLB9MCOB9wdCE6gW5+8l3PhFrDC5IWPL8bk= -github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e/go.mod h1:1ggFFdHTRjPRu9Yc1yA7nVHBYB50w9Ce7VIXNqcW6Ko= +github.com/coder/tailscale v1.1.1-0.20250611020837-f14d20d23d8c h1:d/qBIi3Ez7KkopRgNtfdvTMqvqBg47d36qVfkd3C5EQ= +github.com/coder/tailscale v1.1.1-0.20250611020837-f14d20d23d8c/go.mod h1:l7ml5uu7lFh5hY28lGYM4b/oFSmuPHYX6uk4RAu23Lc= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e h1:JNLPDi2P73laR1oAclY6jWzAbucf70ASAvf5mh2cME0= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI= github.com/coder/terraform-provider-coder/v2 v2.5.3 h1:EwqIIQKe/j8bsR4WyDJ3bD0dVdkfVqJ43TwClyGneUU= diff --git a/tailnet/test/integration/integration.go b/tailnet/test/integration/integration.go index 70320567841a9..5ca1ed9ffd667 100644 --- a/tailnet/test/integration/integration.go +++ b/tailnet/test/integration/integration.go @@ -25,6 +25,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" "github.com/stretchr/testify/require" + "golang.org/x/sys/unix" "golang.org/x/xerrors" "tailscale.com/derp" "tailscale.com/derp/derphttp" @@ -458,6 +459,16 @@ func (UDPEchoService) StartService(t *testing.T, logger slog.Logger, _ *tailnet. Port: EchoPort, }) require.NoError(t, err) + + // set path MTU discovery so that we don't fragment the responses. + c, err := l.SyscallConn() + require.NoError(t, err) + var sockErr error + err = c.Control(func(fd uintptr) { + sockErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IP_PMTUDISC_DO) + }) + require.NoError(t, err) + require.NoError(t, sockErr) logger.Info(context.Background(), "started UDPEcho server") t.Cleanup(func() { lCloseErr := l.Close() diff --git a/tailnet/test/integration/integration_test.go b/tailnet/test/integration/integration_test.go index 260c21a6458f5..e10c2bea57075 100644 --- a/tailnet/test/integration/integration_test.go +++ b/tailnet/test/integration/integration_test.go @@ -112,7 +112,7 @@ var topologies = []integration.TestTopology{ { // Test that direct over normal MTU works. Name: "DirectMTU1500", - NetworkingProvider: integration.TriangleNetwork{InterClientMTU: 1500}, + NetworkingProvider: integration.TriangleNetwork{Client1MTU: 1500}, Server: integration.SimpleServerOptions{}, ClientStarter: integration.BasicClientStarter{ WaitForDirect: true, @@ -124,7 +124,7 @@ var topologies = []integration.TestTopology{ { // Test that small MTU works. Name: "MTU1280", - NetworkingProvider: integration.TriangleNetwork{InterClientMTU: 1280}, + NetworkingProvider: integration.TriangleNetwork{Client1MTU: 1280}, Server: integration.SimpleServerOptions{}, ClientStarter: integration.BasicClientStarter{Service: integration.UDPEchoService{}, LogPackets: true}, RunTests: integration.TestBigUDP, diff --git a/tailnet/test/integration/network.go b/tailnet/test/integration/network.go index 871423974f3eb..30a20ed1f71a3 100644 --- a/tailnet/test/integration/network.go +++ b/tailnet/test/integration/network.go @@ -390,33 +390,38 @@ func createFakeInternet(t *testing.T) fakeInternet { } type TriangleNetwork struct { - InterClientMTU int + Client1MTU int } type fakeTriangleNetwork struct { - NamePrefix string - ServerNetNS *os.File - Client1NetNS *os.File - Client2NetNS *os.File - ServerClient1VethPair vethPair - ServerClient2VethPair vethPair - Client1Client2VethPair vethPair + NamePrefix string + ServerNetNS *os.File + Client1NetNS *os.File + Client2NetNS *os.File + RouterNetNS *os.File + ServerVethPair vethPair + Client1VethPair vethPair + Client2VethPair vethPair } -// SetupNetworking creates multiple namespaces with veth pairs between them -// with the following topology: +// SetupNetworking creates multiple namespaces with a central router in the following topology // . -// . ┌────────────────────────────────────────────┐ -// . │ Server │ -// . └─────┬───────────────────────────────────┬──┘ -// . │fdac:38fa:ffff:2::3 │fdac:38fa:ffff:3::3 -// . veth│ veth│ -// . │fdac:38fa:ffff:2::1 │fdac:38fa:ffff:3::2 -// . ┌───────┴──────┐ ┌─────┴───────┐ -// . │ │ fdac:38fa:ffff:1::2│ │ -// . │ Client 1 ├──────────────────────┤ Client 2 │ -// . │ │fdac:38fa:ffff:1::1 │ │ -// . └──────────────┘ └─────────────┘ +// . ┌──────────────┐ +// . │ │ +// . │ Server ├─────────────────────────────────────┐ +// . │ │fdac:38fa:ffff:3::2 │ +// . └──────────────┘ │ fdac:38fa:ffff:3::1 +// . ┌──────────────┐ ┌─────┴───────┐ +// . │ │ fdac:38fa:ffff:1::1│ │ +// . │ Client 1 ├───────────────────────────────┤ Router │ +// . │ │fdac:38fa:ffff:1::2 │ │ +// . └──────────────┘ └─────┬───────┘ +// . ┌──────────────┐ │ fdac:38fa:ffff:2::1 +// . │ │ │ +// . │ Client 2 ├─────────────────────────────────────┘ +// . │ │fdac:38fa:ffff:2::2 +// . └──────────────┘ +// The veth link between Client 1 and the router has a configurable MTU via Client1MTU. func (n TriangleNetwork) SetupNetworking(t *testing.T, l slog.Logger) TestNetworking { logger := l.Named("setup-networking").Leveled(slog.LevelDebug) t.Helper() @@ -433,89 +438,97 @@ func (n TriangleNetwork) SetupNetworking(t *testing.T, l slog.Logger) TestNetwor network.ServerNetNS = createNetNS(t, namePrefix+"server") network.Client1NetNS = createNetNS(t, namePrefix+"client1") network.Client2NetNS = createNetNS(t, namePrefix+"client2") + network.RouterNetNS = createNetNS(t, namePrefix+"router") - // Create veth pair between server and client1 - network.ServerClient1VethPair = vethPair{ - Outer: namePrefix + "s-1", - Inner: namePrefix + "1-s", + // Create veth pair between server and router + network.ServerVethPair = vethPair{ + Outer: namePrefix + "s-r", + Inner: namePrefix + "r-s", } - err := createVethPair(network.ServerClient1VethPair.Outer, network.ServerClient1VethPair.Inner) + err := createVethPair(network.ServerVethPair.Outer, network.ServerVethPair.Inner) require.NoErrorf(t, err, "create veth pair %q <-> %q", - network.ServerClient1VethPair.Outer, network.ServerClient1VethPair.Inner) + network.ServerVethPair.Outer, network.ServerVethPair.Inner) - // Move server-client1 veth ends to their respective namespaces - err = setVethNetNS(network.ServerClient1VethPair.Outer, int(network.ServerNetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to server NetNS", network.ServerClient1VethPair.Outer) - err = setVethNetNS(network.ServerClient1VethPair.Inner, int(network.Client1NetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to client1 NetNS", network.ServerClient1VethPair.Inner) + // Move server-router veth ends to their respective namespaces + err = setVethNetNS(network.ServerVethPair.Outer, int(network.ServerNetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to server NetNS", network.ServerVethPair.Outer) + err = setVethNetNS(network.ServerVethPair.Inner, int(network.RouterNetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to router NetNS", network.ServerVethPair.Inner) - // Create veth pair between server and client2 - network.ServerClient2VethPair = vethPair{ - Outer: namePrefix + "s-2", - Inner: namePrefix + "2-s", + // Create veth pair between client1 and router + network.Client1VethPair = vethPair{ + Outer: namePrefix + "1-r", + Inner: namePrefix + "r-1", } - err = createVethPair(network.ServerClient2VethPair.Outer, network.ServerClient2VethPair.Inner) + logger.Debug(context.Background(), "creating client1 link", slog.F("mtu", n.Client1MTU)) + err = createVethPair(network.Client1VethPair.Outer, network.Client1VethPair.Inner, withMTU(n.Client1MTU)) require.NoErrorf(t, err, "create veth pair %q <-> %q", - network.ServerClient2VethPair.Outer, network.ServerClient2VethPair.Inner) + network.Client1VethPair.Outer, network.Client1VethPair.Inner) - // Move server-client2 veth ends to their respective namespaces - err = setVethNetNS(network.ServerClient2VethPair.Outer, int(network.ServerNetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to server NetNS", network.ServerClient2VethPair.Outer) - err = setVethNetNS(network.ServerClient2VethPair.Inner, int(network.Client2NetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to client2 NetNS", network.ServerClient2VethPair.Inner) + // Move client1-router veth ends to their respective namespaces + err = setVethNetNS(network.Client1VethPair.Outer, int(network.Client1NetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to server NetNS", network.Client1VethPair.Outer) + err = setVethNetNS(network.Client1VethPair.Inner, int(network.RouterNetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to client2 NetNS", network.Client1VethPair.Inner) // Create veth pair between client1 and client2 - network.Client1Client2VethPair = vethPair{ - Outer: namePrefix + "1-2", - Inner: namePrefix + "2-1", + network.Client2VethPair = vethPair{ + Outer: namePrefix + "2-r", + Inner: namePrefix + "r-2", } - logger.Debug(context.Background(), "creating inter-client link", slog.F("mtu", n.InterClientMTU)) - err = createVethPair(network.Client1Client2VethPair.Outer, network.Client1Client2VethPair.Inner, - withMTU(n.InterClientMTU)) + + err = createVethPair(network.Client2VethPair.Outer, network.Client2VethPair.Inner) require.NoErrorf(t, err, "create veth pair %q <-> %q", - network.Client1Client2VethPair.Outer, network.Client1Client2VethPair.Inner) + network.Client2VethPair.Outer, network.Client2VethPair.Inner) // Move client1-client2 veth ends to their respective namespaces - err = setVethNetNS(network.Client1Client2VethPair.Outer, int(network.Client1NetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to client1 NetNS", network.Client1Client2VethPair.Outer) - err = setVethNetNS(network.Client1Client2VethPair.Inner, int(network.Client2NetNS.Fd())) - require.NoErrorf(t, err, "set veth %q to client2 NetNS", network.Client1Client2VethPair.Inner) + err = setVethNetNS(network.Client2VethPair.Outer, int(network.Client2NetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to client1 NetNS", network.Client2VethPair.Outer) + err = setVethNetNS(network.Client2VethPair.Inner, int(network.RouterNetNS.Fd())) + require.NoErrorf(t, err, "set veth %q to client2 NetNS", network.Client2VethPair.Inner) // Set IP addresses according to the diagram: - err = setInterfaceIP6(network.ServerNetNS, network.ServerClient1VethPair.Outer, ula+"2::3") - require.NoErrorf(t, err, "set IP on server-client1 interface") - err = setInterfaceIP6(network.ServerNetNS, network.ServerClient2VethPair.Outer, ula+"3::3") - require.NoErrorf(t, err, "set IP on server-client2 interface") - - err = setInterfaceIP6(network.Client1NetNS, network.ServerClient1VethPair.Inner, ula+"2::1") - require.NoErrorf(t, err, "set IP on client1-server interface") - err = setInterfaceIP6(network.Client1NetNS, network.Client1Client2VethPair.Outer, ula+"1::1") - require.NoErrorf(t, err, "set IP on client1-client2 interface") - - err = setInterfaceIP6(network.Client2NetNS, network.ServerClient2VethPair.Inner, ula+"3::2") - require.NoErrorf(t, err, "set IP on client2-server interface") - err = setInterfaceIP6(network.Client2NetNS, network.Client1Client2VethPair.Inner, ula+"1::2") - require.NoErrorf(t, err, "set IP on client2-client1 interface") + err = setInterfaceIP6(network.ServerNetNS, network.ServerVethPair.Outer, ula+"3::2") + require.NoErrorf(t, err, "set IP on server interface") + err = setInterfaceIP6(network.Client1NetNS, network.Client1VethPair.Outer, ula+"1::2") + require.NoErrorf(t, err, "set IP on client1 interface") + err = setInterfaceIP6(network.Client2NetNS, network.Client2VethPair.Outer, ula+"2::2") + require.NoErrorf(t, err, "set IP on client2 interface") + + err = setInterfaceIP6(network.RouterNetNS, network.ServerVethPair.Inner, ula+"3::1") + require.NoErrorf(t, err, "set IP on router-server interface") + err = setInterfaceIP6(network.RouterNetNS, network.Client1VethPair.Inner, ula+"1::1") + require.NoErrorf(t, err, "set IP on router-client1 interface") + err = setInterfaceIP6(network.RouterNetNS, network.Client2VethPair.Inner, ula+"2::1") + require.NoErrorf(t, err, "set IP on router-client2 interface") // Bring up all interfaces interfaces := []struct { - netNS *os.File - ifaceName string + netNS *os.File + ifaceName string + defaultRoute string }{ - {network.ServerNetNS, network.ServerClient1VethPair.Outer}, - {network.ServerNetNS, network.ServerClient2VethPair.Outer}, - {network.Client1NetNS, network.ServerClient1VethPair.Inner}, - {network.Client1NetNS, network.Client1Client2VethPair.Outer}, - {network.Client2NetNS, network.ServerClient2VethPair.Inner}, - {network.Client2NetNS, network.Client1Client2VethPair.Inner}, + {network.ServerNetNS, network.ServerVethPair.Outer, ula + "3::1"}, + {network.Client1NetNS, network.Client1VethPair.Outer, ula + "1::1"}, + {network.Client2NetNS, network.Client2VethPair.Outer, ula + "2::1"}, + {network.RouterNetNS, network.ServerVethPair.Inner, ""}, + {network.RouterNetNS, network.Client1VethPair.Inner, ""}, + {network.RouterNetNS, network.Client2VethPair.Inner, ""}, } for _, iface := range interfaces { err = setInterfaceUp(iface.netNS, iface.ifaceName) require.NoErrorf(t, err, "bring up interface %q", iface.ifaceName) - // Note: routes are not needed as we are fully connected, so nothing needs to forward IP to a further - // destination. + + if iface.defaultRoute != "" { + err = addRouteInNetNS(iface.netNS, []string{"default", "via", iface.defaultRoute, "dev", iface.ifaceName}) + require.NoErrorf(t, err, "add peer default route to %s", iface.defaultRoute) + } } + // enable IP forwarding in the router + _, err = commandInNetNS(network.RouterNetNS, "sysctl", []string{"-w", "net.ipv6.conf.all.forwarding=1"}).Output() + require.NoError(t, wrapExitErr(err), "enable IPv6 forwarding in router NetNS") + return TestNetworking{ Server: TestNetworkingServer{ Process: TestNetworkingProcess{NetNS: network.ServerNetNS}, @@ -523,11 +536,11 @@ func (n TriangleNetwork) SetupNetworking(t *testing.T, l slog.Logger) TestNetwor }, Client1: TestNetworkingClient{ Process: TestNetworkingProcess{NetNS: network.Client1NetNS}, - ServerAccessURL: "http://[" + ula + "2::3]:8080", // Client1 accesses server directly + ServerAccessURL: "http://[" + ula + "3::2]:8080", }, Client2: TestNetworkingClient{ Process: TestNetworkingProcess{NetNS: network.Client2NetNS}, - ServerAccessURL: "http://[" + ula + "3::3]:8080", // Client2 accesses server directly + ServerAccessURL: "http://[" + ula + "3::2]:8080", }, } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: