From 7461d4cef422cc6317f03e81c13e4e45f23dc56c Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Wed, 20 May 2026 14:35:19 +0200 Subject: [PATCH] Move v6 forwarding helpers to dedicated file --- .../routemanager/systemops/systemops_linux.go | 64 --------------- .../systemops/v6forwarding_linux.go | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 client/internal/routemanager/systemops/v6forwarding_linux.go diff --git a/client/internal/routemanager/systemops/systemops_linux.go b/client/internal/routemanager/systemops/systemops_linux.go index 6d20dbaee..7d608d886 100644 --- a/client/internal/routemanager/systemops/systemops_linux.go +++ b/client/internal/routemanager/systemops/systemops_linux.go @@ -55,11 +55,6 @@ const ( ipv4ForwardingPath = "net.ipv4.ip_forward" // ipv6ForwardingPath is the path to the file containing the IPv6 forwarding setting. ipv6ForwardingPath = "net.ipv6.conf.all.forwarding" - // 1 (default) accepts RAs only while forwarding is off; 2 keeps RA - // acceptance on regardless, so RA-installed host defaults survive our - // v6 forwarding flip. - acceptRAInterfacePath = "net.ipv6.conf.%s.accept_ra" - acceptRAProcPathFormat = "/proc/sys/net/ipv6/conf/%s/accept_ra" ) var ErrTableIDExists = errors.New("ID exists with different name") @@ -775,65 +770,6 @@ func EnableV4IPForwarding() error { return nil } -// EnableV6IPForwarding bumps accept_ra=2 on host v6 interfaces before flipping -// forwarding=1, so RA-installed host defaults survive. Returns the prior values -// of sysctls we actually changed; entries already at the target are omitted. -func EnableV6IPForwarding(wgIfaceName string) (map[string]int, error) { - saved := map[string]int{} - bumpAcceptRA(saved, wgIfaceName) - - oldVal, err := sysctl.Set(ipv6ForwardingPath, 1, false) - if err != nil { - return saved, err - } - if oldVal != 1 { - saved[ipv6ForwardingPath] = oldVal - } - return saved, nil -} - -// DisableV6IPForwarding restores what EnableV6IPForwarding captured. -func DisableV6IPForwarding(saved map[string]int) error { - var result *multierror.Error - for key, value := range saved { - if _, err := sysctl.Set(key, value, false); err != nil { - result = multierror.Append(result, fmt.Errorf("restore %s: %w", key, err)) - } - } - return nberrors.FormatErrorOrNil(result) -} - -func bumpAcceptRA(saved map[string]int, wgIfaceName string) { - interfaces, err := net.Interfaces() - if err != nil { - log.Warnf("list interfaces for accept_ra: %v", err) - return - } - for _, intf := range interfaces { - if intf.Name == "lo" || intf.Name == wgIfaceName { - continue - } - bumpAcceptRAForInterface(saved, intf.Name) - } -} - -func bumpAcceptRAForInterface(saved map[string]int, name string) { - key := fmt.Sprintf(acceptRAInterfacePath, name) - // Build procfs path from name, not the dotted key: VLAN names like eth0.100. - if _, err := os.Stat(fmt.Sprintf(acceptRAProcPathFormat, name)); err != nil { - return - } - // onlyIfOne=true: leave admin overrides (0, 2) alone. - oldVal, err := sysctl.Set(key, 2, true) - if err != nil { - log.Warnf("bump %s: %v", key, err) - return - } - if oldVal != 2 { - saved[key] = oldVal - } -} - // 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) { diff --git a/client/internal/routemanager/systemops/v6forwarding_linux.go b/client/internal/routemanager/systemops/v6forwarding_linux.go new file mode 100644 index 000000000..14e6b1324 --- /dev/null +++ b/client/internal/routemanager/systemops/v6forwarding_linux.go @@ -0,0 +1,82 @@ +//go:build !android + +package systemops + +import ( + "fmt" + "net" + "os" + + "github.com/hashicorp/go-multierror" + log "github.com/sirupsen/logrus" + + nberrors "github.com/netbirdio/netbird/client/errors" + "github.com/netbirdio/netbird/client/internal/routemanager/sysctl" +) + +const ( + // 1 (default) accepts RAs only while forwarding is off; 2 keeps RA + // acceptance on regardless, so RA-installed host defaults survive our + // v6 forwarding flip. + acceptRAInterfacePath = "net.ipv6.conf.%s.accept_ra" + acceptRAProcPathFormat = "/proc/sys/net/ipv6/conf/%s/accept_ra" +) + +// EnableV6IPForwarding bumps accept_ra=2 on host v6 interfaces before flipping +// forwarding=1, so RA-installed host defaults survive. Returns the prior values +// of sysctls we actually changed; entries already at the target are omitted. +func EnableV6IPForwarding(wgIfaceName string) (map[string]int, error) { + saved := map[string]int{} + bumpAcceptRA(saved, wgIfaceName) + + oldVal, err := sysctl.Set(ipv6ForwardingPath, 1, false) + if err != nil { + return saved, err + } + if oldVal != 1 { + saved[ipv6ForwardingPath] = oldVal + } + return saved, nil +} + +// DisableV6IPForwarding restores what EnableV6IPForwarding captured. +func DisableV6IPForwarding(saved map[string]int) error { + var result *multierror.Error + for key, value := range saved { + if _, err := sysctl.Set(key, value, false); err != nil { + result = multierror.Append(result, fmt.Errorf("restore %s: %w", key, err)) + } + } + return nberrors.FormatErrorOrNil(result) +} + +func bumpAcceptRA(saved map[string]int, wgIfaceName string) { + interfaces, err := net.Interfaces() + if err != nil { + log.Warnf("list interfaces for accept_ra: %v", err) + return + } + for _, intf := range interfaces { + if intf.Name == "lo" || intf.Name == wgIfaceName { + continue + } + bumpAcceptRAForInterface(saved, intf.Name) + } +} + +func bumpAcceptRAForInterface(saved map[string]int, name string) { + key := fmt.Sprintf(acceptRAInterfacePath, name) + // Build procfs path from name, not the dotted key: VLAN names like eth0.100. + if _, err := os.Stat(fmt.Sprintf(acceptRAProcPathFormat, name)); err != nil { + return + } + // onlyIfOne=true: leave admin overrides (0, 2) alone. + oldVal, err := sysctl.Set(key, 2, true) + if err != nil { + log.Warnf("bump %s: %v", key, err) + return + } + if oldVal != 2 { + saved[key] = oldVal + } +}