cmd/tailscale/cli: let ip take a peername
"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)
}
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
+}
