summaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c24
-rw-r--r--net/core/rtnetlink.c19
2 files changed, 32 insertions, 11 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;
}