From 062b3fceb4074ba58f814340e6faa4480bdf9119 Mon Sep 17 00:00:00 2001 From: Marcelo Guerrero Date: Fri, 28 Mar 2025 16:44:23 +0100 Subject: [PATCH] Enable KeepAddrOnDown for ipv6 addresses This enables the keep_addr_on_down sysctl parameter for IPV6 addresses configured via the ConfigureIface function. This prevents IPAM confiuration to be lost when users need to refresh the link state of an interface that has IPV6 addresses. Signed-off-by: Marcelo Guerrero --- pkg/ipam/ipam_linux.go | 16 +++++++++++--- pkg/ipam/ipam_linux_test.go | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/pkg/ipam/ipam_linux.go b/pkg/ipam/ipam_linux.go index 4c5af045..b1814667 100644 --- a/pkg/ipam/ipam_linux.go +++ b/pkg/ipam/ipam_linux.go @@ -29,7 +29,8 @@ import ( const ( // Note: use slash as separator so we can have dots in interface name (VLANs) - DisableIPv6SysctlTemplate = "net/ipv6/conf/%s/disable_ipv6" + DisableIPv6SysctlTemplate = "net/ipv6/conf/%s/disable_ipv6" + KeepAddrOnDownSysctlTemplate = "net/ipv6/conf/%s/keep_addr_on_down" ) // ConfigureIface takes the result of IPAM plugin and @@ -56,8 +57,8 @@ func ConfigureIface(ifName string, res *current.Result) error { return fmt.Errorf("failed to add IP addr %v to %q: invalid interface index", ipc, ifName) } - // Make sure sysctl "disable_ipv6" is 0 if we are about to add - // an IPv6 address to the interface + // Make sure sysctl "disable_ipv6" is 0 and "keep_addr_on_down" is 1 + // if we are about to add an IPv6 address to the interface if !hasEnabledIpv6 && ipc.Address.IP.To4() == nil { // Enabled IPv6 for loopback "lo" and the interface // being configured @@ -80,6 +81,15 @@ func ConfigureIface(ifName string, res *current.Result) error { return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err) } } + + // Enable "keep_addr_on_down" for the interface being configured + // This prevents the kernel from removing the address when the interface is brought down + keepAddrOnDownSysctlValueName := fmt.Sprintf(KeepAddrOnDownSysctlTemplate, ifName) + _, err = sysctl.Sysctl(keepAddrOnDownSysctlValueName, "1") + if err != nil { + return fmt.Errorf("failed to enable keep_addr_on_down for interface %q: %v", ifName, err) + } + hasEnabledIpv6 = true } diff --git a/pkg/ipam/ipam_linux_test.go b/pkg/ipam/ipam_linux_test.go index 9afbcc1c..2de58c21 100644 --- a/pkg/ipam/ipam_linux_test.go +++ b/pkg/ipam/ipam_linux_test.go @@ -201,6 +201,49 @@ var _ = Describe("ConfigureIface", func() { Expect(err).NotTo(HaveOccurred()) }) + It("keeps IPV6 addresses after the interface is brought down", func() { + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + By("Configuring the interface") + + err := ConfigureIface(LINK_NAME, result) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying the IPV6 address is present") + + link, err := netlinksafe.LinkByName(LINK_NAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(LINK_NAME)) + + v6addrs, err := netlinksafe.AddrList(link, syscall.AF_INET6) + Expect(err).NotTo(HaveOccurred()) + Expect(v6addrs).To(HaveLen(2)) + + var found bool + for _, a := range v6addrs { + if ipNetEqual(a.IPNet, ipv6) { + found = true + break + } + } + Expect(found).To(BeTrue()) + + By("Bringing the interface down") + err = netlink.LinkSetDown(link) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying the IPV6 address is still present") + v6addrs, err = netlinksafe.AddrList(link, syscall.AF_INET6) + Expect(err).NotTo(HaveOccurred()) + Expect(v6addrs).To(HaveLen(1)) + Expect(ipNetEqual(v6addrs[0].IPNet, ipv6)).To(BeTrue()) + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + It("configures a link with routes using address gateways", func() { result.Routes[0].GW = nil result.Routes[1].GW = nil