Skip to content

Commit f7d86df

Browse files
author
Paolo Abeni
committed
Merge branch 'net-fix-bugs-in-device-netns-move-and-rename'
Jakub Kicinski says: ==================== net: fix bugs in device netns-move and rename Daniel reported issues with the uevents generated during netdev namespace move, if the netdev is getting renamed at the same time. While the issue that he actually cares about is not fixed here, there is a bunch of seemingly obvious other bugs in this code. Fix the purely networking bugs while the discussion around the uevent fix is still ongoing. ==================== Link: https://lore.kernel.org/r/20231018013817.2391509-1-kuba@kernel.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2 parents a602ee3 + 3920431 commit f7d86df

File tree

4 files changed

+141
-15
lines changed

4 files changed

+141
-15
lines changed

net/core/dev.c

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,6 @@ int netdev_name_node_alt_create(struct net_device *dev, const char *name)
345345
static void __netdev_name_node_alt_destroy(struct netdev_name_node *name_node)
346346
{
347347
list_del(&name_node->list);
348-
netdev_name_node_del(name_node);
349348
kfree(name_node->name);
350349
netdev_name_node_free(name_node);
351350
}
@@ -364,6 +363,8 @@ int netdev_name_node_alt_destroy(struct net_device *dev, const char *name)
364363
if (name_node == dev->name_node || name_node->dev != dev)
365364
return -EINVAL;
366365

366+
netdev_name_node_del(name_node);
367+
synchronize_rcu();
367368
__netdev_name_node_alt_destroy(name_node);
368369

