ipnlocal,proxymap,wgengine/netstack: add optional WhoIs/proxymap debug
Updates tailscale/corp#20600 Change-Id: I2bb17af0f40603ada1ba4cecc087443e00f9392a Co-authored-by: Maisem Ali <maisem@tailscale.com> Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
7d16af8d95
commit
9f9470fc10
|
@ -1139,6 +1139,8 @@ func (b *LocalBackend) WhoIsNodeKey(k key.NodePublic) (n tailcfg.NodeView, u tai
|
||||||
return n, u, false
|
return n, u, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugWhoIs = envknob.RegisterBool("TS_DEBUG_WHOIS")
|
||||||
|
|
||||||
// WhoIs reports the node and user who owns the node with the given IP:port.
|
// 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.
|
// If the IP address is a Tailscale IP, the provided port may be 0.
|
||||||
//
|
//
|
||||||
|
@ -1154,6 +1156,14 @@ func (b *LocalBackend) WhoIs(proto string, ipp netip.AddrPort) (n tailcfg.NodeVi
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
failf := func(format string, args ...any) (tailcfg.NodeView, tailcfg.UserProfile, bool) {
|
||||||
|
if debugWhoIs() {
|
||||||
|
args = append([]any{proto, ipp}, args...)
|
||||||
|
b.logf("whois(%q, %v) :"+format, args...)
|
||||||
|
}
|
||||||
|
return zero, u, false
|
||||||
|
}
|
||||||
|
|
||||||
nid, ok := b.nodeByAddr[ipp.Addr()]
|
nid, ok := b.nodeByAddr[ipp.Addr()]
|
||||||
if !ok {
|
if !ok {
|
||||||
var ip netip.Addr
|
var ip netip.Addr
|
||||||
|
@ -1174,15 +1184,15 @@ func (b *LocalBackend) WhoIs(proto string, ipp netip.AddrPort) (n tailcfg.NodeVi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return zero, u, false
|
return failf("no IP found in ProxyMapper for %v", ipp)
|
||||||
}
|
}
|
||||||
nid, ok = b.nodeByAddr[ip]
|
nid, ok = b.nodeByAddr[ip]
|
||||||
if !ok {
|
if !ok {
|
||||||
return zero, u, false
|
return failf("no node for proxymapped IP %v", ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b.netMap == nil {
|
if b.netMap == nil {
|
||||||
return zero, u, false
|
return failf("no netmap")
|
||||||
}
|
}
|
||||||
n, ok = b.peers[nid]
|
n, ok = b.peers[nid]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1194,7 +1204,7 @@ func (b *LocalBackend) WhoIs(proto string, ipp netip.AddrPort) (n tailcfg.NodeVi
|
||||||
}
|
}
|
||||||
u, ok = b.netMap.UserProfiles[n.User()]
|
u, ok = b.netMap.UserProfiles[n.User()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return zero, u, false
|
return failf("no userprofile for node %v", n.Key())
|
||||||
}
|
}
|
||||||
return n, u, true
|
return n, u, true
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,13 @@
|
||||||
package proxymap
|
package proxymap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/util/mak"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mapper tracks which localhost ip:ports correspond to which remote Tailscale
|
// Mapper tracks which localhost ip:ports correspond to which remote Tailscale
|
||||||
|
@ -24,7 +28,26 @@ type Mapper struct {
|
||||||
// keyed first by the protocol ("tcp" or "udp"), then by the IP:port.
|
// keyed first by the protocol ("tcp" or "udp"), then by the IP:port.
|
||||||
//
|
//
|
||||||
// +checklocks:mu
|
// +checklocks:mu
|
||||||
m map[string]map[netip.AddrPort]netip.Addr
|
m map[mappingKey]netip.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable representation of the current mappings.
|
||||||
|
func (m *Mapper) String() string {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
if len(m.m) == 0 {
|
||||||
|
return "no mappings"
|
||||||
|
}
|
||||||
|
var sb strings.Builder
|
||||||
|
for k, v := range m.m {
|
||||||
|
fmt.Fprintf(&sb, "%v/%v=>%v\n", k.proto, k.ap, v)
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type mappingKey struct {
|
||||||
|
proto string
|
||||||
|
ap netip.AddrPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterIPPortIdentity registers a given node (identified by its
|
// RegisterIPPortIdentity registers a given node (identified by its
|
||||||
|
@ -36,18 +59,15 @@ type Mapper struct {
|
||||||
//
|
//
|
||||||
// The proto is the network protocol that is being proxied; it must be "tcp" or
|
// The proto is the network protocol that is being proxied; it must be "tcp" or
|
||||||
// "udp" (not e.g. "tcp4", "udp6", etc.)
|
// "udp" (not e.g. "tcp4", "udp6", etc.)
|
||||||
func (m *Mapper) RegisterIPPortIdentity(proto string, ipport netip.AddrPort, tsIP netip.Addr) {
|
func (m *Mapper) RegisterIPPortIdentity(proto string, ipport netip.AddrPort, tsIP netip.Addr) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
if m.m == nil {
|
k := mappingKey{proto, ipport}
|
||||||
m.m = make(map[string]map[netip.AddrPort]netip.Addr)
|
if v, ok := m.m[k]; ok {
|
||||||
|
return fmt.Errorf("proxymap: RegisterIPPortIdentity: already registered: %v/%v=>%v", k.proto, k.ap, v)
|
||||||
}
|
}
|
||||||
p, ok := m.m[proto]
|
mak.Set(&m.m, k, tsIP)
|
||||||
if !ok {
|
return nil
|
||||||
p = make(map[netip.AddrPort]netip.Addr)
|
|
||||||
m.m[proto] = p
|
|
||||||
}
|
|
||||||
p[ipport] = tsIP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnregisterIPPortIdentity removes a temporary IP:port registration
|
// UnregisterIPPortIdentity removes a temporary IP:port registration
|
||||||
|
@ -55,8 +75,8 @@ func (m *Mapper) RegisterIPPortIdentity(proto string, ipport netip.AddrPort, tsI
|
||||||
func (m *Mapper) UnregisterIPPortIdentity(proto string, ipport netip.AddrPort) {
|
func (m *Mapper) UnregisterIPPortIdentity(proto string, ipport netip.AddrPort) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
p := m.m[proto]
|
k := mappingKey{proto, ipport}
|
||||||
delete(p, ipport) // safe to delete from a nil map
|
delete(m.m, k) // safe to delete from a nil map
|
||||||
}
|
}
|
||||||
|
|
||||||
var whoIsSleeps = [...]time.Duration{
|
var whoIsSleeps = [...]time.Duration{
|
||||||
|
@ -75,13 +95,11 @@ func (m *Mapper) WhoIsIPPort(proto string, ipport netip.AddrPort) (tsIP netip.Ad
|
||||||
// so loop a few times for now waiting for the registration
|
// so loop a few times for now waiting for the registration
|
||||||
// to appear.
|
// to appear.
|
||||||
// TODO(bradfitz,namansood): remove this once #1616 is fixed.
|
// TODO(bradfitz,namansood): remove this once #1616 is fixed.
|
||||||
|
k := mappingKey{proto, ipport}
|
||||||
for _, d := range whoIsSleeps {
|
for _, d := range whoIsSleeps {
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
p, ok := m.m[proto]
|
tsIP, ok := m.m[k]
|
||||||
if ok {
|
|
||||||
tsIP, ok = p[ipport]
|
|
||||||
}
|
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
if ok {
|
if ok {
|
||||||
return tsIP, true
|
return tsIP, true
|
||||||
|
|
|
@ -1378,12 +1378,23 @@ func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.
|
||||||
var stdDialer net.Dialer
|
var stdDialer net.Dialer
|
||||||
dialFunc = stdDialer.DialContext
|
dialFunc = stdDialer.DialContext
|
||||||
}
|
}
|
||||||
server, err := dialFunc(ctx, "tcp", dialAddrStr)
|
|
||||||
|
// TODO: this is racy, dialing before we register our local address. See
|
||||||
|
// https://github.com/tailscale/tailscale/issues/1616.
|
||||||
|
backend, err := dialFunc(ctx, "tcp", dialAddrStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ns.logf("netstack: could not connect to local server at %s: %v", dialAddr.String(), err)
|
ns.logf("netstack: could not connect to local backend server at %s: %v", dialAddr.String(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer server.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
|
backendLocalAddr := backend.LocalAddr().(*net.TCPAddr)
|
||||||
|
backendLocalIPPort := netaddr.Unmap(backendLocalAddr.AddrPort())
|
||||||
|
if err := ns.pm.RegisterIPPortIdentity("tcp", backendLocalIPPort, clientRemoteIP); err != nil {
|
||||||
|
ns.logf("netstack: could not register TCP mapping %s: %v", backendLocalIPPort, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer ns.pm.UnregisterIPPortIdentity("tcp", backendLocalIPPort)
|
||||||
|
|
||||||
// If we get here, either the getClient call below will succeed and
|
// If we get here, either the getClient call below will succeed and
|
||||||
// return something we can Close, or it will fail and will properly
|
// return something we can Close, or it will fail and will properly
|
||||||
|
@ -1398,17 +1409,13 @@ func (ns *Impl) forwardTCP(getClient func(...tcpip.SettableSocketOption) *gonet.
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
backendLocalAddr := server.LocalAddr().(*net.TCPAddr)
|
|
||||||
backendLocalIPPort := netaddr.Unmap(backendLocalAddr.AddrPort())
|
|
||||||
ns.pm.RegisterIPPortIdentity("tcp", backendLocalIPPort, clientRemoteIP)
|
|
||||||
defer ns.pm.UnregisterIPPortIdentity("tcp", backendLocalIPPort)
|
|
||||||
connClosed := make(chan error, 2)
|
connClosed := make(chan error, 2)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(server, client)
|
_, err := io.Copy(backend, client)
|
||||||
connClosed <- err
|
connClosed <- err
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(client, server)
|
_, err := io.Copy(client, backend)
|
||||||
connClosed <- err
|
connClosed <- err
|
||||||
}()
|
}()
|
||||||
err = <-connClosed
|
err = <-connClosed
|
||||||
|
@ -1620,7 +1627,10 @@ func (ns *Impl) forwardUDP(client *gonet.UDPConn, clientAddr, dstAddr netip.Addr
|
||||||
ns.logf("could not get backend local IP:port from %v:%v", backendLocalAddr.IP, backendLocalAddr.Port)
|
ns.logf("could not get backend local IP:port from %v:%v", backendLocalAddr.IP, backendLocalAddr.Port)
|
||||||
}
|
}
|
||||||
if isLocal {
|
if isLocal {
|
||||||
ns.pm.RegisterIPPortIdentity("udp", backendLocalIPPort, clientAddr.Addr())
|
if err := ns.pm.RegisterIPPortIdentity("udp", backendLocalIPPort, clientAddr.Addr()); err != nil {
|
||||||
|
ns.logf("netstack: could not register UDP mapping %s: %v", backendLocalIPPort, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue