devChangeNetNamespace allows to move a device given by name to a network namespace given by nsPath and optionally change the device name. The device name will be kept the same if device.Name is the zero value. This function ensures that the move and rename operations occur atomically. It preserves e
(name, nsPath string, device configs.LinuxNetDevice)
| 111 | // This function ensures that the move and rename operations occur atomically. |
| 112 | // It preserves existing interface attributes, including global IP addresses. |
| 113 | func devChangeNetNamespace(name, nsPath string, device configs.LinuxNetDevice) error { |
| 114 | logrus.Debugf("attaching network device %s with attrs %+v to network namespace %s", name, device, nsPath) |
| 115 | link, err := netlink.LinkByName(name) |
| 116 | // recover same behavior on vishvananda/netlink@1.2.1 and do not fail when the kernel returns NLM_F_DUMP_INTR. |
| 117 | if err != nil && !errors.Is(err, netlink.ErrDumpInterrupted) { |
| 118 | return fmt.Errorf("link not found for interface %s on runtime namespace: %w", name, err) |
| 119 | } |
| 120 | |
| 121 | // Set the interface link state to DOWN before modifying attributes like namespace or name. |
| 122 | // This prevents potential conflicts or disruptions on the host network during the transition, |
| 123 | // particularly if other host components depend on this specific interface or its properties. |
| 124 | err = netlink.LinkSetDown(link) |
| 125 | if err != nil { |
| 126 | return fmt.Errorf("fail to set link down: %w", err) |
| 127 | } |
| 128 | |
| 129 | // Get the existing IP addresses on the interface. |
| 130 | addresses, err := netlink.AddrList(link, netlink.FAMILY_ALL) |
| 131 | // recover same behavior on vishvananda/netlink@1.2.1 and do not fail when the kernel returns NLM_F_DUMP_INTR. |
| 132 | if err != nil && !errors.Is(err, netlink.ErrDumpInterrupted) { |
| 133 | return fmt.Errorf("fail to get ip addresses: %w", err) |
| 134 | } |
| 135 | |
| 136 | // Do interface rename and namespace change in the same operation to avoid |
| 137 | // possible conflicts with the interface name. |
| 138 | // NLM_F_REQUEST: "It must be set on all request messages." |
| 139 | // NLM_F_ACK: "Request for an acknowledgment on success." |
| 140 | // netlink(7) man page: https://man7.org/linux/man-pages/man7/netlink.7.html |
| 141 | flags := unix.NLM_F_REQUEST | unix.NLM_F_ACK |
| 142 | req := nl.NewNetlinkRequest(unix.RTM_NEWLINK, flags) |
| 143 | |
| 144 | // Get a netlink socket in current namespace |
| 145 | nlSock, err := nl.GetNetlinkSocketAt(netns.None(), netns.None(), unix.NETLINK_ROUTE) |
| 146 | if err != nil { |
| 147 | return fmt.Errorf("could not get network namespace handle: %w", err) |
| 148 | } |
| 149 | defer nlSock.Close() |
| 150 | |
| 151 | req.Sockets = map[int]*nl.SocketHandle{ |
| 152 | unix.NETLINK_ROUTE: {Socket: nlSock}, |
| 153 | } |
| 154 | |
| 155 | // Set the interface index. |
| 156 | msg := nl.NewIfInfomsg(unix.AF_UNSPEC) |
| 157 | msg.Index = int32(link.Attrs().Index) |
| 158 | req.AddData(msg) |
| 159 | |
| 160 | // Set the interface name, also rename if requested. |
| 161 | newName := name |
| 162 | if device.Name != "" { |
| 163 | newName = device.Name |
| 164 | } |
| 165 | nameData := nl.NewRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(newName)) |
| 166 | req.AddData(nameData) |
| 167 | |
| 168 | // Get the new network namespace. |
| 169 | ns, err := netns.GetFromPath(nsPath) |
| 170 | if err != nil { |
no test coverage detected
searching dependent graphs…