369370
return 0;
@@ -380,6 +381,7 @@ static void netdev_name_node_alt_flush(struct net_device *dev)
380381
/* Device list insertion */
381382
static void list_netdevice(struct net_device *dev)
382383
{
384+
struct netdev_name_node *name_node;
383385
struct net *net = dev_net(dev);
384386

385387
ASSERT_RTNL();
@@ -390,6 +392,10 @@ static void list_netdevice(struct net_device *dev)
390392
hlist_add_head_rcu(&dev->index_hlist,
391393
dev_index_hash(net, dev->ifindex));
392394
write_unlock(&dev_base_lock);
395+
396+
netdev_for_each_altname(dev, name_node)
397+
netdev_name_node_add(net, name_node);
398+
393399
/* We reserved the ifindex, this can't fail */
394400
WARN_ON(xa_store(&net->dev_by_index, dev->ifindex, dev, GFP_KERNEL));
395401

@@ -401,12 +407,16 @@ static void list_netdevice(struct net_device *dev)
401407
*/
402408
static void unlist_netdevice(struct net_device *dev, bool lock)
403409
{
410+
struct netdev_name_node *name_node;
404411
struct net *net = dev_net(dev);
405412

406413
ASSERT_RTNL();
407414

408415
xa_erase(&net->dev_by_index, dev->ifindex);
409416

417+
netdev_for_each_altname(dev, name_node)
418+
netdev_name_node_del(name_node);
419+
410420
/* Unlink dev from the device chain */
411421
if (lock)
412422
write_lock(&dev_base_lock);
@@ -1086,7 +1096,8 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf)
10861096

10871097
for_each_netdev(net, d) {
10881098
struct netdev_name_node *name_node;
1089-
list_for_each_entry(name_node, &d->name_node->list, list) {
1099+
1100+
netdev_for_each_altname(d, name_node) {
10901101
if (!sscanf(name_node->name, name, &i))
10911102
continue;
10921103
if (i < 0 || i >= max_netdevices)
@@ -1123,6 +1134,26 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf)
11231134
return -ENFILE;
11241135
}
11251136

1137+
static int dev_prep_valid_name(struct net *net, struct net_device *dev,
1138+
const char *want_name, char *out_name)
1139+
{
1140+
int ret;
1141+
1142+
if (!dev_valid_name(want_name))
1143+
return -EINVAL;
1144+
1145+
if (strchr(want_name, '%')) {
1146+
ret = __dev_alloc_name(net, want_name, out_name);
1147+
return ret < 0 ? ret : 0;
1148+
} else if (netdev_name_in_use(net, want_name)) {
1149+
return -EEXIST;
1150+
} else if (out_name != want_name) {
1151+
strscpy(out_name, want_name, IFNAMSIZ);
1152+
}
1153+
1154+
return 0;
1155+
}
1156+
11261157
static int dev_alloc_name_ns(struct net *net,
11271158
struct net_device *dev,
11281159
const char *name)
@@ -1160,19 +1191,13 @@ EXPORT_SYMBOL(dev_alloc_name);
11601191
static int dev_get_valid_name(struct net *net, struct net_device *dev,
11611192
const char *name)
11621193
{
1163-
BUG_ON(!net);
1164-
1165-
if (!dev_valid_name(name))
1166-
return -EINVAL;
1167-
1168-
if (strchr(name, '%'))
1169-
return dev_alloc_name_ns(net, dev, name);
1170-
else if (netdev_name_in_use(net, name))
1171-
return -EEXIST;
1172-
else if (dev->name != name)
1173-
strscpy(dev->name, name, IFNAMSIZ);
1194+
char buf[IFNAMSIZ];
1195+
int ret;
11741196

1175-
return 0;
1197+
ret = dev_prep_valid_name(net, dev, name, buf);
1198+
if (ret >= 0)
1199+
strscpy(dev->name, buf, IFNAMSIZ);
1200+
return ret;
11761201
}
11771202

11781203
/**
@@ -11037,7 +11062,9 @@ EXPORT_SYMBOL(unregister_netdev);
1103711062
int __dev_change_net_namespace(struct net_device *dev, struct net *net,
1103811063
const char *pat, int new_ifindex)
1103911064
{
11065+
struct netdev_name_node *name_node;
1104011066
struct net *net_old = dev_net(dev);
11067+
char new_name[IFNAMSIZ] = {};
1104111068
int err, new_nsid;
1104211069

1104311070
ASSERT_RTNL();
@@ -11064,10 +11091,15 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
1106411091
/* We get here if we can't use the current device name */
1106511092
if (!pat)
1106611093
goto out;
11067-
err = dev_get_valid_name(net, dev, pat);
11094+
err = dev_prep_valid_name(net, dev, pat, new_name);
1106811095
if (err < 0)
1106911096
goto out;
1107011097
}
11098+
/* Check that none of the altnames conflicts. */
11099+
err = -EEXIST;
11100+
netdev_for_each_altname(dev, name_node)
11101+
if (netdev_name_in_use(net, name_node->name))
11102+
goto out;
1107111103

1107211104
/* Check that new_ifindex isn't used yet. */
1107311105
if (new_ifindex) {
@@ -11135,6 +11167,9 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
1113511167
kobject_uevent(&dev->dev.kobj, KOBJ_ADD);
1113611168
netdev_adjacent_add_links(dev);
1113711169

11170+
if (new_name[0]) /* Rename the netdev to prepared name */
11171+
strscpy(dev->name, new_name, IFNAMSIZ);
11172+
1113811173
/* Fixup kobjects */
1113911174
err = device_rename(&dev->dev, dev->name);
1114011175
WARN_ON(err);

net/core/dev.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ struct netdev_name_node {
6262
int netdev_get_name(struct net *net, char *name, int ifindex);
6363
int dev_change_name(struct net_device *dev, const char *newname);
6464

65+
#define netdev_for_each_altname(dev, namenode) \
66+
list_for_each_entry((namenode), &(dev)->name_node->list, list)
67+
6568
int netdev_name_node_alt_create(struct net_device *dev, const char *name);
6669
int netdev_name_node_alt_destroy(struct net_device *dev, const char *name);
6770

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ TEST_PROGS += gro.sh
3434
TEST_PROGS += gre_gso.sh
3535
TEST_PROGS += cmsg_so_mark.sh
3636
TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh
37+
TEST_PROGS += netns-name.sh
3738
TEST_PROGS += srv6_end_dt46_l3vpn_test.sh
3839
TEST_PROGS += srv6_end_dt4_l3vpn_test.sh
3940
TEST_PROGS += srv6_end_dt6_l3vpn_test.sh
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
set -o pipefail
5+
6+
NS=netns-name-test
7+
DEV=dummy-dev0
8+
DEV2=dummy-dev1
9+
ALT_NAME=some-alt-name
10+
11+
RET_CODE=0
12+
13+
cleanup() {
14+
ip netns del $NS
15+
}
16+
17+
trap cleanup EXIT
18+
19+
fail() {
20+
echo "ERROR: ${1:-unexpected return code} (ret: $_)" >&2
21+
RET_CODE=1
22+
}
23+
24+
ip netns add $NS
25+
26+
#
27+
# Test basic move without a rename
28+
#
29+
ip -netns $NS link add name $DEV type dummy || fail
30+
ip -netns $NS link set dev $DEV netns 1 ||
31+
fail "Can't perform a netns move"
32+
ip link show dev $DEV >> /dev/null || fail "Device not found after move"
33+
ip link del $DEV || fail
34+
35+
#
36+
# Test move with a conflict
37+
#
38+
ip link add name $DEV type dummy
39+
ip -netns $NS link add name $DEV type dummy || fail
40+
ip -netns $NS link set dev $DEV netns 1 2> /dev/null &&
41+
fail "Performed a netns move with a name conflict"
42+
ip link show dev $DEV >> /dev/null || fail "Device not found after move"
43+
ip -netns $NS link del $DEV || fail
44+
ip link del $DEV || fail
45+
46+
#
47+
# Test move with a conflict and rename
48+
#
49+
ip link add name $DEV type dummy
50+
ip -netns $NS link add name $DEV type dummy || fail
51+
ip -netns $NS link set dev $DEV netns 1 name $DEV2 ||
52+
fail "Can't perform a netns move with rename"
53+
ip link del $DEV2 || fail
54+
ip link del $DEV || fail
55+
56+
#
57+
# Test dup alt-name with netns move
58+
#
59+
ip link add name $DEV type dummy || fail
60+
ip link property add dev $DEV altname $ALT_NAME || fail
61+
ip -netns $NS link add name $DEV2 type dummy || fail
62+
ip -netns $NS link property add dev $DEV2 altname $ALT_NAME || fail
63+
64+
ip -netns $NS link set dev $DEV2 netns 1 2> /dev/null &&
65+
fail "Moved with alt-name dup"
66+
67+
ip link del $DEV || fail
68+
ip -netns $NS link del $DEV2 || fail
69+
70+
#
71+
# Test creating alt-name in one net-ns and using in another
72+
#
73+
ip -netns $NS link add name $DEV type dummy || fail
74+
ip -netns $NS link property add dev $DEV altname $ALT_NAME || fail
75+
ip -netns $NS link set dev $DEV netns 1 || fail
76+
ip link show dev $ALT_NAME >> /dev/null || fail "Can't find alt-name after move"
77+
ip -netns $NS link show dev $ALT_NAME 2> /dev/null &&
78+
fail "Can still find alt-name after move"
79+
ip link del $DEV || fail
80+
81+
echo -ne "$(basename $0) \t\t\t\t"
82+
if [ $RET_CODE -eq 0 ]; then
83+
echo "[ OK ]"
84+
else
85+
echo "[ FAIL ]"
86+
fi
87+
exit $RET_CODE

0 commit comments

Comments
 (0)