From 3d68dbafdcea2c1ef277a485118178a8ffa0b6f8 Mon Sep 17 00:00:00 2001 From: mzz <2017@duck.com> Date: Tue, 11 Jun 2024 21:33:21 +0800 Subject: [PATCH] fix: set accept_ra=2 to fix missing ipv6 address on WAN interface if necessary (#504) --- control/control_plane.go | 14 +++++++++++++ control/control_plane_core.go | 1 - control/netns_utils.go | 10 +++++----- control/sysctl.go | 26 +++++++++++++++++++++++-- docs/en/user-guide/kernel-parameters.md | 19 +++++++++++++++++- 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/control/control_plane.go b/control/control_plane.go index a4f710b..27b81cc 100644 --- a/control/control_plane.go +++ b/control/control_plane.go @@ -208,6 +208,7 @@ func NewControlPlane( if len(global.LanInterface) > 0 { if global.AutoConfigKernelParameter { _ = SetIpv4forward("1") + _ = setForwarding("all", consts.IpVersionStr_6, "1") } global.LanInterface = common.Deduplicate(global.LanInterface) for _, ifname := range global.LanInterface { @@ -227,6 +228,19 @@ func NewControlPlane( } } for _, ifname := range global.WanInterface { + if len(global.LanInterface) > 0 { + // FIXME: Code is not elegant here. + // bindLan setting conf.ipv6.all.forwarding=1 suppresses accept_ra=1, + // thus we set it 2 as a workaround. + // See https://sysctl-explorer.net/net/ipv6/accept_ra/ for more information. + if global.AutoConfigKernelParameter { + acceptRa := sysctl.Keyf("net.ipv6.conf.%v.accept_ra", ifname) + val, _ := acceptRa.Get() + if val == "1" { + _ = acceptRa.Set("2", false) + } + } + } if err = core.bindWan(ifname, global.AutoConfigKernelParameter); err != nil { return nil, fmt.Errorf("bindWan: %v: %w", ifname, err) } diff --git a/control/control_plane_core.go b/control/control_plane_core.go index 6be9ad8..ab84170 100644 --- a/control/control_plane_core.go +++ b/control/control_plane_core.go @@ -247,7 +247,6 @@ func (c *controlPlaneCore) bindLan(ifname string, autoConfigKernelParameter bool if autoConfigKernelParameter { SetSendRedirects(ifname, "0") SetForwarding(ifname, "1") - setForwarding("all", consts.IpVersionStr_6, "1") } if err := c._bindLan(ifname); err != nil { var notFoundErr netlink.LinkNotFoundError diff --git a/control/netns_utils.go b/control/netns_utils.go index df7c7a3..307f0d4 100644 --- a/control/netns_utils.go +++ b/control/netns_utils.go @@ -281,11 +281,11 @@ func (ns *DaeNetns) setupNetns() (err error) { func (ns *DaeNetns) setupSysctl() (err error) { // sysctl net.ipv6.conf.dae0.disable_ipv6=0 - if err = sysctl.Set(fmt.Sprintf("net.ipv6.conf.%s.disable_ipv6", HostVethName), "0", true); err != nil { + if err = sysctl.Keyf("net.ipv6.conf.%s.disable_ipv6", HostVethName).Set("0", true); err != nil { return fmt.Errorf("failed to set disable_ipv6 for dae0: %v", err) } // sysctl net.ipv6.conf.dae0.forwarding=1 - if err = sysctl.Set(fmt.Sprintf("net.ipv6.conf.%s.forwarding", HostVethName), "1", true); err != nil { + if err = sysctl.Keyf("net.ipv6.conf.%s.forwarding", HostVethName).Set("1", true); err != nil { return fmt.Errorf("failed to set forwarding for dae0: %v", err) } @@ -295,12 +295,12 @@ func (ns *DaeNetns) setupSysctl() (err error) { defer netns.Set(ns.hostNs) // *_early_demux is not mandatory, but it's recommended to enable it for better performance - sysctl.Set("net.ipv4.tcp_early_demux", "1", false) - sysctl.Set("net.ipv4.ip_early_demux", "1", false) + sysctl.Keyf("net.ipv4.tcp_early_demux").Set("1", false) + sysctl.Keyf("net.ipv4.ip_early_demux").Set("1", false) // (ip net e daens) sysctl net.ipv4.conf.dae0peer.accept_local=1 // This is to prevent kernel from dropping skb due to "martian source" check: https://elixir.bootlin.com/linux/v6.6/source/net/ipv4/fib_frontend.c#L381 - if err = sysctl.Set(fmt.Sprintf("net.ipv4.conf.%s.accept_local", NsVethName), "1", false); err != nil { + if err = sysctl.Keyf("net.ipv4.conf.%s.accept_local", NsVethName).Set("1", false); err != nil { return fmt.Errorf("failed to set accept_local for dae0peer: %v", err) } return diff --git a/control/sysctl.go b/control/sysctl.go index 088ddaa..c390927 100644 --- a/control/sysctl.go +++ b/control/sysctl.go @@ -1,6 +1,7 @@ package control import ( + "fmt" "os" "strings" "sync" @@ -76,8 +77,29 @@ func (s *SysctlManager) startWatch() { } } -func (s *SysctlManager) Set(key string, value string, watch bool) (err error) { - path := SysctlPrefixPath + strings.Replace(key, ".", "/", -1) +type SysctlKey string + +func (s *SysctlManager) Keyf(format string, a ...any) SysctlKey { + return SysctlKey(SysctlPrefixPath + fmt.Sprintf(strings.ReplaceAll(format, ".", "/"), a...)) +} + +func (k SysctlKey) Get() (value string, err error) { + return sysctl.get(string(k)) +} + +func (k SysctlKey) Set(value string, watch bool) (err error) { + return sysctl.set(string(k), value, watch) +} + +func (s *SysctlManager) get(path string) (value string, err error) { + val, err := os.ReadFile(path) + if err != nil { + return "", err + } + return strings.TrimSpace(string(val)), nil +} + +func (s *SysctlManager) set(path string, value string, watch bool) (err error) { if watch { s.mux.Lock() s.expectations[path] = value diff --git a/docs/en/user-guide/kernel-parameters.md b/docs/en/user-guide/kernel-parameters.md index 353bc31..bc5df84 100644 --- a/docs/en/user-guide/kernel-parameters.md +++ b/docs/en/user-guide/kernel-parameters.md @@ -10,7 +10,7 @@ For every LAN interfaces you want to proxy: ```shell export lan_ifname=docker0 -sudo tee /etc/sysctl.d/60-dae-$lan_ifname.conf << EOF +sudo tee /etc/sysctl.d/60-dae-lan-$lan_ifname.conf << EOF net.ipv4.conf.$lan_ifname.forwarding = 1 net.ipv6.conf.$lan_ifname.forwarding = 1 net.ipv4.conf.$lan_ifname.send_redirects = 0 @@ -27,3 +27,20 @@ sudo sysctl --system ``` Please modify `docker0` to your LAN interface. + +For your WAN interfaces that accept RA: + +```shell +export wan_ifname=eth0 + +if [ "$(cat /proc/sys/net/ipv6/conf/$wan_ifname/accept_ra)" == "1" ]; then + sudo tee /etc/sysctl.d/60-dae-wan-$wan_ifname.conf << EOF +net.ipv6.conf.$wan_ifname.accept_ra = 2 +EOF + sudo sysctl --system +fi +``` + +Please modify `eth0` to your WAN interface. + +Setting accept_ra to 2 if it is 1 because `net.ipv6.conf.all.forwarding = 1` will suppress it. See for more information.