wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
231
spn/navigator/region.go
Normal file
231
spn/navigator/region.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/safing/portbase/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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user