aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/anycast.c
diff options
context:
space:
mode:
authorSabrina Dubroca <sd@queasysnail.net>2014-09-02 04:29:29 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-05 14:52:28 -0400
commita9ed4a2986e13011fcf4ed2d1a1647c53112f55b (patch)
tree508293e1b03b96cfdfd05e57ddd8dc60c0157b1d /net/ipv6/anycast.c
parent4ad9a64f53c619969dede1143d56ccda1a453c39 (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.c12
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
138error: 139error:
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
217static void aca_put(struct ifacaddr6 *ac) 225static 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) {