mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 05:37:32 +00:00
ipn/ipnlocal,wgengine/router,cmd/tailscale: add flag to allow local lan access when routing traffic via an exit node.
For #1527 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
@@ -51,12 +51,17 @@ type Config struct {
|
||||
// IPv6/128 (Tailscale ULA).
|
||||
LocalAddrs []netaddr.IPPrefix
|
||||
|
||||
// Routes are the routes that point in to the Tailscale
|
||||
// Routes are the routes that point into the Tailscale
|
||||
// interface. These are the /32 and /128 routes to peers, as
|
||||
// well as any other subnets that peers are advertising and
|
||||
// this node has chosen to use.
|
||||
Routes []netaddr.IPPrefix
|
||||
|
||||
// LocalRoutes are the routes that should not be routed through Tailscale.
|
||||
// There are no priorities set in how these routes are added, normal
|
||||
// routing rules apply.
|
||||
LocalRoutes []netaddr.IPPrefix
|
||||
|
||||
// Linux-only things below, ignored on other platforms.
|
||||
SubnetRoutes []netaddr.IPPrefix // subnets being advertised to other Tailscale nodes
|
||||
SNATSubnetRoutes bool // SNAT traffic to local subnets
|
||||
|
@@ -59,21 +59,26 @@ const (
|
||||
tailscaleBypassMark = "0x80000"
|
||||
)
|
||||
|
||||
// tailscaleRouteTable is the routing table number for Tailscale
|
||||
// network routes. See addIPRules for the detailed policy routing
|
||||
// logic that ends up doing lookups within that table.
|
||||
//
|
||||
// NOTE(danderson): We chose 52 because those are the digits above the
|
||||
// letters "TS" on a qwerty keyboard, and 52 is sufficiently unlikely
|
||||
// to be picked by other software.
|
||||
//
|
||||
// NOTE(danderson): You might wonder why we didn't pick some high
|
||||
// table number like 5252, to further avoid the potential for
|
||||
// collisions with other software. Unfortunately, Busybox's `ip`
|
||||
// implementation believes that table numbers are 8-bit integers, so
|
||||
// for maximum compatibility we have to stay in the 0-255 range even
|
||||
// though linux itself supports larger numbers.
|
||||
const tailscaleRouteTable = "52"
|
||||
const (
|
||||
defaultRouteTable = "default"
|
||||
mainRouteTable = "main"
|
||||
|
||||
// tailscaleRouteTable is the routing table number for Tailscale
|
||||
// network routes. See addIPRules for the detailed policy routing
|
||||
// logic that ends up doing lookups within that table.
|
||||
//
|
||||
// NOTE(danderson): We chose 52 because those are the digits above the
|
||||
// letters "TS" on a qwerty keyboard, and 52 is sufficiently unlikely
|
||||
// to be picked by other software.
|
||||
//
|
||||
// NOTE(danderson): You might wonder why we didn't pick some high
|
||||
// table number like 5252, to further avoid the potential for
|
||||
// collisions with other software. Unfortunately, Busybox's `ip`
|
||||
// implementation believes that table numbers are 8-bit integers, so
|
||||
// for maximum compatibility we have to stay in the 0-255 range even
|
||||
// though linux itself supports larger numbers.
|
||||
tailscaleRouteTable = "52"
|
||||
)
|
||||
|
||||
// netfilterRunner abstracts helpers to run netfilter commands. It
|
||||
// exists purely to swap out go-iptables for a fake implementation in
|
||||
@@ -93,6 +98,7 @@ type linuxRouter struct {
|
||||
tunname string
|
||||
addrs map[netaddr.IPPrefix]bool
|
||||
routes map[netaddr.IPPrefix]bool
|
||||
localRoutes map[netaddr.IPPrefix]bool
|
||||
snatSubnetRoutes bool
|
||||
netfilterMode preftype.NetfilterMode
|
||||
|
||||
@@ -185,9 +191,13 @@ func (r *linuxRouter) Close() error {
|
||||
if err := r.setNetfilterMode(netfilterOff); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.delRoutes(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.addrs = nil
|
||||
r.routes = nil
|
||||
r.localRoutes = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -203,6 +213,12 @@ func (r *linuxRouter) Set(cfg *Config) error {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
newLocalRoutes, err := cidrDiff("localRoute", r.localRoutes, cfg.LocalRoutes, r.addThrowRoute, r.delThrowRoute, r.logf)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.localRoutes = newLocalRoutes
|
||||
|
||||
newRoutes, err := cidrDiff("route", r.routes, cfg.Routes, r.addRoute, r.delRoute, r.logf)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -432,14 +448,25 @@ func (r *linuxRouter) delLoopbackRule(addr netaddr.IP) error {
|
||||
// interface. Fails if the route already exists, or if adding the
|
||||
// route fails.
|
||||
func (r *linuxRouter) addRoute(cidr netaddr.IPPrefix) error {
|
||||
return r.addRouteDef([]string{normalizeCIDR(cidr), "dev", r.tunname}, cidr)
|
||||
}
|
||||
|
||||
// addThrowRoute adds a throw route for the provided cidr.
|
||||
// This has the effect that lookup in the routing table is terminated
|
||||
// pretending that no route was found. Fails if the route already exists,
|
||||
// or if adding the route fails.
|
||||
func (r *linuxRouter) addThrowRoute(cidr netaddr.IPPrefix) error {
|
||||
if !r.ipRuleAvailable {
|
||||
return nil
|
||||
}
|
||||
return r.addRouteDef([]string{"throw", normalizeCIDR(cidr)}, cidr)
|
||||
}
|
||||
|
||||
func (r *linuxRouter) addRouteDef(routeDef []string, cidr netaddr.IPPrefix) error {
|
||||
if !r.v6Available && cidr.IP.Is6() {
|
||||
return nil
|
||||
}
|
||||
args := []string{
|
||||
"ip", "route", "add",
|
||||
normalizeCIDR(cidr),
|
||||
"dev", r.tunname,
|
||||
}
|
||||
args := append([]string{"ip", "route", "add"}, routeDef...)
|
||||
if r.ipRuleAvailable {
|
||||
args = append(args, "table", tailscaleRouteTable)
|
||||
}
|
||||
@@ -450,20 +477,29 @@ func (r *linuxRouter) addRoute(cidr netaddr.IPPrefix) error {
|
||||
// interface. Fails if the route doesn't exist, or if removing the
|
||||
// route fails.
|
||||
func (r *linuxRouter) delRoute(cidr netaddr.IPPrefix) error {
|
||||
return r.delRouteDef([]string{normalizeCIDR(cidr), "dev", r.tunname}, cidr)
|
||||
}
|
||||
|
||||
// delThrowRoute removes the throw route for the cidr. Fails if the route
|
||||
// doesn't exist, or if removing the route fails.
|
||||
func (r *linuxRouter) delThrowRoute(cidr netaddr.IPPrefix) error {
|
||||
if !r.ipRuleAvailable {
|
||||
return nil
|
||||
}
|
||||
return r.delRouteDef([]string{"throw", normalizeCIDR(cidr)}, cidr)
|
||||
}
|
||||
|
||||
func (r *linuxRouter) delRouteDef(routeDef []string, cidr netaddr.IPPrefix) error {
|
||||
if !r.v6Available && cidr.IP.Is6() {
|
||||
return nil
|
||||
}
|
||||
args := []string{
|
||||
"ip", "route", "del",
|
||||
normalizeCIDR(cidr),
|
||||
"dev", r.tunname,
|
||||
}
|
||||
args := append([]string{"ip", "route", "del"}, routeDef...)
|
||||
if r.ipRuleAvailable {
|
||||
args = append(args, "table", tailscaleRouteTable)
|
||||
}
|
||||
err := r.cmd.run(args...)
|
||||
if err != nil {
|
||||
ok, err := r.hasRoute(cidr)
|
||||
ok, err := r.hasRoute(routeDef, cidr)
|
||||
if err != nil {
|
||||
r.logf("warning: error checking whether %v even exists after error deleting it: %v", err)
|
||||
} else {
|
||||
@@ -483,12 +519,8 @@ func dashFam(ip netaddr.IP) string {
|
||||
return "-4"
|
||||
}
|
||||
|
||||
func (r *linuxRouter) hasRoute(cidr netaddr.IPPrefix) (bool, error) {
|
||||
args := []string{
|
||||
"ip", dashFam(cidr.IP), "route", "show",
|
||||
normalizeCIDR(cidr),
|
||||
"dev", r.tunname,
|
||||
}
|
||||
func (r *linuxRouter) hasRoute(routeDef []string, cidr netaddr.IPPrefix) (bool, error) {
|
||||
args := append([]string{"ip", dashFam(cidr.IP), "route", "show"}, routeDef...)
|
||||
if r.ipRuleAvailable {
|
||||
args = append(args, "table", tailscaleRouteTable)
|
||||
}
|
||||
@@ -551,7 +583,7 @@ func (r *linuxRouter) addIPRules() error {
|
||||
"ip", family, "rule", "add",
|
||||
"pref", tailscaleRouteTable+"10",
|
||||
"fwmark", tailscaleBypassMark,
|
||||
"table", "main",
|
||||
"table", mainRouteTable,
|
||||
)
|
||||
// ...and then we try the 'default' table, for correctness,
|
||||
// even though it's been empty on every Linux system I've ever seen.
|
||||
@@ -559,7 +591,7 @@ func (r *linuxRouter) addIPRules() error {
|
||||
"ip", family, "rule", "add",
|
||||
"pref", tailscaleRouteTable+"30",
|
||||
"fwmark", tailscaleBypassMark,
|
||||
"table", "default",
|
||||
"table", defaultRouteTable,
|
||||
)
|
||||
// If neither of those matched (no default route on this system?)
|
||||
// then packets from us should be aborted rather than falling through
|
||||
@@ -590,7 +622,18 @@ func (r *linuxRouter) addIPRules() error {
|
||||
return rg.ErrAcc
|
||||
}
|
||||
|
||||
// delBypassrule removes the policy routing rules that avoid
|
||||
// delRoutes removes any local routes that we added that would not be
|
||||
// cleaned up on interface down.
|
||||
func (r *linuxRouter) delRoutes() error {
|
||||
for rt := range r.localRoutes {
|
||||
if err := r.delThrowRoute(rt); err != nil {
|
||||
r.logf("failed to delete throw route(%q): %v", rt, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// delIPRules removes the policy routing rules that avoid
|
||||
// tailscaled routing loops, if it exists.
|
||||
func (r *linuxRouter) delIPRules() error {
|
||||
if !r.ipRuleAvailable {
|
||||
|
@@ -280,6 +280,54 @@ v6/filter/ts-forward -o tailscale0 -j ACCEPT
|
||||
v6/nat/POSTROUTING -j ts-postrouting
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "addr, routes, and local routes with netfilter",
|
||||
in: &Config{
|
||||
LocalAddrs: mustCIDRs("100.101.102.104/10"),
|
||||
Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"),
|
||||
LocalRoutes: mustCIDRs("10.0.0.0/8"),
|
||||
NetfilterMode: netfilterOn,
|
||||
},
|
||||
want: `
|
||||
up
|
||||
ip addr add 100.101.102.104/10 dev tailscale0
|
||||
ip route add 0.0.0.0/0 dev tailscale0 table 52
|
||||
ip route add 100.100.100.100/32 dev tailscale0 table 52
|
||||
ip route add throw 10.0.0.0/8 table 52` + basic +
|
||||
`v4/filter/FORWARD -j ts-forward
|
||||
v4/filter/INPUT -j ts-input
|
||||
v4/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000
|
||||
v4/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT
|
||||
v4/filter/ts-forward -o tailscale0 -s 100.64.0.0/10 -j DROP
|
||||
v4/filter/ts-forward -o tailscale0 -j ACCEPT
|
||||
v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT
|
||||
v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN
|
||||
v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP
|
||||
v4/nat/POSTROUTING -j ts-postrouting
|
||||
v6/filter/FORWARD -j ts-forward
|
||||
v6/filter/INPUT -j ts-input
|
||||
v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000
|
||||
v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT
|
||||
v6/filter/ts-forward -o tailscale0 -j ACCEPT
|
||||
v6/nat/POSTROUTING -j ts-postrouting
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "addr, routes, and local routes with no netfilter",
|
||||
in: &Config{
|
||||
LocalAddrs: mustCIDRs("100.101.102.104/10"),
|
||||
Routes: mustCIDRs("100.100.100.100/32", "0.0.0.0/0"),
|
||||
LocalRoutes: mustCIDRs("10.0.0.0/8", "192.168.0.0/24"),
|
||||
NetfilterMode: netfilterOff,
|
||||
},
|
||||
want: `
|
||||
up
|
||||
ip addr add 100.101.102.104/10 dev tailscale0
|
||||
ip route add 0.0.0.0/0 dev tailscale0 table 52
|
||||
ip route add 100.100.100.100/32 dev tailscale0 table 52
|
||||
ip route add throw 10.0.0.0/8 table 52
|
||||
ip route add throw 192.168.0.0/24 table 52` + basic,
|
||||
},
|
||||
}
|
||||
|
||||
fake := NewFakeOS(t)
|
||||
|
Reference in New Issue
Block a user