mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-28 18:41:30 +02:00
## Describe your changes The Android route notifier and the DNS search-domain notifier both delivered OnNetworkChanged from a fire-and-forget goroutine per update. Two updates in quick succession could reach the Java side reordered: the TUN rebuild handler applies them in arrival order and compares against the last applied parameters, so a stale route set delivered last won as the final TUN state. This is the same reordering hazard fixed for iOS in #6454. Wrap the Android network change listener into the shared tunnelnotifier FIFO introduced in #6870, the same way RunOniOS does, and deliver both notifiers synchronously into it. Enqueueing is non-blocking, a single delivery goroutine preserves order, and calls into Java never overlap. Also stop hasRouteDiff from sorting the notifier's shared route slices in place; compare sorted copies instead. ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__
120 lines
2.6 KiB
Go
120 lines
2.6 KiB
Go
//go:build android
|
|
|
|
package notifier
|
|
|
|
import (
|
|
"net/netip"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/listener"
|
|
"github.com/netbirdio/netbird/route"
|
|
)
|
|
|
|
type Notifier struct {
|
|
initialRoutes []*route.Route
|
|
currentRoutes []*route.Route
|
|
fakeIPRoutes []*route.Route
|
|
|
|
listener listener.NetworkChangeListener
|
|
listenerMux sync.Mutex
|
|
}
|
|
|
|
func NewNotifier() *Notifier {
|
|
return &Notifier{}
|
|
}
|
|
|
|
func (n *Notifier) SetListener(listener listener.NetworkChangeListener) {
|
|
n.listenerMux.Lock()
|
|
defer n.listenerMux.Unlock()
|
|
n.listener = listener
|
|
}
|
|
|
|
// SetInitialClientRoutes stores the initial route sets for TUN configuration.
|
|
func (n *Notifier) SetInitialClientRoutes(initialRoutes []*route.Route, routesForComparison []*route.Route) {
|
|
n.initialRoutes = filterStatic(initialRoutes)
|
|
n.currentRoutes = filterStatic(routesForComparison)
|
|
}
|
|
|
|
// SetFakeIPRoutes stores the fake IP routes to be included in every TUN rebuild.
|
|
func (n *Notifier) SetFakeIPRoutes(routes []*route.Route) {
|
|
n.fakeIPRoutes = routes
|
|
n.notify()
|
|
}
|
|
|
|
func (n *Notifier) OnNewRoutes(idMap route.HAMap) {
|
|
var newRoutes []*route.Route
|
|
for _, routes := range idMap {
|
|
for _, r := range routes {
|
|
if r.IsDynamic() {
|
|
continue
|
|
}
|
|
newRoutes = append(newRoutes, r)
|
|
}
|
|
}
|
|
|
|
if !n.hasRouteDiff(n.currentRoutes, newRoutes) {
|
|
return
|
|
}
|
|
|
|
n.currentRoutes = newRoutes
|
|
n.notify()
|
|
}
|
|
|
|
func (n *Notifier) OnNewPrefixes([]netip.Prefix) {
|
|
// Not used on Android
|
|
}
|
|
|
|
func (n *Notifier) notify() {
|
|
n.listenerMux.Lock()
|
|
defer n.listenerMux.Unlock()
|
|
if n.listener == nil {
|
|
return
|
|
}
|
|
|
|
allRoutes := slices.Clone(n.currentRoutes)
|
|
allRoutes = append(allRoutes, n.fakeIPRoutes...)
|
|
|
|
routeStrings := n.routesToStrings(allRoutes)
|
|
sort.Strings(routeStrings)
|
|
n.listener.OnNetworkChanged(strings.Join(routeStrings, ","))
|
|
}
|
|
|
|
func filterStatic(routes []*route.Route) []*route.Route {
|
|
out := make([]*route.Route, 0, len(routes))
|
|
for _, r := range routes {
|
|
if !r.IsDynamic() {
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (n *Notifier) routesToStrings(routes []*route.Route) []string {
|
|
nets := make([]string, 0, len(routes))
|
|
for _, r := range routes {
|
|
nets = append(nets, r.NetString())
|
|
}
|
|
return nets
|
|
}
|
|
|
|
func (n *Notifier) hasRouteDiff(a []*route.Route, b []*route.Route) bool {
|
|
as := n.routesToStrings(a)
|
|
bs := n.routesToStrings(b)
|
|
sort.Strings(as)
|
|
sort.Strings(bs)
|
|
return !slices.Equal(as, bs)
|
|
}
|
|
|
|
func (n *Notifier) GetInitialRouteRanges() []string {
|
|
initialStrings := n.routesToStrings(n.initialRoutes)
|
|
sort.Strings(initialStrings)
|
|
return initialStrings
|
|
}
|
|
|
|
func (n *Notifier) Close() {
|
|
// unused
|
|
}
|