2023-04-17 11:15:37 +02:00
//go:build !android
2024-06-13 13:24:24 +02:00
package systemops
2022-09-05 09:06:35 +02:00
import (
2024-04-08 18:56:52 +02:00
"bufio"
"errors"
"fmt"
2022-09-05 09:06:35 +02:00
"net"
"net/netip"
2022-12-04 13:22:21 +01:00
"os"
2023-06-09 18:30:36 +02:00
"syscall"
2023-04-17 11:15:37 +02:00
2024-04-08 18:56:52 +02:00
"github.com/hashicorp/go-multierror"
log "github.com/sirupsen/logrus"
2023-04-17 11:15:37 +02:00
"github.com/vishvananda/netlink"
2024-04-08 18:56:52 +02:00
2024-06-13 13:24:24 +02:00
nberrors "github.com/netbirdio/netbird/client/errors"
"github.com/netbirdio/netbird/client/internal/routemanager/sysctl"
"github.com/netbirdio/netbird/client/internal/routemanager/vars"
2024-04-08 18:56:52 +02:00
nbnet "github.com/netbirdio/netbird/util/net"
)
const (
// NetbirdVPNTableID is the ID of the custom routing table used by Netbird.
NetbirdVPNTableID = 0x1BD0
// NetbirdVPNTableName is the name of the custom routing table used by Netbird.
NetbirdVPNTableName = "netbird"
// rtTablesPath is the path to the file containing the routing table names.
rtTablesPath = "/etc/iproute2/rt_tables"
// ipv4ForwardingPath is the path to the file containing the IP forwarding setting.
2024-04-12 17:53:07 +02:00
ipv4ForwardingPath = "net.ipv4.ip_forward"
2024-03-21 16:49:28 +01:00
)
2023-06-09 18:36:49 +02:00
2024-04-08 18:56:52 +02:00
var ErrTableIDExists = errors . New ( "ID exists with different name" )
2022-09-05 09:06:35 +02:00
2024-04-12 17:53:07 +02:00
// originalSysctl stores the original sysctl values before they are modified
var originalSysctl map [ string ] int
// sysctlFailed is used as an indicator to emit a warning when default routes are configured
var sysctlFailed bool
2024-04-08 18:56:52 +02:00
type ruleParams struct {
2024-04-12 16:07:03 +02:00
priority int
2024-04-08 18:56:52 +02:00
fwmark int
tableID int
family int
invert bool
suppressPrefix int
description string
2024-03-21 16:49:28 +01:00
}
2024-04-26 16:37:27 +02:00
// isLegacy determines whether to use the legacy routing setup
func isLegacy ( ) bool {
return os . Getenv ( "NB_USE_LEGACY_ROUTING" ) == "true" || nbnet . CustomRoutingDisabled ( )
}
// setIsLegacy sets the legacy routing setup
func setIsLegacy ( b bool ) {
if b {
os . Setenv ( "NB_USE_LEGACY_ROUTING" , "true" )
} else {
os . Unsetenv ( "NB_USE_LEGACY_ROUTING" )
}
}
2024-04-08 18:56:52 +02:00
func getSetupRules ( ) [ ] ruleParams {
return [ ] ruleParams {
2024-04-12 16:07:03 +02:00
{ 100 , - 1 , syscall . RT_TABLE_MAIN , netlink . FAMILY_V4 , false , 0 , "rule with suppress prefixlen v4" } ,
{ 100 , - 1 , syscall . RT_TABLE_MAIN , netlink . FAMILY_V6 , false , 0 , "rule with suppress prefixlen v6" } ,
{ 110 , nbnet . NetbirdFwmark , NetbirdVPNTableID , netlink . FAMILY_V4 , true , - 1 , "rule v4 netbird" } ,
{ 110 , nbnet . NetbirdFwmark , NetbirdVPNTableID , netlink . FAMILY_V6 , true , - 1 , "rule v6 netbird" } ,
2024-04-08 18:56:52 +02:00
}
}
2024-04-03 18:04:22 +02:00
2024-06-13 13:24:24 +02:00
// SetupRouting establishes the routing configuration for the VPN, including essential rules
2024-04-08 18:56:52 +02:00
// to ensure proper traffic flow for management, locally configured routes, and VPN traffic.
//
// Rule 1 (Main Route Precedence): Safeguards locally installed routes by giving them precedence over
// potential routes received and configured for the VPN. This rule is skipped for the default route and routes
// that are not in the main table.
//
// Rule 2 (VPN Traffic Routing): Directs all remaining traffic to the 'NetbirdVPNTableID' custom routing table.
// This table is where a default route or other specific routes received from the management server are configured,
// enabling VPN connectivity.
2024-06-17 09:47:17 +02:00
func ( r * SysOps ) SetupRouting ( initAddresses [ ] net . IP ) ( _ nbnet . AddHookFunc , _ nbnet . RemoveHookFunc , err error ) {
2024-04-26 16:37:27 +02:00
if isLegacy ( ) {
2024-04-08 18:56:52 +02:00
log . Infof ( "Using legacy routing setup" )
2024-06-13 13:24:24 +02:00
return r . setupRefCounter ( initAddresses )
2024-04-08 18:56:52 +02:00
}
if err = addRoutingTableName ( ) ; err != nil {
log . Errorf ( "Error adding routing table name: %v" , err )
}
2024-06-13 13:24:24 +02:00
originalValues , err := sysctl . Setup ( r . wgInterface )
2024-04-12 17:53:07 +02:00
if err != nil {
log . Errorf ( "Error setting up sysctl: %v" , err )
sysctlFailed = true
}
originalSysctl = originalValues
2024-04-08 18:56:52 +02:00
defer func ( ) {
if err != nil {
2024-06-13 13:24:24 +02:00
if cleanErr := r . CleanupRouting ( ) ; cleanErr != nil {
2024-04-08 18:56:52 +02:00
log . Errorf ( "Error cleaning up routing: %v" , cleanErr )
}
}
} ( )
rules := getSetupRules ( )
for _ , rule := range rules {
if err := addRule ( rule ) ; err != nil {
if errors . Is ( err , syscall . EOPNOTSUPP ) {
log . Warnf ( "Rule operations are not supported, falling back to the legacy routing setup" )
2024-04-26 16:37:27 +02:00
setIsLegacy ( true )
2024-06-13 13:24:24 +02:00
return r . setupRefCounter ( initAddresses )
2024-04-08 18:56:52 +02:00
}
return nil , nil , fmt . Errorf ( "%s: %w" , rule . description , err )
}
}
return nil , nil , nil
}
2024-06-13 13:24:24 +02:00
// CleanupRouting performs a thorough cleanup of the routing configuration established by 'setupRouting'.
2024-04-08 18:56:52 +02:00
// It systematically removes the three rules and any associated routing table entries to ensure a clean state.
// The function uses error aggregation to report any errors encountered during the cleanup process.
2024-06-13 13:24:24 +02:00
func ( r * SysOps ) CleanupRouting ( ) error {
2024-04-26 16:37:27 +02:00
if isLegacy ( ) {
2024-06-13 13:24:24 +02:00
return r . cleanupRefCounter ( )
2024-04-08 18:56:52 +02:00
}
var result * multierror . Error
if err := flushRoutes ( NetbirdVPNTableID , netlink . FAMILY_V4 ) ; err != nil {
result = multierror . Append ( result , fmt . Errorf ( "flush routes v4: %w" , err ) )
}
if err := flushRoutes ( NetbirdVPNTableID , netlink . FAMILY_V6 ) ; err != nil {
result = multierror . Append ( result , fmt . Errorf ( "flush routes v6: %w" , err ) )
}
rules := getSetupRules ( )
for _ , rule := range rules {
2024-04-12 16:07:03 +02:00
if err := removeRule ( rule ) ; err != nil {
2024-04-08 18:56:52 +02:00
result = multierror . Append ( result , fmt . Errorf ( "%s: %w" , rule . description , err ) )
}
}
2024-06-13 13:24:24 +02:00
if err := sysctl . Cleanup ( originalSysctl ) ; err != nil {
2024-04-12 17:53:07 +02:00
result = multierror . Append ( result , fmt . Errorf ( "cleanup sysctl: %w" , err ) )
}
originalSysctl = nil
sysctlFailed = false
2024-06-13 13:24:24 +02:00
return nberrors . FormatErrorOrNil ( result )
2024-04-08 18:56:52 +02:00
}
2024-06-13 13:24:24 +02:00
func ( r * SysOps ) addToRouteTable ( prefix netip . Prefix , nexthop Nexthop ) error {
return addRoute ( prefix , nexthop , syscall . RT_TABLE_MAIN )
2024-04-08 18:56:52 +02:00
}
2024-06-13 13:24:24 +02:00
func ( r * SysOps ) removeFromRouteTable ( prefix netip . Prefix , nexthop Nexthop ) error {
return removeRoute ( prefix , nexthop , syscall . RT_TABLE_MAIN )
2024-04-08 18:56:52 +02:00
}
2024-06-13 13:24:24 +02:00
func ( r * SysOps ) AddVPNRoute ( prefix netip . Prefix , intf * net . Interface ) error {
2024-04-26 16:37:27 +02:00
if isLegacy ( ) {
2024-06-13 13:24:24 +02:00
return r . genericAddVPNRoute ( prefix , intf )
2024-04-08 18:56:52 +02:00
}
2024-06-13 13:24:24 +02:00
if sysctlFailed && ( prefix == vars . Defaultv4 || prefix == vars . Defaultv6 ) {
2024-04-12 17:53:07 +02:00
log . Warnf ( "Default route is configured but sysctl operations failed, VPN traffic may not be routed correctly, consider using NB_USE_LEGACY_ROUTING=true or setting net.ipv4.conf.*.rp_filter to 2 (loose) or 0 (off)" )
}
2024-04-08 18:56:52 +02:00
// No need to check if routes exist as main table takes precedence over the VPN table via Rule 1
// TODO remove this once we have ipv6 support
2024-06-13 13:24:24 +02:00
if prefix == vars . Defaultv4 {
if err := addUnreachableRoute ( vars . Defaultv6 , NetbirdVPNTableID ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "add blackhole: %w" , err )
}
}
2024-06-13 13:24:24 +02:00
if err := addRoute ( prefix , Nexthop { netip . Addr { } , intf } , NetbirdVPNTableID ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "add route: %w" , err )
}
return nil
}
2024-06-13 13:24:24 +02:00
func ( r * SysOps ) RemoveVPNRoute ( prefix netip . Prefix , intf * net . Interface ) error {
2024-04-26 16:37:27 +02:00
if isLegacy ( ) {
2024-06-13 13:24:24 +02:00
return r . genericRemoveVPNRoute ( prefix , intf )
2024-04-08 18:56:52 +02:00
}
// TODO remove this once we have ipv6 support
2024-06-13 13:24:24 +02:00
if prefix == vars . Defaultv4 {
if err := removeUnreachableRoute ( vars . Defaultv6 , NetbirdVPNTableID ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "remove unreachable route: %w" , err )
}
}
2024-06-13 13:24:24 +02:00
if err := removeRoute ( prefix , Nexthop { netip . Addr { } , intf } , NetbirdVPNTableID ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "remove route: %w" , err )
}
return nil
}
2024-08-02 11:47:12 +02:00
func GetRoutesFromTable ( ) ( [ ] netip . Prefix , error ) {
2024-04-08 18:56:52 +02:00
v4Routes , err := getRoutes ( syscall . RT_TABLE_MAIN , netlink . FAMILY_V4 )
2024-04-03 18:04:22 +02:00
if err != nil {
2024-04-08 18:56:52 +02:00
return nil , fmt . Errorf ( "get v4 routes: %w" , err )
2024-04-03 18:04:22 +02:00
}
2024-04-08 18:56:52 +02:00
v6Routes , err := getRoutes ( syscall . RT_TABLE_MAIN , netlink . FAMILY_V6 )
if err != nil {
return nil , fmt . Errorf ( "get v6 routes: %w" , err )
2024-04-03 18:04:22 +02:00
}
2024-04-08 18:56:52 +02:00
return append ( v4Routes , v6Routes ... ) , nil
}
2024-04-03 18:04:22 +02:00
2024-04-08 18:56:52 +02:00
// getRoutes fetches routes from a specific routing table identified by tableID.
func getRoutes ( tableID , family int ) ( [ ] netip . Prefix , error ) {
var prefixList [ ] netip . Prefix
routes , err := netlink . RouteListFiltered ( family , & netlink . Route { Table : tableID } , netlink . RT_FILTER_TABLE )
2024-04-03 18:04:22 +02:00
if err != nil {
2024-04-08 18:56:52 +02:00
return nil , fmt . Errorf ( "list routes from table %d: %v" , tableID , err )
2024-04-03 18:04:22 +02:00
}
2024-04-08 18:56:52 +02:00
for _ , route := range routes {
if route . Dst != nil {
addr , ok := netip . AddrFromSlice ( route . Dst . IP )
if ! ok {
return nil , fmt . Errorf ( "parse route destination IP: %v" , route . Dst . IP )
}
ones , _ := route . Dst . Mask . Size ( )
prefix := netip . PrefixFrom ( addr , ones )
if prefix . IsValid ( ) {
prefixList = append ( prefixList , prefix )
}
}
}
return prefixList , nil
}
// addRoute adds a route to a specific routing table identified by tableID.
2024-06-13 13:24:24 +02:00
func addRoute ( prefix netip . Prefix , nexthop Nexthop , tableID int ) error {
2022-09-05 09:06:35 +02:00
route := & netlink . Route {
2024-04-08 18:56:52 +02:00
Scope : netlink . SCOPE_UNIVERSE ,
Table : tableID ,
Family : getAddressFamily ( prefix ) ,
2022-09-05 09:06:35 +02:00
}
2024-04-08 18:56:52 +02:00
_ , ipNet , err := net . ParseCIDR ( prefix . String ( ) )
2024-04-03 18:04:22 +02:00
if err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "parse prefix %s: %w" , prefix , err )
}
route . Dst = ipNet
2024-06-13 13:24:24 +02:00
if err := addNextHop ( nexthop , route ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "add gateway and device: %w" , err )
}
if err := netlink . RouteAdd ( route ) ; err != nil && ! errors . Is ( err , syscall . EEXIST ) && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
return fmt . Errorf ( "netlink add route: %w" , err )
2022-09-05 09:06:35 +02:00
}
return nil
}
2024-04-08 18:56:52 +02:00
// addUnreachableRoute adds an unreachable route for the specified IP family and routing table.
// ipFamily should be netlink.FAMILY_V4 for IPv4 or netlink.FAMILY_V6 for IPv6.
// tableID specifies the routing table to which the unreachable route will be added.
func addUnreachableRoute ( prefix netip . Prefix , tableID int ) error {
2022-09-05 09:06:35 +02:00
_ , ipNet , err := net . ParseCIDR ( prefix . String ( ) )
if err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "parse prefix %s: %w" , prefix , err )
2023-11-24 11:31:22 +01:00
}
2024-04-08 18:56:52 +02:00
route := & netlink . Route {
Type : syscall . RTN_UNREACHABLE ,
Table : tableID ,
Family : getAddressFamily ( prefix ) ,
Dst : ipNet ,
2024-03-21 16:49:28 +01:00
}
2024-04-08 18:56:52 +02:00
if err := netlink . RouteAdd ( route ) ; err != nil && ! errors . Is ( err , syscall . EEXIST ) && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
return fmt . Errorf ( "netlink add unreachable route: %w" , err )
}
return nil
}
func removeUnreachableRoute ( prefix netip . Prefix , tableID int ) error {
_ , ipNet , err := net . ParseCIDR ( prefix . String ( ) )
2023-11-24 11:31:22 +01:00
if err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "parse prefix %s: %w" , prefix , err )
2023-11-24 11:31:22 +01:00
}
2022-09-05 09:06:35 +02:00
route := & netlink . Route {
2024-04-08 18:56:52 +02:00
Type : syscall . RTN_UNREACHABLE ,
Table : tableID ,
Family : getAddressFamily ( prefix ) ,
Dst : ipNet ,
2022-09-05 09:06:35 +02:00
}
2024-04-23 14:42:53 +02:00
if err := netlink . RouteDel ( route ) ; err != nil &&
! errors . Is ( err , syscall . ESRCH ) &&
! errors . Is ( err , syscall . ENOENT ) &&
! errors . Is ( err , syscall . EAFNOSUPPORT ) {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "netlink remove unreachable route: %w" , err )
2022-09-05 09:06:35 +02:00
}
return nil
2024-04-08 18:56:52 +02:00
2022-09-05 09:06:35 +02:00
}
2024-04-08 18:56:52 +02:00
// removeRoute removes a route from a specific routing table identified by tableID.
2024-06-13 13:24:24 +02:00
func removeRoute ( prefix netip . Prefix , nexthop Nexthop , tableID int ) error {
2024-04-08 18:56:52 +02:00
_ , ipNet , err := net . ParseCIDR ( prefix . String ( ) )
2023-06-09 18:27:09 +02:00
if err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "parse prefix %s: %w" , prefix , err )
}
route := & netlink . Route {
Scope : netlink . SCOPE_UNIVERSE ,
Table : tableID ,
Family : getAddressFamily ( prefix ) ,
Dst : ipNet ,
}
2024-06-13 13:24:24 +02:00
if err := addNextHop ( nexthop , route ) ; err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "add gateway and device: %w" , err )
}
if err := netlink . RouteDel ( route ) ; err != nil && ! errors . Is ( err , syscall . ESRCH ) && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
return fmt . Errorf ( "netlink remove route: %w" , err )
2024-03-21 16:49:28 +01:00
}
2024-04-08 18:56:52 +02:00
return nil
}
func flushRoutes ( tableID , family int ) error {
routes , err := netlink . RouteListFiltered ( family , & netlink . Route { Table : tableID } , netlink . RT_FILTER_TABLE )
2023-06-09 18:27:09 +02:00
if err != nil {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "list routes from table %d: %w" , tableID , err )
2023-06-09 18:27:09 +02:00
}
2024-03-21 16:49:28 +01:00
2024-04-08 18:56:52 +02:00
var result * multierror . Error
for i := range routes {
route := routes [ i ]
// unreachable default routes don't come back with Dst set
if route . Gw == nil && route . Src == nil && route . Dst == nil {
if family == netlink . FAMILY_V4 {
routes [ i ] . Dst = & net . IPNet { IP : net . IPv4zero , Mask : net . CIDRMask ( 0 , 32 ) }
} else {
routes [ i ] . Dst = & net . IPNet { IP : net . IPv6zero , Mask : net . CIDRMask ( 0 , 128 ) }
2023-06-09 18:27:09 +02:00
}
2024-03-21 16:49:28 +01:00
}
2024-04-08 18:56:52 +02:00
if err := netlink . RouteDel ( & routes [ i ] ) ; err != nil && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
result = multierror . Append ( result , fmt . Errorf ( "failed to delete route %v from table %d: %w" , routes [ i ] , tableID , err ) )
}
2024-03-21 16:49:28 +01:00
}
2024-04-08 18:56:52 +02:00
2024-06-13 13:24:24 +02:00
return nberrors . FormatErrorOrNil ( result )
2024-03-21 16:49:28 +01:00
}
2024-06-13 13:24:24 +02:00
func EnableIPForwarding ( ) error {
_ , err := sysctl . Set ( ipv4ForwardingPath , 1 , false )
2024-04-12 17:53:07 +02:00
return err
2024-04-08 18:56:52 +02:00
}
// entryExists checks if the specified ID or name already exists in the rt_tables file
// and verifies if existing names start with "netbird_".
func entryExists ( file * os . File , id int ) ( bool , error ) {
if _ , err := file . Seek ( 0 , 0 ) ; err != nil {
return false , fmt . Errorf ( "seek rt_tables: %w" , err )
}
scanner := bufio . NewScanner ( file )
for scanner . Scan ( ) {
line := scanner . Text ( )
var existingID int
var existingName string
if _ , err := fmt . Sscanf ( line , "%d %s\n" , & existingID , & existingName ) ; err == nil {
if existingID == id {
if existingName != NetbirdVPNTableName {
return true , ErrTableIDExists
}
return true , nil
}
}
}
if err := scanner . Err ( ) ; err != nil {
return false , fmt . Errorf ( "scan rt_tables: %w" , err )
}
return false , nil
}
// addRoutingTableName adds human-readable names for custom routing tables.
func addRoutingTableName ( ) error {
file , err := os . Open ( rtTablesPath )
if err != nil {
if errors . Is ( err , os . ErrNotExist ) {
return nil
}
return fmt . Errorf ( "open rt_tables: %w" , err )
}
defer func ( ) {
if err := file . Close ( ) ; err != nil {
log . Errorf ( "Error closing rt_tables: %v" , err )
}
} ( )
exists , err := entryExists ( file , NetbirdVPNTableID )
if err != nil {
return fmt . Errorf ( "verify entry %d, %s: %w" , NetbirdVPNTableID , NetbirdVPNTableName , err )
}
if exists {
return nil
}
// Reopen the file in append mode to add new entries
if err := file . Close ( ) ; err != nil {
log . Errorf ( "Error closing rt_tables before appending: %v" , err )
}
file , err = os . OpenFile ( rtTablesPath , os . O_WRONLY | os . O_APPEND | os . O_CREATE , 0644 )
if err != nil {
return fmt . Errorf ( "open rt_tables for appending: %w" , err )
}
if _ , err := file . WriteString ( fmt . Sprintf ( "\n%d\t%s\n" , NetbirdVPNTableID , NetbirdVPNTableName ) ) ; err != nil {
return fmt . Errorf ( "append entry to rt_tables: %w" , err )
}
return nil
}
// addRule adds a routing rule to a specific routing table identified by tableID.
func addRule ( params ruleParams ) error {
rule := netlink . NewRule ( )
rule . Table = params . tableID
rule . Mark = params . fwmark
rule . Family = params . family
rule . Priority = params . priority
rule . Invert = params . invert
rule . SuppressPrefixlen = params . suppressPrefix
2024-04-12 16:07:03 +02:00
if err := netlink . RuleAdd ( rule ) ; err != nil && ! errors . Is ( err , syscall . EEXIST ) && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "add routing rule: %w" , err )
}
return nil
}
// removeRule removes a routing rule from a specific routing table identified by tableID.
func removeRule ( params ruleParams ) error {
rule := netlink . NewRule ( )
rule . Table = params . tableID
rule . Mark = params . fwmark
rule . Family = params . family
rule . Invert = params . invert
rule . Priority = params . priority
rule . SuppressPrefixlen = params . suppressPrefix
2024-04-12 16:07:03 +02:00
if err := netlink . RuleDel ( rule ) ; err != nil && ! errors . Is ( err , syscall . ENOENT ) && ! errors . Is ( err , syscall . EAFNOSUPPORT ) {
2024-04-08 18:56:52 +02:00
return fmt . Errorf ( "remove routing rule: %w" , err )
}
return nil
}
// addNextHop adds the gateway and device to the route.
2024-06-13 13:24:24 +02:00
func addNextHop ( nexthop Nexthop , route * netlink . Route ) error {
if nexthop . Intf != nil {
route . LinkIndex = nexthop . Intf . Index
2024-04-26 16:37:27 +02:00
}
2024-06-13 13:24:24 +02:00
if nexthop . IP . IsValid ( ) {
route . Gw = nexthop . IP . AsSlice ( )
2024-04-08 18:56:52 +02:00
2024-04-26 16:37:27 +02:00
// if zone is set, it means the gateway is a link-local address, so we set the link index
2024-06-13 13:24:24 +02:00
if nexthop . IP . Zone ( ) != "" && nexthop . Intf == nil {
link , err := netlink . LinkByName ( nexthop . IP . Zone ( ) )
2024-04-26 16:37:27 +02:00
if err != nil {
2024-06-13 13:24:24 +02:00
return fmt . Errorf ( "get link by name for zone %s: %w" , nexthop . IP . Zone ( ) , err )
2024-04-26 16:37:27 +02:00
}
route . LinkIndex = link . Attrs ( ) . Index
2024-04-08 18:56:52 +02:00
}
}
return nil
}
func getAddressFamily ( prefix netip . Prefix ) int {
if prefix . Addr ( ) . Is4 ( ) {
return netlink . FAMILY_V4
}
return netlink . FAMILY_V6
2024-04-03 18:04:22 +02:00
}
2024-06-17 09:47:17 +02:00
func hasSeparateRouting ( ) ( [ ] netip . Prefix , error ) {
if isLegacy ( ) {
2024-08-02 11:47:12 +02:00
return GetRoutesFromTable ( )
2024-06-17 09:47:17 +02:00
}
return nil , ErrRoutingIsSeparate
}