From d413850bd7b6e6d7bee5f258e6e5251fb8f134d2 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 20 Apr 2022 14:25:27 -0700 Subject: [PATCH] cmd/tailscale: add "debug via" subcommand to do CIDR math for via ranges $ tailscale debug via 0xb 10.2.0.0/16 fd7a:115c:a1e0:b1a:0:b:a02:0/112 $ tailscale debug via fd7a:115c:a1e0:b1a:0:b:a02:0/112 site 11 (0xb), 10.2.0.0/16 Previously: 3ae701f0ebe053a1f7b6a3fa345a56b3132c818f This adds a little debug tool to do CIDR math to make converting between those ranges easier for now. Updates #3616 Change-Id: I98302e95d17765bfaced3ecbb71cbd43e84bff46 Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/cli/debug.go | 51 ++++++++++++++++++++++++++++++++++++++ net/tsaddr/tsaddr.go | 16 ++++++++++++ 2 files changed, 67 insertions(+) diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go index 0d11e165a..5f879d4a7 100644 --- a/cmd/tailscale/cli/debug.go +++ b/cmd/tailscale/cli/debug.go @@ -8,6 +8,7 @@ "bufio" "bytes" "context" + "encoding/binary" "encoding/json" "errors" "flag" @@ -21,9 +22,11 @@ "time" "github.com/peterbourgon/ff/v3/ffcli" + "inet.af/netaddr" "tailscale.com/client/tailscale" "tailscale.com/hostinfo" "tailscale.com/ipn" + "tailscale.com/net/tsaddr" "tailscale.com/paths" "tailscale.com/safesocket" ) @@ -106,6 +109,11 @@ return fs })(), }, + { + Name: "via", + Exec: runVia, + ShortHelp: "convert between site-specific IPv4 CIDRs and IPv6 'via' routes", + }, }, } @@ -348,3 +356,46 @@ type change struct { time.Sleep(time.Second) } } + +func runVia(ctx context.Context, args []string) error { + switch len(args) { + default: + return errors.New("expect either or ") + case 1: + ipp, err := netaddr.ParseIPPrefix(args[0]) + if err != nil { + return err + } + if !ipp.IP().Is6() { + return errors.New("with one argument, expect an IPv6 CIDR") + } + if !tsaddr.TailscaleViaRange().Contains(ipp.IP()) { + return errors.New("not a via route") + } + if ipp.Bits() < 96 { + return errors.New("short length, want /96 or more") + } + v4 := tsaddr.UnmapVia(ipp.IP()) + a := ipp.IP().As16() + siteID := binary.BigEndian.Uint32(a[8:12]) + fmt.Printf("site %v (0x%x), %v\n", siteID, siteID, netaddr.IPPrefixFrom(v4, ipp.Bits()-96)) + case 2: + siteID, err := strconv.ParseUint(args[0], 0, 32) + if err != nil { + return fmt.Errorf("invalid site-id %q; must be decimal or hex with 0x prefix", args[0]) + } + if siteID > 0xff { + return fmt.Errorf("site-id values over 255 are currently reserved") + } + ipp, err := netaddr.ParseIPPrefix(args[1]) + if err != nil { + return err + } + via, err := tsaddr.MapVia(uint32(siteID), ipp) + if err != nil { + return err + } + fmt.Println(via) + } + return nil +} diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go index 0f32b069b..85ab89b09 100644 --- a/net/tsaddr/tsaddr.go +++ b/net/tsaddr/tsaddr.go @@ -6,6 +6,8 @@ package tsaddr import ( + "encoding/binary" + "errors" "sync" "inet.af/netaddr" @@ -280,3 +282,17 @@ func UnmapVia(ip netaddr.IP) netaddr.IP { } return ip } + +// MapVia returns an IPv6 "via" route for an IPv4 CIDR in a given siteID. +func MapVia(siteID uint32, v4 netaddr.IPPrefix) (via netaddr.IPPrefix, err error) { + if !v4.IP().Is4() { + return via, errors.New("want IPv4 CIDR with a site ID") + } + viaRange16 := TailscaleViaRange().IP().As16() + var a [16]byte + copy(a[:], viaRange16[:8]) + binary.BigEndian.PutUint32(a[8:], siteID) + ip4a := v4.IP().As4() + copy(a[12:], ip4a[:]) + return netaddr.IPPrefixFrom(netaddr.IPFrom16(a), v4.Bits()+64+32), nil +}