summaryrefslogtreecommitdiffhomepage
path: root/cmd/tailscaled/cli/ip.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/tailscaled/cli/ip.go')
-rw-r--r--cmd/tailscaled/cli/ip.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/cmd/tailscaled/cli/ip.go b/cmd/tailscaled/cli/ip.go
new file mode 100644
index 000000000..2122d6022
--- /dev/null
+++ b/cmd/tailscaled/cli/ip.go
@@ -0,0 +1,105 @@
+// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cli
+
+import (
+ "context"
+ "errors"
+ "flag"
+ "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] [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)
+ fs.BoolVar(&ipArgs.want4, "4", false, "only print IPv4 address")
+ fs.BoolVar(&ipArgs.want6, "6", false, "only print IPv6 address")
+ return fs
+ })(),
+}
+
+var ipArgs struct {
+ want4 bool
+ want6 bool
+}
+
+func runIP(ctx context.Context, args []string) error {
+ 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")
+ }
+ if !v4 && !v6 {
+ v4, v6 = true, true
+ }
+ st, err := tailscale.Status(ctx)
+ if err != nil {
+ return err
+ }
+ 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 ips {
+ if ip.Is4() && v4 || ip.Is6() && v6 {
+ match = true
+ fmt.Println(ip)
+ }
+ }
+ if !match {
+ if ipArgs.want4 {
+ return errors.New("no Tailscale IPv4 address")
+ }
+ if ipArgs.want6 {
+ return errors.New("no Tailscale IPv6 address")
+ }
+ }
+ 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
+}