Skip to content

Commit b484ca4

Browse files
committed
fix: advertise exit node & accept routes
1 parent b29d58a commit b484ca4

File tree

2 files changed

+74
-24
lines changed

2 files changed

+74
-24
lines changed

util/linuxfw/iptables_runner.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,18 @@ func (i *iptablesRunner) addBase4(tunname string) error {
269269
return fmt.Errorf("adding %v in v4/filter/ts-forward: %w", args, err)
270270
}
271271

272-
// Mark packets from tun device with bypass mark so they don't loop back
273-
args = []string{"-i", tunname, "-j", "MARK", "--set-mark", bypassMark + "/" + fwmarkMask}
272+
// For Android: mark in PREROUTING so routing decision can use the mark
273+
args = []string{"-i", tunname, "-j", "MARK", "--set-mark", subnetRouteMark + "/" + fwmarkMask}
274274
if err := i.ipt4.Append("mangle", "ts-prerouting", args...); err != nil {
275275
return fmt.Errorf("adding %v in v4/mangle/ts-prerouting: %w", args, err)
276276
}
277277

278+
// MASQUERADE exit node traffic going to physical interfaces
279+
args = []string{"-i", tunname, "!", "-o", tunname, "-j", "MASQUERADE"}
280+
if err := i.ipt4.Append("nat", "ts-postrouting", args...); err != nil {
281+
return fmt.Errorf("adding %v in v4/nat/ts-postrouting: %w", args, err)
282+
}
283+
278284
// Allow hotspot clients to access Tailscale network
279285
args = []string{"-o", tunname, "-j", "MASQUERADE"}
280286
if err := i.ipt4.Append("nat", "ts-postrouting", args...); err != nil {
@@ -385,12 +391,20 @@ func (i *iptablesRunner) addBase6(tunname string) error {
385391
return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err)
386392
}
387393

388-
// Mark packets from tun device with bypass mark so they don't loop back
389-
args = []string{"-i", tunname, "-j", "MARK", "--set-mark", bypassMark + "/" + fwmarkMask}
394+
// Mark packets from subnetRouter, need enable IP6 NAT zcat /proc/config.gz | grep IP6_NF_NAT 2>/dev/null
395+
args = []string{"-i", tunname, "-j", "MARK", "--set-mark", subnetRouteMark + "/" + fwmarkMask}
390396
if err := i.ipt6.Append("mangle", "ts-prerouting", args...); err != nil {
391397
return fmt.Errorf("adding %v in v6/mangle/ts-prerouting: %w", args, err)
392398
}
393399

