mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-23 00:23:36 +01:00
fdd23d4644
Most operating systems add a /32 route for the default gateway address to its routing table This will allow routes to be configured into the system even when the incoming range contains the default gateway. In case a range is a sub-range of an existing route and this range happens to contain the default gateway it attempts to create a default gateway route to prevent loop issues
178 lines
5.2 KiB
Go
178 lines
5.2 KiB
Go
package routemanager
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"sync"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/listener"
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
|
"github.com/netbirdio/netbird/iface"
|
|
"github.com/netbirdio/netbird/route"
|
|
"github.com/netbirdio/netbird/version"
|
|
)
|
|
|
|
// Manager is a route manager interface
|
|
type Manager interface {
|
|
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
|
|
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
|
InitialRouteRange() []string
|
|
Stop()
|
|
}
|
|
|
|
// DefaultManager is the default instance of a route manager
|
|
type DefaultManager struct {
|
|
ctx context.Context
|
|
stop context.CancelFunc
|
|
mux sync.Mutex
|
|
clientNetworks map[string]*clientNetwork
|
|
serverRouter serverRouter
|
|
statusRecorder *peer.Status
|
|
wgInterface *iface.WGIface
|
|
pubKey string
|
|
notifier *notifier
|
|
}
|
|
|
|
// NewManager returns a new route manager
|
|
func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface, statusRecorder *peer.Status, initialRoutes []*route.Route) *DefaultManager {
|
|
srvRouter, err := newServerRouter(ctx, wgInterface)
|
|
if err != nil {
|
|
log.Errorf("server router is not supported: %s", err)
|
|
}
|
|
|
|
mCTX, cancel := context.WithCancel(ctx)
|
|
dm := &DefaultManager{
|
|
ctx: mCTX,
|
|
stop: cancel,
|
|
clientNetworks: make(map[string]*clientNetwork),
|
|
serverRouter: srvRouter,
|
|
statusRecorder: statusRecorder,
|
|
wgInterface: wgInterface,
|
|
pubKey: pubKey,
|
|
notifier: newNotifier(),
|
|
}
|
|
|
|
if runtime.GOOS == "android" {
|
|
cr := dm.clientRoutes(initialRoutes)
|
|
dm.notifier.setInitialClientRoutes(cr)
|
|
}
|
|
return dm
|
|
}
|
|
|
|
// Stop stops the manager watchers and clean firewall rules
|
|
func (m *DefaultManager) Stop() {
|
|
m.stop()
|
|
if m.serverRouter != nil {
|
|
m.serverRouter.cleanUp()
|
|
}
|
|
m.ctx = nil
|
|
}
|
|
|
|
// UpdateRoutes compares received routes with existing routes and remove, update or add them to the client and server maps
|
|
func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error {
|
|
select {
|
|
case <-m.ctx.Done():
|
|
log.Infof("not updating routes as context is closed")
|
|
return m.ctx.Err()
|
|
default:
|
|
m.mux.Lock()
|
|
defer m.mux.Unlock()
|
|
|
|
newServerRoutesMap, newClientRoutesIDMap := m.classifiesRoutes(newRoutes)
|
|
|
|
m.updateClientNetworks(updateSerial, newClientRoutesIDMap)
|
|
m.notifier.onNewRoutes(newClientRoutesIDMap)
|
|
|
|
if m.serverRouter != nil {
|
|
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// SetRouteChangeListener set RouteListener for route change notifier
|
|
func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
|
|
m.notifier.setListener(listener)
|
|
}
|
|
|
|
// InitialRouteRange return the list of initial routes. It used by mobile systems
|
|
func (m *DefaultManager) InitialRouteRange() []string {
|
|
return m.notifier.initialRouteRanges()
|
|
}
|
|
|
|
func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[string][]*route.Route) {
|
|
// removing routes that do not exist as per the update from the Management service.
|
|
for id, client := range m.clientNetworks {
|
|
_, found := networks[id]
|
|
if !found {
|
|
log.Debugf("stopping client network watcher, %s", id)
|
|
client.stop()
|
|
delete(m.clientNetworks, id)
|
|
}
|
|
}
|
|
|
|
for id, routes := range networks {
|
|
clientNetworkWatcher, found := m.clientNetworks[id]
|
|
if !found {
|
|
clientNetworkWatcher = newClientNetworkWatcher(m.ctx, m.wgInterface, m.statusRecorder, routes[0].Network)
|
|
m.clientNetworks[id] = clientNetworkWatcher
|
|
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
|
}
|
|
update := routesUpdate{
|
|
updateSerial: updateSerial,
|
|
routes: routes,
|
|
}
|
|
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(update)
|
|
}
|
|
}
|
|
|
|
func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route) {
|
|
newClientRoutesIDMap := make(map[string][]*route.Route)
|
|
newServerRoutesMap := make(map[string]*route.Route)
|
|
ownNetworkIDs := make(map[string]bool)
|
|
|
|
for _, newRoute := range newRoutes {
|
|
networkID := route.GetHAUniqueID(newRoute)
|
|
if newRoute.Peer == m.pubKey {
|
|
ownNetworkIDs[networkID] = true
|
|
// only linux is supported for now
|
|
if runtime.GOOS != "linux" {
|
|
log.Warnf("received a route to manage, but agent doesn't support router mode on %s OS", runtime.GOOS)
|
|
continue
|
|
}
|
|
newServerRoutesMap[newRoute.ID] = newRoute
|
|
}
|
|
}
|
|
|
|
for _, newRoute := range newRoutes {
|
|
networkID := route.GetHAUniqueID(newRoute)
|
|
if !ownNetworkIDs[networkID] {
|
|
// if prefix is too small, lets assume is a possible default route which is not yet supported
|
|
// we skip this route management
|
|
if newRoute.Network.Bits() < minRangeBits {
|
|
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skipping this route",
|
|
version.NetbirdVersion(), newRoute.Network)
|
|
continue
|
|
}
|
|
newClientRoutesIDMap[networkID] = append(newClientRoutesIDMap[networkID], newRoute)
|
|
}
|
|
}
|
|
|
|
return newServerRoutesMap, newClientRoutesIDMap
|
|
}
|
|
|
|
func (m *DefaultManager) clientRoutes(initialRoutes []*route.Route) []*route.Route {
|
|
_, crMap := m.classifiesRoutes(initialRoutes)
|
|
rs := make([]*route.Route, 0)
|
|
for _, routes := range crMap {
|
|
rs = append(rs, routes...)
|
|
}
|
|
return rs
|
|
}
|