Fix route notificaiton

This commit is contained in:
Viktor Liu
2025-07-02 15:49:45 +02:00
parent 5299e9fda3
commit 0f79a8942d
2 changed files with 65 additions and 43 deletions

View File

@@ -8,6 +8,7 @@ import (
"net/netip"
"net/url"
"runtime"
"slices"
"sync"
"time"
@@ -142,6 +143,7 @@ func NewManager(config ManagerConfig) *DefaultManager {
func (m *DefaultManager) setupAndroidRoutes(config ManagerConfig) {
cr := m.initialClientRoutes(config.InitialRoutes)
routesForComparison := slices.Clone(cr)
if config.DNSFeatureFlag {
m.fakeIPManager = fakeip.NewManager()
@@ -156,7 +158,7 @@ func (m *DefaultManager) setupAndroidRoutes(config ManagerConfig) {
cr = append(cr, fakeIPRoute)
}
m.notifier.SetInitialClientRoutes(cr, config.DNSFeatureFlag)
m.notifier.SetInitialClientRoutes(cr, routesForComparison, config.DNSFeatureFlag)
}
func (m *DefaultManager) setupRefCounters(useNoop bool) {

View File

@@ -3,6 +3,7 @@ package notifier
import (
"net/netip"
"runtime"
"slices"
"sort"
"strings"
"sync"
@@ -12,8 +13,9 @@ import (
)
type Notifier struct {
initialRouteRanges []string
routeRanges []string
initialRoutes []*route.Route
routesForComparison []*route.Route
dnsFeatureFlag bool
listener listener.NetworkChangeListener
listenerMux sync.Mutex
@@ -29,17 +31,10 @@ func (n *Notifier) SetListener(listener listener.NetworkChangeListener) {
n.listener = listener
}
func (n *Notifier) SetInitialClientRoutes(clientRoutes []*route.Route, dnsFeatureFlag bool) {
nets := make([]string, 0)
for _, r := range clientRoutes {
if r.IsDynamic() && !dnsFeatureFlag {
// this kind of dynamic route is not supported on android
continue
}
nets = append(nets, r.Network.String())
}
sort.Strings(nets)
n.initialRouteRanges = nets
func (n *Notifier) SetInitialClientRoutes(allRoutes []*route.Route, routesForComparison []*route.Route, dnsFeatureFlag bool) {
n.dnsFeatureFlag = dnsFeatureFlag
n.initialRoutes = allRoutes
n.routesForComparison = routesForComparison
}
func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
@@ -47,22 +42,16 @@ func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
return
}
var newNets []string
var newRoutes []*route.Route
for _, routes := range idMap {
for _, r := range routes {
if r.IsDynamic() {
continue
}
newNets = append(newNets, r.Network.String())
}
newRoutes = append(newRoutes, routes...)
}
sort.Strings(newNets)
if !n.hasDiff(n.initialRouteRanges, newNets) {
if !n.hasRouteDiff(n.routesForComparison, newRoutes) {
return
}
n.routeRanges = newNets
n.routesForComparison = newRoutes
n.notify()
}
@@ -74,11 +63,12 @@ func (n *Notifier) OnNewPrefixes(prefixes []netip.Prefix) {
}
sort.Strings(newNets)
if !n.hasDiff(n.routeRanges, newNets) {
currentNets := n.routesToStrings(n.routesForComparison)
if slices.Equal(currentNets, newNets) {
return
}
n.routeRanges = newNets
n.notify()
}
@@ -89,37 +79,67 @@ func (n *Notifier) notify() {
return
}
routeStrings := n.routesToStrings(n.routesForComparison)
sort.Strings(routeStrings)
go func(l listener.NetworkChangeListener) {
l.OnNetworkChanged(strings.Join(addIPv6RangeIfNeeded(n.routeRanges), ","))
l.OnNetworkChanged(strings.Join(n.addIPv6RangeIfNeeded(routeStrings, n.routesForComparison), ","))
}(n.listener)
}
func (n *Notifier) hasDiff(a []string, b []string) bool {
if len(a) != len(b) {
return true
}
for i, v := range a {
if v != b[i] {
return true
// hasRouteDiff compares two route slices for differences
func (n *Notifier) hasRouteDiff(a []*route.Route, b []*route.Route) bool {
aFiltered := n.filterRoutes(a)
bFiltered := n.filterRoutes(b)
slices.SortFunc(aFiltered, func(x, y *route.Route) int {
return strings.Compare(x.NetString(), y.NetString())
})
slices.SortFunc(bFiltered, func(x, y *route.Route) int {
return strings.Compare(x.NetString(), y.NetString())
})
return !slices.EqualFunc(aFiltered, bFiltered, func(x, y *route.Route) bool {
return x.NetString() == y.NetString()
})
}
// filterRoutes filters routes based on DNS feature flag
func (n *Notifier) filterRoutes(routes []*route.Route) []*route.Route {
filtered := make([]*route.Route, 0, len(routes))
for _, r := range routes {
if r.IsDynamic() && !n.dnsFeatureFlag {
// this kind of dynamic route is not supported on android
continue
}
filtered = append(filtered, r)
}
return false
return filtered
}
// routesToStrings converts routes to string slice (caller should sort if needed)
func (n *Notifier) routesToStrings(routes []*route.Route) []string {
filtered := n.filterRoutes(routes)
nets := make([]string, 0, len(filtered))
for _, r := range filtered {
nets = append(nets, r.NetString())
}
return nets
}
func (n *Notifier) GetInitialRouteRanges() []string {
return addIPv6RangeIfNeeded(n.initialRouteRanges)
initialStrings := n.routesToStrings(n.initialRoutes)
sort.Strings(initialStrings)
return n.addIPv6RangeIfNeeded(initialStrings, n.initialRoutes)
}
// addIPv6RangeIfNeeded returns the input ranges with the default IPv6 range when there is an IPv4 default route.
func addIPv6RangeIfNeeded(inputRanges []string) []string {
ranges := inputRanges
for _, r := range inputRanges {
func (n *Notifier) addIPv6RangeIfNeeded(inputRanges []string, routes []*route.Route) []string {
for _, r := range routes {
// we are intentionally adding the ipv6 default range in case of ipv4 default range
// to ensure that all traffic is managed by the tunnel interface on android
if r == "0.0.0.0/0" {
ranges = append(ranges, "::/0")
break
if r.Network.Addr().Is4() && r.Network.Bits() == 0 {
return append(slices.Clone(inputRanges), "::/0")
}
}
return ranges
return inputRanges
}