Showing error 1490

User: Jiri Slaby
Error type: Leaving function in locked state
Error type description: Some lock is not unlocked on all paths of a function, so it is leaked
File location: net/ipv6/anycast.c
Line in file: 448
Project: Linux Kernel
Project version: 2.6.28
Tools: Stanse (1.2)
Entered: 2012-05-21 20:30:05 UTC


Source:

  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
 33#include <net/net_namespace.h>
 34#include <net/sock.h>
 35#include <net/snmp.h>
 36
 37#include <net/ipv6.h>
 38#include <net/protocol.h>
 39#include <net/if_inet6.h>
 40#include <net/ndisc.h>
 41#include <net/addrconf.h>
 42#include <net/ip6_route.h>
 43
 44#include <net/checksum.h>
 45
 46static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr);
 47
 48/* Big ac list lock for all the sockets */
 49static DEFINE_RWLOCK(ipv6_sk_ac_lock);
 50
 51
 52/*
 53 *        socket join an anycast group
 54 */
 55
 56int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
 57{
 58        struct ipv6_pinfo *np = inet6_sk(sk);
 59        struct net_device *dev = NULL;
 60        struct inet6_dev *idev;
 61        struct ipv6_ac_socklist *pac;
 62        struct net *net = sock_net(sk);
 63        int        ishost = !net->ipv6.devconf_all->forwarding;
 64        int        err = 0;
 65
 66        if (!capable(CAP_NET_ADMIN))
 67                return -EPERM;
 68        if (ipv6_addr_is_multicast(addr))
 69                return -EINVAL;
 70        if (ipv6_chk_addr(net, addr, NULL, 0))
 71                return -EINVAL;
 72
 73        pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
 74        if (pac == NULL)
 75                return -ENOMEM;
 76        pac->acl_next = NULL;
 77        ipv6_addr_copy(&pac->acl_addr, addr);
 78
 79        if (ifindex == 0) {
 80                struct rt6_info *rt;
 81
 82                rt = rt6_lookup(net, addr, NULL, 0, 0);
 83                if (rt) {
 84                        dev = rt->rt6i_dev;
 85                        dev_hold(dev);
 86                        dst_release(&rt->u.dst);
 87                } else if (ishost) {
 88                        err = -EADDRNOTAVAIL;
 89                        goto out_free_pac;
 90                } else {
 91                        /* router, no matching interface: just pick one */
 92
 93                        dev = dev_get_by_flags(net, IFF_UP, IFF_UP|IFF_LOOPBACK);
 94                }
 95        } else
 96                dev = dev_get_by_index(net, ifindex);
 97
 98        if (dev == NULL) {
 99                err = -ENODEV;
100                goto out_free_pac;
101        }
102
103        idev = in6_dev_get(dev);
104        if (!idev) {
105                if (ifindex)
106                        err = -ENODEV;
107                else
108                        err = -EADDRNOTAVAIL;
109                goto out_dev_put;
110        }
111        /* reset ishost, now that we have a specific device */
112        ishost = !idev->cnf.forwarding;
113        in6_dev_put(idev);
114
115        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        if (!ipv6_chk_prefix(addr, dev)) {
123                if (ishost)
124                        err = -EADDRNOTAVAIL;
125                if (err)
126                        goto out_dev_put;
127        }
128
129        err = ipv6_dev_ac_inc(dev, addr);
130        if (err)
131                goto out_dev_put;
132
133        write_lock_bh(&ipv6_sk_ac_lock);
134        pac->acl_next = np->ipv6_ac_list;
135        np->ipv6_ac_list = pac;
136        write_unlock_bh(&ipv6_sk_ac_lock);
137
138        dev_put(dev);
139
140        return 0;
141
142out_dev_put:
143        dev_put(dev);
144out_free_pac:
145        sock_kfree_s(sk, pac, sizeof(*pac));
146        return err;
147}
148
149/*
150 *        socket leave an anycast group
151 */
152int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
153{
154        struct ipv6_pinfo *np = inet6_sk(sk);
155        struct net_device *dev;
156        struct ipv6_ac_socklist *pac, *prev_pac;
157        struct net *net = sock_net(sk);
158
159        write_lock_bh(&ipv6_sk_ac_lock);
160        prev_pac = NULL;
161        for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
162                if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
163                     ipv6_addr_equal(&pac->acl_addr, addr))
164                        break;
165                prev_pac = pac;
166        }
167        if (!pac) {
168                write_unlock_bh(&ipv6_sk_ac_lock);
169                return -ENOENT;
170        }
171        if (prev_pac)
172                prev_pac->acl_next = pac->acl_next;
173        else
174                np->ipv6_ac_list = pac->acl_next;
175
176        write_unlock_bh(&ipv6_sk_ac_lock);
177
178        dev = dev_get_by_index(net, pac->acl_ifindex);
179        if (dev) {
180                ipv6_dev_ac_dec(dev, &pac->acl_addr);
181                dev_put(dev);
182        }
183        sock_kfree_s(sk, pac, sizeof(*pac));
184        return 0;
185}
186
187void ipv6_sock_ac_close(struct sock *sk)
188{
189        struct ipv6_pinfo *np = inet6_sk(sk);
190        struct net_device *dev = NULL;
191        struct ipv6_ac_socklist *pac;
192        struct net *net = sock_net(sk);
193        int        prev_index;
194
195        write_lock_bh(&ipv6_sk_ac_lock);
196        pac = np->ipv6_ac_list;
197        np->ipv6_ac_list = NULL;
198        write_unlock_bh(&ipv6_sk_ac_lock);
199
200        prev_index = 0;
201        while (pac) {
202                struct ipv6_ac_socklist *next = pac->acl_next;
203
204                if (pac->acl_ifindex != prev_index) {
205                        if (dev)
206                                dev_put(dev);
207                        dev = dev_get_by_index(net, pac->acl_ifindex);
208                        prev_index = pac->acl_ifindex;
209                }
210                if (dev)
211                        ipv6_dev_ac_dec(dev, &pac->acl_addr);
212                sock_kfree_s(sk, pac, sizeof(*pac));
213                pac = next;
214        }
215        if (dev)
216                dev_put(dev);
217}
218
219#if 0
220/* The function is not used, which is funny. Apparently, author
221 * supposed to use it to filter out datagrams inside udp/raw but forgot.
222 *
223 * It is OK, anycasts are not special comparing to delivery to unicasts.
224 */
225
226int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex)
227{
228        struct ipv6_ac_socklist *pac;
229        struct ipv6_pinfo *np = inet6_sk(sk);
230        int        found;
231
232        found = 0;
233        read_lock(&ipv6_sk_ac_lock);
234        for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) {
235                if (ifindex && pac->acl_ifindex != ifindex)
236                        continue;
237                found = ipv6_addr_equal(&pac->acl_addr, addr);
238                if (found)
239                        break;
240        }
241        read_unlock(&ipv6_sk_ac_lock);
242
243        return found;
244}
245
246#endif
247
248static void aca_put(struct ifacaddr6 *ac)
249{
250        if (atomic_dec_and_test(&ac->aca_refcnt)) {
251                in6_dev_put(ac->aca_idev);
252                dst_release(&ac->aca_rt->u.dst);
253                kfree(ac);
254        }
255}
256
257/*
258 *        device anycast group inc (add if not found)
259 */
260int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
261{
262        struct ifacaddr6 *aca;
263        struct inet6_dev *idev;
264        struct rt6_info *rt;
265        int err;
266
267        idev = in6_dev_get(dev);
268
269        if (idev == NULL)
270                return -EINVAL;
271
272        write_lock_bh(&idev->lock);
273        if (idev->dead) {
274                err = -ENODEV;
275                goto out;
276        }
277
278        for (aca = idev->ac_list; aca; aca = aca->aca_next) {
279                if (ipv6_addr_equal(&aca->aca_addr, addr)) {
280                        aca->aca_users++;
281                        err = 0;
282                        goto out;
283                }
284        }
285
286        /*
287         *        not found: create a new one.
288         */
289
290        aca = kzalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);
291
292        if (aca == NULL) {
293                err = -ENOMEM;
294                goto out;
295        }
296
297        rt = addrconf_dst_alloc(idev, addr, 1);
298        if (IS_ERR(rt)) {
299                kfree(aca);
300                err = PTR_ERR(rt);
301                goto out;
302        }
303
304        ipv6_addr_copy(&aca->aca_addr, addr);
305        aca->aca_idev = idev;
306        aca->aca_rt = rt;
307        aca->aca_users = 1;
308        /* aca_tstamp should be updated upon changes */
309        aca->aca_cstamp = aca->aca_tstamp = jiffies;
310        atomic_set(&aca->aca_refcnt, 2);
311        spin_lock_init(&aca->aca_lock);
312
313        aca->aca_next = idev->ac_list;
314        idev->ac_list = aca;
315        write_unlock_bh(&idev->lock);
316
317        ip6_ins_rt(rt);
318
319        addrconf_join_solict(dev, &aca->aca_addr);
320
321        aca_put(aca);
322        return 0;
323out:
324        write_unlock_bh(&idev->lock);
325        in6_dev_put(idev);
326        return err;
327}
328
329/*
330 *        device anycast group decrement
331 */
332int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr)
333{
334        struct ifacaddr6 *aca, *prev_aca;
335
336        write_lock_bh(&idev->lock);
337        prev_aca = NULL;
338        for (aca = idev->ac_list; aca; aca = aca->aca_next) {
339                if (ipv6_addr_equal(&aca->aca_addr, addr))
340                        break;
341                prev_aca = aca;
342        }
343        if (!aca) {
344                write_unlock_bh(&idev->lock);
345                return -ENOENT;
346        }
347        if (--aca->aca_users > 0) {
348                write_unlock_bh(&idev->lock);
349                return 0;
350        }
351        if (prev_aca)
352                prev_aca->aca_next = aca->aca_next;
353        else
354                idev->ac_list = aca->aca_next;
355        write_unlock_bh(&idev->lock);
356        addrconf_leave_solict(idev, &aca->aca_addr);
357
358        dst_hold(&aca->aca_rt->u.dst);
359        ip6_del_rt(aca->aca_rt);
360
361        aca_put(aca);
362        return 0;
363}
364
365static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
366{
367        int ret;
368        struct inet6_dev *idev = in6_dev_get(dev);
369        if (idev == NULL)
370                return -ENODEV;
371        ret = __ipv6_dev_ac_dec(idev, addr);
372        in6_dev_put(idev);
373        return ret;
374}
375
376/*
377 *        check if the interface has this anycast address
378 */
379static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr)
380{
381        struct inet6_dev *idev;
382        struct ifacaddr6 *aca;
383
384        idev = in6_dev_get(dev);
385        if (idev) {
386                read_lock_bh(&idev->lock);
387                for (aca = idev->ac_list; aca; aca = aca->aca_next)
388                        if (ipv6_addr_equal(&aca->aca_addr, addr))
389                                break;
390                read_unlock_bh(&idev->lock);
391                in6_dev_put(idev);
392                return aca != NULL;
393        }
394        return 0;
395}
396
397/*
398 *        check if given interface (or any, if dev==0) has this anycast address
399 */
400int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
401                        struct in6_addr *addr)
402{
403        int found = 0;
404
405        if (dev)
406                return ipv6_chk_acast_dev(dev, addr);
407        read_lock(&dev_base_lock);
408        for_each_netdev(net, dev)
409                if (ipv6_chk_acast_dev(dev, addr)) {
410                        found = 1;
411                        break;
412                }
413        read_unlock(&dev_base_lock);
414        return found;
415}
416
417
418#ifdef CONFIG_PROC_FS
419struct ac6_iter_state {
420        struct seq_net_private p;
421        struct net_device *dev;
422        struct inet6_dev *idev;
423};
424
425#define ac6_seq_private(seq)        ((struct ac6_iter_state *)(seq)->private)
426
427static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
428{
429        struct ifacaddr6 *im = NULL;
430        struct ac6_iter_state *state = ac6_seq_private(seq);
431        struct net *net = seq_file_net(seq);
432
433        state->idev = NULL;
434        for_each_netdev(net, state->dev) {
435                struct inet6_dev *idev;
436                idev = in6_dev_get(state->dev);
437                if (!idev)
438                        continue;
439                read_lock_bh(&idev->lock);
440                im = idev->ac_list;
441                if (im) {
442                        state->idev = idev;
443                        break;
444                }
445                read_unlock_bh(&idev->lock);
446                in6_dev_put(idev);
447        }
448        return im;
449}
450
451static struct ifacaddr6 *ac6_get_next(struct seq_file *seq, struct ifacaddr6 *im)
452{
453        struct ac6_iter_state *state = ac6_seq_private(seq);
454
455        im = im->aca_next;
456        while (!im) {
457                if (likely(state->idev != NULL)) {
458                        read_unlock_bh(&state->idev->lock);
459                        in6_dev_put(state->idev);
460                }
461                state->dev = next_net_device(state->dev);
462                if (!state->dev) {
463                        state->idev = NULL;
464                        break;
465                }
466                state->idev = in6_dev_get(state->dev);
467                if (!state->idev)
468                        continue;
469                read_lock_bh(&state->idev->lock);
470                im = state->idev->ac_list;
471        }
472        return im;
473}
474
475static struct ifacaddr6 *ac6_get_idx(struct seq_file *seq, loff_t pos)
476{
477        struct ifacaddr6 *im = ac6_get_first(seq);
478        if (im)
479                while (pos && (im = ac6_get_next(seq, im)) != NULL)
480                        --pos;
481        return pos ? NULL : im;
482}
483
484static void *ac6_seq_start(struct seq_file *seq, loff_t *pos)
485        __acquires(dev_base_lock)
486{
487        read_lock(&dev_base_lock);
488        return ac6_get_idx(seq, *pos);
489}
490
491static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
492{
493        struct ifacaddr6 *im;
494        im = ac6_get_next(seq, v);
495        ++*pos;
496        return im;
497}
498
499static void ac6_seq_stop(struct seq_file *seq, void *v)
500        __releases(dev_base_lock)
501{
502        struct ac6_iter_state *state = ac6_seq_private(seq);
503        if (likely(state->idev != NULL)) {
504                read_unlock_bh(&state->idev->lock);
505                in6_dev_put(state->idev);
506        }
507        read_unlock(&dev_base_lock);
508}
509
510static int ac6_seq_show(struct seq_file *seq, void *v)
511{
512        struct ifacaddr6 *im = (struct ifacaddr6 *)v;
513        struct ac6_iter_state *state = ac6_seq_private(seq);
514
515        seq_printf(seq,
516                   "%-4d %-15s " NIP6_SEQFMT " %5d\n",
517                   state->dev->ifindex, state->dev->name,
518                   NIP6(im->aca_addr),
519                   im->aca_users);
520        return 0;
521}
522
523static const struct seq_operations ac6_seq_ops = {
524        .start        =        ac6_seq_start,
525        .next        =        ac6_seq_next,
526        .stop        =        ac6_seq_stop,
527        .show        =        ac6_seq_show,
528};
529
530static int ac6_seq_open(struct inode *inode, struct file *file)
531{
532        return seq_open_net(inode, file, &ac6_seq_ops,
533                            sizeof(struct ac6_iter_state));
534}
535
536static const struct file_operations ac6_seq_fops = {
537        .owner                =        THIS_MODULE,
538        .open                =        ac6_seq_open,
539        .read                =        seq_read,
540        .llseek                =        seq_lseek,
541        .release        =        seq_release_net,
542};
543
544int ac6_proc_init(struct net *net)
545{
546        if (!proc_net_fops_create(net, "anycast6", S_IRUGO, &ac6_seq_fops))
547                return -ENOMEM;
548
549        return 0;
550}
551
552void ac6_proc_exit(struct net *net)
553{
554        proc_net_remove(net, "anycast6");
555}
556#endif
557