safing-portmaster/spn/navigator/region.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

231 lines
5.5 KiB
Go

package navigator
import (
"context"
"math"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/profile/endpoints"
"github.com/safing/portmaster/spn/hub"
)
const (
defaultRegionalMinLanesPerHub = 0.5
defaultRegionalMaxLanesOnHub = 2
defaultSatelliteMinLanesPerHub = 0.3
defaultInternalMinLanesOnHub = 3
defaultInternalMaxHops = 3
)
// Region specifies a group of Hubs for optimization purposes.
type Region struct {
ID string
Name string
config *hub.RegionConfig
memberPolicy endpoints.Endpoints
pins []*Pin
regardedPins []*Pin
regionalMinLanes int
regionalMaxLanesOnHub int
satelliteMinLanes int
internalMinLanesOnHub int
internalMaxHops int
}
func (region *Region) getName() string {
switch {
case region == nil:
return "-"
case region.Name != "":
return region.Name
default:
return region.ID
}
}
func (m *Map) updateRegions(config []*hub.RegionConfig) {
// Reset map and pins.
m.regions = make([]*Region, 0, len(config))
for _, pin := range m.all {
pin.region = nil
}
// Stop if not regions are defined.
if len(config) == 0 {
return
}
// Build regions from config.
for _, regionConfig := range config {
// Check if region has an ID.
if regionConfig.ID == "" {
log.Error("spn/navigator: region is missing ID")
// Abort adding this region to the map.
continue
}
// Create new region.
region := &Region{
ID: regionConfig.ID,
Name: regionConfig.Name,
config: regionConfig,
}
// Parse member policy.
if len(regionConfig.MemberPolicy) == 0 {
log.Errorf("spn/navigator: member policy of region %s is missing", region.ID)
// Abort adding this region to the map.
continue
}
memberPolicy, err := endpoints.ParseEndpoints(regionConfig.MemberPolicy)
if err != nil {
log.Errorf("spn/navigator: failed to parse member policy of region %s: %s", region.ID, err)
// Abort adding this region to the map.
continue
}
region.memberPolicy = memberPolicy
// Recalculate region properties.
region.recalculateProperties()
// Add region to map.
m.regions = append(m.regions, region)
}
// Update region in all Pins.
for _, pin := range m.all {
m.updatePinRegion(pin)
}
}
func (region *Region) addPin(pin *Pin) {
// Find pin in region.
for _, regionPin := range region.pins {
if pin.Hub.ID == regionPin.Hub.ID {
// Pin is already part of region.
return
}
}
// Check if pin is already part of this region.
if pin.region != nil && pin.region.ID == region.ID {
return
}
// Remove pin from previous region.
if pin.region != nil {
pin.region.removePin(pin)
}
// Add new pin to region.
region.pins = append(region.pins, pin)
pin.region = region
// Recalculate region properties.
region.recalculateProperties()
}
func (region *Region) removePin(pin *Pin) {
// Find pin index in region.
removeIndex := -1
for index, regionPin := range region.pins {
if pin.Hub.ID == regionPin.Hub.ID {
removeIndex = index
break
}
}
if removeIndex < 0 {
// Pin is not part of region.
return
}
// Remove pin from region.
region.pins = append(region.pins[:removeIndex], region.pins[removeIndex+1:]...)
// Recalculate region properties.
region.recalculateProperties()
}
func (region *Region) recalculateProperties() {
// Regional properties.
region.regionalMinLanes = calculateMinLanes(
len(region.pins),
region.config.RegionalMinLanes,
region.config.RegionalMinLanesPerHub,
defaultRegionalMinLanesPerHub,
)
region.regionalMaxLanesOnHub = region.config.RegionalMaxLanesOnHub
if region.regionalMaxLanesOnHub <= 0 {
region.regionalMaxLanesOnHub = defaultRegionalMaxLanesOnHub
}
// Satellite properties.
region.satelliteMinLanes = calculateMinLanes(
len(region.pins),
region.config.SatelliteMinLanes,
region.config.SatelliteMinLanesPerHub,
defaultSatelliteMinLanesPerHub,
)
// Internal properties.
region.internalMinLanesOnHub = region.config.InternalMinLanesOnHub
if region.internalMinLanesOnHub <= 0 {
region.internalMinLanesOnHub = defaultInternalMinLanesOnHub
}
region.internalMaxHops = region.config.InternalMaxHops
if region.internalMaxHops <= 0 {
region.internalMaxHops = defaultInternalMaxHops
}
// Values below 2 do not make any sense for max hops.
if region.internalMaxHops < 2 {
region.internalMaxHops = 2
}
}
func calculateMinLanes(regionHubCount, minLanes int, minLanesPerHub, defaultMinLanesPerHub float64) (minLaneCount int) {
// Validate hub count.
if regionHubCount <= 0 {
// Reset to safe value.
regionHubCount = 1
}
// Set to configured minimum lanes.
minLaneCount = minLanes
// Raise to configured minimum lanes per Hub.
if minLanesPerHub != 0 {
minLanesFromSize := int(math.Ceil(float64(regionHubCount) * minLanesPerHub))
if minLanesFromSize > minLaneCount {
minLaneCount = minLanesFromSize
}
}
// Raise to default minimum lanes per Hub, if still 0.
if minLaneCount <= 0 {
minLaneCount = int(math.Ceil(float64(regionHubCount) * defaultMinLanesPerHub))
}
return minLaneCount
}
func (m *Map) updatePinRegion(pin *Pin) {
for _, region := range m.regions {
// Check if pin matches the region's member policy.
if pin.EntityV4 != nil {
result, _ := region.memberPolicy.Match(context.TODO(), pin.EntityV4)
if result == endpoints.Permitted {
region.addPin(pin)
return
}
}
if pin.EntityV6 != nil {
result, _ := region.memberPolicy.Match(context.TODO(), pin.EntityV6)
if result == endpoints.Permitted {
region.addPin(pin)
return
}
}
}
}