M ipn/ipnlocal/local.go => ipn/ipnlocal/local.go +24 -16
@@ 3329,13 3329,24 @@ var (
// TCPHandlerForDst returns a TCP handler for connections to dst, or nil if
// no handler is needed. It also returns a list of TCP socket options to
// apply to the socket before calling the handler.
+// TCPHandlerForDst is called both for connections to our node's local IP
+// as well as to the service IP (quad 100).
func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c net.Conn) error, opts []tcpip.SettableSocketOption) {
- if dst.Port() == 80 && (dst.Addr() == magicDNSIP || dst.Addr() == magicDNSIPv6) {
- if b.ShouldRunWebClient() {
- return b.handleWebClientConn, opts
+ // First handle internal connections to the service IP
+ hittingServiceIP := dst.Addr() == magicDNSIP || dst.Addr() == magicDNSIPv6
+ if hittingServiceIP {
+ switch dst.Port() {
+ case 80:
+ if b.ShouldRunWebClient() {
+ return b.handleWebClientConn, opts
+ }
+ return b.HandleQuad100Port80Conn, opts
+ case TailFSLocalPort:
+ return b.handleTailFSConn, opts
}
- return b.HandleQuad100Port80Conn, opts
}
+
+ // Then handle external connections to the local IP.
if !b.isLocalIP(dst.Addr()) {
return nil, nil
}
@@ 3353,18 3364,6 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
if dst.Port() == webClientPort && b.ShouldRunWebClient() {
return b.handleWebClientConn, opts
}
- if dst.Port() == TailFSLocalPort {
- fs, ok := b.sys.TailFSForLocal.GetOK()
- if ok {
- return func(conn net.Conn) error {
- if !b.TailFSAccessEnabled() {
- conn.Close()
- return nil
- }
- return fs.HandleConn(conn, conn.RemoteAddr())
- }, opts
- }
- }
if port, ok := b.GetPeerAPIPort(dst.Addr()); ok && dst.Port() == port {
return func(c net.Conn) error {
b.handlePeerAPIConn(src, dst, c)
@@ 3377,6 3376,15 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
return nil, nil
}
+func (b *LocalBackend) handleTailFSConn(conn net.Conn) error {
+ fs, ok := b.sys.TailFSForLocal.GetOK()
+ if !ok || !b.TailFSAccessEnabled() {
+ conn.Close()
+ return nil
+ }
+ return fs.HandleConn(conn, conn.RemoteAddr())
+}
+
func (b *LocalBackend) peerAPIServicesLocked() (ret []tailcfg.Service) {
for _, pln := range b.peerAPIListeners {
proto := tailcfg.PeerAPI4
M ipn/ipnlocal/local_test.go => ipn/ipnlocal/local_test.go +69 -0
@@ 2173,3 2173,72 @@ func TestOnTailnetDefaultAutoUpdate(t *testing.T) {
})
}
}
+
+func TestTCPHandlerForDst(t *testing.T) {
+ b := newTestBackend(t)
+
+ tests := []struct {
+ desc string
+ dst string
+ intercept bool
+ }{
+ {
+ desc: "intercept port 80 (Web UI) on quad100 IPv4",
+ dst: "100.100.100.100:80",
+ intercept: true,
+ },
+ {
+ desc: "intercept port 80 (Web UI) on quad100 IPv6",
+ dst: "[fd7a:115c:a1e0::53]:80",
+ intercept: true,
+ },
+ {
+ desc: "don't intercept port 80 on local ip",
+ dst: "100.100.103.100:80",
+ intercept: false,
+ },
+ {
+ desc: "intercept port 8080 (TailFS) on quad100 IPv4",
+ dst: "100.100.100.100:8080",
+ intercept: true,
+ },
+ {
+ desc: "intercept port 8080 (TailFS) on quad100 IPv6",
+ dst: "[fd7a:115c:a1e0::53]:8080",
+ intercept: true,
+ },
+ {
+ desc: "don't intercept port 8080 on local ip",
+ dst: "100.100.103.100:8080",
+ intercept: false,
+ },
+ {
+ desc: "don't intercept port 9080 on quad100 IPv4",
+ dst: "100.100.100.100:9080",
+ intercept: false,
+ },
+ {
+ desc: "don't intercept port 9080 on quad100 IPv6",
+ dst: "[fd7a:115c:a1e0::53]:9080",
+ intercept: false,
+ },
+ {
+ desc: "don't intercept port 9080 on local ip",
+ dst: "100.100.103.100:9080",
+ intercept: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.dst, func(t *testing.T) {
+ t.Log(tt.desc)
+ src := netip.MustParseAddrPort("100.100.102.100:51234")
+ h, _ := b.TCPHandlerForDst(src, netip.MustParseAddrPort(tt.dst))
+ if !tt.intercept && h != nil {
+ t.Error("intercepted traffic we shouldn't have")
+ } else if tt.intercept && h == nil {
+ t.Error("failed to intercept traffic we should have")
+ }
+ })
+ }
+}
M ipn/ipnlocal/serve_test.go => ipn/ipnlocal/serve_test.go +2 -1
@@ 684,7 684,8 @@ func newTestBackend(t *testing.T) *LocalBackend {
b.netMap = &netmap.NetworkMap{
SelfNode: (&tailcfg.Node{
- Name: "example.ts.net",
+ Name: "example.ts.net",
+ Capabilities: []tailcfg.NodeCapability{tailcfg.NodeAttrsTailFSAccess},
}).View(),
UserProfiles: map[tailcfg.UserID]tailcfg.UserProfile{
tailcfg.UserID(1): {
M wgengine/netstack/netstack.go => wgengine/netstack/netstack.go +2 -14
@@ 1131,25 1131,13 @@ func (ns *Impl) acceptTCP(r *tcp.ForwarderRequest) {
// Local Services (DNS and WebDAV)
hittingServiceIP := dialIP == serviceIP || dialIP == serviceIPv6
hittingDNS := hittingServiceIP && reqDetails.LocalPort == 53
- hittingTailFS := hittingServiceIP && ns.tailFSForLocal != nil && reqDetails.LocalPort == ipnlocal.TailFSLocalPort
- if hittingDNS || hittingTailFS {
+ if hittingDNS {
c := getConnOrReset()
if c == nil {
return
}
addrPort := netip.AddrPortFrom(clientRemoteIP, reqDetails.RemotePort)
- if hittingDNS {
- go ns.dns.HandleTCPConn(c, addrPort)
- } else if hittingTailFS {
- if !ns.lb.TailFSAccessEnabled() {
- c.Close()
- return
- }
- err := ns.tailFSForLocal.HandleConn(c, net.TCPAddrFromAddrPort(addrPort))
- if err != nil {
- ns.logf("netstack: tailfs.HandleConn: %v", err)
- }
- }
+ go ns.dns.HandleTCPConn(c, addrPort)
return
}