Skip to content

Commit ce286d3

Browse files
ebiedermDavid S. Miller
authored andcommitted
[NET]: Implement network device movement between namespaces
This patch introduces NETIF_F_NETNS_LOCAL a flag to indicate a network device is local to a single network namespace and should never be moved. Useful for pseudo devices that we need an instance in each network namespace (like the loopback device) and for any device we find that cannot handle multiple network namespaces so we may trap them in the initial network namespace. This patch introduces the function dev_change_net_namespace a function used to move a network device from one network namespace to another. To the network device nothing special appears to happen, to the components of the network stack it appears as if the network device was unregistered in the network namespace it is in, and a new device was registered in the network namespace the device was moved to. This patch sets up a namespace device destructor that upon the exit of a network namespace moves all of the movable network devices to the initial network namespace so they are not lost. Signed-off-by: Eric W. Biederman <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent b267b17 commit ce286d3

File tree

3 files changed

+184
-11
lines changed

3 files changed

+184
-11
lines changed

drivers/net/loopback.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ struct net_device loopback_dev = {
222222
| NETIF_F_TSO
223223
#endif
224224
| NETIF_F_NO_CSUM | NETIF_F_HIGHDMA
225-
| NETIF_F_LLTX,
225+
| NETIF_F_LLTX
226+
| NETIF_F_NETNS_LOCAL,
226227
.ethtool_ops = &loopback_ethtool_ops,
227228
};
228229

include/linux/netdevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ struct net_device
449449
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
450450
#define NETIF_F_GSO 2048 /* Enable software GSO. */
451451
#define NETIF_F_LLTX 4096 /* LockLess TX */
452+
#define NETIF_F_NETNS_LOCAL 8192 /* Does not change network namespaces */
452453
#define NETIF_F_MULTI_QUEUE 16384 /* Has multiple TX/RX queues */
453454
#define NETIF_F_LRO 32768 /* large receive offload */
454455

@@ -1016,6 +1017,8 @@ extern int dev_ethtool(struct net *net, struct ifreq *);
10161017
extern unsigned dev_get_flags(const struct net_device *);
10171018
extern int dev_change_flags(struct net_device *, unsigned);
10181019
extern int dev_change_name(struct net_device *, char *);
1020+
extern int dev_change_net_namespace(struct net_device *,
1021+
struct net *, const char *);
10191022
extern int dev_set_mtu(struct net_device *, int);
10201023
extern int dev_set_mac_address(struct net_device *,
10211024
struct sockaddr *);

net/core/dev.c

Lines changed: 179 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,34 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
208208
return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
209209
}
210210

211+
/* Device list insertion */
212+
static int list_netdevice(struct net_device *dev)
213+
{
214+
struct net *net = dev->nd_net;
215+
216+
ASSERT_RTNL();
217+
218+
write_lock_bh(&dev_base_lock);
219+
list_add_tail(&dev->dev_list, &net->dev_base_head);
220+
hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));
221+
hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
222+
write_unlock_bh(&dev_base_lock);
223+
return 0;
224+
}
225+
226+
/* Device list removal */
227+
static void unlist_netdevice(struct net_device *dev)
228+
{
229+
ASSERT_RTNL();
230+
231+
/* Unlink dev from the device chain */
232+
write_lock_bh(&dev_base_lock);
233+
list_del(&dev->dev_list);
234+
hlist_del(&dev->name_hlist);
235+
hlist_del(&dev->index_hlist);
236+
write_unlock_bh(&dev_base_lock);
237+
}
238+
211239
/*
212240
* Our notifier list
213241
*/
@@ -3571,12 +3599,8 @@ int register_netdevice(struct net_device *dev)
35713599
set_bit(__LINK_STATE_PRESENT, &dev->state);
35723600

35733601
dev_init_scheduler(dev);
3574-
write_lock_bh(&dev_base_lock);
3575-
list_add_tail(&dev->dev_list, &net->dev_base_head);
3576-
hlist_add_head(&dev->name_hlist, head);
3577-
hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
35783602
dev_hold(dev);
3579-
write_unlock_bh(&dev_base_lock);
3603+
list_netdevice(dev);
35803604

35813605
/* Notify protocols, that a new device appeared. */
35823606
ret = raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
@@ -3883,11 +3907,7 @@ void unregister_netdevice(struct net_device *dev)
38833907
dev_close(dev);
38843908

38853909
/* And unlink it from device chain. */
3886-
write_lock_bh(&dev_base_lock);
3887-
list_del(&dev->dev_list);
3888-
hlist_del(&dev->name_hlist);
3889-
hlist_del(&dev->index_hlist);
3890-
write_unlock_bh(&dev_base_lock);
3910+
unlist_netdevice(dev);
38913911

38923912
dev->reg_state = NETREG_UNREGISTERING;
38933913

@@ -3945,6 +3965,122 @@ void unregister_netdev(struct net_device *dev)
39453965

39463966
EXPORT_SYMBOL(unregister_netdev);
39473967

