summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorAndrei Vagin <avagin@gmail.com>2021-04-05 00:12:23 -0700
committerDavid S. Miller <davem@davemloft.net>2021-04-05 14:49:40 -0700
commiteeb85a14ee3494febb85ccfbee0772eda0823b13 (patch)
tree7d5dbe29daf44df707f4439e8f8e6441ed13ba43 /net
parentd3295869c40cb69a2c599000009b3fde43cec2ec (diff)
net: Allow to specify ifindex when device is moved to another namespace
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 <alexander.mikhalitsyn@virtuozzo.com> Suggested-by: Christian Brauner <christian.brauner@ubuntu.com> Signed-off-by: Andrei Vagin <avagin@gmail.com> Reviewed-by: Christian Brauner <christian.brauner@ubuntu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c24
-rw-r--r--net/core/rtnetlink.c19
-rw-r--r--net/ieee802154/core.c4
-rw-r--r--net/wireless/core.c4
4 files changed, 36 insertions, 15 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index b4c67a5be606..9d1a8fac793f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -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
@@ -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();
@@ -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.
*/
@@ -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);
@@ -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);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 1bdcb33fb561..d51252afde0a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -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;
@@ -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;
@@ -3452,7 +3463,7 @@ replay:
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;
}
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index de259b5170ab..ec3068937fc3 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -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;
@@ -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;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index a2785379df6e..fabb677b7d58 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -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;
@@ -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;
}