proxymap, various: distinguish between different protocols

Previously, we were registering TCP and UDP connections in the same map,
which could result in erroneously removing a mapping if one of the two
connections completes while the other one is still active.

Add a "proto string" argument to these functions to avoid this.
Additionally, take the "proto" argument in LocalAPI, and plumb that
through from the CLI and add a new LocalClient method.

Updates tailscale/corp#20600

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I35d5efaefdfbf4721e315b8ca123f0c8af9125fb
This commit is contained in:
Andrew Dunham
2024-06-06 14:48:40 -04:00
parent 2cb408f9b1
commit 45d2f4301f
12 changed files with 89 additions and 30 deletions

View File

@@ -995,8 +995,15 @@ func (b *LocalBackend) WhoIsNodeKey(k key.NodePublic) (n tailcfg.NodeView, u tai
// WhoIs reports the node and user who owns the node with the given IP:port.
// If the IP address is a Tailscale IP, the provided port may be 0.
//
// The 'proto' is used when looking up the IP:port in our proxy mapper; it
// tracks which local IP:ports correspond to connections proxied by tailscaled,
// and since tailscaled proxies both TCP and UDP, the 'proto' is needed to look
// up the correct IP:port based on the connection's protocol. If not provided,
// the lookup will be done for TCP and then UDP, in that order.
//
// If ok == true, n and u are valid.
func (b *LocalBackend) WhoIs(ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
func (b *LocalBackend) WhoIs(proto string, ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.UserProfile, ok bool) {
var zero tailcfg.NodeView
b.mu.Lock()
defer b.mu.Unlock()
@@ -1005,7 +1012,20 @@ func (b *LocalBackend) WhoIs(ipp netip.AddrPort) (n tailcfg.NodeView, u tailcfg.
if !ok {
var ip netip.Addr
if ipp.Port() != 0 {
ip, ok = b.sys.ProxyMapper().WhoIsIPPort(ipp)
var protos []string
if proto != "" {
protos = []string{proto}
} else {
// If the user didn't specify a protocol, try all of them
protos = []string{"tcp", "udp"}
}
for _, tryproto := range protos {
ip, ok = b.sys.ProxyMapper().WhoIsIPPort(tryproto, ipp)
if ok {
break
}
}
}
if !ok {
return zero, u, false
@@ -5044,7 +5064,7 @@ func (dt *driveTransport) RoundTrip(req *http.Request) (resp *http.Response, err
dt.b.mu.Lock()
selfNodeKey := dt.b.netMap.SelfNode.Key().ShortString()
dt.b.mu.Unlock()
n, _, ok := dt.b.WhoIs(netip.MustParseAddrPort(req.URL.Host))
n, _, ok := dt.b.WhoIs("tcp", netip.MustParseAddrPort(req.URL.Host))
shareNodeKey := "unknown"
if ok {
shareNodeKey = string(n.Key().ShortString())

View File

@@ -1057,7 +1057,7 @@ func TestWhoIs(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.q, func(t *testing.T) {
nv, up, ok := b.WhoIs(netip.MustParseAddrPort(tt.q))
nv, up, ok := b.WhoIs("", netip.MustParseAddrPort(tt.q))
var got tailcfg.NodeID
if ok {
got = nv.ID()

View File

@@ -187,7 +187,7 @@ func (pln *peerAPIListener) serve() {
func (pln *peerAPIListener) ServeConn(src netip.AddrPort, c net.Conn) {
logf := pln.lb.logf
peerNode, peerUser, ok := pln.lb.WhoIs(src)
peerNode, peerUser, ok := pln.lb.WhoIs("tcp", src)
if !ok {
logf("peerapi: unknown peer %v", src)
c.Close()

View File

@@ -710,7 +710,7 @@ func (b *LocalBackend) addTailscaleIdentityHeaders(r *httputil.ProxyRequest) {
if !ok {
return
}
node, user, ok := b.WhoIs(c.SrcAddr)
node, user, ok := b.WhoIs("tcp", c.SrcAddr)
if !ok {
return // traffic from outside of Tailnet (funneled)
}