mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-29 19:43:57 +01:00
0f0c7ec2ed
In case the route management feature is not supported then do not create unnecessary firewall and manager instances. This can happen if the nftables nor iptables is not available on the host OS. - Move the error handling to upper layer - Remove fake, useless implementations of interfaces - Update go-iptables because In Docker the old version can not determine well the path of executable file - update lib to 0.70
177 lines
5.2 KiB
Go
177 lines
5.2 KiB
Go
package routemanager
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"sync"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"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 RouteListener)
|
|
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 {
|
|
serverRouter, 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: serverRouter,
|
|
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 RouteListener) {
|
|
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() < 7 {
|
|
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping 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
|
|
}
|