3968+
/**
3969+
* dev_change_net_namespace - move device to different nethost namespace
3970+
* @dev: device
3971+
* @net: network namespace
3972+
* @pat: If not NULL name pattern to try if the current device name
3973+
* is already taken in the destination network namespace.
3974+
*
3975+
* This function shuts down a device interface and moves it
3976+
* to a new network namespace. On success 0 is returned, on
3977+
* a failure a netagive errno code is returned.
3978+
*
3979+
* Callers must hold the rtnl semaphore.
3980+
*/
3981+
3982+
int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
3983+
{
3984+
char buf[IFNAMSIZ];
3985+
const char *destname;
3986+
int err;
3987+
3988+
ASSERT_RTNL();
3989+
3990+
/* Don't allow namespace local devices to be moved. */
3991+
err = -EINVAL;
3992+
if (dev->features & NETIF_F_NETNS_LOCAL)
3993+
goto out;
3994+
3995+
/* Ensure the device has been registrered */
3996+
err = -EINVAL;
3997+
if (dev->reg_state != NETREG_REGISTERED)
3998+
goto out;
3999+
4000+
/* Get out if there is nothing todo */
4001+
err = 0;
4002+
if (dev->nd_net == net)
4003+
goto out;
4004+
4005+
/* Pick the destination device name, and ensure
4006+
* we can use it in the destination network namespace.
4007+
*/
4008+
err = -EEXIST;
4009+
destname = dev->name;
4010+
if (__dev_get_by_name(net, destname)) {
4011+
/* We get here if we can't use the current device name */
4012+
if (!pat)
4013+
goto out;
4014+
if (!dev_valid_name(pat))
4015+
goto out;
4016+
if (strchr(pat, '%')) {
4017+
if (__dev_alloc_name(net, pat, buf) < 0)
4018+
goto out;
4019+
destname = buf;
4020+
} else
4021+
destname = pat;
4022+
if (__dev_get_by_name(net, destname))
4023+
goto out;
4024+
}
4025+
4026+
/*
4027+
* And now a mini version of register_netdevice unregister_netdevice.
4028+
*/
4029+
4030+
/* If device is running close it first. */
4031+
if (dev->flags & IFF_UP)
4032+
dev_close(dev);
4033+
4034+
/* And unlink it from device chain */
4035+
err = -ENODEV;
4036+
unlist_netdevice(dev);
4037+
4038+
synchronize_net();
4039+
4040+
/* Shutdown queueing discipline. */
4041+
dev_shutdown(dev);
4042+
4043+
/* Notify protocols, that we are about to destroy
4044+
this device. They should clean all the things.
4045+
*/
4046+
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
4047+
4048+
/*
4049+
* Flush the unicast and multicast chains
4050+
*/
4051+
dev_addr_discard(dev);
4052+
4053+
/* Actually switch the network namespace */
4054+
dev->nd_net = net;
4055+
4056+
/* Assign the new device name */
4057+
if (destname != dev->name)
4058+
strcpy(dev->name, destname);
4059+
4060+
/* If there is an ifindex conflict assign a new one */
4061+
if (__dev_get_by_index(net, dev->ifindex)) {
4062+
int iflink = (dev->iflink == dev->ifindex);
4063+
dev->ifindex = dev_new_index(net);
4064+
if (iflink)
4065+
dev->iflink = dev->ifindex;
4066+
}
4067+
4068+
/* Fixup sysfs */
4069+
err = device_rename(&dev->dev, dev->name);
4070+
BUG_ON(err);
4071+
4072+
/* Add the device back in the hashes */
4073+
list_netdevice(dev);
4074+
4075+
/* Notify protocols, that a new device appeared. */
4076+
call_netdevice_notifiers(NETDEV_REGISTER, dev);
4077+
4078+
synchronize_net();
4079+
err = 0;
4080+
out:
4081+
return err;
4082+
}
4083+
39484084
static int dev_cpu_callback(struct notifier_block *nfb,
39494085
unsigned long action,
39504086
void *ocpu)
@@ -4177,6 +4313,36 @@ static struct pernet_operations netdev_net_ops = {
41774313
.exit = netdev_exit,
41784314
};
41794315

4316+
static void default_device_exit(struct net *net)
4317+
{
4318+
struct net_device *dev, *next;
4319+
/*
4320+
* Push all migratable of the network devices back to the
4321+
* initial network namespace
4322+
*/
4323+
rtnl_lock();
4324+
for_each_netdev_safe(net, dev, next) {
4325+
int err;
4326+
4327+
/* Ignore unmoveable devices (i.e. loopback) */
4328+
if (dev->features & NETIF_F_NETNS_LOCAL)
4329+
continue;
4330+
4331+
/* Push remaing network devices to init_net */
4332+
err = dev_change_net_namespace(dev, &init_net, "dev%d");
4333+
if (err) {
4334+
printk(KERN_WARNING "%s: failed to move %s to init_net: %d\n",
4335+
__func__, dev->name, err);
4336+
unregister_netdevice(dev);
4337+
}
4338+
}
4339+
rtnl_unlock();
4340+
}
4341+
4342+
static struct pernet_operations default_device_ops = {
4343+
.exit = default_device_exit,
4344+
};
4345+
41804346
/*
41814347
* Initialize the DEV module. At boot time this walks the device list and
41824348
* unhooks any devices that fail to initialise (normally hardware not
@@ -4207,6 +4373,9 @@ static int __init net_dev_init(void)
42074373
if (register_pernet_subsys(&netdev_net_ops))
42084374
goto out;
42094375

4376+
if (register_pernet_device(&default_device_ops))
4377+
goto out;
4378+
42104379
/*
42114380
* Initialise the packet receive queues.
42124381
*/

0 commit comments

Comments
 (0)