11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * ip6_flowlabel.c IPv6 flowlabel manager. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 124fc268d2SRandy Dunlap #include <linux/capability.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/types.h> 151da177e4SLinus Torvalds #include <linux/socket.h> 161da177e4SLinus Torvalds #include <linux/net.h> 171da177e4SLinus Torvalds #include <linux/netdevice.h> 181da177e4SLinus Torvalds #include <linux/in6.h> 191da177e4SLinus Torvalds #include <linux/proc_fs.h> 201da177e4SLinus Torvalds #include <linux/seq_file.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 22bc3b2d7fSPaul Gortmaker #include <linux/export.h> 234f82f457SEric W. Biederman #include <linux/pid_namespace.h> 241da177e4SLinus Torvalds 25457c4cbcSEric W. Biederman #include <net/net_namespace.h> 261da177e4SLinus Torvalds #include <net/sock.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <net/ipv6.h> 291da177e4SLinus Torvalds #include <net/rawv6.h> 301da177e4SLinus Torvalds #include <net/transp_v6.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds #include <asm/uaccess.h> 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds #define FL_MIN_LINGER 6 /* Minimal linger. It is set to 6sec specified 351da177e4SLinus Torvalds in old IPv6 RFC. Well, it was reasonable value. 361da177e4SLinus Torvalds */ 3753b47106SFlorent Fourcot #define FL_MAX_LINGER 150 /* Maximal linger timeout */ 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds /* FL hash table */ 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds #define FL_MAX_PER_SOCK 32 421da177e4SLinus Torvalds #define FL_MAX_SIZE 4096 431da177e4SLinus Torvalds #define FL_HASH_MASK 255 441da177e4SLinus Torvalds #define FL_HASH(l) (ntohl(l)&FL_HASH_MASK) 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds static atomic_t fl_size = ATOMIC_INIT(0); 47d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1]; 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds static void ip6_fl_gc(unsigned long dummy); 508d06afabSIngo Molnar static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0); 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds /* FL hash table lock: it protects only of GC */ 531da177e4SLinus Torvalds 54d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_fl_lock); 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds /* Big socket sock */ 571da177e4SLinus Torvalds 5818367681SYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_sk_fl_lock); 591da177e4SLinus Torvalds 60d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 #define for_each_fl_rcu(hash, fl) \ 616a98dcf0SAmerigo Wang for (fl = rcu_dereference_bh(fl_ht[(hash)]); \ 62d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl != NULL; \ 636a98dcf0SAmerigo Wang fl = rcu_dereference_bh(fl->next)) 64d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 #define for_each_fl_continue_rcu(fl) \ 656a98dcf0SAmerigo Wang for (fl = rcu_dereference_bh(fl->next); \ 66d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl != NULL; \ 676a98dcf0SAmerigo Wang fl = rcu_dereference_bh(fl->next)) 681da177e4SLinus Torvalds 6918367681SYOSHIFUJI Hideaki / 吉藤英明 #define for_each_sk_fl_rcu(np, sfl) \ 7018367681SYOSHIFUJI Hideaki / 吉藤英明 for (sfl = rcu_dereference_bh(np->ipv6_fl_list); \ 7118367681SYOSHIFUJI Hideaki / 吉藤英明 sfl != NULL; \ 7218367681SYOSHIFUJI Hideaki / 吉藤英明 sfl = rcu_dereference_bh(sfl->next)) 7318367681SYOSHIFUJI Hideaki / 吉藤英明 7460e8fbc4SBenjamin Thery static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds struct ip6_flowlabel *fl; 771da177e4SLinus Torvalds 78d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(FL_HASH(label), fl) { 7909ad9bc7SOctavian Purdila if (fl->label == label && net_eq(fl->fl_net, net)) 801da177e4SLinus Torvalds return fl; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds return NULL; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 8560e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) 861da177e4SLinus Torvalds { 871da177e4SLinus Torvalds struct ip6_flowlabel *fl; 881da177e4SLinus Torvalds 89d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 9060e8fbc4SBenjamin Thery fl = __fl_lookup(net, label); 91d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (fl && !atomic_inc_not_zero(&fl->users)) 92d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 93d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 941da177e4SLinus Torvalds return fl; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds static void fl_free(struct ip6_flowlabel *fl) 991da177e4SLinus Torvalds { 10060e8fbc4SBenjamin Thery if (fl) { 101898132aeSDan Carpenter if (fl->share == IPV6_FL_S_PROCESS) 102898132aeSDan Carpenter put_pid(fl->owner.pid); 1031da177e4SLinus Torvalds kfree(fl->opt); 104d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(fl, rcu); 10560e8fbc4SBenjamin Thery } 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds static void fl_release(struct ip6_flowlabel *fl) 1091da177e4SLinus Torvalds { 110d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds fl->lastuse = jiffies; 1131da177e4SLinus Torvalds if (atomic_dec_and_test(&fl->users)) { 1141da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1151da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1161da177e4SLinus Torvalds fl->expires = ttd; 1171da177e4SLinus Torvalds ttd = fl->expires; 1181da177e4SLinus Torvalds if (fl->opt && fl->share == IPV6_FL_S_EXCL) { 1191da177e4SLinus Torvalds struct ipv6_txoptions *opt = fl->opt; 1201da177e4SLinus Torvalds fl->opt = NULL; 1211da177e4SLinus Torvalds kfree(opt); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds if (!timer_pending(&ip6_fl_gc_timer) || 1241da177e4SLinus Torvalds time_after(ip6_fl_gc_timer.expires, ttd)) 1251da177e4SLinus Torvalds mod_timer(&ip6_fl_gc_timer, ttd); 1261da177e4SLinus Torvalds } 127d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds static void ip6_fl_gc(unsigned long dummy) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds int i; 1331da177e4SLinus Torvalds unsigned long now = jiffies; 1341da177e4SLinus Torvalds unsigned long sched = 0; 1351da177e4SLinus Torvalds 136d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock(&ip6_fl_lock); 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds for (i = 0; i <= FL_HASH_MASK; i++) { 1397f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1407f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1417f0e44acSEric Dumazet 1421da177e4SLinus Torvalds flp = &fl_ht[i]; 143d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 144d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 1451da177e4SLinus Torvalds if (atomic_read(&fl->users) == 0) { 1461da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1471da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1481da177e4SLinus Torvalds fl->expires = ttd; 1491da177e4SLinus Torvalds ttd = fl->expires; 1501da177e4SLinus Torvalds if (time_after_eq(now, ttd)) { 1511da177e4SLinus Torvalds *flp = fl->next; 1521da177e4SLinus Torvalds fl_free(fl); 1531da177e4SLinus Torvalds atomic_dec(&fl_size); 1541da177e4SLinus Torvalds continue; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds if (!sched || time_before(ttd, sched)) 1571da177e4SLinus Torvalds sched = ttd; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds flp = &fl->next; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds if (!sched && atomic_read(&fl_size)) 1631da177e4SLinus Torvalds sched = now + FL_MAX_LINGER; 1641da177e4SLinus Torvalds if (sched) { 16560e8fbc4SBenjamin Thery mod_timer(&ip6_fl_gc_timer, sched); 1661da177e4SLinus Torvalds } 167d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1702c8c1e72SAlexey Dobriyan static void __net_exit ip6_fl_purge(struct net *net) 17160e8fbc4SBenjamin Thery { 17260e8fbc4SBenjamin Thery int i; 17360e8fbc4SBenjamin Thery 1744762fb98SJan Stancek spin_lock_bh(&ip6_fl_lock); 17560e8fbc4SBenjamin Thery for (i = 0; i <= FL_HASH_MASK; i++) { 1767f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1777f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1787f0e44acSEric Dumazet 17960e8fbc4SBenjamin Thery flp = &fl_ht[i]; 180d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 181d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 18209ad9bc7SOctavian Purdila if (net_eq(fl->fl_net, net) && 18309ad9bc7SOctavian Purdila atomic_read(&fl->users) == 0) { 18460e8fbc4SBenjamin Thery *flp = fl->next; 18560e8fbc4SBenjamin Thery fl_free(fl); 18660e8fbc4SBenjamin Thery atomic_dec(&fl_size); 18760e8fbc4SBenjamin Thery continue; 18860e8fbc4SBenjamin Thery } 18960e8fbc4SBenjamin Thery flp = &fl->next; 19060e8fbc4SBenjamin Thery } 19160e8fbc4SBenjamin Thery } 1924762fb98SJan Stancek spin_unlock_bh(&ip6_fl_lock); 19360e8fbc4SBenjamin Thery } 19460e8fbc4SBenjamin Thery 19560e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_intern(struct net *net, 19660e8fbc4SBenjamin Thery struct ip6_flowlabel *fl, __be32 label) 1971da177e4SLinus Torvalds { 19878c2e502SPavel Emelyanov struct ip6_flowlabel *lfl; 19978c2e502SPavel Emelyanov 2001da177e4SLinus Torvalds fl->label = label & IPV6_FLOWLABEL_MASK; 2011da177e4SLinus Torvalds 202d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 2031da177e4SLinus Torvalds if (label == 0) { 2041da177e4SLinus Torvalds for (;;) { 20563862b5bSAruna-Hewapathirane fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK; 2061da177e4SLinus Torvalds if (fl->label) { 20760e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 20863159f29SIan Morris if (!lfl) 2091da177e4SLinus Torvalds break; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds } 21278c2e502SPavel Emelyanov } else { 21378c2e502SPavel Emelyanov /* 21478c2e502SPavel Emelyanov * we dropper the ip6_fl_lock, so this entry could reappear 21578c2e502SPavel Emelyanov * and we need to recheck with it. 21678c2e502SPavel Emelyanov * 21778c2e502SPavel Emelyanov * OTOH no need to search the active socket first, like it is 21878c2e502SPavel Emelyanov * done in ipv6_flowlabel_opt - sock is locked, so new entry 21978c2e502SPavel Emelyanov * with the same label can only appear on another sock 22078c2e502SPavel Emelyanov */ 22160e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 22253b24b8fSIan Morris if (lfl) { 22378c2e502SPavel Emelyanov atomic_inc(&lfl->users); 224d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 22578c2e502SPavel Emelyanov return lfl; 22678c2e502SPavel Emelyanov } 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds fl->lastuse = jiffies; 2301da177e4SLinus Torvalds fl->next = fl_ht[FL_HASH(fl->label)]; 231d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); 2321da177e4SLinus Torvalds atomic_inc(&fl_size); 233d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 23478c2e502SPavel Emelyanov return NULL; 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* Socket flowlabel lists */ 2401da177e4SLinus Torvalds 24190bcaf7bSAl Viro struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label) 2421da177e4SLinus Torvalds { 2431da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2441da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds label &= IPV6_FLOWLABEL_MASK; 2471da177e4SLinus Torvalds 24818367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 24918367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 2501da177e4SLinus Torvalds struct ip6_flowlabel *fl = sfl->fl; 2511da177e4SLinus Torvalds if (fl->label == label) { 2521da177e4SLinus Torvalds fl->lastuse = jiffies; 2531da177e4SLinus Torvalds atomic_inc(&fl->users); 25418367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2551da177e4SLinus Torvalds return fl; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds } 25818367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2591da177e4SLinus Torvalds return NULL; 2601da177e4SLinus Torvalds } 2613cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(fl6_sock_lookup); 2623cf3dc6cSArnaldo Carvalho de Melo 2631da177e4SLinus Torvalds void fl6_free_socklist(struct sock *sk) 2641da177e4SLinus Torvalds { 2651da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2661da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2671da177e4SLinus Torvalds 26818367681SYOSHIFUJI Hideaki / 吉藤英明 if (!rcu_access_pointer(np->ipv6_fl_list)) 269f256dc59SYOSHIFUJI Hideaki / 吉藤英明 return; 270f256dc59SYOSHIFUJI Hideaki / 吉藤英明 27118367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 27218367681SYOSHIFUJI Hideaki / 吉藤英明 while ((sfl = rcu_dereference_protected(np->ipv6_fl_list, 27318367681SYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_sk_fl_lock))) != NULL) { 27418367681SYOSHIFUJI Hideaki / 吉藤英明 np->ipv6_fl_list = sfl->next; 27518367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 276f256dc59SYOSHIFUJI Hideaki / 吉藤英明 2771da177e4SLinus Torvalds fl_release(sfl->fl); 27818367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 27918367681SYOSHIFUJI Hideaki / 吉藤英明 28018367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 2811da177e4SLinus Torvalds } 28218367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds /* Service routines */ 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds /* 2891da177e4SLinus Torvalds It is the only difficult place. flowlabel enforces equal headers 2901da177e4SLinus Torvalds before and including routing header, however user may supply options 2911da177e4SLinus Torvalds following rthdr. 2921da177e4SLinus Torvalds */ 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, 2951da177e4SLinus Torvalds struct ip6_flowlabel *fl, 2961da177e4SLinus Torvalds struct ipv6_txoptions *fopt) 2971da177e4SLinus Torvalds { 298df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions *fl_opt = fl->opt; 2991da177e4SLinus Torvalds 30063159f29SIan Morris if (!fopt || fopt->opt_flen == 0) 3011da177e4SLinus Torvalds return fl_opt; 3021da177e4SLinus Torvalds 30353b24b8fSIan Morris if (fl_opt) { 3041da177e4SLinus Torvalds opt_space->hopopt = fl_opt->hopopt; 305df9890c3SYOSHIFUJI Hideaki opt_space->dst0opt = fl_opt->dst0opt; 3061da177e4SLinus Torvalds opt_space->srcrt = fl_opt->srcrt; 3071da177e4SLinus Torvalds opt_space->opt_nflen = fl_opt->opt_nflen; 3081da177e4SLinus Torvalds } else { 3091da177e4SLinus Torvalds if (fopt->opt_nflen == 0) 3101da177e4SLinus Torvalds return fopt; 3111da177e4SLinus Torvalds opt_space->hopopt = NULL; 3121da177e4SLinus Torvalds opt_space->dst0opt = NULL; 3131da177e4SLinus Torvalds opt_space->srcrt = NULL; 3141da177e4SLinus Torvalds opt_space->opt_nflen = 0; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds opt_space->dst1opt = fopt->dst1opt; 3171da177e4SLinus Torvalds opt_space->opt_flen = fopt->opt_flen; 3181da177e4SLinus Torvalds return opt_space; 3191da177e4SLinus Torvalds } 320a495f836SChris Elston EXPORT_SYMBOL_GPL(fl6_merge_options); 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds static unsigned long check_linger(unsigned long ttl) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds if (ttl < FL_MIN_LINGER) 3251da177e4SLinus Torvalds return FL_MIN_LINGER*HZ; 3261da177e4SLinus Torvalds if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN)) 3271da177e4SLinus Torvalds return 0; 3281da177e4SLinus Torvalds return ttl*HZ; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds linger = check_linger(linger); 3341da177e4SLinus Torvalds if (!linger) 3351da177e4SLinus Torvalds return -EPERM; 3361da177e4SLinus Torvalds expires = check_linger(expires); 3371da177e4SLinus Torvalds if (!expires) 3381da177e4SLinus Torvalds return -EPERM; 339394055f6SFlorent Fourcot 340394055f6SFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 3411da177e4SLinus Torvalds fl->lastuse = jiffies; 3421da177e4SLinus Torvalds if (time_before(fl->linger, linger)) 3431da177e4SLinus Torvalds fl->linger = linger; 3441da177e4SLinus Torvalds if (time_before(expires, fl->linger)) 3451da177e4SLinus Torvalds expires = fl->linger; 3461da177e4SLinus Torvalds if (time_before(fl->expires, fl->lastuse + expires)) 3471da177e4SLinus Torvalds fl->expires = fl->lastuse + expires; 348394055f6SFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 349394055f6SFlorent Fourcot 3501da177e4SLinus Torvalds return 0; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds static struct ip6_flowlabel * 354ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, 355ec0506dbSMaciej Żenczykowski char __user *optval, int optlen, int *err_p) 3561da177e4SLinus Torvalds { 357684de409SDavid S. Miller struct ip6_flowlabel *fl = NULL; 3581da177e4SLinus Torvalds int olen; 3591da177e4SLinus Torvalds int addr_type; 3601da177e4SLinus Torvalds int err; 3611da177e4SLinus Torvalds 362684de409SDavid S. Miller olen = optlen - CMSG_ALIGN(sizeof(*freq)); 363684de409SDavid S. Miller err = -EINVAL; 364684de409SDavid S. Miller if (olen > 64 * 1024) 365684de409SDavid S. Miller goto done; 366684de409SDavid S. Miller 3671da177e4SLinus Torvalds err = -ENOMEM; 3680c600edaSIngo Oeser fl = kzalloc(sizeof(*fl), GFP_KERNEL); 36963159f29SIan Morris if (!fl) 3701da177e4SLinus Torvalds goto done; 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds if (olen > 0) { 3731da177e4SLinus Torvalds struct msghdr msg; 3744c9483b2SDavid S. Miller struct flowi6 flowi6; 375ad1e46a8SSoheil Hassas Yeganeh struct sockcm_cookie sockc_junk; 3761da177e4SLinus Torvalds int junk; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds err = -ENOMEM; 3791da177e4SLinus Torvalds fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL); 38063159f29SIan Morris if (!fl->opt) 3811da177e4SLinus Torvalds goto done; 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds memset(fl->opt, 0, sizeof(*fl->opt)); 3841da177e4SLinus Torvalds fl->opt->tot_len = sizeof(*fl->opt) + olen; 3851da177e4SLinus Torvalds err = -EFAULT; 3861da177e4SLinus Torvalds if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen)) 3871da177e4SLinus Torvalds goto done; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds msg.msg_controllen = olen; 3901da177e4SLinus Torvalds msg.msg_control = (void *)(fl->opt+1); 3914c9483b2SDavid S. Miller memset(&flowi6, 0, sizeof(flowi6)); 3921da177e4SLinus Torvalds 39373df66f8STom Parkin err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, 394ad1e46a8SSoheil Hassas Yeganeh &junk, &junk, &junk, &sockc_junk); 3951da177e4SLinus Torvalds if (err) 3961da177e4SLinus Torvalds goto done; 3971da177e4SLinus Torvalds err = -EINVAL; 3981da177e4SLinus Torvalds if (fl->opt->opt_flen) 3991da177e4SLinus Torvalds goto done; 4001da177e4SLinus Torvalds if (fl->opt->opt_nflen == 0) { 4011da177e4SLinus Torvalds kfree(fl->opt); 4021da177e4SLinus Torvalds fl->opt = NULL; 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 406efd7ef1cSEric W. Biederman fl->fl_net = net; 4071da177e4SLinus Torvalds fl->expires = jiffies; 4081da177e4SLinus Torvalds err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); 4091da177e4SLinus Torvalds if (err) 4101da177e4SLinus Torvalds goto done; 4111da177e4SLinus Torvalds fl->share = freq->flr_share; 4121da177e4SLinus Torvalds addr_type = ipv6_addr_type(&freq->flr_dst); 41335700212SJoe Perches if ((addr_type & IPV6_ADDR_MAPPED) || 41435700212SJoe Perches addr_type == IPV6_ADDR_ANY) { 415c6817e4cSJames Morris err = -EINVAL; 4161da177e4SLinus Torvalds goto done; 417c6817e4cSJames Morris } 4184e3fd7a0SAlexey Dobriyan fl->dst = freq->flr_dst; 4191da177e4SLinus Torvalds atomic_set(&fl->users, 1); 4201da177e4SLinus Torvalds switch (fl->share) { 4211da177e4SLinus Torvalds case IPV6_FL_S_EXCL: 4221da177e4SLinus Torvalds case IPV6_FL_S_ANY: 4231da177e4SLinus Torvalds break; 4241da177e4SLinus Torvalds case IPV6_FL_S_PROCESS: 4254f82f457SEric W. Biederman fl->owner.pid = get_task_pid(current, PIDTYPE_PID); 4261da177e4SLinus Torvalds break; 4271da177e4SLinus Torvalds case IPV6_FL_S_USER: 4284f82f457SEric W. Biederman fl->owner.uid = current_euid(); 4291da177e4SLinus Torvalds break; 4301da177e4SLinus Torvalds default: 4311da177e4SLinus Torvalds err = -EINVAL; 4321da177e4SLinus Torvalds goto done; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds return fl; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds done: 4371da177e4SLinus Torvalds fl_free(fl); 4381da177e4SLinus Torvalds *err_p = err; 4391da177e4SLinus Torvalds return NULL; 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds static int mem_check(struct sock *sk) 4431da177e4SLinus Torvalds { 4441da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 4451da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 4461da177e4SLinus Torvalds int room = FL_MAX_SIZE - atomic_read(&fl_size); 4471da177e4SLinus Torvalds int count = 0; 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) 4501da177e4SLinus Torvalds return 0; 4511da177e4SLinus Torvalds 452f8c31c8fSHannes Frederic Sowa rcu_read_lock_bh(); 45318367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) 4541da177e4SLinus Torvalds count++; 455f8c31c8fSHannes Frederic Sowa rcu_read_unlock_bh(); 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds if (room <= 0 || 4581da177e4SLinus Torvalds ((count >= FL_MAX_PER_SOCK || 45935700212SJoe Perches (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && 46035700212SJoe Perches !capable(CAP_NET_ADMIN))) 4611da177e4SLinus Torvalds return -ENOBUFS; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds return 0; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 46604028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, 46704028045SPavel Emelyanov struct ip6_flowlabel *fl) 46804028045SPavel Emelyanov { 46918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 47004028045SPavel Emelyanov sfl->fl = fl; 47104028045SPavel Emelyanov sfl->next = np->ipv6_fl_list; 47218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(np->ipv6_fl_list, sfl); 47318367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 47404028045SPavel Emelyanov } 47504028045SPavel Emelyanov 47646e5f401SFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, 47746e5f401SFlorent Fourcot int flags) 4783fdfa5ffSFlorent Fourcot { 4793fdfa5ffSFlorent Fourcot struct ipv6_pinfo *np = inet6_sk(sk); 4803fdfa5ffSFlorent Fourcot struct ipv6_fl_socklist *sfl; 4813fdfa5ffSFlorent Fourcot 48246e5f401SFlorent Fourcot if (flags & IPV6_FL_F_REMOTE) { 48346e5f401SFlorent Fourcot freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK; 48446e5f401SFlorent Fourcot return 0; 48546e5f401SFlorent Fourcot } 48646e5f401SFlorent Fourcot 487df3687ffSFlorent Fourcot if (np->repflow) { 488df3687ffSFlorent Fourcot freq->flr_label = np->flow_label; 489df3687ffSFlorent Fourcot return 0; 490df3687ffSFlorent Fourcot } 491df3687ffSFlorent Fourcot 4923fdfa5ffSFlorent Fourcot rcu_read_lock_bh(); 4933fdfa5ffSFlorent Fourcot 4943fdfa5ffSFlorent Fourcot for_each_sk_fl_rcu(np, sfl) { 4953fdfa5ffSFlorent Fourcot if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) { 4963fdfa5ffSFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 4973fdfa5ffSFlorent Fourcot freq->flr_label = sfl->fl->label; 4983fdfa5ffSFlorent Fourcot freq->flr_dst = sfl->fl->dst; 4993fdfa5ffSFlorent Fourcot freq->flr_share = sfl->fl->share; 5003fdfa5ffSFlorent Fourcot freq->flr_expires = (sfl->fl->expires - jiffies) / HZ; 5013fdfa5ffSFlorent Fourcot freq->flr_linger = sfl->fl->linger / HZ; 5023fdfa5ffSFlorent Fourcot 5033fdfa5ffSFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 5043fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5053fdfa5ffSFlorent Fourcot return 0; 5063fdfa5ffSFlorent Fourcot } 5073fdfa5ffSFlorent Fourcot } 5083fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5093fdfa5ffSFlorent Fourcot 5103fdfa5ffSFlorent Fourcot return -ENOENT; 5113fdfa5ffSFlorent Fourcot } 5123fdfa5ffSFlorent Fourcot 5131da177e4SLinus Torvalds int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 5141da177e4SLinus Torvalds { 51555205d40SIngo Molnar int uninitialized_var(err); 51660e8fbc4SBenjamin Thery struct net *net = sock_net(sk); 5171da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 5181da177e4SLinus Torvalds struct in6_flowlabel_req freq; 5191da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl1 = NULL; 5207f0e44acSEric Dumazet struct ipv6_fl_socklist *sfl; 5217f0e44acSEric Dumazet struct ipv6_fl_socklist __rcu **sflp; 52278c2e502SPavel Emelyanov struct ip6_flowlabel *fl, *fl1 = NULL; 52378c2e502SPavel Emelyanov 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds if (optlen < sizeof(freq)) 5261da177e4SLinus Torvalds return -EINVAL; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds if (copy_from_user(&freq, optval, sizeof(freq))) 5291da177e4SLinus Torvalds return -EFAULT; 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds switch (freq.flr_action) { 5321da177e4SLinus Torvalds case IPV6_FL_A_PUT: 533df3687ffSFlorent Fourcot if (freq.flr_flags & IPV6_FL_F_REFLECT) { 534df3687ffSFlorent Fourcot if (sk->sk_protocol != IPPROTO_TCP) 535df3687ffSFlorent Fourcot return -ENOPROTOOPT; 536df3687ffSFlorent Fourcot if (!np->repflow) 537df3687ffSFlorent Fourcot return -ESRCH; 538df3687ffSFlorent Fourcot np->flow_label = 0; 539df3687ffSFlorent Fourcot np->repflow = 0; 540df3687ffSFlorent Fourcot return 0; 541df3687ffSFlorent Fourcot } 54218367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 54318367681SYOSHIFUJI Hideaki / 吉藤英明 for (sflp = &np->ipv6_fl_list; 54444c3d0c1SEric Dumazet (sfl = rcu_dereference_protected(*sflp, 54544c3d0c1SEric Dumazet lockdep_is_held(&ip6_sk_fl_lock))) != NULL; 54618367681SYOSHIFUJI Hideaki / 吉藤英明 sflp = &sfl->next) { 5471da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5481da177e4SLinus Torvalds if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) 5491da177e4SLinus Torvalds np->flow_label &= ~IPV6_FLOWLABEL_MASK; 55044c3d0c1SEric Dumazet *sflp = sfl->next; 55118367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5521da177e4SLinus Torvalds fl_release(sfl->fl); 55318367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 5541da177e4SLinus Torvalds return 0; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds } 55718367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5581da177e4SLinus Torvalds return -ESRCH; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds case IPV6_FL_A_RENEW: 56118367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 56218367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 5631da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5641da177e4SLinus Torvalds err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); 56518367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5661da177e4SLinus Torvalds return err; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds } 56918367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5701da177e4SLinus Torvalds 571af31f412SEric W. Biederman if (freq.flr_share == IPV6_FL_S_NONE && 572af31f412SEric W. Biederman ns_capable(net->user_ns, CAP_NET_ADMIN)) { 57360e8fbc4SBenjamin Thery fl = fl_lookup(net, freq.flr_label); 5741da177e4SLinus Torvalds if (fl) { 5751da177e4SLinus Torvalds err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); 5761da177e4SLinus Torvalds fl_release(fl); 5771da177e4SLinus Torvalds return err; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds return -ESRCH; 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds case IPV6_FL_A_GET: 583df3687ffSFlorent Fourcot if (freq.flr_flags & IPV6_FL_F_REFLECT) { 5846444f72bSFlorent Fourcot struct net *net = sock_net(sk); 5856444f72bSFlorent Fourcot if (net->ipv6.sysctl.flowlabel_consistency) { 5866444f72bSFlorent Fourcot net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); 5876444f72bSFlorent Fourcot return -EPERM; 5886444f72bSFlorent Fourcot } 5896444f72bSFlorent Fourcot 590df3687ffSFlorent Fourcot if (sk->sk_protocol != IPPROTO_TCP) 591df3687ffSFlorent Fourcot return -ENOPROTOOPT; 5926444f72bSFlorent Fourcot 593df3687ffSFlorent Fourcot np->repflow = 1; 594df3687ffSFlorent Fourcot return 0; 595df3687ffSFlorent Fourcot } 596df3687ffSFlorent Fourcot 5971da177e4SLinus Torvalds if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) 5981da177e4SLinus Torvalds return -EINVAL; 5991da177e4SLinus Torvalds 60082a584b7STom Herbert if (net->ipv6.sysctl.flowlabel_state_ranges && 60182a584b7STom Herbert (freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) 60282a584b7STom Herbert return -ERANGE; 60382a584b7STom Herbert 604ec0506dbSMaciej Żenczykowski fl = fl_create(net, sk, &freq, optval, optlen, &err); 60563159f29SIan Morris if (!fl) 6061da177e4SLinus Torvalds return err; 6071da177e4SLinus Torvalds sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds if (freq.flr_label) { 6101da177e4SLinus Torvalds err = -EEXIST; 61118367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 61218367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 6131da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 6141da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) { 61518367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 6161da177e4SLinus Torvalds goto done; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds fl1 = sfl->fl; 6194ea6a804SYan Zheng atomic_inc(&fl1->users); 6201da177e4SLinus Torvalds break; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds } 62318367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 6241da177e4SLinus Torvalds 62563159f29SIan Morris if (!fl1) 62660e8fbc4SBenjamin Thery fl1 = fl_lookup(net, freq.flr_label); 6271da177e4SLinus Torvalds if (fl1) { 62878c2e502SPavel Emelyanov recheck: 6291da177e4SLinus Torvalds err = -EEXIST; 6301da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) 6311da177e4SLinus Torvalds goto release; 6321da177e4SLinus Torvalds err = -EPERM; 6331da177e4SLinus Torvalds if (fl1->share == IPV6_FL_S_EXCL || 6341da177e4SLinus Torvalds fl1->share != fl->share || 6354f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_PROCESS) && 6364f82f457SEric W. Biederman (fl1->owner.pid == fl->owner.pid)) || 6374f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_USER) && 6384f82f457SEric W. Biederman uid_eq(fl1->owner.uid, fl->owner.uid))) 6391da177e4SLinus Torvalds goto release; 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds err = -ENOMEM; 64263159f29SIan Morris if (!sfl1) 6431da177e4SLinus Torvalds goto release; 6441da177e4SLinus Torvalds if (fl->linger > fl1->linger) 6451da177e4SLinus Torvalds fl1->linger = fl->linger; 6461da177e4SLinus Torvalds if ((long)(fl->expires - fl1->expires) > 0) 6471da177e4SLinus Torvalds fl1->expires = fl->expires; 64804028045SPavel Emelyanov fl_link(np, sfl1, fl1); 6491da177e4SLinus Torvalds fl_free(fl); 6501da177e4SLinus Torvalds return 0; 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds release: 6531da177e4SLinus Torvalds fl_release(fl1); 6541da177e4SLinus Torvalds goto done; 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds err = -ENOENT; 6581da177e4SLinus Torvalds if (!(freq.flr_flags&IPV6_FL_F_CREATE)) 6591da177e4SLinus Torvalds goto done; 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds err = -ENOMEM; 66263159f29SIan Morris if (!sfl1) 663e5d08d71SIan Morris goto done; 664e5d08d71SIan Morris 665e5d08d71SIan Morris err = mem_check(sk); 666e5d08d71SIan Morris if (err != 0) 6671da177e4SLinus Torvalds goto done; 6681da177e4SLinus Torvalds 66960e8fbc4SBenjamin Thery fl1 = fl_intern(net, fl, freq.flr_label); 67053b24b8fSIan Morris if (fl1) 67178c2e502SPavel Emelyanov goto recheck; 6721da177e4SLinus Torvalds 6736c94d361SDavid S. Miller if (!freq.flr_label) { 6746c94d361SDavid S. Miller if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, 6756c94d361SDavid S. Miller &fl->label, sizeof(fl->label))) { 6766c94d361SDavid S. Miller /* Intentionally ignore fault. */ 6776c94d361SDavid S. Miller } 6786c94d361SDavid S. Miller } 6791da177e4SLinus Torvalds 68004028045SPavel Emelyanov fl_link(np, sfl1, fl); 6811da177e4SLinus Torvalds return 0; 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds default: 6841da177e4SLinus Torvalds return -EINVAL; 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds done: 6881da177e4SLinus Torvalds fl_free(fl); 6891da177e4SLinus Torvalds kfree(sfl1); 6901da177e4SLinus Torvalds return err; 6911da177e4SLinus Torvalds } 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds struct ip6fl_iter_state { 6965983a3dfSBenjamin Thery struct seq_net_private p; 6974f82f457SEric W. Biederman struct pid_namespace *pid_ns; 6981da177e4SLinus Torvalds int bucket; 6991da177e4SLinus Torvalds }; 7001da177e4SLinus Torvalds 7011da177e4SLinus Torvalds #define ip6fl_seq_private(seq) ((struct ip6fl_iter_state *)(seq)->private) 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) 7041da177e4SLinus Torvalds { 7051da177e4SLinus Torvalds struct ip6_flowlabel *fl = NULL; 7061da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7075983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { 710d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 711d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 712d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 7131da177e4SLinus Torvalds } 714d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 715d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 716d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7171da177e4SLinus Torvalds return fl; 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl) 7211da177e4SLinus Torvalds { 7221da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7235983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 7241da177e4SLinus Torvalds 725d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_continue_rcu(fl) { 726d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 727d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 7281da177e4SLinus Torvalds } 729d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 730d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again: 731d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (++state->bucket <= FL_HASH_MASK) { 732d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 733d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 734d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 735d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 736d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto try_again; 737d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 738d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 739d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 740d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7411da177e4SLinus Torvalds return fl; 7421da177e4SLinus Torvalds } 7431da177e4SLinus Torvalds 7441da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos) 7451da177e4SLinus Torvalds { 7461da177e4SLinus Torvalds struct ip6_flowlabel *fl = ip6fl_get_first(seq); 7471da177e4SLinus Torvalds if (fl) 7481da177e4SLinus Torvalds while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL) 7491da177e4SLinus Torvalds --pos; 7501da177e4SLinus Torvalds return pos ? NULL : fl; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos) 754d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __acquires(RCU) 7551da177e4SLinus Torvalds { 756d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 7571da177e4SLinus Torvalds return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos) 7611da177e4SLinus Torvalds { 7621da177e4SLinus Torvalds struct ip6_flowlabel *fl; 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 7651da177e4SLinus Torvalds fl = ip6fl_get_first(seq); 7661da177e4SLinus Torvalds else 7671da177e4SLinus Torvalds fl = ip6fl_get_next(seq, v); 7681da177e4SLinus Torvalds ++*pos; 7691da177e4SLinus Torvalds return fl; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v) 773d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __releases(RCU) 7741da177e4SLinus Torvalds { 775d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 7761da177e4SLinus Torvalds } 7771da177e4SLinus Torvalds 7781b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v) 7791da177e4SLinus Torvalds { 7804f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 781869ba988SFlorent Fourcot if (v == SEQ_START_TOKEN) { 7821744bea1SJoe Perches seq_puts(seq, "Label S Owner Users Linger Expires Dst Opt\n"); 783869ba988SFlorent Fourcot } else { 7841b7c2dbcSJames Morris struct ip6_flowlabel *fl = v; 7851da177e4SLinus Torvalds seq_printf(seq, 7864b7a4274SHarvey Harrison "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", 78795c96174SEric Dumazet (unsigned int)ntohl(fl->label), 7881da177e4SLinus Torvalds fl->share, 7894f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_PROCESS) ? 7904f82f457SEric W. Biederman pid_nr_ns(fl->owner.pid, state->pid_ns) : 7914f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_USER) ? 7924f82f457SEric W. Biederman from_kuid_munged(seq_user_ns(seq), fl->owner.uid) : 7934f82f457SEric W. Biederman 0)), 7941da177e4SLinus Torvalds atomic_read(&fl->users), 7951da177e4SLinus Torvalds fl->linger/HZ, 7961da177e4SLinus Torvalds (long)(fl->expires - jiffies)/HZ, 797b071195dSHarvey Harrison &fl->dst, 7981da177e4SLinus Torvalds fl->opt ? fl->opt->opt_nflen : 0); 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds return 0; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 80356b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = { 8041da177e4SLinus Torvalds .start = ip6fl_seq_start, 8051da177e4SLinus Torvalds .next = ip6fl_seq_next, 8061da177e4SLinus Torvalds .stop = ip6fl_seq_stop, 8071da177e4SLinus Torvalds .show = ip6fl_seq_show, 8081da177e4SLinus Torvalds }; 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds static int ip6fl_seq_open(struct inode *inode, struct file *file) 8111da177e4SLinus Torvalds { 8124f82f457SEric W. Biederman struct seq_file *seq; 8134f82f457SEric W. Biederman struct ip6fl_iter_state *state; 8144f82f457SEric W. Biederman int err; 8154f82f457SEric W. Biederman 8164f82f457SEric W. Biederman err = seq_open_net(inode, file, &ip6fl_seq_ops, 817cf7732e4SPavel Emelyanov sizeof(struct ip6fl_iter_state)); 8184f82f457SEric W. Biederman 8194f82f457SEric W. Biederman if (!err) { 8204f82f457SEric W. Biederman seq = file->private_data; 8214f82f457SEric W. Biederman state = ip6fl_seq_private(seq); 8224f82f457SEric W. Biederman rcu_read_lock(); 8234f82f457SEric W. Biederman state->pid_ns = get_pid_ns(task_active_pid_ns(current)); 8244f82f457SEric W. Biederman rcu_read_unlock(); 8254f82f457SEric W. Biederman } 8264f82f457SEric W. Biederman return err; 8274f82f457SEric W. Biederman } 8284f82f457SEric W. Biederman 8294f82f457SEric W. Biederman static int ip6fl_seq_release(struct inode *inode, struct file *file) 8304f82f457SEric W. Biederman { 8314f82f457SEric W. Biederman struct seq_file *seq = file->private_data; 8324f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 8334f82f457SEric W. Biederman put_pid_ns(state->pid_ns); 8344f82f457SEric W. Biederman return seq_release_net(inode, file); 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds 8379a32144eSArjan van de Ven static const struct file_operations ip6fl_seq_fops = { 8381da177e4SLinus Torvalds .owner = THIS_MODULE, 8391da177e4SLinus Torvalds .open = ip6fl_seq_open, 8401da177e4SLinus Torvalds .read = seq_read, 8411da177e4SLinus Torvalds .llseek = seq_lseek, 8424f82f457SEric W. Biederman .release = ip6fl_seq_release, 8431da177e4SLinus Torvalds }; 8441da177e4SLinus Torvalds 8452c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net) 8461da177e4SLinus Torvalds { 847d4beaa66SGao feng if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net, 848d4beaa66SGao feng &ip6fl_seq_fops)) 8490a3e78acSDaniel Lezcano return -ENOMEM; 8500a3e78acSDaniel Lezcano return 0; 8510a3e78acSDaniel Lezcano } 8520a3e78acSDaniel Lezcano 8532c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net) 8540a3e78acSDaniel Lezcano { 855ece31ffdSGao feng remove_proc_entry("ip6_flowlabel", net->proc_net); 8560a3e78acSDaniel Lezcano } 8570a3e78acSDaniel Lezcano #else 8580a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net) 8590a3e78acSDaniel Lezcano { 8600a3e78acSDaniel Lezcano return 0; 8610a3e78acSDaniel Lezcano } 8620a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net) 8630a3e78acSDaniel Lezcano { 8640a3e78acSDaniel Lezcano } 8651da177e4SLinus Torvalds #endif 8660a3e78acSDaniel Lezcano 8672c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net) 86860e8fbc4SBenjamin Thery { 86960e8fbc4SBenjamin Thery ip6_fl_purge(net); 8705983a3dfSBenjamin Thery ip6_flowlabel_proc_fini(net); 87160e8fbc4SBenjamin Thery } 87260e8fbc4SBenjamin Thery 87360e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = { 8745983a3dfSBenjamin Thery .init = ip6_flowlabel_proc_init, 87560e8fbc4SBenjamin Thery .exit = ip6_flowlabel_net_exit, 87660e8fbc4SBenjamin Thery }; 87760e8fbc4SBenjamin Thery 8780a3e78acSDaniel Lezcano int ip6_flowlabel_init(void) 8790a3e78acSDaniel Lezcano { 8805983a3dfSBenjamin Thery return register_pernet_subsys(&ip6_flowlabel_net_ops); 8811da177e4SLinus Torvalds } 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void) 8841da177e4SLinus Torvalds { 8851da177e4SLinus Torvalds del_timer(&ip6_fl_gc_timer); 88660e8fbc4SBenjamin Thery unregister_pernet_subsys(&ip6_flowlabel_net_ops); 8871da177e4SLinus Torvalds } 888