diff --git a/cmd/tailscale/cli/ip.go b/cmd/tailscale/cli/ip.go index 053ea165e..2122d6022 100644 --- a/cmd/tailscale/cli/ip.go +++ b/cmd/tailscale/cli/ip.go @@ -11,13 +11,16 @@ import ( "fmt" "github.com/peterbourgon/ff/v2/ffcli" + "inet.af/netaddr" "tailscale.com/client/tailscale" + "tailscale.com/ipn/ipnstate" ) var ipCmd = &ffcli.Command{ Name: "ip", - ShortUsage: "ip [-4] [-6]", - ShortHelp: "Show this machine's current Tailscale IP address(es)", + ShortUsage: "ip [-4] [-6] [peername]", + ShortHelp: "Show current Tailscale IP address(es)", + LongHelp: "Shows the Tailscale IP address of the current machine without an argument. With an argument, it shows the IP of a named peer.", Exec: runIP, FlagSet: (func() *flag.FlagSet { fs := flag.NewFlagSet("ip", flag.ExitOnError) @@ -33,9 +36,14 @@ var ipArgs struct { } func runIP(ctx context.Context, args []string) error { - if len(args) > 0 { + if len(args) > 1 { return errors.New("unknown arguments") } + var of string + if len(args) == 1 { + of = args[0] + } + v4, v6 := ipArgs.want4, ipArgs.want6 if v4 && v6 { return errors.New("tailscale up -4 and -6 are mutually exclusive") @@ -47,11 +55,24 @@ func runIP(ctx context.Context, args []string) error { if err != nil { return err } - if len(st.TailscaleIPs) == 0 { + ips := st.TailscaleIPs + if of != "" { + ip, err := tailscaleIPFromArg(ctx, of) + if err != nil { + return err + } + peer, ok := peerMatchingIP(st, ip) + if !ok { + return fmt.Errorf("no peer found with IP %v", ip) + } + ips = peer.TailscaleIPs + } + if len(ips) == 0 { return fmt.Errorf("no current Tailscale IPs; state: %v", st.BackendState) } + match := false - for _, ip := range st.TailscaleIPs { + for _, ip := range ips { if ip.Is4() && v4 || ip.Is6() && v6 { match = true fmt.Println(ip) @@ -67,3 +88,18 @@ func runIP(ctx context.Context, args []string) error { } return nil } + +func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus, ok bool) { + ip, err := netaddr.ParseIP(ipStr) + if err != nil { + return + } + for _, ps = range st.Peer { + for _, pip := range ps.TailscaleIPs { + if ip == pip { + return ps, true + } + } + } + return nil, false +}