400+
// MASQUERADE exit node traffic going to physical interfaces
401+
if i.v6NATAvailable {
402+
args = []string{"-i", tunname, "!", "-o", tunname, "-j", "MASQUERADE"}
403+
if err := i.ipt6.Append("nat", "ts-postrouting", args...); err != nil {
404+
return fmt.Errorf("adding %v in v6/nat/ts-postrouting: %w", args, err)
405+
}
406+
}
407+
394408
// Allow hotspot clients to access Tailscale network
395409
if i.v6NATAvailable {
396410
args = []string{"-o", tunname, "-j", "MASQUERADE"}

wgengine/router/osrouter/router_linux.go

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ func (r *linuxRouter) Set(cfg *router.Config) error {
482482

483483
// Issue 11405: enable IP forwarding on gokrazy.
484484
advertisingRoutes := len(cfg.SubnetRoutes) > 0
485-
if getDistroFunc() == distro.Gokrazy && advertisingRoutes {
485+
if getDistroFunc() == distro.Gokrazy || runtime.GOOS == "android" && advertisingRoutes {
486486
r.enableIPForwarding()
487487
}
488488

@@ -1297,24 +1297,60 @@ var ubntIPRules = []netlink.Rule{
12971297
},
12981298
}
12991299

1300-
var androidIPRules = []netlink.Rule{
1301-
// Priority 7300 (12500): Tailscale CGNAT range (100.64.0.0/10) always uses table 52, before VPN rules
1302-
// This ensures peer-to-peer traffic doesn't go through other VPNs
1303-
{
1304-
Priority: 7300, // 5200 + 7300 = 12500
1305-
Dst: netipx.PrefixIPNet(netip.MustParsePrefix("100.64.0.0/10")),
1306-
Table: tailscaleRouteTable.Num,
1307-
},
1308-
// Priority 13001: after Android VPN rules at 13000, before default network (14999+)
1309-
// When VPN active: VPN rules at 13000 catch traffic first (VPN wins)
1310-
// When VPN off: Tailscale catches traffic as fallback
1311-
{
1312-
Priority: 7801, // 5200 + 7801 = 13001
1313-
Invert: true,
1314-
Mark: tsconst.LinuxBypassMarkNum,
1315-
Mask: tsconst.LinuxFwmarkMaskNum,
1316-
Table: tailscaleRouteTable.Num,
1317-
},
1300+
// detectAndroidDefaultTable returns the routing table number for the current
1301+
// default route on Android. Returns 0 if detection fails.
1302+
func detectAndroidDefaultTable() int {
1303+
routes, err := netlink.RouteGet(net.IPv4(8, 8, 8, 8))
1304+
if err != nil || len(routes) == 0 {
1305+
return 0
1306+
}
1307+
if routes[0].Table > 0 {
1308+
return routes[0].Table
1309+
}
1310+
return 0
1311+
}
1312+
1313+
// getAndroidIPRules returns Android-specific IP rules, including a dynamic
1314+
// exit node rule that uses the current default network's routing table.
1315+
func getAndroidIPRules() []netlink.Rule {
1316+
rules := []netlink.Rule{
1317+
// Priority 7300 (12500): Tailscale CGNAT range (100.64.0.0/10) always uses table 52, before VPN rules
1318+
// This ensures peer-to-peer traffic doesn't go through other VPNs
1319+
{
1320+
Priority: 7300, // 5200 + 7300 = 12500
1321+
Dst: netipx.PrefixIPNet(netip.MustParsePrefix("100.64.0.0/10")),
1322+
Table: tailscaleRouteTable.Num,
1323+
},
1324+
{
1325+
Priority: 7300, // 5200 + 7300 = 12500
1326+
Dst: netipx.PrefixIPNet(netip.MustParsePrefix("fd7a:115c:a1e0::/48")),
1327+
Table: tailscaleRouteTable.Num,
1328+
},
1329+
// Priority 13001: after Android VPN rules at 13000, before default network (14999+)
1330+
// When VPN active: VPN rules at 13000 catch traffic first (VPN wins)
1331+
// When VPN off: Tailscale catches traffic as fallback
1332+
{
1333+
Priority: 7801, // 5200 + 7801 = 13001
1334+
Invert: true,
1335+
Mark: tsconst.LinuxBypassMarkNum,
1336+
Mask: tsconst.LinuxFwmarkMaskNum,
1337+
Table: tailscaleRouteTable.Num,
1338+
},
1339+
}
1340+
1341+
// Add exit node rule: route traffic FROM Tailscale network to internet
1342+
// using the current default network's routing table
1343+
if table := detectAndroidDefaultTable(); table > 0 {
1344+
// Traffic from tailscale0 is marked with LinuxSubnetRouteMark in ts-forward
1345+
rules = append(rules, netlink.Rule{
1346+
Priority: 7801, // 5200 + 7801 = 13001
1347+
Mark: tsconst.LinuxSubnetRouteMarkNum, // 0x8000000
1348+
Mask: tsconst.LinuxFwmarkMaskNum,
1349+
Table: table,
1350+
})
1351+
}
1352+
1353+
return rules
13181354
}
13191355

13201356
// ipRules returns the appropriate list of ip rules to be used by Tailscale. See
@@ -1323,7 +1359,7 @@ func ipRules() []netlink.Rule {
13231359
if getDistroFunc() == distro.UBNT {
13241360
return ubntIPRules
13251361
} else if runtime.GOOS == "android" {
1326-
return androidIPRules
1362+
return getAndroidIPRules()
13271363
}
13281364
return baseIPRules
13291365
}

0 commit comments

Comments
 (0)