Branch data Line data Source code
1 : : /*
2 : : * Anycast support for IPv6
3 : : * Linux INET6 implementation
4 : : *
5 : : * Authors:
6 : : * David L Stevens (dlstevens@us.ibm.com)
7 : : *
8 : : * based heavily on net/ipv6/mcast.c
9 : : *
10 : : * This program is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU General Public License
12 : : * as published by the Free Software Foundation; either version
13 : : * 2 of the License, or (at your option) any later version.
14 : : */
15 : :
16 : : #include <linux/capability.h>
17 : : #include <linux/module.h>
18 : : #include <linux/errno.h>
19 : : #include <linux/types.h>
20 : : #include <linux/random.h>
21 : : #include <linux/string.h>
22 : : #include <linux/socket.h>
23 : : #include <linux/sockios.h>
24 : : #include <linux/net.h>
25 : : #include <linux/in6.h>
26 : : #include <linux/netdevice.h>
27 : : #include <linux/if_arp.h>
28 : : #include <linux/route.h>
29 : : #include <linux/init.h>
30 : : #include <linux/proc_fs.h>
31 : : #include <linux/seq_file.h>
32 : : #include <linux/slab.h>
33 : :
34 : : #include <net/net_namespace.h>
35 : : #include <net/sock.h>
36 : : #include <net/snmp.h>
37 : :
38 : : #include <net/ipv6.h>
39 : : #include <net/protocol.h>
40 : : #include <net/if_inet6.h>
41 : : #include <net/ndisc.h>
42 : : #include <net/addrconf.h>
43 : : #include <net/ip6_route.h>
44 : :
45 : : #include <net/checksum.h>
46 : :
47 : : static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);
48 : :
49 : : /* Big ac list lock for all the sockets */
50 : : static DEFINE_SPINLOCK(ipv6_sk_ac_lock);
51 : :
52 : :
53 : : /*
54 : : * socket join an anycast group
55 : : */
56 : :
57 : 0 : int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
58 : : {
59 : : struct ipv6_pinfo *np = inet6_sk(sk);
60 : 0 : struct net_device *dev = NULL;
61 : : struct inet6_dev *idev;
62 : : struct ipv6_ac_socklist *pac;
63 : : struct net *net = sock_net(sk);
64 : 0 : int ishost = !net->ipv6.devconf_all->forwarding;
65 : : int err = 0;
66 : :
67 [ # # ]: 0 : if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
68 : : return -EPERM;
69 [ # # ]: 0 : if (ipv6_addr_is_multicast(addr))
70 : : return -EINVAL;
71 [ # # ]: 0 : if (ipv6_chk_addr(net, addr, NULL, 0))
72 : : return -EINVAL;
73 : :
74 : 0 : pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
75 [ # # ]: 0 : if (pac == NULL)
76 : : return -ENOMEM;
77 : 0 : pac->acl_next = NULL;
78 : 0 : pac->acl_addr = *addr;
79 : :
80 : : rcu_read_lock();
81 [ # # ]: 0 : if (ifindex == 0) {
82 : : struct rt6_info *rt;
83 : :
84 : 0 : rt = rt6_lookup(net, addr, NULL, 0, 0);
85 [ # # ]: 0 : if (rt) {
86 : 0 : dev = rt->dst.dev;
87 : : ip6_rt_put(rt);
88 [ # # ]: 0 : } else if (ishost) {
89 : : err = -EADDRNOTAVAIL;
90 : : goto error;
91 : : } else {
92 : : /* router, no matching interface: just pick one */
93 : 0 : dev = dev_get_by_flags_rcu(net, IFF_UP,
94 : : IFF_UP | IFF_LOOPBACK);
95 : : }
96 : : } else
97 : 0 : dev = dev_get_by_index_rcu(net, ifindex);
98 : :
99 [ # # ]: 0 : if (dev == NULL) {
100 : : err = -ENODEV;
101 : : goto error;
102 : : }
103 : :
104 : : idev = __in6_dev_get(dev);
105 [ # # ]: 0 : if (!idev) {
106 [ # # ]: 0 : if (ifindex)
107 : : err = -ENODEV;
108 : : else
109 : : err = -EADDRNOTAVAIL;
110 : : goto error;
111 : : }
112 : : /* reset ishost, now that we have a specific device */
113 : 0 : ishost = !idev->cnf.forwarding;
114 : :
115 : 0 : pac->acl_ifindex = dev->ifindex;
116 : :
117 : : /* XXX
118 : : * For hosts, allow link-local or matching prefix anycasts.
119 : : * This obviates the need for propagating anycast routes while
120 : : * still allowing some non-router anycast participation.
121 : : */
122 [ # # ]: 0 : if (!ipv6_chk_prefix(addr, dev)) {
123 [ # # ]: 0 : if (ishost)
124 : : err = -EADDRNOTAVAIL;
125 [ # # ]: 0 : if (err)
126 : : goto error;
127 : : }
128 : :
129 : 0 : err = ipv6_dev_ac_inc(dev, addr);
130 [ # # ]: 0 : if (!err) {
131 : : spin_lock_bh(&ipv6_sk_ac_lock);
132 : 0 : pac->acl_next = np->ipv6_ac_list;
133 : 0 : np->ipv6_ac_list = pac;
134 : : spin_unlock_bh(&ipv6_sk_ac_lock);
135 : : pac = NULL;
136 : : }
137 : :
138 : : error:
139 : : rcu_read_unlock();
140 [ # # ]: 0 : if (pac)
141 : 0 : sock_kfree_s(sk, pac, sizeof(*pac));
142 : 0 : return err;
143 : : }
144 : :
145 : : /*
146 : : * socket leave an anycast group
147 : : */
148 : 0 : int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
149 : : {
150 : : struct ipv6_pinfo *np = inet6_sk(sk);
151 : : struct net_device *dev;
152 : : struct ipv6_ac_socklist *pac, *prev_pac;
153 : : struct net *net = sock_net(sk);
154 : :
155 : : spin_lock_bh(&ipv6_sk_ac_lock);
156 : : prev_pac = NULL;
157 [ # # ]: 0 : for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
158 [ # # ][ # # ]: 0 : if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
[ # # ]
159 : : ipv6_addr_equal(&pac->acl_addr, addr))
160 : : break;
161 : : prev_pac = pac;
162 : : }
163 [ # # ]: 0 : if (!pac) {
164 : : spin_unlock_bh(&ipv6_sk_ac_lock);
165 : 0 : return -ENOENT;
166 : : }
167 [ # # ]: 0 : if (prev_pac)
168 : 0 : prev_pac->acl_next = pac->acl_next;
169 : : else
170 : 0 : np->ipv6_ac_list = pac->acl_next;
171 : :
172 : : spin_unlock_bh(&ipv6_sk_ac_lock);
173 : :
174 : : rcu_read_lock();
175 : 0 : dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
176 [ # # ]: 0 : if (dev)
177 : 0 : ipv6_dev_ac_dec(dev, &pac->acl_addr);
178 : : rcu_read_unlock();
179 : :
180 : 0 : sock_kfree_s(sk, pac, sizeof(*pac));
181 : 0 : return 0;
182 : : }
183 : :
184 : 0 : void ipv6_sock_ac_close(struct sock *sk)
185 : : {
186 : : struct ipv6_pinfo *np = inet6_sk(sk);
187 : : struct net_device *dev = NULL;
188 : : struct ipv6_ac_socklist *pac;
189 : : struct net *net = sock_net(sk);
190 : : int prev_index;
191 : :
192 [ # # ]: 0 : if (!np->ipv6_ac_list)
193 : 0 : return;
194 : :
195 : : spin_lock_bh(&ipv6_sk_ac_lock);
196 : 0 : pac = np->ipv6_ac_list;
197 : 0 : np->ipv6_ac_list = NULL;
198 : : spin_unlock_bh(&ipv6_sk_ac_lock);
199 : :
200 : : prev_index = 0;
201 : : rcu_read_lock();
202 [ # # ]: 0 : while (pac) {
203 : 0 : struct ipv6_ac_socklist *next = pac->acl_next;
204 : :
205 [ # # ]: 0 : if (pac->acl_ifindex != prev_index) {
206 : 0 : dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
207 : 0 : prev_index = pac->acl_ifindex;
208 : : }
209 [ # # ]: 0 : if (dev)
210 : 0 : ipv6_dev_ac_dec(dev, &pac->acl_addr);
211 : 0 : sock_kfree_s(sk, pac, sizeof(*pac));
212 : : pac = next;
213 : : }
214 : : rcu_read_unlock();
215 : : }
216 : :
217 : 0 : static void aca_put(struct ifacaddr6 *ac)
218 : : {
219 [ # # ]: 0 : if (atomic_dec_and_test(&ac->aca_refcnt)) {
220 : 0 : in6_dev_put(ac->aca_idev);
221 : 0 : dst_release(&ac->aca_rt->dst);
222 : 0 : kfree(ac);
223 : : }
224 : 0 : }
225 : :
226 : : /*
227 : : * device anycast group inc (add if not found)
228 : : */
229 : 0 : int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)
230 : : {
231 : : struct ifacaddr6 *aca;
232 : : struct inet6_dev *idev;
233 : : struct rt6_info *rt;
234 : : int err;
235 : :
236 : : idev = in6_dev_get(dev);
237 : :
238 [ # # ]: 0 : if (idev == NULL)
239 : : return -EINVAL;
240 : :
241 : 0 : write_lock_bh(&idev->lock);
242 [ # # ]: 0 : if (idev->dead) {
243 : : err = -ENODEV;
244 : : goto out;
245 : : }
246 : :
247 [ # # ]: 0 : for (aca = idev->ac_list; aca; aca = aca->aca_next) {
248 [ # # ]: 0 : if (ipv6_addr_equal(&aca->aca_addr, addr)) {
249 : 0 : aca->aca_users++;
250 : : err = 0;
251 : 0 : goto out;
252 : : }
253 : : }
254 : :
255 : : /*
256 : : * not found: create a new one.
257 : : */
258 : :
259 : : aca = kzalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);
260 : :
261 [ # # ]: 0 : if (aca == NULL) {
262 : : err = -ENOMEM;
263 : : goto out;
264 : : }
265 : :
266 : 0 : rt = addrconf_dst_alloc(idev, addr, true);
267 [ # # ]: 0 : if (IS_ERR(rt)) {
268 : 0 : kfree(aca);
269 : : err = PTR_ERR(rt);
270 : 0 : goto out;
271 : : }
272 : :
273 : 0 : aca->aca_addr = *addr;
274 : 0 : aca->aca_idev = idev;
275 : 0 : aca->aca_rt = rt;
276 : 0 : aca->aca_users = 1;
277 : : /* aca_tstamp should be updated upon changes */
278 : 0 : aca->aca_cstamp = aca->aca_tstamp = jiffies;
279 : 0 : atomic_set(&aca->aca_refcnt, 2);
280 : 0 : spin_lock_init(&aca->aca_lock);
281 : :
282 : 0 : aca->aca_next = idev->ac_list;
283 : 0 : idev->ac_list = aca;
284 : 0 : write_unlock_bh(&idev->lock);
285 : :
286 : 0 : ip6_ins_rt(rt);
287 : :
288 : 0 : addrconf_join_solict(dev, &aca->aca_addr);
289 : :
290 : 0 : aca_put(aca);
291 : 0 : return 0;
292 : : out:
293 : 0 : write_unlock_bh(&idev->lock);
294 : : in6_dev_put(idev);
295 : 0 : return err;
296 : : }
297 : :
298 : : /*
299 : : * device anycast group decrement
300 : : */
301 : 0 : int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
302 : : {
303 : : struct ifacaddr6 *aca, *prev_aca;
304 : :
305 : 0 : write_lock_bh(&idev->lock);
306 : : prev_aca = NULL;
307 [ # # ]: 0 : for (aca = idev->ac_list; aca; aca = aca->aca_next) {
308 [ # # ]: 0 : if (ipv6_addr_equal(&aca->aca_addr, addr))
309 : : break;
310 : : prev_aca = aca;
311 : : }
312 [ # # ]: 0 : if (!aca) {
313 : 0 : write_unlock_bh(&idev->lock);
314 : 0 : return -ENOENT;
315 : : }
316 [ # # ]: 0 : if (--aca->aca_users > 0) {
317 : 0 : write_unlock_bh(&idev->lock);
318 : 0 : return 0;
319 : : }
320 [ # # ]: 0 : if (prev_aca)
321 : 0 : prev_aca->aca_next = aca->aca_next;
322 : : else
323 : 0 : idev->ac_list = aca->aca_next;
324 : 0 : write_unlock_bh(&idev->lock);
325 : 0 : addrconf_leave_solict(idev, &aca->aca_addr);
326 : :
327 : 0 : dst_hold(&aca->aca_rt->dst);
328 : 0 : ip6_del_rt(aca->aca_rt);
329 : :
330 : 0 : aca_put(aca);
331 : 0 : return 0;
332 : : }
333 : :
334 : : /* called with rcu_read_lock() */
335 : 0 : static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr)
336 : : {
337 : : struct inet6_dev *idev = __in6_dev_get(dev);
338 : :
339 [ # # ][ # # ]: 0 : if (idev == NULL)
340 : : return -ENODEV;
341 : 0 : return __ipv6_dev_ac_dec(idev, addr);
342 : : }
343 : :
344 : : /*
345 : : * check if the interface has this anycast address
346 : : * called with rcu_read_lock()
347 : : */
348 : 0 : static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *addr)
349 : : {
350 : : struct inet6_dev *idev;
351 : : struct ifacaddr6 *aca;
352 : :
353 : : idev = __in6_dev_get(dev);
354 [ # # ]: 0 : if (idev) {
355 : 0 : read_lock_bh(&idev->lock);
356 [ # # ]: 0 : for (aca = idev->ac_list; aca; aca = aca->aca_next)
357 [ # # ]: 0 : if (ipv6_addr_equal(&aca->aca_addr, addr))
358 : : break;
359 : 0 : read_unlock_bh(&idev->lock);
360 : 0 : return aca != NULL;
361 : : }
362 : : return false;
363 : : }
364 : :
365 : : /*
366 : : * check if given interface (or any, if dev==0) has this anycast address
367 : : */
368 : 0 : bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
369 : : const struct in6_addr *addr)
370 : : {
371 : : bool found = false;
372 : :
373 : : rcu_read_lock();
374 [ # # ]: 0 : if (dev)
375 : 0 : found = ipv6_chk_acast_dev(dev, addr);
376 : : else
377 [ # # ]: 0 : for_each_netdev_rcu(net, dev)
378 [ # # ]: 0 : if (ipv6_chk_acast_dev(dev, addr)) {
379 : : found = true;
380 : : break;
381 : : }
382 : : rcu_read_unlock();
383 : 0 : return found;
384 : : }
385 : :
386 : : /* check if this anycast address is link-local on given interface or
387 : : * is global
388 : : */
389 : 0 : bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev,
390 : : const struct in6_addr *addr)
391 : : {
392 [ # # ]: 0 : return ipv6_chk_acast_addr(net,
393 : 0 : (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL ?
394 : : dev : NULL),
395 : : addr);
396 : : }
397 : :
398 : : #ifdef CONFIG_PROC_FS
399 : : struct ac6_iter_state {
400 : : struct seq_net_private p;
401 : : struct net_device *dev;
402 : : struct inet6_dev *idev;
403 : : };
404 : :
405 : : #define ac6_seq_private(seq) ((struct ac6_iter_state *)(seq)->private)
406 : :
407 : : static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
408 : : {
409 : : struct ifacaddr6 *im = NULL;
410 : : struct ac6_iter_state *state = ac6_seq_private(seq);
411 : : struct net *net = seq_file_net(seq);
412 : :
413 : 1 : state->idev = NULL;
414 [ + + ]: 4 : for_each_netdev_rcu(net, state->dev) {
415 : : struct inet6_dev *idev;
416 : 3 : idev = __in6_dev_get(state->dev);
417 [ - + ]: 3 : if (!idev)
418 : 0 : continue;
419 : 3 : read_lock_bh(&idev->lock);
420 : 3 : im = idev->ac_list;
421 [ - + ]: 3 : if (im) {
422 : 0 : state->idev = idev;
423 : : break;
424 : : }
425 : 3 : read_unlock_bh(&idev->lock);
426 : : }
427 : : return im;
428 : : }
429 : :
430 : 0 : static struct ifacaddr6 *ac6_get_next(struct seq_file *seq, struct ifacaddr6 *im)
431 : : {
432 : 0 : struct ac6_iter_state *state = ac6_seq_private(seq);
433 : :
434 : 0 : im = im->aca_next;
435 [ # # ]: 0 : while (!im) {
436 [ # # ]: 0 : if (likely(state->idev != NULL))
437 : 0 : read_unlock_bh(&state->idev->lock);
438 : :
439 : 0 : state->dev = next_net_device_rcu(state->dev);
440 [ # # ]: 0 : if (!state->dev) {
441 : 0 : state->idev = NULL;
442 : : break;
443 : : }
444 : 0 : state->idev = __in6_dev_get(state->dev);
445 [ # # ]: 0 : if (!state->idev)
446 : 0 : continue;
447 : 0 : read_lock_bh(&state->idev->lock);
448 : 0 : im = state->idev->ac_list;
449 : : }
450 : 0 : return im;
451 : : }
452 : :
453 : 0 : static struct ifacaddr6 *ac6_get_idx(struct seq_file *seq, loff_t pos)
454 : : {
455 : 0 : struct ifacaddr6 *im = ac6_get_first(seq);
456 [ - + ]: 1 : if (im)
457 [ # # ][ # # ]: 0 : while (pos && (im = ac6_get_next(seq, im)) != NULL)
458 : 0 : --pos;
459 [ + ]: 1 : return pos ? NULL : im;
460 : : }
461 : :
462 : 0 : static void *ac6_seq_start(struct seq_file *seq, loff_t *pos)
463 : : __acquires(RCU)
464 : : {
465 : : rcu_read_lock();
466 : 1 : return ac6_get_idx(seq, *pos);
467 : : }
468 : :
469 : 0 : static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
470 : : {
471 : 0 : struct ifacaddr6 *im = ac6_get_next(seq, v);
472 : :
473 : 0 : ++*pos;
474 : 0 : return im;
475 : : }
476 : :
477 : 0 : static void ac6_seq_stop(struct seq_file *seq, void *v)
478 : : __releases(RCU)
479 : : {
480 : 1 : struct ac6_iter_state *state = ac6_seq_private(seq);
481 : :
482 [ - + ]: 1 : if (likely(state->idev != NULL)) {
483 : 0 : read_unlock_bh(&state->idev->lock);
484 : 0 : state->idev = NULL;
485 : : }
486 : : rcu_read_unlock();
487 : 1 : }
488 : :
489 : 0 : static int ac6_seq_show(struct seq_file *seq, void *v)
490 : : {
491 : : struct ifacaddr6 *im = (struct ifacaddr6 *)v;
492 : 0 : struct ac6_iter_state *state = ac6_seq_private(seq);
493 : :
494 : 0 : seq_printf(seq, "%-4d %-15s %pi6 %5d\n",
495 : 0 : state->dev->ifindex, state->dev->name,
496 : : &im->aca_addr, im->aca_users);
497 : 0 : return 0;
498 : : }
499 : :
500 : : static const struct seq_operations ac6_seq_ops = {
501 : : .start = ac6_seq_start,
502 : : .next = ac6_seq_next,
503 : : .stop = ac6_seq_stop,
504 : : .show = ac6_seq_show,
505 : : };
506 : :
507 : 0 : static int ac6_seq_open(struct inode *inode, struct file *file)
508 : : {
509 : 1 : return seq_open_net(inode, file, &ac6_seq_ops,
510 : : sizeof(struct ac6_iter_state));
511 : : }
512 : :
513 : : static const struct file_operations ac6_seq_fops = {
514 : : .owner = THIS_MODULE,
515 : : .open = ac6_seq_open,
516 : : .read = seq_read,
517 : : .llseek = seq_lseek,
518 : : .release = seq_release_net,
519 : : };
520 : :
521 : 0 : int __net_init ac6_proc_init(struct net *net)
522 : : {
523 [ # # ]: 0 : if (!proc_create("anycast6", S_IRUGO, net->proc_net, &ac6_seq_fops))
524 : : return -ENOMEM;
525 : :
526 : 0 : return 0;
527 : : }
528 : :
529 : 0 : void ac6_proc_exit(struct net *net)
530 : : {
531 : 0 : remove_proc_entry("anycast6", net->proc_net);
532 : 0 : }
533 : : #endif
534 : :
|