wgengine, net/packet, cmd/tailscale: add ICMP echo

Updates tailscale/corp#754

Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
James Tucker
2022-04-21 18:49:01 -07:00
committed by James Tucker
parent 66f9292835
commit ae483d3446
14 changed files with 215 additions and 45 deletions

View File

@ -18,6 +18,7 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
)
var pingCmd = &ffcli.Command{
@ -26,7 +27,7 @@ var pingCmd = &ffcli.Command{
ShortHelp: "Ping a host at the Tailscale layer, see how it routed",
LongHelp: strings.TrimSpace(`
The 'tailscale ping' command pings a peer node at the Tailscale layer
The 'tailscale ping' command pings a peer node from the Tailscale layer
and reports which route it took for each response. The first ping or
so will likely go over DERP (Tailscale's TCP relay protocol) while NAT
traversal finds a direct path through.
@ -48,7 +49,8 @@ relay node.
fs := newFlagSet("ping")
fs.BoolVar(&pingArgs.verbose, "verbose", false, "verbose output")
fs.BoolVar(&pingArgs.untilDirect, "until-direct", true, "stop once a direct path is established")
fs.BoolVar(&pingArgs.tsmp, "tsmp", false, "do a TSMP-level ping (through IP + wireguard, but not involving host OS stack)")
fs.BoolVar(&pingArgs.tsmp, "tsmp", false, "do a TSMP-level ping (through wireguard, but not either host OS stack)")
fs.BoolVar(&pingArgs.icmp, "icmp", false, "do a ICMP-level ping (through wireguard, but not the local host OS stack)")
fs.IntVar(&pingArgs.num, "c", 10, "max number of pings to send")
fs.DurationVar(&pingArgs.timeout, "timeout", 5*time.Second, "timeout before giving up on a ping")
return fs
@ -60,9 +62,20 @@ var pingArgs struct {
untilDirect bool
verbose bool
tsmp bool
icmp bool
timeout time.Duration
}
func pingType() tailcfg.PingType {
if pingArgs.tsmp {
return tailcfg.PingTSMP
}
if pingArgs.icmp {
return tailcfg.PingICMP
}
return tailcfg.PingDisco
}
func runPing(ctx context.Context, args []string) error {
st, err := localClient.Status(ctx)
if err != nil {
@ -111,7 +124,7 @@ func runPing(ctx context.Context, args []string) error {
anyPong := false
for {
n++
bc.Ping(ip, pingArgs.tsmp)
bc.Ping(ip, pingType())
timer := time.NewTimer(pingArgs.timeout)
select {
case <-timer.C:
@ -132,10 +145,10 @@ func runPing(ctx context.Context, args []string) error {
if pr.DERPRegionID != 0 {
via = fmt.Sprintf("DERP(%s)", pr.DERPRegionCode)
}
if pingArgs.tsmp {
if via == "" {
// TODO(bradfitz): populate the rest of ipnstate.PingResult for TSMP queries?
// For now just say it came via TSMP.
via = "TSMP"
// For now just say which protocol it used.
via = string(pingType())
}
anyPong = true
extra := ""
@ -143,7 +156,7 @@ func runPing(ctx context.Context, args []string) error {
extra = fmt.Sprintf(", %d", pr.PeerAPIPort)
}
printf("pong from %s (%s%s) via %v in %v\n", pr.NodeName, pr.NodeIP, extra, via, latency)
if pingArgs.tsmp {
if pingArgs.tsmp || pingArgs.icmp {
return nil
}
if pr.Endpoint != "" && pingArgs.untilDirect {