|
|
|
|
@ -12,6 +12,8 @@ import ( |
|
|
|
|
"io" |
|
|
|
|
"log" |
|
|
|
|
"net" |
|
|
|
|
"os/exec" |
|
|
|
|
"runtime" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
"sync" |
|
|
|
|
@ -35,6 +37,7 @@ import ( |
|
|
|
|
"tailscale.com/net/tsaddr" |
|
|
|
|
"tailscale.com/net/tsdial" |
|
|
|
|
"tailscale.com/net/tstun" |
|
|
|
|
"tailscale.com/syncs" |
|
|
|
|
"tailscale.com/types/logger" |
|
|
|
|
"tailscale.com/types/netmap" |
|
|
|
|
"tailscale.com/wgengine" |
|
|
|
|
@ -377,11 +380,69 @@ func (ns *Impl) shouldProcessInbound(p *packet.Parsed, t *tstun.Wrapper) bool { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var userPingSem = syncs.NewSemaphore(20) // 20 child ping processes at once
|
|
|
|
|
|
|
|
|
|
// userPing tried to ping dstIP and if it succeeds, injects pingResPkt
|
|
|
|
|
// into the tundev.
|
|
|
|
|
//
|
|
|
|
|
// It's used in userspace/netstack mode when we don't have kernel
|
|
|
|
|
// support or raw socket access. As such, this does the dumbest thing
|
|
|
|
|
// that can work: runs the ping command. It's not super efficient, so
|
|
|
|
|
// it bounds the number of pings going on at once. The idea is that
|
|
|
|
|
// people only use ping occasionally to see if their internet's working
|
|
|
|
|
// so this doesn't need to be great.
|
|
|
|
|
//
|
|
|
|
|
// TODO(bradfitz): when we're running on Windows as the system user, use
|
|
|
|
|
// raw socket APIs instead of ping child processes.
|
|
|
|
|
func (ns *Impl) userPing(dstIP netaddr.IP, pingResPkt []byte) { |
|
|
|
|
if !userPingSem.TryAcquire() { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
defer userPingSem.Release() |
|
|
|
|
|
|
|
|
|
t0 := time.Now() |
|
|
|
|
var err error |
|
|
|
|
switch runtime.GOOS { |
|
|
|
|
case "windows": |
|
|
|
|
err = exec.Command("ping", "-n", "1", "-w", "3000", dstIP.String()).Run() |
|
|
|
|
default: |
|
|
|
|
err = exec.Command("ping", "-c", "1", "-W", "3", dstIP.String()).Run() |
|
|
|
|
} |
|
|
|
|
d := time.Since(t0) |
|
|
|
|
if err != nil { |
|
|
|
|
ns.logf("exec ping of %v failed in %v", dstIP, d) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if debugNetstack { |
|
|
|
|
ns.logf("exec pinged %v in %v", dstIP, time.Since(t0)) |
|
|
|
|
} |
|
|
|
|
if err := ns.tundev.InjectOutbound(pingResPkt); err != nil { |
|
|
|
|
ns.logf("InjectOutbound ping response: %v", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Response { |
|
|
|
|
if !ns.shouldProcessInbound(p, t) { |
|
|
|
|
// Let the host network stack (if any) deal with it.
|
|
|
|
|
return filter.Accept |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
destIP := p.Dst.IP() |
|
|
|
|
if p.IsEchoRequest() && ns.ProcessSubnets && !tsaddr.IsTailscaleIP(destIP) { |
|
|
|
|
var pong []byte // the reply to the ping, if our relayed ping works
|
|
|
|
|
if destIP.Is4() { |
|
|
|
|
h := p.ICMP4Header() |
|
|
|
|
h.ToResponse() |
|
|
|
|
pong = packet.Generate(&h, p.Payload()) |
|
|
|
|
} else if destIP.Is6() { |
|
|
|
|
h := p.ICMP6Header() |
|
|
|
|
h.ToResponse() |
|
|
|
|
pong = packet.Generate(&h, p.Payload()) |
|
|
|
|
} |
|
|
|
|
go ns.userPing(destIP, pong) |
|
|
|
|
return filter.DropSilently |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var pn tcpip.NetworkProtocolNumber |
|
|
|
|
switch p.IPVersion { |
|
|
|
|
case 4: |
|
|
|
|
|