aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/anycast.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/anycast.c')
-rw-r--r--net/ipv6/anycast.c99
1 files changed, 47 insertions, 52 deletions
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index f1c74c8ef9de..0e5e943446f0 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -29,6 +29,7 @@
29#include <linux/init.h> 29#include <linux/init.h>
30#include <linux/proc_fs.h> 30#include <linux/proc_fs.h>
31#include <linux/seq_file.h> 31#include <linux/seq_file.h>
32#include <linux/slab.h>
32 33
33#include <net/net_namespace.h> 34#include <net/net_namespace.h>
34#include <net/sock.h> 35#include <net/sock.h>
@@ -76,41 +77,40 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
76 pac->acl_next = NULL; 77 pac->acl_next = NULL;
77 ipv6_addr_copy(&pac->acl_addr, addr); 78 ipv6_addr_copy(&pac->acl_addr, addr);
78 79
80 rcu_read_lock();
79 if (ifindex == 0) { 81 if (ifindex == 0) {
80 struct rt6_info *rt; 82 struct rt6_info *rt;
81 83
82 rt = rt6_lookup(net, addr, NULL, 0, 0); 84 rt = rt6_lookup(net, addr, NULL, 0, 0);
83 if (rt) { 85 if (rt) {
84 dev = rt->rt6i_dev; 86 dev = rt->rt6i_dev;
85 dev_hold(dev); 87 dst_release(&rt->dst);
86 dst_release(&rt->u.dst);
87 } else if (ishost) { 88 } else if (ishost) {
88 err = -EADDRNOTAVAIL; 89 err = -EADDRNOTAVAIL;
89 goto out_free_pac; 90 goto error;
90 } else { 91 } else {
91 /* router, no matching interface: just pick one */ 92 /* router, no matching interface: just pick one */
92 93 dev = dev_get_by_flags_rcu(net, IFF_UP,
93 dev = dev_get_by_flags(net, IFF_UP, IFF_UP|IFF_LOOPBACK); 94 IFF_UP | IFF_LOOPBACK);
94 } 95 }
95 } else 96 } else
96 dev = dev_get_by_index(net, ifindex); 97 dev = dev_get_by_index_rcu(net, ifindex);
97 98
98 if (dev == NULL) { 99 if (dev == NULL) {
99 err = -ENODEV; 100 err = -ENODEV;
100 goto out_free_pac; 101 goto error;
101 } 102 }
102 103
103 idev = in6_dev_get(dev); 104 idev = __in6_dev_get(dev);
104 if (!idev) { 105 if (!idev) {
105 if (ifindex) 106 if (ifindex)
106 err = -ENODEV; 107 err = -ENODEV;
107 else 108 else
108 err = -EADDRNOTAVAIL; 109 err = -EADDRNOTAVAIL;
109 goto out_dev_put; 110 goto error;
110 } 111 }
111 /* reset ishost, now that we have a specific device */ 112 /* reset ishost, now that we have a specific device */
112 ishost = !idev->cnf.forwarding; 113 ishost = !idev->cnf.forwarding;
113 in6_dev_put(idev);
114 114
115 pac->acl_ifindex = dev->ifindex; 115 pac->acl_ifindex = dev->ifindex;
116 116
@@ -123,26 +123,22 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
123 if (ishost) 123 if (ishost)
124 err = -EADDRNOTAVAIL; 124 err = -EADDRNOTAVAIL;
125 if (err) 125 if (err)
126 goto out_dev_put; 126 goto error;
127 } 127 }
128 128
129 err = ipv6_dev_ac_inc(dev, addr); 129 err = ipv6_dev_ac_inc(dev, addr);
130 if (err) 130 if (!err) {
131 goto out_dev_put; 131 write_lock_bh(&ipv6_sk_ac_lock);
132 132 pac->acl_next = np->ipv6_ac_list;
133 write_lock_bh(&ipv6_sk_ac_lock); 133 np->ipv6_ac_list = pac;
134 pac->acl_next = np->ipv6_ac_list; 134 write_unlock_bh(&ipv6_sk_ac_lock);
135 np->ipv6_ac_list = pac; 135 pac = NULL;
136 write_unlock_bh(&ipv6_sk_ac_lock); 136 }
137
138 dev_put(dev);
139
140 return 0;
141 137
142out_dev_put: 138error:
143 dev_put(dev); 139 rcu_read_unlock();
144out_free_pac: 140 if (pac)
145 sock_kfree_s(sk, pac, sizeof(*pac)); 141 sock_kfree_s(sk, pac, sizeof(*pac));
146 return err; 142 return err;
147} 143}
148 144
@@ -175,11 +171,12 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
175 171
176 write_unlock_bh(&ipv6_sk_ac_lock); 172 write_unlock_bh(&ipv6_sk_ac_lock);
177 173
178 dev = dev_get_by_index(net, pac->acl_ifindex); 174 rcu_read_lock();
179 if (dev) { 175 dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
176 if (dev)
180 ipv6_dev_ac_dec(dev, &pac->acl_addr); 177 ipv6_dev_ac_dec(dev, &pac->acl_addr);
181 dev_put(dev); 178 rcu_read_unlock();
182 } 179
183 sock_kfree_s(sk, pac, sizeof(*pac)); 180 sock_kfree_s(sk, pac, sizeof(*pac));
184 return 0; 181 return 0;
185} 182}
@@ -198,13 +195,12 @@ void ipv6_sock_ac_close(struct sock *sk)
198 write_unlock_bh(&ipv6_sk_ac_lock); 195 write_unlock_bh(&ipv6_sk_ac_lock);
199 196
200 prev_index = 0; 197 prev_index = 0;
198 rcu_read_lock();
201 while (pac) { 199 while (pac) {
202 struct ipv6_ac_socklist *next = pac->acl_next; 200 struct ipv6_ac_socklist *next = pac->acl_next;
203 201
204 if (pac->acl_ifindex != prev_index) { 202 if (pac->acl_ifindex != prev_index) {
205 if (dev) 203 dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
206 dev_put(dev);
207 dev = dev_get_by_index(net, pac->acl_ifindex);
208 prev_index = pac->acl_ifindex; 204 prev_index = pac->acl_ifindex;
209 } 205 }
210 if (dev) 206 if (dev)
@@ -212,8 +208,7 @@ void ipv6_sock_ac_close(struct sock *sk)
212 sock_kfree_s(sk, pac, sizeof(*pac)); 208 sock_kfree_s(sk, pac, sizeof(*pac));
213 pac = next; 209 pac = next;
214 } 210 }
215 if (dev) 211 rcu_read_unlock();
216 dev_put(dev);
217} 212}
218 213
219#if 0 214#if 0
@@ -249,7 +244,7 @@ static void aca_put(struct ifacaddr6 *ac)
249{ 244{
250 if (atomic_dec_and_test(&ac->aca_refcnt)) { 245 if (atomic_dec_and_test(&ac->aca_refcnt)) {
251 in6_dev_put(ac->aca_idev); 246 in6_dev_put(ac->aca_idev);
252 dst_release(&ac->aca_rt->u.dst); 247 dst_release(&ac->aca_rt->dst);
253 kfree(ac); 248 kfree(ac);
254 } 249 }
255} 250}
@@ -355,40 +350,39 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr)
355 write_unlock_bh(&idev->lock); 350 write_unlock_bh(&idev->lock);
356 addrconf_leave_solict(idev, &aca->aca_addr); 351 addrconf_leave_solict(idev, &aca->aca_addr);
357 352
358 dst_hold(&aca->aca_rt->u.dst); 353 dst_hold(&aca->aca_rt->dst);
359 ip6_del_rt(aca->aca_rt); 354 ip6_del_rt(aca->aca_rt);
360 355
361 aca_put(aca); 356 aca_put(aca);
362 return 0; 357 return 0;
363} 358}
364 359
360/* called with rcu_read_lock() */
365static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) 361static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
366{ 362{
367 int ret; 363 struct inet6_dev *idev = __in6_dev_get(dev);
368 struct inet6_dev *idev = in6_dev_get(dev); 364
369 if (idev == NULL) 365 if (idev == NULL)
370 return -ENODEV; 366 return -ENODEV;
371 ret = __ipv6_dev_ac_dec(idev, addr); 367 return __ipv6_dev_ac_dec(idev, addr);
372 in6_dev_put(idev);
373 return ret;
374} 368}
375 369
376/* 370/*
377 * check if the interface has this anycast address 371 * check if the interface has this anycast address
372 * called with rcu_read_lock()
378 */ 373 */
379static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) 374static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr)
380{ 375{
381 struct inet6_dev *idev; 376 struct inet6_dev *idev;
382 struct ifacaddr6 *aca; 377 struct ifacaddr6 *aca;
383 378
384 idev = in6_dev_get(dev); 379 idev = __in6_dev_get(dev);
385 if (idev) { 380 if (idev) {
386 read_lock_bh(&idev->lock); 381 read_lock_bh(&idev->lock);
387 for (aca = idev->ac_list; aca; aca = aca->aca_next) 382 for (aca = idev->ac_list; aca; aca = aca->aca_next)
388 if (ipv6_addr_equal(&aca->aca_addr, addr)) 383 if (ipv6_addr_equal(&aca->aca_addr, addr))
389 break; 384 break;
390 read_unlock_bh(&idev->lock); 385 read_unlock_bh(&idev->lock);
391 in6_dev_put(idev);
392 return aca != NULL; 386 return aca != NULL;
393 } 387 }
394 return 0; 388 return 0;
@@ -402,14 +396,15 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
402{ 396{
403 int found = 0; 397 int found = 0;
404 398
405 if (dev)
406 return ipv6_chk_acast_dev(dev, addr);
407 rcu_read_lock(); 399 rcu_read_lock();
408 for_each_netdev_rcu(net, dev) 400 if (dev)
409 if (ipv6_chk_acast_dev(dev, addr)) { 401 found = ipv6_chk_acast_dev(dev, addr);
410 found = 1; 402 else
411 break; 403 for_each_netdev_rcu(net, dev)
412 } 404 if (ipv6_chk_acast_dev(dev, addr)) {
405 found = 1;
406 break;
407 }
413 rcu_read_unlock(); 408 rcu_read_unlock();
414 return found; 409 return found;
415} 410}
@@ -538,7 +533,7 @@ static const struct file_operations ac6_seq_fops = {
538 .release = seq_release_net, 533 .release = seq_release_net,
539}; 534};
540 535
541int ac6_proc_init(struct net *net) 536int __net_init ac6_proc_init(struct net *net)
542{ 537{
543 if (!proc_net_fops_create(net, "anycast6", S_IRUGO, &ac6_seq_fops)) 538 if (!proc_net_fops_create(net, "anycast6", S_IRUGO, &ac6_seq_fops))
544 return -ENOMEM; 539 return -ENOMEM;