ipn,ipn/ipnlocal,cmd/tailscale/cli: add support for passing a route filter via --accept-routes

Signed-off-by: James Tucker <james@tailscale.com>
pull/5770/head
James Tucker 2022-09-27 18:45:02 -07:00
parent 627c60b0d1
commit 919ad7df82
No known key found for this signature in database
3 changed files with 59 additions and 37 deletions

View File

@ -69,14 +69,17 @@ func effectiveGOOS() string {
// acceptRouteDefault returns the CLI's default value of --accept-routes as
// a function of the platform it's running on.
func acceptRouteDefault(goos string) bool {
func acceptRouteDefault(goos string) string {
switch goos {
case "windows":
return true
return "true"
case "darwin":
return version.IsSandboxedMacOS()
if version.IsSandboxedMacOS() {
return "true"
}
return "false"
default:
return false
return "false"
}
}
@ -93,7 +96,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
upf.BoolVar(&upArgs.reset, "reset", false, "reset unspecified settings to their default values")
upf.StringVar(&upArgs.server, "login-server", ipn.DefaultControlURL, "base URL of control server")
upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", acceptRouteDefault(goos), "accept routes advertised by other Tailscale nodes")
upf.StringVar(&upArgs.acceptRoutes, "accept-routes", acceptRouteDefault(goos), "accept routes advertised by other Tailscale nodes")
upf.BoolVar(&upArgs.acceptDNS, "accept-dns", true, "accept DNS configuration from the admin panel")
upf.BoolVar(&upArgs.singleRoutes, "host-routes", true, "install host routes to other Tailscale nodes")
upf.StringVar(&upArgs.exitNodeIP, "exit-node", "", "Tailscale exit node (IP or base name) for internet traffic, or empty string to not use an exit node")
@ -131,7 +134,7 @@ type upArgsT struct {
qr bool
reset bool
server string
acceptRoutes bool
acceptRoutes string
acceptDNS bool
singleRoutes bool
exitNodeIP string
@ -307,7 +310,25 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
prefs := ipn.NewPrefs()
prefs.ControlURL = upArgs.server
prefs.WantRunning = true
prefs.RouteAll = upArgs.acceptRoutes
switch upArgs.acceptRoutes {
case "0", "f", "false":
prefs.RouteAll = false
case "1", "t", "true":
prefs.RouteAll = true
default:
prefs.RouteAll = true
prefs.AcceptRoutesFilter = upArgs.acceptRoutes
// accept-routes accepts an include/exclude ip range of the form:
// 0.0.0.0/0,-192.168.20.0/24
// Ensure that the provided values parse correctly, as the backend can only
// bury errors in the logs.
_, err := ipn.ParseAcceptRoutesFilter(prefs.AcceptRoutesFilter)
if err != nil {
return nil, fmt.Errorf("accept-routes filter %q did not parse: %w", prefs.AcceptRoutesFilter, err)
}
}
if upArgs.exitNodeIP != "" {
if err := prefs.SetExitNodeIP(upArgs.exitNodeIP, st); err != nil {
@ -453,7 +474,7 @@ func runUp(ctx context.Context, args []string) (retErr error) {
if distro.Get() == distro.Synology {
notSupported := "not supported on Synology; see https://github.com/tailscale/tailscale/issues/1995"
if upArgs.acceptRoutes {
if upArgs.acceptRoutes != "" && upArgs.acceptRoutes != "f" && upArgs.acceptRoutes != "false" {
return errors.New("--accept-routes is " + notSupported)
}
if upArgs.exitNodeIP != "" {
@ -735,10 +756,10 @@ func init() {
// And this flag has two ipn.Prefs:
addPrefFlagMapping("exit-node", "ExitNodeIP", "ExitNodeID")
addPrefFlagMapping("accept-routes", "RouteAll", "AcceptRoutesFilter")
// The rest are 1:1:
addPrefFlagMapping("accept-dns", "CorpDNS")
addPrefFlagMapping("accept-routes", "RouteAll")
addPrefFlagMapping("advertise-tags", "AdvertiseTags")
addPrefFlagMapping("host-routes", "AllowSingleHosts")
addPrefFlagMapping("hostname", "Hostname")

View File

@ -2775,32 +2775,6 @@ func ipPrefixLess(ri, rj netip.Prefix) bool {
return ri.Addr().Less(rj.Addr())
}
func (b *LocalBackend) parseAcceptRoutesFilter(acceptFilter string) (*netipx.IPSet, error) {
var acceptFilterBuilder netipx.IPSetBuilder
for _, af := range strings.Split(acceptFilter, ",") {
af = strings.TrimSpace(af)
if af == "" {
continue
}
includeRange := true
if strings.HasPrefix(af, "-") {
includeRange = false
af = af[1:]
}
pfx, err := netip.ParsePrefix(af)
if err != nil {
b.logf("accept routes filter: invalid prefix %q will be ignored: %v (check accept-routes flag)", af, err)
continue
}
if includeRange {
acceptFilterBuilder.AddPrefix(pfx)
} else {
acceptFilterBuilder.RemovePrefix(pfx)
}
}
return acceptFilterBuilder.IPSet()
}
func (b *LocalBackend) filterRoutes(routes []netip.Prefix, acceptFilter *netipx.IPSet) []netip.Prefix {
if acceptFilter == nil {
return routes
@ -2815,6 +2789,7 @@ func (b *LocalBackend) filterRoutes(routes []netip.Prefix, acceptFilter *netipx.
b.logf("accept routes filter: failed to build filtered set, all routes will be accepted: %v (check accept-routes flag)", err)
return routes
}
b.logf("accept routes filter: accepting routes: %v", set.Ranges())
return set.Prefixes()
}
@ -2825,9 +2800,9 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs, oneCGNA
singleRouteThreshold = 1
}
acceptRoutesFilterSet, err := b.parseAcceptRoutesFilter(prefs.AcceptRoutesFilter)
acceptRoutesFilterSet, err := ipn.ParseAcceptRoutesFilter(prefs.AcceptRoutesFilter)
if err != nil {
b.logf("accept routes filter: failed to build filter set: %v", err)
b.logf("accept routes filter: failed to build filter set from %q: %v", prefs.AcceptRoutesFilter, err)
}
rs := &router.Config{

View File

@ -17,6 +17,7 @@ import (
"runtime"
"strings"
"go4.org/netipx"
"tailscale.com/atomicfile"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/netaddr"
@ -657,3 +658,28 @@ func SavePrefs(filename string, p *Prefs) {
log.Printf("SavePrefs: %v\n", err)
}
}
func ParseAcceptRoutesFilter(acceptFilter string) (*netipx.IPSet, error) {
var acceptFilterBuilder netipx.IPSetBuilder
for _, af := range strings.Split(acceptFilter, ",") {
af = strings.TrimSpace(af)
if af == "" {
continue
}
includeRange := true
if strings.HasPrefix(af, "-") {
includeRange = false
af = af[1:]
}
pfx, err := netip.ParsePrefix(af)
if err != nil {
return nil, err
}
if includeRange {
acceptFilterBuilder.AddPrefix(pfx)
} else {
acceptFilterBuilder.RemovePrefix(pfx)
}
}
return acceptFilterBuilder.IPSet()
}