@@ -24,29 +24,31 @@ import (
2424 "github.com/vishvananda/netlink/nl"
2525 "github.com/vishvananda/netns"
2626 "golang.org/x/sys/unix"
27+
28+ resourceapi "k8s.io/api/resource/v1beta1"
2729)
2830
29- func nsAttachNetdev (hostIfName string , containerNsPAth string , ifName string ) error {
31+ func nsAttachNetdev (hostIfName string , containerNsPAth string , ifName string ) ( * resourceapi. NetworkDeviceData , error ) {
3032 hostDev , err := netlink .LinkByName (hostIfName )
3133 // recover same behavior on vishvananda/netlink@1.2.1 and do not fail when the kernel returns NLM_F_DUMP_INTR.
3234 if err != nil && ! errors .Is (err , netlink .ErrDumpInterrupted ) {
33- return err
35+ return nil , err
3436 }
3537
3638 // Devices can be renamed only when down
3739 if err = netlink .LinkSetDown (hostDev ); err != nil {
38- return fmt .Errorf ("failed to set %q down: %v" , hostDev .Attrs ().Name , err )
40+ return nil , fmt .Errorf ("failed to set %q down: %v" , hostDev .Attrs ().Name , err )
3941 }
4042
4143 // get the existing IP addresses
4244 addresses , err := netlink .AddrList (hostDev , netlink .FAMILY_ALL )
4345 if err != nil && ! errors .Is (err , netlink .ErrDumpInterrupted ) {
44- return fmt .Errorf ("fail to get ip addresses: %w" , err )
46+ return nil , fmt .Errorf ("fail to get ip addresses: %w" , err )
4547 }
4648
4749 containerNs , err := netns .GetFromPath (containerNsPAth )
4850 if err != nil {
49- return err
51+ return nil , err
5052 }
5153 defer containerNs .Close ()
5254
@@ -60,7 +62,7 @@ func nsAttachNetdev(hostIfName string, containerNsPAth string, ifName string) er
6062 // Get a netlink socket in current namespace
6163 s , err := nl .GetNetlinkSocketAt (netns .None (), netns .None (), unix .NETLINK_ROUTE )
6264 if err != nil {
63- return fmt .Errorf ("could not get network namespace handle: %w" , err )
65+ return nil , fmt .Errorf ("could not get network namespace handle: %w" , err )
6466 }
6567 defer s .Close ()
6668
@@ -87,37 +89,65 @@ func nsAttachNetdev(hostIfName string, containerNsPAth string, ifName string) er
8789
8890 _ , err = req .Execute (unix .NETLINK_ROUTE , 0 )
8991 if err != nil && ! errors .Is (err , netlink .ErrDumpInterrupted ) {
90- return err
92+ return nil , err
9193 }
9294
9395 // to avoid golang problem with goroutines we create the socket in the
9496 // namespace and use it directly
9597 nhNs , err := netlink .NewHandleAt (containerNs )
9698 if err != nil {
97- return err
99+ return nil , err
98100 }
99101 defer nhNs .Close ()
100102
101103 nsLink , err := nhNs .LinkByName (attrs .Name )
102104 if err != nil && ! errors .Is (err , netlink .ErrDumpInterrupted ) {
103- return fmt .Errorf ("link not found for interface %s on namespace %s: %w" , attrs .Name , containerNsPAth , err )
105+ return nil , fmt .Errorf ("link not found for interface %s on namespace %s: %w" , attrs .Name , containerNsPAth , err )
104106 }
105107
108+ // Re-add the original IP addresses to the interface in the new namespace.
109+ // The kernel removes IP addresses when an interface is moved between network namespaces.
106110 for _ , address := range addresses {
111+ // Only move permanent IP addresses configured by the user, dynamic addresses are excluded because
112+ // their validity may rely on the original network namespace's context and they may have limited
113+ // lifetimes and are not guaranteed to be available in a new namespace.
114+ // Ref: https://www.ietf.org/rfc/rfc3549.txt
115+ if address .Flags & unix .IFA_F_PERMANENT == 0 {
116+ continue
117+ }
118+ // Only move IP addresses with global scope because those are not host-specific, auto-configured,
119+ // or have limited network scope, making them unsuitable inside the container namespace.
120+ // Ref: https://www.ietf.org/rfc/rfc3549.txt
121+ if address .Scope != unix .RT_SCOPE_UNIVERSE {
122+ continue
123+ }
107124 // remove the interface attribute of the original address
108125 // to avoid issues when the interface is renamed.
109126 err = nhNs .AddrAdd (nsLink , & netlink.Addr {IPNet : address .IPNet })
110127 if err != nil {
111- return fmt .Errorf ("fail to set up address %s on namespace %s: %w" , address .String (), containerNsPAth , err )
128+ return nil , fmt .Errorf ("fail to set up address %s on namespace %s: %w" , address .String (), containerNsPAth , err )
112129 }
113130 }
114131
115132 err = nhNs .LinkSetUp (nsLink )
116133 if err != nil {
117- return fmt .Errorf ("failt to set up interface %s on namespace %s: %w" , nsLink .Attrs ().Name , containerNsPAth , err )
134+ return nil , fmt .Errorf ("failt to set up interface %s on namespace %s: %w" , nsLink .Attrs ().Name , containerNsPAth , err )
118135 }
119136
120- return nil
137+ networkData := & resourceapi.NetworkDeviceData {
138+ InterfaceName : nsLink .Attrs ().Name ,
139+ HardwareAddress : string (nsLink .Attrs ().HardwareAddr .String ()),
140+ }
141+
142+ // get the existing IP addresses
143+ addresses , err = nhNs .AddrList (nsLink , netlink .FAMILY_ALL )
144+ if err == nil || errors .Is (err , netlink .ErrDumpInterrupted ) {
145+ for _ , address := range addresses {
146+ networkData .IPs = append (networkData .IPs , address .IPNet .String ())
147+ }
148+ }
149+
150+ return networkData , nil
121151}
122152
123153func nsDetachNetdev (containerNsPAth string , devName string ) error {
0 commit comments