summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNaman Sood <mail@nsood.in>2021-03-15 17:59:35 -0400
committerNaman Sood <mail@nsood.in>2021-03-15 18:14:09 -0400
commit770aa71ffbfbb01f7c8bfc65ce5e0505c783efde (patch)
tree32356706a18c3b5604b2f7c6785c4c57f7591266
parent44ab0acbdbd8b79af74ea1f8187c4d782ce38635 (diff)
downloadtailscale-770aa71ffbfbb01f7c8bfc65ce5e0505c783efde.tar.xz
tailscale-770aa71ffbfbb01f7c8bfc65ce5e0505c783efde.zip
client, cmd/hello, ipn, wgengine: fix whois for netstack-forwarded connections
Updates #504 Updates #707 Signed-off-by: Naman Sood <mail@nsood.in>
-rw-r--r--client/tailscale/tailscale.go12
-rw-r--r--cmd/hello/hello.go22
-rw-r--r--ipn/ipnlocal/local.go19
-rw-r--r--ipn/localapi/localapi.go14
-rw-r--r--wgengine/netstack/netstack.go20
-rw-r--r--wgengine/userspace.go26
-rw-r--r--wgengine/watchdog.go10
-rw-r--r--wgengine/wgengine.go13
8 files changed, 108 insertions, 28 deletions
diff --git a/client/tailscale/tailscale.go b/client/tailscale/tailscale.go
index 9ebbf8fcb..a6d304332 100644
--- a/client/tailscale/tailscale.go
+++ b/client/tailscale/tailscale.go
@@ -56,17 +56,7 @@ func DoLocalRequest(req *http.Request) (*http.Response, error) {
// WhoIs returns the owner of the remoteAddr, which must be an IP or IP:port.
func WhoIs(ctx context.Context, remoteAddr string) (*tailcfg.WhoIsResponse, error) {
- var ip string
- if net.ParseIP(remoteAddr) != nil {
- ip = remoteAddr
- } else {
- var err error
- ip, _, err = net.SplitHostPort(remoteAddr)
- if err != nil {
- return nil, fmt.Errorf("invalid remoteAddr %q", remoteAddr)
- }
- }
- req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/whois?ip="+url.QueryEscape(ip), nil)
+ req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/whois?addr="+url.QueryEscape(remoteAddr), nil)
if err != nil {
return nil, err
}
diff --git a/cmd/hello/hello.go b/cmd/hello/hello.go
index fc7343150..1c14fb71b 100644
--- a/cmd/hello/hello.go
+++ b/cmd/hello/hello.go
@@ -13,12 +13,12 @@ import (
"html/template"
"io/ioutil"
"log"
- "net"
"net/http"
"os"
"strings"
"tailscale.com/client/tailscale"
+ "tailscale.com/tailcfg"
)
var (
@@ -107,6 +107,23 @@ type tmplData struct {
IP string // "100.2.3.4"
}
+func tailscaleIP(who *tailcfg.WhoIsResponse) string {
+ if who == nil {
+ return ""
+ }
+ for _, nodeIP := range who.Node.Addresses {
+ if nodeIP.IP.Is4() && nodeIP.IsSingleIP() {
+ return nodeIP.IP.String()
+ }
+ }
+ for _, nodeIP := range who.Node.Addresses {
+ if nodeIP.IsSingleIP() {
+ return nodeIP.IP.String()
+ }
+ }
+ return ""
+}
+
func root(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil && *httpsAddr != "" {
host := r.Host
@@ -146,14 +163,13 @@ func root(w http.ResponseWriter, r *http.Request) {
return
}
} else {
- ip, _, _ := net.SplitHostPort(r.RemoteAddr)
data = tmplData{
DisplayName: who.UserProfile.DisplayName,
LoginName: who.UserProfile.LoginName,
ProfilePicURL: who.UserProfile.ProfilePicURL,
MachineName: firstLabel(who.Node.ComputedName),
MachineOS: who.Node.Hostinfo.OS,
- IP: ip,
+ IP: tailscaleIP(who),
}
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index 89b2562a9..6036d5000 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -254,14 +254,25 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
}
}
-// WhoIs reports the node and user who owns the node with the given IP.
+// 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 ok == true, n and u are valid.
-func (b *LocalBackend) WhoIs(ip netaddr.IP) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
+func (b *LocalBackend) WhoIs(ipp netaddr.IPPort) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
b.mu.Lock()
defer b.mu.Unlock()
- n, ok = b.nodeByAddr[ip]
+ n, ok = b.nodeByAddr[ipp.IP]
if !ok {
- return nil, u, false
+ var ip netaddr.IP
+ if ipp.Port != 0 {
+ ip, ok = b.e.WhoIsIPPort(ipp)
+ }
+ if !ok {
+ return nil, u, false
+ }
+ n, ok = b.nodeByAddr[ip]
+ if !ok {
+ return nil, u, false
+ }
}
u, ok = b.netMap.UserProfiles[n.User]
if !ok {
diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go
index 7162a65ee..4e0dba3da 100644
--- a/ipn/localapi/localapi.go
+++ b/ipn/localapi/localapi.go
@@ -67,21 +67,21 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
return
}
b := h.b
- var ip netaddr.IP
- if v := r.FormValue("ip"); v != "" {
+ var ipp netaddr.IPPort
+ if v := r.FormValue("addr"); v != "" {
var err error
- ip, err = netaddr.ParseIP(r.FormValue("ip"))
+ ipp, err = netaddr.ParseIPPort(v)
if err != nil {
- http.Error(w, "invalid 'ip' parameter", 400)
+ http.Error(w, "invalid 'addr' parameter", 400)
return
}
} else {
- http.Error(w, "missing 'ip' parameter", 400)
+ http.Error(w, "missing 'addr' parameter", 400)
return
}
- n, u, ok := b.WhoIs(ip)
+ n, u, ok := b.WhoIs(ipp)
if !ok {
- http.Error(w, "no match for IP", 404)
+ http.Error(w, "no match for IP:port", 404)
return
}
res := &tailcfg.WhoIsResponse{
diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go
index 61b1cdb9b..1131490ec 100644
--- a/wgengine/netstack/netstack.go
+++ b/wgengine/netstack/netstack.go
@@ -367,6 +367,11 @@ func (ns *Impl) forwardTCP(client *gonet.TCPConn, wq *waiter.Queue, port uint16)
return
}
defer server.Close()
+ backendLocalAddr := server.LocalAddr().(*net.TCPAddr)
+ backendLocalIPPort, _ := netaddr.FromStdAddr(backendLocalAddr.IP, backendLocalAddr.Port, backendLocalAddr.Zone)
+ clientRemoteIP, _ := netaddr.FromStdIP(client.RemoteAddr().(*net.TCPAddr).IP)
+ ns.e.RegisterIPPortIdentity(backendLocalIPPort, clientRemoteIP)
+ defer ns.e.UnregisterIPPortIdentity(backendLocalIPPort)
connClosed := make(chan error, 2)
go func() {
_, err := io.Copy(server, client)
@@ -406,19 +411,28 @@ func (ns *Impl) acceptUDP(r *udp.ForwarderRequest) {
func (ns *Impl) forwardUDP(client *gonet.UDPConn, wq *waiter.Queue, clientLocalAddr, clientRemoteAddr tcpip.FullAddress) {
port := clientLocalAddr.Port
ns.logf("[v2] netstack: forwarding incoming UDP connection on port %v", port)
- backendLocalAddr := &net.UDPAddr{Port: int(clientRemoteAddr.Port)}
+ backendListenAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(clientRemoteAddr.Port)}
backendRemoteAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(port)}
- backendConn, err := net.ListenUDP("udp4", backendLocalAddr)
+ backendConn, err := net.ListenUDP("udp4", backendListenAddr)
if err != nil {
ns.logf("netstack: could not bind local port %v: %v, trying again with random port", clientRemoteAddr.Port, err)
- backendConn, err = net.ListenUDP("udp4", nil)
+ backendListenAddr.Port = 0
+ backendConn, err = net.ListenUDP("udp4", backendListenAddr)
if err != nil {
ns.logf("netstack: could not connect to local UDP server on port %v: %v", port, err)
return
}
}
+ backendLocalAddr := backendConn.LocalAddr().(*net.UDPAddr)
+ backendLocalIPPort, ok := netaddr.FromStdAddr(backendListenAddr.IP, backendLocalAddr.Port, backendLocalAddr.Zone)
+ if !ok {
+ ns.logf("could not get backend local IP:port from %v:%v", backendLocalAddr.IP, backendLocalAddr.Port)
+ }
+ clientRemoteIP, _ := netaddr.FromStdIP(net.ParseIP(clientRemoteAddr.Addr.String()))
+ ns.e.RegisterIPPortIdentity(backendLocalIPPort, clientRemoteIP)
ctx, cancel := context.WithCancel(context.Background())
timer := time.AfterFunc(2*time.Minute, func() {
+ ns.e.UnregisterIPPortIdentity(backendLocalIPPort)
ns.logf("netstack: UDP session between %s and %s timed out", clientRemoteAddr, backendRemoteAddr)
cancel()
client.Close()
diff --git a/wgengine/userspace.go b/wgengine/userspace.go
index 1baf7f040..fccca8c8b 100644
--- a/wgengine/userspace.go
+++ b/wgengine/userspace.go
@@ -125,6 +125,7 @@ type userspaceEngine struct {
pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
networkMapCallbacks map[*someHandle]NetworkMapCallback
+ tsIPByIPPort map[netaddr.IPPort]netaddr.IP // allows registration of IP:ports as belonging to a certain Tailscale IP for whois lookups
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
}
@@ -1341,6 +1342,31 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
e.magicConn.Ping(ip, cb)
}
+func (e *userspaceEngine) RegisterIPPortIdentity(ipport netaddr.IPPort, tsIP netaddr.IP) {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ if e.tsIPByIPPort == nil {
+ e.tsIPByIPPort = make(map[netaddr.IPPort]netaddr.IP)
+ }
+ e.tsIPByIPPort[ipport] = tsIP
+}
+
+func (e *userspaceEngine) UnregisterIPPortIdentity(ipport netaddr.IPPort) {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ if e.tsIPByIPPort == nil {
+ return
+ }
+ delete(e.tsIPByIPPort, ipport)
+}
+
+func (e *userspaceEngine) WhoIsIPPort(ipport netaddr.IPPort) (tsIP netaddr.IP, ok bool) {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ tsIP, ok = e.tsIPByIPPort[ipport]
+ return tsIP, ok
+}
+
// diagnoseTUNFailure is called if tun.CreateTUN fails, to poke around
// the system and log some diagnostic info that might help debug why
// TUN failed. Because TUN's already failed and things the program's
diff --git a/wgengine/watchdog.go b/wgengine/watchdog.go
index 3b96f2e8e..f4f7d3085 100644
--- a/wgengine/watchdog.go
+++ b/wgengine/watchdog.go
@@ -120,6 +120,16 @@ func (e *watchdogEngine) DiscoPublicKey() (k tailcfg.DiscoKey) {
func (e *watchdogEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
e.watchdog("Ping", func() { e.wrap.Ping(ip, cb) })
}
+func (e *watchdogEngine) RegisterIPPortIdentity(ipp netaddr.IPPort, tsIP netaddr.IP) {
+ e.watchdog("RegisterIPPortIdentity", func() { e.wrap.RegisterIPPortIdentity(ipp, tsIP) })
+}
+func (e *watchdogEngine) UnregisterIPPortIdentity(ipp netaddr.IPPort) {
+ e.watchdog("UnregisterIPPortIdentity", func() { e.wrap.UnregisterIPPortIdentity(ipp) })
+}
+func (e *watchdogEngine) WhoIsIPPort(ipp netaddr.IPPort) (tsIP netaddr.IP, ok bool) {
+ e.watchdog("UnregisterIPPortIdentity", func() { tsIP, ok = e.wrap.WhoIsIPPort(ipp) })
+ return tsIP, ok
+}
func (e *watchdogEngine) Close() {
e.watchdog("Close", e.wrap.Close)
}
diff --git a/wgengine/wgengine.go b/wgengine/wgengine.go
index 57bc7cb77..c8e7963db 100644
--- a/wgengine/wgengine.go
+++ b/wgengine/wgengine.go
@@ -137,4 +137,17 @@ type Engine interface {
// Ping is a request to start a discovery ping with the peer handling
// the given IP and then call cb with its ping latency & method.
Ping(ip netaddr.IP, cb func(*ipnstate.PingResult))
+
+ // RegisterIPPortIdentity registers a given node (identified by its
+ // Tailscale IP) as temporarily having the given IP:port for whois lookups.
+ // The IP:port is generally a localhost IP and an ephemeral port, used
+ // while proxying connections to localhost.
+ RegisterIPPortIdentity(netaddr.IPPort, netaddr.IP)
+
+ // UnregisterIPPortIdentity removes a temporary IP:port registration.
+ UnregisterIPPortIdentity(netaddr.IPPort)
+
+ // WhoIsIPPort looks up an IP:port in the temporary registrations,
+ // and returns a matching Tailscale IP, if it exists.
+ WhoIsIPPort(netaddr.IPPort) (netaddr.IP, bool)
}