diff options
-rw-r--r-- | net/core/rtnetlink.c | 35 | ||||
-rw-r--r-- | tools/testing/selftests/net/Makefile | 2 | ||||
-rwxr-xr-x | tools/testing/selftests/net/rtnetlink.sh | 199 |
3 files changed, 227 insertions, 9 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dd4e50dfa248..9e9f1419be60 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -172,7 +172,7 @@ int __rtnl_register(int protocol, int msgtype, | |||
172 | BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); | 172 | BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); |
173 | msgindex = rtm_msgindex(msgtype); | 173 | msgindex = rtm_msgindex(msgtype); |
174 | 174 | ||
175 | tab = rcu_dereference(rtnl_msg_handlers[protocol]); | 175 | tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); |
176 | if (tab == NULL) { | 176 | if (tab == NULL) { |
177 | tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); | 177 | tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); |
178 | if (tab == NULL) | 178 | if (tab == NULL) |
@@ -262,7 +262,7 @@ void rtnl_unregister_all(int protocol) | |||
262 | 262 | ||
263 | synchronize_net(); | 263 | synchronize_net(); |
264 | 264 | ||
265 | while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) | 265 | while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) |
266 | schedule(); | 266 | schedule(); |
267 | kfree(handlers); | 267 | kfree(handlers); |
268 | } | 268 | } |
@@ -402,16 +402,24 @@ static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) | |||
402 | { | 402 | { |
403 | struct net_device *master_dev; | 403 | struct net_device *master_dev; |
404 | const struct rtnl_link_ops *ops; | 404 | const struct rtnl_link_ops *ops; |
405 | size_t size = 0; | ||
405 | 406 | ||
406 | master_dev = netdev_master_upper_dev_get((struct net_device *) dev); | 407 | rcu_read_lock(); |
408 | |||
409 | master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev); | ||
407 | if (!master_dev) | 410 | if (!master_dev) |
408 | return 0; | 411 | goto out; |
412 | |||
409 | ops = master_dev->rtnl_link_ops; | 413 | ops = master_dev->rtnl_link_ops; |
410 | if (!ops || !ops->get_slave_size) | 414 | if (!ops || !ops->get_slave_size) |
411 | return 0; | 415 | goto out; |
412 | /* IFLA_INFO_SLAVE_DATA + nested data */ | 416 | /* IFLA_INFO_SLAVE_DATA + nested data */ |
413 | return nla_total_size(sizeof(struct nlattr)) + | 417 | size = nla_total_size(sizeof(struct nlattr)) + |
414 | ops->get_slave_size(master_dev, dev); | 418 | ops->get_slave_size(master_dev, dev); |
419 | |||
420 | out: | ||
421 | rcu_read_unlock(); | ||
422 | return size; | ||
415 | } | 423 | } |
416 | 424 | ||
417 | static size_t rtnl_link_get_size(const struct net_device *dev) | 425 | static size_t rtnl_link_get_size(const struct net_device *dev) |
@@ -4167,7 +4175,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4167 | if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) | 4175 | if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) |
4168 | return -EPERM; | 4176 | return -EPERM; |
4169 | 4177 | ||
4170 | if (family > ARRAY_SIZE(rtnl_msg_handlers)) | 4178 | if (family >= ARRAY_SIZE(rtnl_msg_handlers)) |
4171 | family = PF_UNSPEC; | 4179 | family = PF_UNSPEC; |
4172 | 4180 | ||
4173 | rcu_read_lock(); | 4181 | rcu_read_lock(); |
@@ -4196,7 +4204,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4196 | 4204 | ||
4197 | refcount_inc(&rtnl_msg_handlers_ref[family]); | 4205 | refcount_inc(&rtnl_msg_handlers_ref[family]); |
4198 | 4206 | ||
4199 | if (type == RTM_GETLINK) | 4207 | if (type == RTM_GETLINK - RTM_BASE) |
4200 | min_dump_alloc = rtnl_calcit(skb, nlh); | 4208 | min_dump_alloc = rtnl_calcit(skb, nlh); |
4201 | 4209 | ||
4202 | rcu_read_unlock(); | 4210 | rcu_read_unlock(); |
@@ -4213,6 +4221,12 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4213 | return err; | 4221 | return err; |
4214 | } | 4222 | } |
4215 | 4223 | ||
4224 | doit = READ_ONCE(handlers[type].doit); | ||
4225 | if (!doit) { | ||
4226 | family = PF_UNSPEC; | ||
4227 | handlers = rcu_dereference(rtnl_msg_handlers[family]); | ||
4228 | } | ||
4229 | |||
4216 | flags = READ_ONCE(handlers[type].flags); | 4230 | flags = READ_ONCE(handlers[type].flags); |
4217 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { | 4231 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { |
4218 | refcount_inc(&rtnl_msg_handlers_ref[family]); | 4232 | refcount_inc(&rtnl_msg_handlers_ref[family]); |
@@ -4316,6 +4330,11 @@ static struct pernet_operations rtnetlink_net_ops = { | |||
4316 | 4330 | ||
4317 | void __init rtnetlink_init(void) | 4331 | void __init rtnetlink_init(void) |
4318 | { | 4332 | { |
4333 | int i; | ||
4334 | |||
4335 | for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++) | ||
4336 | refcount_set(&rtnl_msg_handlers_ref[i], 1); | ||
4337 | |||
4319 | if (register_pernet_subsys(&rtnetlink_net_ops)) | 4338 | if (register_pernet_subsys(&rtnetlink_net_ops)) |
4320 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); | 4339 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); |
4321 | 4340 | ||
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 6135a8448900..de1f5772b878 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile | |||
@@ -3,7 +3,7 @@ | |||
3 | CFLAGS = -Wall -Wl,--no-as-needed -O2 -g | 3 | CFLAGS = -Wall -Wl,--no-as-needed -O2 -g |
4 | CFLAGS += -I../../../../usr/include/ | 4 | CFLAGS += -I../../../../usr/include/ |
5 | 5 | ||
6 | TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh | 6 | TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh |
7 | TEST_GEN_FILES = socket | 7 | TEST_GEN_FILES = socket |
8 | TEST_GEN_FILES += psock_fanout psock_tpacket | 8 | TEST_GEN_FILES += psock_fanout psock_tpacket |
9 | TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa | 9 | TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa |
diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh new file mode 100755 index 000000000000..5b04ad912525 --- /dev/null +++ b/tools/testing/selftests/net/rtnetlink.sh | |||
@@ -0,0 +1,199 @@ | |||
1 | #!/bin/sh | ||
2 | # | ||
3 | # This test is for checking rtnetlink callpaths, and get as much coverage as possible. | ||
4 | # | ||
5 | # set -e | ||
6 | |||
7 | devdummy="test-dummy0" | ||
8 | ret=0 | ||
9 | |||
10 | # set global exit status, but never reset nonzero one. | ||
11 | check_err() | ||
12 | { | ||
13 | if [ $ret -eq 0 ]; then | ||
14 | ret=$1 | ||
15 | fi | ||
16 | } | ||
17 | |||
18 | kci_add_dummy() | ||
19 | { | ||
20 | ip link add name "$devdummy" type dummy | ||
21 | check_err $? | ||
22 | ip link set "$devdummy" up | ||
23 | check_err $? | ||
24 | } | ||
25 | |||
26 | kci_del_dummy() | ||
27 | { | ||
28 | ip link del dev "$devdummy" | ||
29 | check_err $? | ||
30 | } | ||
31 | |||
32 | # add a bridge with vlans on top | ||
33 | kci_test_bridge() | ||
34 | { | ||
35 | devbr="test-br0" | ||
36 | vlandev="testbr-vlan1" | ||
37 | |||
38 | ret=0 | ||
39 | ip link add name "$devbr" type bridge | ||
40 | check_err $? | ||
41 | |||
42 | ip link set dev "$devdummy" master "$devbr" | ||
43 | check_err $? | ||
44 | |||
45 | ip link set "$devbr" up | ||
46 | check_err $? | ||
47 | |||
48 | ip link add link "$devbr" name "$vlandev" type vlan id 1 | ||
49 | check_err $? | ||
50 | ip addr add dev "$vlandev" 10.200.7.23/30 | ||
51 | check_err $? | ||
52 | ip -6 addr add dev "$vlandev" dead:42::1234/64 | ||
53 | check_err $? | ||
54 | ip -d link > /dev/null | ||
55 | check_err $? | ||
56 | ip r s t all > /dev/null | ||
57 | check_err $? | ||
58 | ip -6 addr del dev "$vlandev" dead:42::1234/64 | ||
59 | check_err $? | ||
60 | |||
61 | ip link del dev "$vlandev" | ||
62 | check_err $? | ||
63 | ip link del dev "$devbr" | ||
64 | check_err $? | ||
65 | |||
66 | if [ $ret -ne 0 ];then | ||
67 | echo "FAIL: bridge setup" | ||
68 | return 1 | ||
69 | fi | ||
70 | echo "PASS: bridge setup" | ||
71 | |||
72 | } | ||
73 | |||
74 | kci_test_gre() | ||
75 | { | ||
76 | gredev=neta | ||
77 | rem=10.42.42.1 | ||
78 | loc=10.0.0.1 | ||
79 | |||
80 | ret=0 | ||
81 | ip tunnel add $gredev mode gre remote $rem local $loc ttl 1 | ||
82 | check_err $? | ||
83 | ip link set $gredev up | ||
84 | check_err $? | ||
85 | ip addr add 10.23.7.10 dev $gredev | ||
86 | check_err $? | ||
87 | ip route add 10.23.8.0/30 dev $gredev | ||
88 | check_err $? | ||
89 | ip addr add dev "$devdummy" 10.23.7.11/24 | ||
90 | check_err $? | ||
91 | ip link > /dev/null | ||
92 | check_err $? | ||
93 | ip addr > /dev/null | ||
94 | check_err $? | ||
95 | ip addr del dev "$devdummy" 10.23.7.11/24 | ||
96 | check_err $? | ||
97 | |||
98 | ip link del $gredev | ||
99 | check_err $? | ||
100 | |||
101 | if [ $ret -ne 0 ];then | ||
102 | echo "FAIL: gre tunnel endpoint" | ||
103 | return 1 | ||
104 | fi | ||
105 | echo "PASS: gre tunnel endpoint" | ||
106 | } | ||
107 | |||
108 | # tc uses rtnetlink too, for full tc testing | ||
109 | # please see tools/testing/selftests/tc-testing. | ||
110 | kci_test_tc() | ||
111 | { | ||
112 | dev=lo | ||
113 | ret=0 | ||
114 | |||
115 | tc qdisc add dev "$dev" root handle 1: htb | ||
116 | check_err $? | ||
117 | tc class add dev "$dev" parent 1: classid 1:10 htb rate 1mbit | ||
118 | check_err $? | ||
119 | tc filter add dev "$dev" parent 1:0 prio 5 handle ffe: protocol ip u32 divisor 256 | ||
120 | check_err $? | ||
121 | tc filter add dev "$dev" parent 1:0 prio 5 handle ffd: protocol ip u32 divisor 256 | ||
122 | check_err $? | ||
123 | tc filter add dev "$dev" parent 1:0 prio 5 handle ffc: protocol ip u32 divisor 256 | ||
124 | check_err $? | ||
125 | tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 ht ffe:2: match ip src 10.0.0.3 flowid 1:10 | ||
126 | check_err $? | ||
127 | tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:2 u32 ht ffe:2: match ip src 10.0.0.2 flowid 1:10 | ||
128 | check_err $? | ||
129 | tc filter show dev "$dev" parent 1:0 > /dev/null | ||
130 | check_err $? | ||
131 | tc filter del dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 | ||
132 | check_err $? | ||
133 | tc filter show dev "$dev" parent 1:0 > /dev/null | ||
134 | check_err $? | ||
135 | tc qdisc del dev "$dev" root handle 1: htb | ||
136 | check_err $? | ||
137 | |||
138 | if [ $ret -ne 0 ];then | ||
139 | echo "FAIL: tc htb hierarchy" | ||
140 | return 1 | ||
141 | fi | ||
142 | echo "PASS: tc htb hierarchy" | ||
143 | |||
144 | } | ||
145 | |||
146 | kci_test_polrouting() | ||
147 | { | ||
148 | ret=0 | ||
149 | ip rule add fwmark 1 lookup 100 | ||
150 | check_err $? | ||
151 | ip route add local 0.0.0.0/0 dev lo table 100 | ||
152 | check_err $? | ||
153 | ip r s t all > /dev/null | ||
154 | check_err $? | ||
155 | ip rule del fwmark 1 lookup 100 | ||
156 | check_err $? | ||
157 | ip route del local 0.0.0.0/0 dev lo table 100 | ||
158 | check_err $? | ||
159 | |||
160 | if [ $ret -ne 0 ];then | ||
161 | echo "FAIL: policy route test" | ||
162 | return 1 | ||
163 | fi | ||
164 | echo "PASS: policy routing" | ||
165 | } | ||
166 | |||
167 | kci_test_rtnl() | ||
168 | { | ||
169 | kci_add_dummy | ||
170 | if [ $ret -ne 0 ];then | ||
171 | echo "FAIL: cannot add dummy interface" | ||
172 | return 1 | ||
173 | fi | ||
174 | |||
175 | kci_test_polrouting | ||
176 | kci_test_tc | ||
177 | kci_test_gre | ||
178 | kci_test_bridge | ||
179 | |||
180 | kci_del_dummy | ||
181 | } | ||
182 | |||
183 | #check for needed privileges | ||
184 | if [ "$(id -u)" -ne 0 ];then | ||
185 | echo "SKIP: Need root privileges" | ||
186 | exit 0 | ||
187 | fi | ||
188 | |||
189 | for x in ip tc;do | ||
190 | $x -Version 2>/dev/null >/dev/null | ||
191 | if [ $? -ne 0 ];then | ||
192 | echo "SKIP: Could not run test without the $x tool" | ||
193 | exit 0 | ||
194 | fi | ||
195 | done | ||
196 | |||
197 | kci_test_rtnl | ||
198 | |||
199 | exit $ret | ||