Use temporary name for netdevice when moving in/out of NS
Today, it is not possible to use host-device CNI to move a host device to container namespace if a device already exists in that namespace. e.g when a delegate plugin (such as multus) is used to provide multiple networks to a container, CNI Add call will fail if the targeted host device name already exists in container network namespace. to overcome this, we use a temporary name for the interface before moving it in/out of container network namespace. Signed-off-by: adrianc <adrianc@nvidia.com>
This commit is contained in:
@ -199,34 +199,112 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setTempName sets a temporary name for netdevice, returns updated Link object or error
|
||||
// if occurred.
|
||||
func setTempName(dev netlink.Link) (netlink.Link, error) {
|
||||
tempName := fmt.Sprintf("%s%d", "temp_", dev.Attrs().Index)
|
||||
|
||||
// rename to tempName
|
||||
if err := netlink.LinkSetName(dev, tempName); err != nil {
|
||||
return nil, fmt.Errorf("failed to rename device %q to %q: %v", dev.Attrs().Name, tempName, err)
|
||||
}
|
||||
|
||||
// Get updated Link obj
|
||||
tempDev, err := netlink.LinkByName(tempName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find %q after rename to %q: %v", dev.Attrs().Name, tempName, err)
|
||||
}
|
||||
|
||||
return tempDev, nil
|
||||
}
|
||||
|
||||
func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netlink.Link, error) {
|
||||
if err := netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
|
||||
return nil, err
|
||||
origLinkFlags := hostDev.Attrs().Flags
|
||||
hostDevName := hostDev.Attrs().Name
|
||||
defaultNs, err := ns.GetCurrentNS()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get host namespace: %v", err)
|
||||
}
|
||||
|
||||
// Devices can be renamed only when down
|
||||
if err = netlink.LinkSetDown(hostDev); err != nil {
|
||||
return nil, fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
||||
}
|
||||
|
||||
// restore original link state in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if origLinkFlags&net.FlagUp == net.FlagUp && hostDev != nil {
|
||||
_ = netlink.LinkSetUp(hostDev)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
hostDev, err = setTempName(hostDev)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to rename device %q to temporary name: %v", hostDevName, err)
|
||||
}
|
||||
|
||||
// restore original netdev name in case of error
|
||||
defer func() {
|
||||
if err != nil && hostDev != nil {
|
||||
_ = netlink.LinkSetName(hostDev, hostDevName)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
|
||||
return nil, fmt.Errorf("failed to move %q to container ns: %v", hostDev.Attrs().Name, err)
|
||||
}
|
||||
|
||||
var contDev netlink.Link
|
||||
if err := containerNs.Do(func(_ ns.NetNS) error {
|
||||
tempDevName := hostDev.Attrs().Name
|
||||
if err = containerNs.Do(func(_ ns.NetNS) error {
|
||||
var err error
|
||||
contDev, err = netlink.LinkByName(hostDev.Attrs().Name)
|
||||
contDev, err = netlink.LinkByName(tempDevName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find %q: %v", hostDev.Attrs().Name, err)
|
||||
}
|
||||
// Devices can be renamed only when down
|
||||
if err = netlink.LinkSetDown(contDev); err != nil {
|
||||
return fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
||||
return fmt.Errorf("failed to find %q: %v", tempDevName, err)
|
||||
}
|
||||
|
||||
// move netdev back to host namespace in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = netlink.LinkSetNsFd(contDev, int(defaultNs.Fd()))
|
||||
// we need to get updated link object as link was moved back to host namepsace
|
||||
_ = defaultNs.Do(func(_ ns.NetNS) error {
|
||||
hostDev, _ = netlink.LinkByName(tempDevName)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
// Save host device name into the container device's alias property
|
||||
if err := netlink.LinkSetAlias(contDev, hostDev.Attrs().Name); err != nil {
|
||||
return fmt.Errorf("failed to set alias to %q: %v", hostDev.Attrs().Name, err)
|
||||
if err = netlink.LinkSetAlias(contDev, hostDevName); err != nil {
|
||||
return fmt.Errorf("failed to set alias to %q: %v", tempDevName, err)
|
||||
}
|
||||
// Rename container device to respect args.IfName
|
||||
if err := netlink.LinkSetName(contDev, ifName); err != nil {
|
||||
return fmt.Errorf("failed to rename device %q to %q: %v", hostDev.Attrs().Name, ifName, err)
|
||||
if err = netlink.LinkSetName(contDev, ifName); err != nil {
|
||||
return fmt.Errorf("failed to rename device %q to %q: %v", tempDevName, ifName, err)
|
||||
}
|
||||
|
||||
// restore tempDevName in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = netlink.LinkSetName(contDev, tempDevName)
|
||||
}
|
||||
}()
|
||||
|
||||
// Bring container device up
|
||||
if err = netlink.LinkSetUp(contDev); err != nil {
|
||||
return fmt.Errorf("failed to set %q up: %v", ifName, err)
|
||||
}
|
||||
|
||||
// bring device down in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = netlink.LinkSetDown(contDev)
|
||||
}
|
||||
}()
|
||||
|
||||
// Retrieve link again to get up-to-date name and attributes
|
||||
contDev, err = netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
@ -247,11 +325,14 @@ func moveLinkOut(containerNs ns.NetNS, ifName string) error {
|
||||
}
|
||||
defer defaultNs.Close()
|
||||
|
||||
return containerNs.Do(func(_ ns.NetNS) error {
|
||||
var tempName string
|
||||
var origDev netlink.Link
|
||||
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||
dev, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find %q: %v", ifName, err)
|
||||
}
|
||||
origDev = dev
|
||||
|
||||
// Devices can be renamed only when down
|
||||
if err = netlink.LinkSetDown(dev); err != nil {
|
||||
@ -269,16 +350,49 @@ func moveLinkOut(containerNs ns.NetNS, ifName string) error {
|
||||
}
|
||||
}()
|
||||
|
||||
// Rename the device to its original name from the host namespace
|
||||
if err = netlink.LinkSetName(dev, dev.Attrs().Alias); err != nil {
|
||||
return fmt.Errorf("failed to restore %q to original name %q: %v", ifName, dev.Attrs().Alias, err)
|
||||
newLink, err := setTempName(dev)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to rename device %q to temporary name: %v", ifName, err)
|
||||
}
|
||||
dev = newLink
|
||||
tempName = dev.Attrs().Name
|
||||
|
||||
if err = netlink.LinkSetNsFd(dev, int(defaultNs.Fd())); err != nil {
|
||||
return fmt.Errorf("failed to move %q to host netns: %v", dev.Attrs().Alias, err)
|
||||
return fmt.Errorf("failed to move %q to host netns: %v", tempName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename the device to its original name from the host namespace
|
||||
tempDev, err := netlink.LinkByName(tempName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find %q in host namespace: %v", tempName, err)
|
||||
}
|
||||
|
||||
if err = netlink.LinkSetName(tempDev, tempDev.Attrs().Alias); err != nil {
|
||||
// move device back to container ns so it may be retired
|
||||
defer func() {
|
||||
_ = netlink.LinkSetNsFd(tempDev, int(containerNs.Fd()))
|
||||
_ = containerNs.Do(func(_ ns.NetNS) error {
|
||||
lnk, err := netlink.LinkByName(tempName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = netlink.LinkSetName(lnk, ifName)
|
||||
if origDev.Attrs().Flags&net.FlagUp == net.FlagUp {
|
||||
_ = netlink.LinkSetUp(lnk)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}()
|
||||
return fmt.Errorf("failed to restore %q to original name %q: %v", tempName, tempDev.Attrs().Alias, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasDpdkDriver(pciaddr string) (bool, error) {
|
||||
|
Reference in New Issue
Block a user