diff options
author | Sabrina Dubroca <sd@queasysnail.net> | 2014-09-02 04:29:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-05 14:52:28 -0400 |
commit | a9ed4a2986e13011fcf4ed2d1a1647c53112f55b (patch) | |
tree | 508293e1b03b96cfdfd05e57ddd8dc60c0157b1d /net/ipv6/anycast.c | |
parent | 4ad9a64f53c619969dede1143d56ccda1a453c39 (diff) |
ipv6: fix rtnl locking in setsockopt for anycast and multicast
Calling setsockopt with IPV6_JOIN_ANYCAST or IPV6_LEAVE_ANYCAST
triggers the assertion in addrconf_join_solict()/addrconf_leave_solict()
ipv6_sock_ac_join(), ipv6_sock_ac_drop(), ipv6_sock_ac_close() need to
take RTNL before calling ipv6_dev_ac_inc/dec. Same thing with
ipv6_sock_mc_join(), ipv6_sock_mc_drop(), ipv6_sock_mc_close() before
calling ipv6_dev_mc_inc/dec.
This patch moves ASSERT_RTNL() up a level in the call stack.
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Reported-by: Tommi Rantala <tt.rantala@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/anycast.c')
-rw-r--r-- | net/ipv6/anycast.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 210183244689..45b9d81d91e8 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c | |||
@@ -77,6 +77,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
77 | pac->acl_next = NULL; | 77 | pac->acl_next = NULL; |
78 | pac->acl_addr = *addr; | 78 | pac->acl_addr = *addr; |
79 | 79 | ||
80 | rtnl_lock(); | ||
80 | rcu_read_lock(); | 81 | rcu_read_lock(); |
81 | if (ifindex == 0) { | 82 | if (ifindex == 0) { |
82 | struct rt6_info *rt; | 83 | struct rt6_info *rt; |
@@ -137,6 +138,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
137 | 138 | ||
138 | error: | 139 | error: |
139 | rcu_read_unlock(); | 140 | rcu_read_unlock(); |
141 | rtnl_unlock(); | ||
140 | if (pac) | 142 | if (pac) |
141 | sock_kfree_s(sk, pac, sizeof(*pac)); | 143 | sock_kfree_s(sk, pac, sizeof(*pac)); |
142 | return err; | 144 | return err; |
@@ -171,13 +173,17 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
171 | 173 | ||
172 | spin_unlock_bh(&ipv6_sk_ac_lock); | 174 | spin_unlock_bh(&ipv6_sk_ac_lock); |
173 | 175 | ||
176 | rtnl_lock(); | ||
174 | rcu_read_lock(); | 177 | rcu_read_lock(); |
175 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); | 178 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); |
176 | if (dev) | 179 | if (dev) |
177 | ipv6_dev_ac_dec(dev, &pac->acl_addr); | 180 | ipv6_dev_ac_dec(dev, &pac->acl_addr); |
178 | rcu_read_unlock(); | 181 | rcu_read_unlock(); |
182 | rtnl_unlock(); | ||
179 | 183 | ||
180 | sock_kfree_s(sk, pac, sizeof(*pac)); | 184 | sock_kfree_s(sk, pac, sizeof(*pac)); |
185 | if (!dev) | ||
186 | return -ENODEV; | ||
181 | return 0; | 187 | return 0; |
182 | } | 188 | } |
183 | 189 | ||
@@ -198,6 +204,7 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
198 | spin_unlock_bh(&ipv6_sk_ac_lock); | 204 | spin_unlock_bh(&ipv6_sk_ac_lock); |
199 | 205 | ||
200 | prev_index = 0; | 206 | prev_index = 0; |
207 | rtnl_lock(); | ||
201 | rcu_read_lock(); | 208 | rcu_read_lock(); |
202 | while (pac) { | 209 | while (pac) { |
203 | struct ipv6_ac_socklist *next = pac->acl_next; | 210 | struct ipv6_ac_socklist *next = pac->acl_next; |
@@ -212,6 +219,7 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
212 | pac = next; | 219 | pac = next; |
213 | } | 220 | } |
214 | rcu_read_unlock(); | 221 | rcu_read_unlock(); |
222 | rtnl_unlock(); | ||
215 | } | 223 | } |
216 | 224 | ||
217 | static void aca_put(struct ifacaddr6 *ac) | 225 | static void aca_put(struct ifacaddr6 *ac) |
@@ -233,6 +241,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr) | |||
233 | struct rt6_info *rt; | 241 | struct rt6_info *rt; |
234 | int err; | 242 | int err; |
235 | 243 | ||
244 | ASSERT_RTNL(); | ||
245 | |||
236 | idev = in6_dev_get(dev); | 246 | idev = in6_dev_get(dev); |
237 | 247 | ||
238 | if (idev == NULL) | 248 | if (idev == NULL) |
@@ -302,6 +312,8 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
302 | { | 312 | { |
303 | struct ifacaddr6 *aca, *prev_aca; | 313 | struct ifacaddr6 *aca, *prev_aca; |
304 | 314 | ||
315 | ASSERT_RTNL(); | ||
316 | |||
305 | write_lock_bh(&idev->lock); | 317 | write_lock_bh(&idev->lock); |
306 | prev_aca = NULL; | 318 | prev_aca = NULL; |
307 | for (aca = idev->ac_list; aca; aca = aca->aca_next) { | 319 | for (aca = idev->ac_list; aca; aca = aca->aca_next) { |