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🅱️a02:0/112
$ tailscale debug via fd7a:115c:a1e0:b1a:0🅱️a02:0/112
site 11 (0xb), 10.2.0.0/16

Previously: 3ae701f0eb

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 <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2022-04-20 14:25:27 -07:00 committed by Brad Fitzpatrick
parent f74ee80abe
commit d413850bd7
2 changed files with 67 additions and 0 deletions

View File

@ -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 <site-id> <v4-cidr> or <v6-route>")
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
}

View File

@ -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
}