diff options
| author | David S. Miller <davem@davemloft.net> | 2017-08-10 12:50:22 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-08-10 12:50:22 -0400 |
| commit | 2e2d5d767c9ddb2b10d74f2c20a257101c7070eb (patch) | |
| tree | 0059c050729eacf01ebc6bb3691b46343ddab376 | |
| parent | 2d5716456404a1ba097d46770f82f23a2457a873 (diff) | |
| parent | 33b01b7b4f19f82198a298936de225eef942fc7c (diff) | |
Merge branch 'rtnetlink-fix-initial-rtnl-pushdown-fallout'
Florian Westphal says:
====================
rtnetlink: fix initial rtnl pushdown fallout
This series fixes various bugs and splats reported since the
allow-handler-to-run-with-no-rtnl series went in.
Last patch adds a script that can be used to add further
tests in case more bugs are reported.
In case you prefer reverting the original series instead of
fixing fallout I can resend this patch on its own.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
| -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 | ||
