Showing error 1492

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/ip6_flowlabel.c
Line in file: 670
Project: Linux Kernel
Project version: 2.6.28
Tools: Stanse (1.2)
Entered: 2012-05-21 20:30:05 UTC


Source:

  1/*
  2 *        ip6_flowlabel.c                IPv6 flowlabel manager.
  3 *
  4 *        This program is free software; you can redistribute it and/or
  5 *      modify it under the terms of the GNU General Public License
  6 *      as published by the Free Software Foundation; either version
  7 *      2 of the License, or (at your option) any later version.
  8 *
  9 *        Authors:        Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 10 */
 11
 12#include <linux/capability.h>
 13#include <linux/errno.h>
 14#include <linux/types.h>
 15#include <linux/socket.h>
 16#include <linux/net.h>
 17#include <linux/netdevice.h>
 18#include <linux/if_arp.h>
 19#include <linux/in6.h>
 20#include <linux/route.h>
 21#include <linux/proc_fs.h>
 22#include <linux/seq_file.h>
 23
 24#include <net/net_namespace.h>
 25#include <net/sock.h>
 26
 27#include <net/ipv6.h>
 28#include <net/ndisc.h>
 29#include <net/protocol.h>
 30#include <net/ip6_route.h>
 31#include <net/addrconf.h>
 32#include <net/rawv6.h>
 33#include <net/icmp.h>
 34#include <net/transp_v6.h>
 35
 36#include <asm/uaccess.h>
 37
 38#define FL_MIN_LINGER        6        /* Minimal linger. It is set to 6sec specified
 39                                   in old IPv6 RFC. Well, it was reasonable value.
 40                                 */
 41#define FL_MAX_LINGER        60        /* Maximal linger timeout */
 42
 43/* FL hash table */
 44
 45#define FL_MAX_PER_SOCK        32
 46#define FL_MAX_SIZE        4096
 47#define FL_HASH_MASK        255
 48#define FL_HASH(l)        (ntohl(l)&FL_HASH_MASK)
 49
 50static atomic_t fl_size = ATOMIC_INIT(0);
 51static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1];
 52
 53static void ip6_fl_gc(unsigned long dummy);
 54static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0);
 55
 56/* FL hash table lock: it protects only of GC */
 57
 58static DEFINE_RWLOCK(ip6_fl_lock);
 59
 60/* Big socket sock */
 61
 62static DEFINE_RWLOCK(ip6_sk_fl_lock);
 63
 64
 65static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label)
 66{
 67        struct ip6_flowlabel *fl;
 68
 69        for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
 70                if (fl->label == label && fl->fl_net == net)
 71                        return fl;
 72        }
 73        return NULL;
 74}
 75
 76static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
 77{
 78        struct ip6_flowlabel *fl;
 79
 80        read_lock_bh(&ip6_fl_lock);
 81        fl = __fl_lookup(net, label);
 82        if (fl)
 83                atomic_inc(&fl->users);
 84        read_unlock_bh(&ip6_fl_lock);
 85        return fl;
 86}
 87
 88
 89static void fl_free(struct ip6_flowlabel *fl)
 90{
 91        if (fl) {
 92                release_net(fl->fl_net);
 93                kfree(fl->opt);
 94        }
 95        kfree(fl);
 96}
 97
 98static void fl_release(struct ip6_flowlabel *fl)
 99{
100        write_lock_bh(&ip6_fl_lock);
101
102        fl->lastuse = jiffies;
103        if (atomic_dec_and_test(&fl->users)) {
104                unsigned long ttd = fl->lastuse + fl->linger;
105                if (time_after(ttd, fl->expires))
106                        fl->expires = ttd;
107                ttd = fl->expires;
108                if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
109                        struct ipv6_txoptions *opt = fl->opt;
110                        fl->opt = NULL;
111                        kfree(opt);
112                }
113                if (!timer_pending(&ip6_fl_gc_timer) ||
114                    time_after(ip6_fl_gc_timer.expires, ttd))
115                        mod_timer(&ip6_fl_gc_timer, ttd);
116        }
117        write_unlock_bh(&ip6_fl_lock);
118}
119
120static void ip6_fl_gc(unsigned long dummy)
121{
122        int i;
123        unsigned long now = jiffies;
124        unsigned long sched = 0;
125
126        write_lock(&ip6_fl_lock);
127
128        for (i=0; i<=FL_HASH_MASK; i++) {
129                struct ip6_flowlabel *fl, **flp;
130                flp = &fl_ht[i];
131                while ((fl=*flp) != NULL) {
132                        if (atomic_read(&fl->users) == 0) {
133                                unsigned long ttd = fl->lastuse + fl->linger;
134                                if (time_after(ttd, fl->expires))
135                                        fl->expires = ttd;
136                                ttd = fl->expires;
137                                if (time_after_eq(now, ttd)) {
138                                        *flp = fl->next;
139                                        fl_free(fl);
140                                        atomic_dec(&fl_size);
141                                        continue;
142                                }
143                                if (!sched || time_before(ttd, sched))
144                                        sched = ttd;
145                        }
146                        flp = &fl->next;
147                }
148        }
149        if (!sched && atomic_read(&fl_size))
150                sched = now + FL_MAX_LINGER;
151        if (sched) {
152                mod_timer(&ip6_fl_gc_timer, sched);
153        }
154        write_unlock(&ip6_fl_lock);
155}
156
157static void ip6_fl_purge(struct net *net)
158{
159        int i;
160
161        write_lock(&ip6_fl_lock);
162        for (i = 0; i <= FL_HASH_MASK; i++) {
163                struct ip6_flowlabel *fl, **flp;
164                flp = &fl_ht[i];
165                while ((fl = *flp) != NULL) {
166                        if (fl->fl_net == net && atomic_read(&fl->users) == 0) {
167                                *flp = fl->next;
168                                fl_free(fl);
169                                atomic_dec(&fl_size);
170                                continue;
171                        }
172                        flp = &fl->next;
173                }
174        }
175        write_unlock(&ip6_fl_lock);
176}
177
178static struct ip6_flowlabel *fl_intern(struct net *net,
179                                       struct ip6_flowlabel *fl, __be32 label)
180{
181        struct ip6_flowlabel *lfl;
182
183        fl->label = label & IPV6_FLOWLABEL_MASK;
184
185        write_lock_bh(&ip6_fl_lock);
186        if (label == 0) {
187                for (;;) {
188                        fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
189                        if (fl->label) {
190                                lfl = __fl_lookup(net, fl->label);
191                                if (lfl == NULL)
192                                        break;
193                        }
194                }
195        } else {
196                /*
197                 * we dropper the ip6_fl_lock, so this entry could reappear
198                 * and we need to recheck with it.
199                 *
200                 * OTOH no need to search the active socket first, like it is
201                 * done in ipv6_flowlabel_opt - sock is locked, so new entry
202                 * with the same label can only appear on another sock
203                 */
204                lfl = __fl_lookup(net, fl->label);
205                if (lfl != NULL) {
206                        atomic_inc(&lfl->users);
207                        write_unlock_bh(&ip6_fl_lock);
208                        return lfl;
209                }
210        }
211
212        fl->lastuse = jiffies;
213        fl->next = fl_ht[FL_HASH(fl->label)];
214        fl_ht[FL_HASH(fl->label)] = fl;
215        atomic_inc(&fl_size);
216        write_unlock_bh(&ip6_fl_lock);
217        return NULL;
218}
219
220
221
222/* Socket flowlabel lists */
223
224struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label)
225{
226        struct ipv6_fl_socklist *sfl;
227        struct ipv6_pinfo *np = inet6_sk(sk);
228
229        label &= IPV6_FLOWLABEL_MASK;
230
231        read_lock_bh(&ip6_sk_fl_lock);
232        for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) {
233                struct ip6_flowlabel *fl = sfl->fl;
234                if (fl->label == label) {
235                        fl->lastuse = jiffies;
236                        atomic_inc(&fl->users);
237                        read_unlock_bh(&ip6_sk_fl_lock);
238                        return fl;
239                }
240        }
241        read_unlock_bh(&ip6_sk_fl_lock);
242        return NULL;
243}
244
245EXPORT_SYMBOL_GPL(fl6_sock_lookup);
246
247void fl6_free_socklist(struct sock *sk)
248{
249        struct ipv6_pinfo *np = inet6_sk(sk);
250        struct ipv6_fl_socklist *sfl;
251
252        while ((sfl = np->ipv6_fl_list) != NULL) {
253                np->ipv6_fl_list = sfl->next;
254                fl_release(sfl->fl);
255                kfree(sfl);
256        }
257}
258
259/* Service routines */
260
261
262/*
263   It is the only difficult place. flowlabel enforces equal headers
264   before and including routing header, however user may supply options
265   following rthdr.
266 */
267
268struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
269                                         struct ip6_flowlabel * fl,
270                                         struct ipv6_txoptions * fopt)
271{
272        struct ipv6_txoptions * fl_opt = fl->opt;
273
274        if (fopt == NULL || fopt->opt_flen == 0)
275                return fl_opt;
276
277        if (fl_opt != NULL) {
278                opt_space->hopopt = fl_opt->hopopt;
279                opt_space->dst0opt = fl_opt->dst0opt;
280                opt_space->srcrt = fl_opt->srcrt;
281                opt_space->opt_nflen = fl_opt->opt_nflen;
282        } else {
283                if (fopt->opt_nflen == 0)
284                        return fopt;
285                opt_space->hopopt = NULL;
286                opt_space->dst0opt = NULL;
287                opt_space->srcrt = NULL;
288                opt_space->opt_nflen = 0;
289        }
290        opt_space->dst1opt = fopt->dst1opt;
291        opt_space->opt_flen = fopt->opt_flen;
292        return opt_space;
293}
294
295static unsigned long check_linger(unsigned long ttl)
296{
297        if (ttl < FL_MIN_LINGER)
298                return FL_MIN_LINGER*HZ;
299        if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))
300                return 0;
301        return ttl*HZ;
302}
303
304static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires)
305{
306        linger = check_linger(linger);
307        if (!linger)
308                return -EPERM;
309        expires = check_linger(expires);
310        if (!expires)
311                return -EPERM;
312        fl->lastuse = jiffies;
313        if (time_before(fl->linger, linger))
314                fl->linger = linger;
315        if (time_before(expires, fl->linger))
316                expires = fl->linger;
317        if (time_before(fl->expires, fl->lastuse + expires))
318                fl->expires = fl->lastuse + expires;
319        return 0;
320}
321
322static struct ip6_flowlabel *
323fl_create(struct net *net, struct in6_flowlabel_req *freq, char __user *optval,
324          int optlen, int *err_p)
325{
326        struct ip6_flowlabel *fl;
327        int olen;
328        int addr_type;
329        int err;
330
331        err = -ENOMEM;
332        fl = kzalloc(sizeof(*fl), GFP_KERNEL);
333        if (fl == NULL)
334                goto done;
335
336        olen = optlen - CMSG_ALIGN(sizeof(*freq));
337        if (olen > 0) {
338                struct msghdr msg;
339                struct flowi flowi;
340                int junk;
341
342                err = -ENOMEM;
343                fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
344                if (fl->opt == NULL)
345                        goto done;
346
347                memset(fl->opt, 0, sizeof(*fl->opt));
348                fl->opt->tot_len = sizeof(*fl->opt) + olen;
349                err = -EFAULT;
350                if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
351                        goto done;
352
353                msg.msg_controllen = olen;
354                msg.msg_control = (void*)(fl->opt+1);
355                flowi.oif = 0;
356
357                err = datagram_send_ctl(net, &msg, &flowi, fl->opt, &junk, &junk);
358                if (err)
359                        goto done;
360                err = -EINVAL;
361                if (fl->opt->opt_flen)
362                        goto done;
363                if (fl->opt->opt_nflen == 0) {
364                        kfree(fl->opt);
365                        fl->opt = NULL;
366                }
367        }
368
369        fl->fl_net = hold_net(net);
370        fl->expires = jiffies;
371        err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
372        if (err)
373                goto done;
374        fl->share = freq->flr_share;
375        addr_type = ipv6_addr_type(&freq->flr_dst);
376        if ((addr_type&IPV6_ADDR_MAPPED)
377            || addr_type == IPV6_ADDR_ANY) {
378                err = -EINVAL;
379                goto done;
380        }
381        ipv6_addr_copy(&fl->dst, &freq->flr_dst);
382        atomic_set(&fl->users, 1);
383        switch (fl->share) {
384        case IPV6_FL_S_EXCL:
385        case IPV6_FL_S_ANY:
386                break;
387        case IPV6_FL_S_PROCESS:
388                fl->owner = current->pid;
389                break;
390        case IPV6_FL_S_USER:
391                fl->owner = current->euid;
392                break;
393        default:
394                err = -EINVAL;
395                goto done;
396        }
397        return fl;
398
399done:
400        fl_free(fl);
401        *err_p = err;
402        return NULL;
403}
404
405static int mem_check(struct sock *sk)
406{
407        struct ipv6_pinfo *np = inet6_sk(sk);
408        struct ipv6_fl_socklist *sfl;
409        int room = FL_MAX_SIZE - atomic_read(&fl_size);
410        int count = 0;
411
412        if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
413                return 0;
414
415        for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next)
416                count++;
417
418        if (room <= 0 ||
419            ((count >= FL_MAX_PER_SOCK ||
420             (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4)
421             && !capable(CAP_NET_ADMIN)))
422                return -ENOBUFS;
423
424        return 0;
425}
426
427static int ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2)
428{
429        if (h1 == h2)
430                return 0;
431        if (h1 == NULL || h2 == NULL)
432                return 1;
433        if (h1->hdrlen != h2->hdrlen)
434                return 1;
435        return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));
436}
437
438static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
439{
440        if (o1 == o2)
441                return 0;
442        if (o1 == NULL || o2 == NULL)
443                return 1;
444        if (o1->opt_nflen != o2->opt_nflen)
445                return 1;
446        if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt))
447                return 1;
448        if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt))
449                return 1;
450        if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
451                return 1;
452        return 0;
453}
454
455static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
456                struct ip6_flowlabel *fl)
457{
458        write_lock_bh(&ip6_sk_fl_lock);
459        sfl->fl = fl;
460        sfl->next = np->ipv6_fl_list;
461        np->ipv6_fl_list = sfl;
462        write_unlock_bh(&ip6_sk_fl_lock);
463}
464
465int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
466{
467        int err;
468        struct net *net = sock_net(sk);
469        struct ipv6_pinfo *np = inet6_sk(sk);
470        struct in6_flowlabel_req freq;
471        struct ipv6_fl_socklist *sfl1=NULL;
472        struct ipv6_fl_socklist *sfl, **sflp;
473        struct ip6_flowlabel *fl, *fl1 = NULL;
474
475
476        if (optlen < sizeof(freq))
477                return -EINVAL;
478
479        if (copy_from_user(&freq, optval, sizeof(freq)))
480                return -EFAULT;
481
482        switch (freq.flr_action) {
483        case IPV6_FL_A_PUT:
484                write_lock_bh(&ip6_sk_fl_lock);
485                for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {
486                        if (sfl->fl->label == freq.flr_label) {
487                                if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
488                                        np->flow_label &= ~IPV6_FLOWLABEL_MASK;
489                                *sflp = sfl->next;
490                                write_unlock_bh(&ip6_sk_fl_lock);
491                                fl_release(sfl->fl);
492                                kfree(sfl);
493                                return 0;
494                        }
495                }
496                write_unlock_bh(&ip6_sk_fl_lock);
497                return -ESRCH;
498
499        case IPV6_FL_A_RENEW:
500                read_lock_bh(&ip6_sk_fl_lock);
501                for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
502                        if (sfl->fl->label == freq.flr_label) {
503                                err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
504                                read_unlock_bh(&ip6_sk_fl_lock);
505                                return err;
506                        }
507                }
508                read_unlock_bh(&ip6_sk_fl_lock);
509
510                if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
511                        fl = fl_lookup(net, freq.flr_label);
512                        if (fl) {
513                                err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
514                                fl_release(fl);
515                                return err;
516                        }
517                }
518                return -ESRCH;
519
520        case IPV6_FL_A_GET:
521                if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
522                        return -EINVAL;
523
524                fl = fl_create(net, &freq, optval, optlen, &err);
525                if (fl == NULL)
526                        return err;
527                sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
528
529                if (freq.flr_label) {
530                        err = -EEXIST;
531                        read_lock_bh(&ip6_sk_fl_lock);
532                        for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
533                                if (sfl->fl->label == freq.flr_label) {
534                                        if (freq.flr_flags&IPV6_FL_F_EXCL) {
535                                                read_unlock_bh(&ip6_sk_fl_lock);
536                                                goto done;
537                                        }
538                                        fl1 = sfl->fl;
539                                        atomic_inc(&fl1->users);
540                                        break;
541                                }
542                        }
543                        read_unlock_bh(&ip6_sk_fl_lock);
544
545                        if (fl1 == NULL)
546                                fl1 = fl_lookup(net, freq.flr_label);
547                        if (fl1) {
548recheck:
549                                err = -EEXIST;
550                                if (freq.flr_flags&IPV6_FL_F_EXCL)
551                                        goto release;
552                                err = -EPERM;
553                                if (fl1->share == IPV6_FL_S_EXCL ||
554                                    fl1->share != fl->share ||
555                                    fl1->owner != fl->owner)
556                                        goto release;
557
558                                err = -EINVAL;
559                                if (!ipv6_addr_equal(&fl1->dst, &fl->dst) ||
560                                    ipv6_opt_cmp(fl1->opt, fl->opt))
561                                        goto release;
562
563                                err = -ENOMEM;
564                                if (sfl1 == NULL)
565                                        goto release;
566                                if (fl->linger > fl1->linger)
567                                        fl1->linger = fl->linger;
568                                if ((long)(fl->expires - fl1->expires) > 0)
569                                        fl1->expires = fl->expires;
570                                fl_link(np, sfl1, fl1);
571                                fl_free(fl);
572                                return 0;
573
574release:
575                                fl_release(fl1);
576                                goto done;
577                        }
578                }
579                err = -ENOENT;
580                if (!(freq.flr_flags&IPV6_FL_F_CREATE))
581                        goto done;
582
583                err = -ENOMEM;
584                if (sfl1 == NULL || (err = mem_check(sk)) != 0)
585                        goto done;
586
587                fl1 = fl_intern(net, fl, freq.flr_label);
588                if (fl1 != NULL)
589                        goto recheck;
590
591                if (!freq.flr_label) {
592                        if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
593                                         &fl->label, sizeof(fl->label))) {
594                                /* Intentionally ignore fault. */
595                        }
596                }
597
598                fl_link(np, sfl1, fl);
599                return 0;
600
601        default:
602                return -EINVAL;
603        }
604
605done:
606        fl_free(fl);
607        kfree(sfl1);
608        return err;
609}
610
611#ifdef CONFIG_PROC_FS
612
613struct ip6fl_iter_state {
614        struct seq_net_private p;
615        int bucket;
616};
617
618#define ip6fl_seq_private(seq)        ((struct ip6fl_iter_state *)(seq)->private)
619
620static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq)
621{
622        struct ip6_flowlabel *fl = NULL;
623        struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
624        struct net *net = seq_file_net(seq);
625
626        for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
627                fl = fl_ht[state->bucket];
628
629                while (fl && fl->fl_net != net)
630                        fl = fl->next;
631                if (fl)
632                        break;
633        }
634        return fl;
635}
636
637static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl)
638{
639        struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
640        struct net *net = seq_file_net(seq);
641
642        fl = fl->next;
643try_again:
644        while (fl && fl->fl_net != net)
645                fl = fl->next;
646
647        while (!fl) {
648                if (++state->bucket <= FL_HASH_MASK) {
649                        fl = fl_ht[state->bucket];
650                        goto try_again;
651                } else
652                        break;
653        }
654        return fl;
655}
656
657static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos)
658{
659        struct ip6_flowlabel *fl = ip6fl_get_first(seq);
660        if (fl)
661                while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL)
662                        --pos;
663        return pos ? NULL : fl;
664}
665
666static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
667        __acquires(ip6_fl_lock)
668{
669        read_lock_bh(&ip6_fl_lock);
670        return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
671}
672
673static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
674{
675        struct ip6_flowlabel *fl;
676
677        if (v == SEQ_START_TOKEN)
678                fl = ip6fl_get_first(seq);
679        else
680                fl = ip6fl_get_next(seq, v);
681        ++*pos;
682        return fl;
683}
684
685static void ip6fl_seq_stop(struct seq_file *seq, void *v)
686        __releases(ip6_fl_lock)
687{
688        read_unlock_bh(&ip6_fl_lock);
689}
690
691static int ip6fl_seq_show(struct seq_file *seq, void *v)
692{
693        if (v == SEQ_START_TOKEN)
694                seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n",
695                           "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt");
696        else {
697                struct ip6_flowlabel *fl = v;
698                seq_printf(seq,
699                           "%05X %-1d %-6d %-6d %-6ld %-8ld " NIP6_SEQFMT " %-4d\n",
700                           (unsigned)ntohl(fl->label),
701                           fl->share,
702                           (unsigned)fl->owner,
703                           atomic_read(&fl->users),
704                           fl->linger/HZ,
705                           (long)(fl->expires - jiffies)/HZ,
706                           NIP6(fl->dst),
707                           fl->opt ? fl->opt->opt_nflen : 0);
708        }
709        return 0;
710}
711
712static const struct seq_operations ip6fl_seq_ops = {
713        .start        =        ip6fl_seq_start,
714        .next        =        ip6fl_seq_next,
715        .stop        =        ip6fl_seq_stop,
716        .show        =        ip6fl_seq_show,
717};
718
719static int ip6fl_seq_open(struct inode *inode, struct file *file)
720{
721        return seq_open_net(inode, file, &ip6fl_seq_ops,
722                            sizeof(struct ip6fl_iter_state));
723}
724
725static const struct file_operations ip6fl_seq_fops = {
726        .owner                =        THIS_MODULE,
727        .open                =        ip6fl_seq_open,
728        .read                =        seq_read,
729        .llseek                =        seq_lseek,
730        .release        =        seq_release_net,
731};
732
733static int ip6_flowlabel_proc_init(struct net *net)
734{
735        if (!proc_net_fops_create(net, "ip6_flowlabel",
736                                  S_IRUGO, &ip6fl_seq_fops))
737                return -ENOMEM;
738        return 0;
739}
740
741static void ip6_flowlabel_proc_fini(struct net *net)
742{
743        proc_net_remove(net, "ip6_flowlabel");
744}
745#else
746static inline int ip6_flowlabel_proc_init(struct net *net)
747{
748        return 0;
749}
750static inline void ip6_flowlabel_proc_fini(struct net *net)
751{
752        return ;
753}
754#endif
755
756static inline void ip6_flowlabel_net_exit(struct net *net)
757{
758        ip6_fl_purge(net);
759        ip6_flowlabel_proc_fini(net);
760}
761
762static struct pernet_operations ip6_flowlabel_net_ops = {
763        .init = ip6_flowlabel_proc_init,
764        .exit = ip6_flowlabel_net_exit,
765};
766
767int ip6_flowlabel_init(void)
768{
769        return register_pernet_subsys(&ip6_flowlabel_net_ops);
770}
771
772void ip6_flowlabel_cleanup(void)
773{
774        del_timer(&ip6_fl_gc_timer);
775        unregister_pernet_subsys(&ip6_flowlabel_net_ops);
776}