Skip to content

Commit 7483fec

Browse files
authored
Fix Android internet blackhole caused by stale route re-injection on TUN rebuild (#5865)
extraInitialRoutes() was meant to preserve only the fake IP route (240.0.0.0/8) across TUN rebuilds, but it re-injected any initial route missing from the current set. When the management server advertised exit node routes (0.0.0.0/0) that were later filtered by the route selector, extraInitialRoutes() re-added them, causing the Android VPN to capture all traffic with no peer to handle it. Store the fake IP route explicitly and append only that in notify(), removing the overly broad initial route diffing.
1 parent 5259e5d commit 7483fec

4 files changed

Lines changed: 19 additions & 20 deletions

File tree

client/internal/routemanager/manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ func (m *DefaultManager) setupAndroidRoutes(config ManagerConfig) {
168168
NetworkType: route.IPv4Network,
169169
}
170170
cr = append(cr, fakeIPRoute)
171+
m.notifier.SetFakeIPRoute(fakeIPRoute)
171172
}
172173

173174
m.notifier.SetInitialClientRoutes(cr, routesForComparison)

client/internal/routemanager/notifier/notifier_android.go

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
type Notifier struct {
1717
initialRoutes []*route.Route
1818
currentRoutes []*route.Route
19+
fakeIPRoute *route.Route
1920

2021
listener listener.NetworkChangeListener
2122
listenerMux sync.Mutex
@@ -31,13 +32,17 @@ func (n *Notifier) SetListener(listener listener.NetworkChangeListener) {
3132
n.listener = listener
3233
}
3334

34-
// SetInitialClientRoutes stores the full initial route set (including fake IP blocks)
35-
// and a separate comparison set (without fake IP blocks) for diff detection.
35+
// SetInitialClientRoutes stores the initial route sets for TUN configuration.
3636
func (n *Notifier) SetInitialClientRoutes(initialRoutes []*route.Route, routesForComparison []*route.Route) {
3737
n.initialRoutes = filterStatic(initialRoutes)
3838
n.currentRoutes = filterStatic(routesForComparison)
3939
}
4040

41+
// SetFakeIPRoute stores the fake IP route to be included in every TUN rebuild.
42+
func (n *Notifier) SetFakeIPRoute(r *route.Route) {
43+
n.fakeIPRoute = r
44+
}
45+
4146
func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
4247
var newRoutes []*route.Route
4348
for _, routes := range idMap {
@@ -69,7 +74,9 @@ func (n *Notifier) notify() {
6974
}
7075

7176
allRoutes := slices.Clone(n.currentRoutes)
72-
allRoutes = append(allRoutes, n.extraInitialRoutes()...)
77+
if n.fakeIPRoute != nil {
78+
allRoutes = append(allRoutes, n.fakeIPRoute)
79+
}
7380

7481
routeStrings := n.routesToStrings(allRoutes)
7582
sort.Strings(routeStrings)
@@ -78,23 +85,6 @@ func (n *Notifier) notify() {
7885
}(n.listener)
7986
}
8087

81-
// extraInitialRoutes returns initialRoutes whose network prefix is absent
82-
// from currentRoutes (e.g. the fake IP block added at setup time).
83-
func (n *Notifier) extraInitialRoutes() []*route.Route {
84-
currentNets := make(map[netip.Prefix]struct{}, len(n.currentRoutes))
85-
for _, r := range n.currentRoutes {
86-
currentNets[r.Network] = struct{}{}
87-
}
88-
89-
var extra []*route.Route
90-
for _, r := range n.initialRoutes {
91-
if _, ok := currentNets[r.Network]; !ok {
92-
extra = append(extra, r)
93-
}
94-
}
95-
return extra
96-
}
97-
9888
func filterStatic(routes []*route.Route) []*route.Route {
9989
out := make([]*route.Route, 0, len(routes))
10090
for _, r := range routes {

client/internal/routemanager/notifier/notifier_ios.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ func (n *Notifier) SetInitialClientRoutes([]*route.Route, []*route.Route) {
3434
// iOS doesn't care about initial routes
3535
}
3636

37+
func (n *Notifier) SetFakeIPRoute(*route.Route) {
38+
// Not used on iOS
39+
}
40+
3741
func (n *Notifier) OnNewRoutes(route.HAMap) {
3842
// Not used on iOS
3943
}

client/internal/routemanager/notifier/notifier_other.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ func (n *Notifier) SetInitialClientRoutes([]*route.Route, []*route.Route) {
2323
// Not used on non-mobile platforms
2424
}
2525

26+
func (n *Notifier) SetFakeIPRoute(*route.Route) {
27+
// Not used on non-mobile platforms
28+
}
29+
2630
func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
2731
// Not used on non-mobile platforms
2832
}

0 commit comments

Comments
 (0)