Skip to content

Commit

Permalink
net: Allow to specify ifindex when device is moved to another namespace
Browse files Browse the repository at this point in the history
Currently, we can specify ifindex on link creation. This change allows
to specify ifindex when a device is moved to another network namespace.

Even now, a device ifindex can be changed if there is another device
with the same ifindex in the target namespace. So this change doesn't
introduce completely new behavior, it adds more control to the process.

CRIU users want to restore containers with pre-created network devices.
A user will provide network devices and instructions where they have to
be restored, then CRIU will restore network namespaces and move devices
into them. The problem is that devices have to be restored with the same
indexes that they have before C/R.

Cc: Alexander Mikhalitsyn <[email protected]>
Suggested-by: Christian Brauner <[email protected]>
Signed-off-by: Andrei Vagin <[email protected]>
Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
avagin authored and davem330 committed Apr 5, 2021
1 parent d329586 commit eeb85a1
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 17 deletions.
2 changes: 1 addition & 1 deletion drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -2354,7 +2354,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev)
*/
if (!net_eq(dev_net(ndev), dev_net(vf_netdev))) {
ret = dev_change_net_namespace(vf_netdev,
dev_net(ndev), "eth%d");
dev_net(ndev), "eth%d", 0);
if (ret)
netdev_err(vf_netdev,
"could not move to same namespace as %s: %d\n",
Expand Down
3 changes: 2 additions & 1 deletion include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -4026,7 +4026,8 @@ void __dev_notify_flags(struct net_device *, unsigned int old_flags,
int dev_change_name(struct net_device *, const char *);
int dev_set_alias(struct net_device *, const char *, size_t);
int dev_get_alias(const struct net_device *, char *, size_t);
int dev_change_net_namespace(struct net_device *, struct net *, const char *);
int dev_change_net_namespace(struct net_device *dev, struct net *net,
const char *pat, int new_ifindex);
int __dev_set_mtu(struct net_device *, int);
int dev_validate_mtu(struct net_device *dev, int mtu,
struct netlink_ext_ack *extack);
Expand Down
24 changes: 17 additions & 7 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -11067,6 +11067,8 @@ EXPORT_SYMBOL(unregister_netdev);
* @net: network namespace
* @pat: If not NULL name pattern to try if the current device name
* is already taken in the destination network namespace.
* @new_ifindex: If not zero, specifies device index in the target
* namespace.
*
* This function shuts down a device interface and moves it
* to a new network namespace. On success 0 is returned, on
Expand All @@ -11075,10 +11077,11 @@ EXPORT_SYMBOL(unregister_netdev);
* Callers must hold the rtnl semaphore.
*/

int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
int dev_change_net_namespace(struct net_device *dev, struct net *net,
const char *pat, int new_ifindex)
{
struct net *net_old = dev_net(dev);
int err, new_nsid, new_ifindex;
int err, new_nsid;

ASSERT_RTNL();

Expand Down Expand Up @@ -11109,6 +11112,11 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
goto out;
}

/* Check that new_ifindex isn't used yet. */
err = -EBUSY;
if (new_ifindex && __dev_get_by_index(net, new_ifindex))
goto out;

/*
* And now a mini version of register_netdevice unregister_netdevice.
*/
Expand Down Expand Up @@ -11136,10 +11144,12 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char

new_nsid = peernet2id_alloc(dev_net(dev), net, GFP_KERNEL);
/* If there is an ifindex conflict assign a new one */
if (__dev_get_by_index(net, dev->ifindex))
new_ifindex = dev_new_index(net);
else
new_ifindex = dev->ifindex;
if (!new_ifindex) {
if (__dev_get_by_index(net, dev->ifindex))
new_ifindex = dev_new_index(net);
else
new_ifindex = dev->ifindex;
}

rtmsg_ifinfo_newnet(RTM_DELLINK, dev, ~0U, GFP_KERNEL, &new_nsid,
new_ifindex);
Expand Down Expand Up @@ -11448,7 +11458,7 @@ static void __net_exit default_device_exit(struct net *net)
snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
if (__dev_get_by_name(&init_net, fb_name))
snprintf(fb_name, IFNAMSIZ, "dev%%d");
err = dev_change_net_namespace(dev, &init_net, fb_name);
err = dev_change_net_namespace(dev, &init_net, fb_name, 0);
if (err) {
pr_emerg("%s: failed to move %s to init_net: %d\n",
__func__, dev->name, err);
Expand Down
19 changes: 15 additions & 4 deletions net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -2266,6 +2266,9 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
return -EINVAL;
}

if (tb[IFLA_NEW_IFINDEX] && nla_get_s32(tb[IFLA_NEW_IFINDEX]) <= 0)
return -EINVAL;

if (tb[IFLA_AF_SPEC]) {
struct nlattr *af;
int rem, err;
Expand Down Expand Up @@ -2603,14 +2606,22 @@ static int do_setlink(const struct sk_buff *skb,
return err;

if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD] || tb[IFLA_TARGET_NETNSID]) {
struct net *net = rtnl_link_get_net_capable(skb, dev_net(dev),
tb, CAP_NET_ADMIN);
struct net *net;
int new_ifindex;

net = rtnl_link_get_net_capable(skb, dev_net(dev),
tb, CAP_NET_ADMIN);
if (IS_ERR(net)) {
err = PTR_ERR(net);
goto errout;
}

err = dev_change_net_namespace(dev, net, ifname);
if (tb[IFLA_NEW_IFINDEX])
new_ifindex = nla_get_s32(tb[IFLA_NEW_IFINDEX]);
else
new_ifindex = 0;

err = dev_change_net_namespace(dev, net, ifname, new_ifindex);
put_net(net);
if (err)
goto errout;
Expand Down Expand Up @@ -3452,7 +3463,7 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
goto out_unregister;
if (link_net) {
err = dev_change_net_namespace(dev, dest_net, ifname);
err = dev_change_net_namespace(dev, dest_net, ifname, 0);
if (err < 0)
goto out_unregister;
}
Expand Down
4 changes: 2 additions & 2 deletions net/ieee802154/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
if (!wpan_dev->netdev)
continue;
wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d");
err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d", 0);
if (err)
break;
wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
Expand All @@ -222,7 +222,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
continue;
wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
err = dev_change_net_namespace(wpan_dev->netdev, net,
"wpan%d");
"wpan%d", 0);
WARN_ON(err);
wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
}
Expand Down
4 changes: 2 additions & 2 deletions net/wireless/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
if (!wdev->netdev)
continue;
wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
err = dev_change_net_namespace(wdev->netdev, net, "wlan%d", 0);
if (err)
break;
wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
Expand All @@ -182,7 +182,7 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
continue;
wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
err = dev_change_net_namespace(wdev->netdev, net,
"wlan%d");
"wlan%d", 0);
WARN_ON(err);
wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
}
Expand Down

0 comments on commit eeb85a1

Please sign in to comment.