2022-08-18 18:22:15 +02:00
|
|
|
package route
|
|
|
|
|
|
|
|
import (
|
2024-06-13 13:24:24 +02:00
|
|
|
"fmt"
|
2022-08-18 18:22:15 +02:00
|
|
|
"net/netip"
|
2024-06-13 13:24:24 +02:00
|
|
|
"slices"
|
2023-08-22 17:56:39 +02:00
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
|
|
|
"github.com/netbirdio/netbird/management/domain"
|
2023-08-22 17:56:39 +02:00
|
|
|
"github.com/netbirdio/netbird/management/server/status"
|
2022-08-18 18:22:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Windows has some limitation regarding metric size that differ from Unix-like systems.
|
|
|
|
// Because of that we are limiting the min and max metric size based on Windows limits:
|
|
|
|
// see based on info from https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/route_ws2008
|
|
|
|
const (
|
|
|
|
// MinMetric max metric input
|
|
|
|
MinMetric = 1
|
|
|
|
// MaxMetric max metric input
|
|
|
|
MaxMetric = 9999
|
2022-08-22 14:10:24 +02:00
|
|
|
// MaxNetIDChar Max Network Identifier
|
|
|
|
MaxNetIDChar = 40
|
2022-08-18 18:22:15 +02:00
|
|
|
)
|
2022-08-22 14:10:24 +02:00
|
|
|
|
2022-08-18 18:22:15 +02:00
|
|
|
const (
|
2022-08-22 14:10:24 +02:00
|
|
|
// InvalidNetworkString invalid network type string
|
|
|
|
InvalidNetworkString = "Invalid"
|
|
|
|
// IPv4NetworkString IPv4 network type string
|
|
|
|
IPv4NetworkString = "IPv4"
|
|
|
|
// IPv6NetworkString IPv6 network type string
|
|
|
|
IPv6NetworkString = "IPv6"
|
2024-06-13 13:24:24 +02:00
|
|
|
// DomainNetworkString domain network type string
|
|
|
|
DomainNetworkString = "Domain"
|
2022-08-18 18:22:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-08-22 14:10:24 +02:00
|
|
|
// InvalidNetwork invalid network type
|
|
|
|
InvalidNetwork NetworkType = iota
|
|
|
|
// IPv4Network IPv4 network type
|
|
|
|
IPv4Network
|
|
|
|
// IPv6Network IPv6 network type
|
|
|
|
IPv6Network
|
2024-06-13 13:24:24 +02:00
|
|
|
// DomainNetwork domain network type
|
|
|
|
DomainNetwork
|
2022-08-18 18:22:15 +02:00
|
|
|
)
|
|
|
|
|
2024-05-06 14:47:49 +02:00
|
|
|
type ID string
|
|
|
|
|
|
|
|
type NetID string
|
|
|
|
|
|
|
|
type HAMap map[HAUniqueID][]*Route
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
// NetworkType route network type
|
|
|
|
type NetworkType int
|
2022-08-18 18:22:15 +02:00
|
|
|
|
|
|
|
// String returns prefix type string
|
2022-08-22 14:10:24 +02:00
|
|
|
func (p NetworkType) String() string {
|
2022-08-18 18:22:15 +02:00
|
|
|
switch p {
|
2022-08-22 14:10:24 +02:00
|
|
|
case IPv4Network:
|
|
|
|
return IPv4NetworkString
|
|
|
|
case IPv6Network:
|
|
|
|
return IPv6NetworkString
|
2024-06-13 13:24:24 +02:00
|
|
|
case DomainNetwork:
|
|
|
|
return DomainNetworkString
|
2022-08-18 18:22:15 +02:00
|
|
|
default:
|
2022-08-22 14:10:24 +02:00
|
|
|
return InvalidNetworkString
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToPrefixType returns a prefix type
|
2022-08-22 14:10:24 +02:00
|
|
|
func ToPrefixType(prefix string) NetworkType {
|
2022-08-18 18:22:15 +02:00
|
|
|
switch prefix {
|
2022-08-22 14:10:24 +02:00
|
|
|
case IPv4NetworkString:
|
|
|
|
return IPv4Network
|
|
|
|
case IPv6NetworkString:
|
|
|
|
return IPv6Network
|
2024-06-13 13:24:24 +02:00
|
|
|
case DomainNetworkString:
|
|
|
|
return DomainNetwork
|
2022-08-18 18:22:15 +02:00
|
|
|
default:
|
2022-08-22 14:10:24 +02:00
|
|
|
return InvalidNetwork
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Route represents a route
|
|
|
|
type Route struct {
|
2024-05-06 14:47:49 +02:00
|
|
|
ID ID `gorm:"primaryKey"`
|
2023-10-12 15:42:36 +02:00
|
|
|
// AccountID is a reference to Account that this object belongs
|
2024-06-13 13:24:24 +02:00
|
|
|
AccountID string `gorm:"index"`
|
|
|
|
// Network and Domains are mutually exclusive
|
2024-04-18 18:14:21 +02:00
|
|
|
Network netip.Prefix `gorm:"serializer:json"`
|
2024-06-13 13:24:24 +02:00
|
|
|
Domains domain.List `gorm:"serializer:json"`
|
|
|
|
KeepRoute bool
|
2024-05-06 14:47:49 +02:00
|
|
|
NetID NetID
|
2022-08-18 18:22:15 +02:00
|
|
|
Description string
|
|
|
|
Peer string
|
2024-04-18 18:14:21 +02:00
|
|
|
PeerGroups []string `gorm:"serializer:json"`
|
2022-08-22 14:10:24 +02:00
|
|
|
NetworkType NetworkType
|
2022-08-18 18:22:15 +02:00
|
|
|
Masquerade bool
|
|
|
|
Metric int
|
|
|
|
Enabled bool
|
2023-10-12 15:42:36 +02:00
|
|
|
Groups []string `gorm:"serializer:json"`
|
2024-10-02 13:41:00 +02:00
|
|
|
AccessControlGroups []string `gorm:"serializer:json"`
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2023-01-25 16:29:59 +01:00
|
|
|
// EventMeta returns activity event meta related to the route
|
|
|
|
func (r *Route) EventMeta() map[string]any {
|
2024-06-13 13:24:24 +02:00
|
|
|
return map[string]any{"name": r.NetID, "network_range": r.Network.String(), "domains": r.Domains.SafeString(), "peer_id": r.Peer, "peer_groups": r.PeerGroups}
|
2023-01-25 16:29:59 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 18:22:15 +02:00
|
|
|
// Copy copies a route object
|
|
|
|
func (r *Route) Copy() *Route {
|
2023-08-22 17:56:39 +02:00
|
|
|
route := &Route{
|
2022-08-18 18:22:15 +02:00
|
|
|
ID: r.ID,
|
|
|
|
Description: r.Description,
|
2022-08-22 14:10:24 +02:00
|
|
|
NetID: r.NetID,
|
|
|
|
Network: r.Network,
|
2024-06-13 13:24:24 +02:00
|
|
|
Domains: slices.Clone(r.Domains),
|
|
|
|
KeepRoute: r.KeepRoute,
|
2022-08-22 14:10:24 +02:00
|
|
|
NetworkType: r.NetworkType,
|
2022-08-18 18:22:15 +02:00
|
|
|
Peer: r.Peer,
|
2024-06-13 13:24:24 +02:00
|
|
|
PeerGroups: slices.Clone(r.PeerGroups),
|
2022-08-18 18:22:15 +02:00
|
|
|
Metric: r.Metric,
|
|
|
|
Masquerade: r.Masquerade,
|
|
|
|
Enabled: r.Enabled,
|
2024-06-13 13:24:24 +02:00
|
|
|
Groups: slices.Clone(r.Groups),
|
2024-10-02 13:41:00 +02:00
|
|
|
AccessControlGroups: slices.Clone(r.AccessControlGroups),
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
2023-08-22 17:56:39 +02:00
|
|
|
return route
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsEqual compares one route with the other
|
|
|
|
func (r *Route) IsEqual(other *Route) bool {
|
2024-04-29 18:43:14 +02:00
|
|
|
if r == nil && other == nil {
|
|
|
|
return true
|
|
|
|
} else if r == nil || other == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-08-18 18:22:15 +02:00
|
|
|
return other.ID == r.ID &&
|
|
|
|
other.Description == r.Description &&
|
2022-08-22 14:10:24 +02:00
|
|
|
other.NetID == r.NetID &&
|
|
|
|
other.Network == r.Network &&
|
2024-06-13 13:24:24 +02:00
|
|
|
slices.Equal(r.Domains, other.Domains) &&
|
|
|
|
other.KeepRoute == r.KeepRoute &&
|
2022-08-22 14:10:24 +02:00
|
|
|
other.NetworkType == r.NetworkType &&
|
2022-08-18 18:22:15 +02:00
|
|
|
other.Peer == r.Peer &&
|
|
|
|
other.Metric == r.Metric &&
|
|
|
|
other.Masquerade == r.Masquerade &&
|
2022-12-06 10:11:57 +01:00
|
|
|
other.Enabled == r.Enabled &&
|
2024-06-13 13:24:24 +02:00
|
|
|
slices.Equal(r.Groups, other.Groups) &&
|
2024-10-02 13:41:00 +02:00
|
|
|
slices.Equal(r.PeerGroups, other.PeerGroups)&&
|
|
|
|
slices.Equal(r.AccessControlGroups, other.AccessControlGroups)
|
2024-06-13 13:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsDynamic returns if the route is dynamic, i.e. has domains
|
|
|
|
func (r *Route) IsDynamic() bool {
|
|
|
|
return r.NetworkType == DomainNetwork
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) GetHAUniqueID() HAUniqueID {
|
|
|
|
if r.IsDynamic() {
|
|
|
|
domains, err := r.Domains.String()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Failed to convert domains to string: %v", err)
|
|
|
|
domains = r.Domains.PunycodeString()
|
|
|
|
}
|
|
|
|
return HAUniqueID(fmt.Sprintf("%s%s%s", r.NetID, haSeparator, domains))
|
|
|
|
}
|
|
|
|
return HAUniqueID(fmt.Sprintf("%s%s%s", r.NetID, haSeparator, r.Network.String()))
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
// ParseNetwork Parses a network prefix string and returns a netip.Prefix object and if is invalid, IPv4 or IPv6
|
|
|
|
func ParseNetwork(networkString string) (NetworkType, netip.Prefix, error) {
|
|
|
|
prefix, err := netip.ParsePrefix(networkString)
|
2022-08-18 18:22:15 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
return InvalidNetwork, netip.Prefix{}, status.Errorf(status.InvalidArgument, "invalid network %s", networkString)
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
masked := prefix.Masked()
|
|
|
|
|
|
|
|
if !masked.IsValid() {
|
2022-11-11 20:36:45 +01:00
|
|
|
return InvalidNetwork, netip.Prefix{}, status.Errorf(status.InvalidArgument, "invalid range %s", networkString)
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if masked.Addr().Is6() {
|
2022-08-22 14:10:24 +02:00
|
|
|
return IPv6Network, masked, nil
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
return IPv4Network, masked, nil
|
2022-08-18 18:22:15 +02:00
|
|
|
}
|