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/if_arp.h> 191da177e4SLinus Torvalds #include <linux/in6.h> 201da177e4SLinus Torvalds #include <linux/route.h> 211da177e4SLinus Torvalds #include <linux/proc_fs.h> 221da177e4SLinus Torvalds #include <linux/seq_file.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 24bc3b2d7fSPaul Gortmaker #include <linux/export.h> 254f82f457SEric W. Biederman #include <linux/pid_namespace.h> 261da177e4SLinus Torvalds 27457c4cbcSEric W. Biederman #include <net/net_namespace.h> 281da177e4SLinus Torvalds #include <net/sock.h> 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds #include <net/ipv6.h> 311da177e4SLinus Torvalds #include <net/ndisc.h> 321da177e4SLinus Torvalds #include <net/protocol.h> 331da177e4SLinus Torvalds #include <net/ip6_route.h> 341da177e4SLinus Torvalds #include <net/addrconf.h> 351da177e4SLinus Torvalds #include <net/rawv6.h> 361da177e4SLinus Torvalds #include <net/icmp.h> 371da177e4SLinus Torvalds #include <net/transp_v6.h> 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds #include <asm/uaccess.h> 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds #define FL_MIN_LINGER 6 /* Minimal linger. It is set to 6sec specified 421da177e4SLinus Torvalds in old IPv6 RFC. Well, it was reasonable value. 431da177e4SLinus Torvalds */ 4453b47106SFlorent Fourcot #define FL_MAX_LINGER 150 /* Maximal linger timeout */ 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds /* FL hash table */ 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds #define FL_MAX_PER_SOCK 32 491da177e4SLinus Torvalds #define FL_MAX_SIZE 4096 501da177e4SLinus Torvalds #define FL_HASH_MASK 255 511da177e4SLinus Torvalds #define FL_HASH(l) (ntohl(l)&FL_HASH_MASK) 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds static atomic_t fl_size = ATOMIC_INIT(0); 54d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1]; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds static void ip6_fl_gc(unsigned long dummy); 578d06afabSIngo Molnar static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0); 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds /* FL hash table lock: it protects only of GC */ 601da177e4SLinus Torvalds 61d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_fl_lock); 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* Big socket sock */ 641da177e4SLinus Torvalds 6518367681SYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_sk_fl_lock); 661da177e4SLinus Torvalds 67d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 #define for_each_fl_rcu(hash, fl) \ 686a98dcf0SAmerigo Wang for (fl = rcu_dereference_bh(fl_ht[(hash)]); \ 69d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl != NULL; \ 706a98dcf0SAmerigo Wang fl = rcu_dereference_bh(fl->next)) 71d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 #define for_each_fl_continue_rcu(fl) \ 726a98dcf0SAmerigo Wang for (fl = rcu_dereference_bh(fl->next); \ 73d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl != NULL; \ 746a98dcf0SAmerigo Wang fl = rcu_dereference_bh(fl->next)) 751da177e4SLinus Torvalds 7618367681SYOSHIFUJI Hideaki / 吉藤英明 #define for_each_sk_fl_rcu(np, sfl) \ 7718367681SYOSHIFUJI Hideaki / 吉藤英明 for (sfl = rcu_dereference_bh(np->ipv6_fl_list); \ 7818367681SYOSHIFUJI Hideaki / 吉藤英明 sfl != NULL; \ 7918367681SYOSHIFUJI Hideaki / 吉藤英明 sfl = rcu_dereference_bh(sfl->next)) 8018367681SYOSHIFUJI Hideaki / 吉藤英明 8160e8fbc4SBenjamin Thery static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label) 821da177e4SLinus Torvalds { 831da177e4SLinus Torvalds struct ip6_flowlabel *fl; 841da177e4SLinus Torvalds 85d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(FL_HASH(label), fl) { 8609ad9bc7SOctavian Purdila if (fl->label == label && net_eq(fl->fl_net, net)) 871da177e4SLinus Torvalds return fl; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds return NULL; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 9260e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) 931da177e4SLinus Torvalds { 941da177e4SLinus Torvalds struct ip6_flowlabel *fl; 951da177e4SLinus Torvalds 96d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 9760e8fbc4SBenjamin Thery fl = __fl_lookup(net, label); 98d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (fl && !atomic_inc_not_zero(&fl->users)) 99d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 100d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 1011da177e4SLinus Torvalds return fl; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds static void fl_free(struct ip6_flowlabel *fl) 1061da177e4SLinus Torvalds { 10760e8fbc4SBenjamin Thery if (fl) { 108898132aeSDan Carpenter if (fl->share == IPV6_FL_S_PROCESS) 109898132aeSDan Carpenter put_pid(fl->owner.pid); 11060e8fbc4SBenjamin Thery release_net(fl->fl_net); 1111da177e4SLinus Torvalds kfree(fl->opt); 112d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(fl, rcu); 11360e8fbc4SBenjamin Thery } 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static void fl_release(struct ip6_flowlabel *fl) 1171da177e4SLinus Torvalds { 118d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds fl->lastuse = jiffies; 1211da177e4SLinus Torvalds if (atomic_dec_and_test(&fl->users)) { 1221da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1231da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1241da177e4SLinus Torvalds fl->expires = ttd; 1251da177e4SLinus Torvalds ttd = fl->expires; 1261da177e4SLinus Torvalds if (fl->opt && fl->share == IPV6_FL_S_EXCL) { 1271da177e4SLinus Torvalds struct ipv6_txoptions *opt = fl->opt; 1281da177e4SLinus Torvalds fl->opt = NULL; 1291da177e4SLinus Torvalds kfree(opt); 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds if (!timer_pending(&ip6_fl_gc_timer) || 1321da177e4SLinus Torvalds time_after(ip6_fl_gc_timer.expires, ttd)) 1331da177e4SLinus Torvalds mod_timer(&ip6_fl_gc_timer, ttd); 1341da177e4SLinus Torvalds } 135d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds static void ip6_fl_gc(unsigned long dummy) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds int i; 1411da177e4SLinus Torvalds unsigned long now = jiffies; 1421da177e4SLinus Torvalds unsigned long sched = 0; 1431da177e4SLinus Torvalds 144d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock(&ip6_fl_lock); 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds for (i=0; i<=FL_HASH_MASK; i++) { 1477f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1487f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1497f0e44acSEric Dumazet 1501da177e4SLinus Torvalds flp = &fl_ht[i]; 151d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 152d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 1531da177e4SLinus Torvalds if (atomic_read(&fl->users) == 0) { 1541da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1551da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1561da177e4SLinus Torvalds fl->expires = ttd; 1571da177e4SLinus Torvalds ttd = fl->expires; 1581da177e4SLinus Torvalds if (time_after_eq(now, ttd)) { 1591da177e4SLinus Torvalds *flp = fl->next; 1601da177e4SLinus Torvalds fl_free(fl); 1611da177e4SLinus Torvalds atomic_dec(&fl_size); 1621da177e4SLinus Torvalds continue; 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds if (!sched || time_before(ttd, sched)) 1651da177e4SLinus Torvalds sched = ttd; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds flp = &fl->next; 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds if (!sched && atomic_read(&fl_size)) 1711da177e4SLinus Torvalds sched = now + FL_MAX_LINGER; 1721da177e4SLinus Torvalds if (sched) { 17360e8fbc4SBenjamin Thery mod_timer(&ip6_fl_gc_timer, sched); 1741da177e4SLinus Torvalds } 175d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds 1782c8c1e72SAlexey Dobriyan static void __net_exit ip6_fl_purge(struct net *net) 17960e8fbc4SBenjamin Thery { 18060e8fbc4SBenjamin Thery int i; 18160e8fbc4SBenjamin Thery 182d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock(&ip6_fl_lock); 18360e8fbc4SBenjamin Thery for (i = 0; i <= FL_HASH_MASK; i++) { 1847f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1857f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1867f0e44acSEric Dumazet 18760e8fbc4SBenjamin Thery flp = &fl_ht[i]; 188d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 189d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 19009ad9bc7SOctavian Purdila if (net_eq(fl->fl_net, net) && 19109ad9bc7SOctavian Purdila atomic_read(&fl->users) == 0) { 19260e8fbc4SBenjamin Thery *flp = fl->next; 19360e8fbc4SBenjamin Thery fl_free(fl); 19460e8fbc4SBenjamin Thery atomic_dec(&fl_size); 19560e8fbc4SBenjamin Thery continue; 19660e8fbc4SBenjamin Thery } 19760e8fbc4SBenjamin Thery flp = &fl->next; 19860e8fbc4SBenjamin Thery } 19960e8fbc4SBenjamin Thery } 200d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 20160e8fbc4SBenjamin Thery } 20260e8fbc4SBenjamin Thery 20360e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_intern(struct net *net, 20460e8fbc4SBenjamin Thery struct ip6_flowlabel *fl, __be32 label) 2051da177e4SLinus Torvalds { 20678c2e502SPavel Emelyanov struct ip6_flowlabel *lfl; 20778c2e502SPavel Emelyanov 2081da177e4SLinus Torvalds fl->label = label & IPV6_FLOWLABEL_MASK; 2091da177e4SLinus Torvalds 210d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 2111da177e4SLinus Torvalds if (label == 0) { 2121da177e4SLinus Torvalds for (;;) { 21363862b5bSAruna-Hewapathirane fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK; 2141da177e4SLinus Torvalds if (fl->label) { 21560e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 2161da177e4SLinus Torvalds if (lfl == NULL) 2171da177e4SLinus Torvalds break; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds } 22078c2e502SPavel Emelyanov } else { 22178c2e502SPavel Emelyanov /* 22278c2e502SPavel Emelyanov * we dropper the ip6_fl_lock, so this entry could reappear 22378c2e502SPavel Emelyanov * and we need to recheck with it. 22478c2e502SPavel Emelyanov * 22578c2e502SPavel Emelyanov * OTOH no need to search the active socket first, like it is 22678c2e502SPavel Emelyanov * done in ipv6_flowlabel_opt - sock is locked, so new entry 22778c2e502SPavel Emelyanov * with the same label can only appear on another sock 22878c2e502SPavel Emelyanov */ 22960e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 23078c2e502SPavel Emelyanov if (lfl != NULL) { 23178c2e502SPavel Emelyanov atomic_inc(&lfl->users); 232d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 23378c2e502SPavel Emelyanov return lfl; 23478c2e502SPavel Emelyanov } 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds fl->lastuse = jiffies; 2381da177e4SLinus Torvalds fl->next = fl_ht[FL_HASH(fl->label)]; 239d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); 2401da177e4SLinus Torvalds atomic_inc(&fl_size); 241d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 24278c2e502SPavel Emelyanov return NULL; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds /* Socket flowlabel lists */ 2481da177e4SLinus Torvalds 24990bcaf7bSAl Viro struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2521da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds label &= IPV6_FLOWLABEL_MASK; 2551da177e4SLinus Torvalds 25618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 25718367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 2581da177e4SLinus Torvalds struct ip6_flowlabel *fl = sfl->fl; 2591da177e4SLinus Torvalds if (fl->label == label) { 2601da177e4SLinus Torvalds fl->lastuse = jiffies; 2611da177e4SLinus Torvalds atomic_inc(&fl->users); 26218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2631da177e4SLinus Torvalds return fl; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds } 26618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2671da177e4SLinus Torvalds return NULL; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2703cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(fl6_sock_lookup); 2713cf3dc6cSArnaldo Carvalho de Melo 2721da177e4SLinus Torvalds void fl6_free_socklist(struct sock *sk) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2751da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2761da177e4SLinus Torvalds 27718367681SYOSHIFUJI Hideaki / 吉藤英明 if (!rcu_access_pointer(np->ipv6_fl_list)) 278f256dc59SYOSHIFUJI Hideaki / 吉藤英明 return; 279f256dc59SYOSHIFUJI Hideaki / 吉藤英明 28018367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 28118367681SYOSHIFUJI Hideaki / 吉藤英明 while ((sfl = rcu_dereference_protected(np->ipv6_fl_list, 28218367681SYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_sk_fl_lock))) != NULL) { 28318367681SYOSHIFUJI Hideaki / 吉藤英明 np->ipv6_fl_list = sfl->next; 28418367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 285f256dc59SYOSHIFUJI Hideaki / 吉藤英明 2861da177e4SLinus Torvalds fl_release(sfl->fl); 28718367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 28818367681SYOSHIFUJI Hideaki / 吉藤英明 28918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 2901da177e4SLinus Torvalds } 29118367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds /* Service routines */ 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds /* 2981da177e4SLinus Torvalds It is the only difficult place. flowlabel enforces equal headers 2991da177e4SLinus Torvalds before and including routing header, however user may supply options 3001da177e4SLinus Torvalds following rthdr. 3011da177e4SLinus Torvalds */ 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, 3041da177e4SLinus Torvalds struct ip6_flowlabel * fl, 3051da177e4SLinus Torvalds struct ipv6_txoptions * fopt) 3061da177e4SLinus Torvalds { 307df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions * fl_opt = fl->opt; 3081da177e4SLinus Torvalds 309df9890c3SYOSHIFUJI Hideaki if (fopt == NULL || fopt->opt_flen == 0) 3101da177e4SLinus Torvalds return fl_opt; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds if (fl_opt != NULL) { 3131da177e4SLinus Torvalds opt_space->hopopt = fl_opt->hopopt; 314df9890c3SYOSHIFUJI Hideaki opt_space->dst0opt = fl_opt->dst0opt; 3151da177e4SLinus Torvalds opt_space->srcrt = fl_opt->srcrt; 3161da177e4SLinus Torvalds opt_space->opt_nflen = fl_opt->opt_nflen; 3171da177e4SLinus Torvalds } else { 3181da177e4SLinus Torvalds if (fopt->opt_nflen == 0) 3191da177e4SLinus Torvalds return fopt; 3201da177e4SLinus Torvalds opt_space->hopopt = NULL; 3211da177e4SLinus Torvalds opt_space->dst0opt = NULL; 3221da177e4SLinus Torvalds opt_space->srcrt = NULL; 3231da177e4SLinus Torvalds opt_space->opt_nflen = 0; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds opt_space->dst1opt = fopt->dst1opt; 3261da177e4SLinus Torvalds opt_space->opt_flen = fopt->opt_flen; 3271da177e4SLinus Torvalds return opt_space; 3281da177e4SLinus Torvalds } 329a495f836SChris Elston EXPORT_SYMBOL_GPL(fl6_merge_options); 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static unsigned long check_linger(unsigned long ttl) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds if (ttl < FL_MIN_LINGER) 3341da177e4SLinus Torvalds return FL_MIN_LINGER*HZ; 3351da177e4SLinus Torvalds if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN)) 3361da177e4SLinus Torvalds return 0; 3371da177e4SLinus Torvalds return ttl*HZ; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds linger = check_linger(linger); 3431da177e4SLinus Torvalds if (!linger) 3441da177e4SLinus Torvalds return -EPERM; 3451da177e4SLinus Torvalds expires = check_linger(expires); 3461da177e4SLinus Torvalds if (!expires) 3471da177e4SLinus Torvalds return -EPERM; 348394055f6SFlorent Fourcot 349394055f6SFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 3501da177e4SLinus Torvalds fl->lastuse = jiffies; 3511da177e4SLinus Torvalds if (time_before(fl->linger, linger)) 3521da177e4SLinus Torvalds fl->linger = linger; 3531da177e4SLinus Torvalds if (time_before(expires, fl->linger)) 3541da177e4SLinus Torvalds expires = fl->linger; 3551da177e4SLinus Torvalds if (time_before(fl->expires, fl->lastuse + expires)) 3561da177e4SLinus Torvalds fl->expires = fl->lastuse + expires; 357394055f6SFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 358394055f6SFlorent Fourcot 3591da177e4SLinus Torvalds return 0; 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds static struct ip6_flowlabel * 363ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, 364ec0506dbSMaciej Żenczykowski char __user *optval, int optlen, int *err_p) 3651da177e4SLinus Torvalds { 366684de409SDavid S. Miller struct ip6_flowlabel *fl = NULL; 3671da177e4SLinus Torvalds int olen; 3681da177e4SLinus Torvalds int addr_type; 3691da177e4SLinus Torvalds int err; 3701da177e4SLinus Torvalds 371684de409SDavid S. Miller olen = optlen - CMSG_ALIGN(sizeof(*freq)); 372684de409SDavid S. Miller err = -EINVAL; 373684de409SDavid S. Miller if (olen > 64 * 1024) 374684de409SDavid S. Miller goto done; 375684de409SDavid S. Miller 3761da177e4SLinus Torvalds err = -ENOMEM; 3770c600edaSIngo Oeser fl = kzalloc(sizeof(*fl), GFP_KERNEL); 3781da177e4SLinus Torvalds if (fl == NULL) 3791da177e4SLinus Torvalds goto done; 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds if (olen > 0) { 3821da177e4SLinus Torvalds struct msghdr msg; 3834c9483b2SDavid S. Miller struct flowi6 flowi6; 3841da177e4SLinus Torvalds int junk; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds err = -ENOMEM; 3871da177e4SLinus Torvalds fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL); 3881da177e4SLinus Torvalds if (fl->opt == NULL) 3891da177e4SLinus Torvalds goto done; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds memset(fl->opt, 0, sizeof(*fl->opt)); 3921da177e4SLinus Torvalds fl->opt->tot_len = sizeof(*fl->opt) + olen; 3931da177e4SLinus Torvalds err = -EFAULT; 3941da177e4SLinus Torvalds if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen)) 3951da177e4SLinus Torvalds goto done; 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds msg.msg_controllen = olen; 3981da177e4SLinus Torvalds msg.msg_control = (void*)(fl->opt+1); 3994c9483b2SDavid S. Miller memset(&flowi6, 0, sizeof(flowi6)); 4001da177e4SLinus Torvalds 40173df66f8STom Parkin err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, 40273df66f8STom Parkin &junk, &junk, &junk); 4031da177e4SLinus Torvalds if (err) 4041da177e4SLinus Torvalds goto done; 4051da177e4SLinus Torvalds err = -EINVAL; 4061da177e4SLinus Torvalds if (fl->opt->opt_flen) 4071da177e4SLinus Torvalds goto done; 4081da177e4SLinus Torvalds if (fl->opt->opt_nflen == 0) { 4091da177e4SLinus Torvalds kfree(fl->opt); 4101da177e4SLinus Torvalds fl->opt = NULL; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds 41460e8fbc4SBenjamin Thery fl->fl_net = hold_net(net); 4151da177e4SLinus Torvalds fl->expires = jiffies; 4161da177e4SLinus Torvalds err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); 4171da177e4SLinus Torvalds if (err) 4181da177e4SLinus Torvalds goto done; 4191da177e4SLinus Torvalds fl->share = freq->flr_share; 4201da177e4SLinus Torvalds addr_type = ipv6_addr_type(&freq->flr_dst); 42135700212SJoe Perches if ((addr_type & IPV6_ADDR_MAPPED) || 42235700212SJoe Perches addr_type == IPV6_ADDR_ANY) { 423c6817e4cSJames Morris err = -EINVAL; 4241da177e4SLinus Torvalds goto done; 425c6817e4cSJames Morris } 4264e3fd7a0SAlexey Dobriyan fl->dst = freq->flr_dst; 4271da177e4SLinus Torvalds atomic_set(&fl->users, 1); 4281da177e4SLinus Torvalds switch (fl->share) { 4291da177e4SLinus Torvalds case IPV6_FL_S_EXCL: 4301da177e4SLinus Torvalds case IPV6_FL_S_ANY: 4311da177e4SLinus Torvalds break; 4321da177e4SLinus Torvalds case IPV6_FL_S_PROCESS: 4334f82f457SEric W. Biederman fl->owner.pid = get_task_pid(current, PIDTYPE_PID); 4341da177e4SLinus Torvalds break; 4351da177e4SLinus Torvalds case IPV6_FL_S_USER: 4364f82f457SEric W. Biederman fl->owner.uid = current_euid(); 4371da177e4SLinus Torvalds break; 4381da177e4SLinus Torvalds default: 4391da177e4SLinus Torvalds err = -EINVAL; 4401da177e4SLinus Torvalds goto done; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds return fl; 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds done: 4451da177e4SLinus Torvalds fl_free(fl); 4461da177e4SLinus Torvalds *err_p = err; 4471da177e4SLinus Torvalds return NULL; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds static int mem_check(struct sock *sk) 4511da177e4SLinus Torvalds { 4521da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 4531da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 4541da177e4SLinus Torvalds int room = FL_MAX_SIZE - atomic_read(&fl_size); 4551da177e4SLinus Torvalds int count = 0; 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) 4581da177e4SLinus Torvalds return 0; 4591da177e4SLinus Torvalds 460f8c31c8fSHannes Frederic Sowa rcu_read_lock_bh(); 46118367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) 4621da177e4SLinus Torvalds count++; 463f8c31c8fSHannes Frederic Sowa rcu_read_unlock_bh(); 4641da177e4SLinus Torvalds 4651da177e4SLinus Torvalds if (room <= 0 || 4661da177e4SLinus Torvalds ((count >= FL_MAX_PER_SOCK || 46735700212SJoe Perches (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && 46835700212SJoe Perches !capable(CAP_NET_ADMIN))) 4691da177e4SLinus Torvalds return -ENOBUFS; 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds return 0; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds 47404028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, 47504028045SPavel Emelyanov struct ip6_flowlabel *fl) 47604028045SPavel Emelyanov { 47718367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 47804028045SPavel Emelyanov sfl->fl = fl; 47904028045SPavel Emelyanov sfl->next = np->ipv6_fl_list; 48018367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(np->ipv6_fl_list, sfl); 48118367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 48204028045SPavel Emelyanov } 48304028045SPavel Emelyanov 4843fdfa5ffSFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq) 4853fdfa5ffSFlorent Fourcot { 4863fdfa5ffSFlorent Fourcot struct ipv6_pinfo *np = inet6_sk(sk); 4873fdfa5ffSFlorent Fourcot struct ipv6_fl_socklist *sfl; 4883fdfa5ffSFlorent Fourcot 4893fdfa5ffSFlorent Fourcot rcu_read_lock_bh(); 4903fdfa5ffSFlorent Fourcot 4913fdfa5ffSFlorent Fourcot for_each_sk_fl_rcu(np, sfl) { 4923fdfa5ffSFlorent Fourcot if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) { 4933fdfa5ffSFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 4943fdfa5ffSFlorent Fourcot freq->flr_label = sfl->fl->label; 4953fdfa5ffSFlorent Fourcot freq->flr_dst = sfl->fl->dst; 4963fdfa5ffSFlorent Fourcot freq->flr_share = sfl->fl->share; 4973fdfa5ffSFlorent Fourcot freq->flr_expires = (sfl->fl->expires - jiffies) / HZ; 4983fdfa5ffSFlorent Fourcot freq->flr_linger = sfl->fl->linger / HZ; 4993fdfa5ffSFlorent Fourcot 5003fdfa5ffSFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 5013fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5023fdfa5ffSFlorent Fourcot return 0; 5033fdfa5ffSFlorent Fourcot } 5043fdfa5ffSFlorent Fourcot } 5053fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5063fdfa5ffSFlorent Fourcot 5073fdfa5ffSFlorent Fourcot return -ENOENT; 5083fdfa5ffSFlorent Fourcot } 5093fdfa5ffSFlorent Fourcot 5101da177e4SLinus Torvalds int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 5111da177e4SLinus Torvalds { 51255205d40SIngo Molnar int uninitialized_var(err); 51360e8fbc4SBenjamin Thery struct net *net = sock_net(sk); 5141da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 5151da177e4SLinus Torvalds struct in6_flowlabel_req freq; 5161da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl1=NULL; 5177f0e44acSEric Dumazet struct ipv6_fl_socklist *sfl; 5187f0e44acSEric Dumazet struct ipv6_fl_socklist __rcu **sflp; 51978c2e502SPavel Emelyanov struct ip6_flowlabel *fl, *fl1 = NULL; 52078c2e502SPavel Emelyanov 5211da177e4SLinus Torvalds 5221da177e4SLinus Torvalds if (optlen < sizeof(freq)) 5231da177e4SLinus Torvalds return -EINVAL; 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds if (copy_from_user(&freq, optval, sizeof(freq))) 5261da177e4SLinus Torvalds return -EFAULT; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds switch (freq.flr_action) { 5291da177e4SLinus Torvalds case IPV6_FL_A_PUT: 53018367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 53118367681SYOSHIFUJI Hideaki / 吉藤英明 for (sflp = &np->ipv6_fl_list; 53218367681SYOSHIFUJI Hideaki / 吉藤英明 (sfl = rcu_dereference(*sflp))!=NULL; 53318367681SYOSHIFUJI Hideaki / 吉藤英明 sflp = &sfl->next) { 5341da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5351da177e4SLinus Torvalds if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) 5361da177e4SLinus Torvalds np->flow_label &= ~IPV6_FLOWLABEL_MASK; 53718367681SYOSHIFUJI Hideaki / 吉藤英明 *sflp = rcu_dereference(sfl->next); 53818367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5391da177e4SLinus Torvalds fl_release(sfl->fl); 54018367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 5411da177e4SLinus Torvalds return 0; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds } 54418367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5451da177e4SLinus Torvalds return -ESRCH; 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds case IPV6_FL_A_RENEW: 54818367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 54918367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 5501da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5511da177e4SLinus Torvalds err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); 55218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5531da177e4SLinus Torvalds return err; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds } 55618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5571da177e4SLinus Torvalds 558af31f412SEric W. Biederman if (freq.flr_share == IPV6_FL_S_NONE && 559af31f412SEric W. Biederman ns_capable(net->user_ns, CAP_NET_ADMIN)) { 56060e8fbc4SBenjamin Thery fl = fl_lookup(net, freq.flr_label); 5611da177e4SLinus Torvalds if (fl) { 5621da177e4SLinus Torvalds err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); 5631da177e4SLinus Torvalds fl_release(fl); 5641da177e4SLinus Torvalds return err; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds return -ESRCH; 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds case IPV6_FL_A_GET: 5701da177e4SLinus Torvalds if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) 5711da177e4SLinus Torvalds return -EINVAL; 5721da177e4SLinus Torvalds 573ec0506dbSMaciej Żenczykowski fl = fl_create(net, sk, &freq, optval, optlen, &err); 5741da177e4SLinus Torvalds if (fl == NULL) 5751da177e4SLinus Torvalds return err; 5761da177e4SLinus Torvalds sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds if (freq.flr_label) { 5791da177e4SLinus Torvalds err = -EEXIST; 58018367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 58118367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 5821da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5831da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) { 58418367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5851da177e4SLinus Torvalds goto done; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds fl1 = sfl->fl; 5884ea6a804SYan Zheng atomic_inc(&fl1->users); 5891da177e4SLinus Torvalds break; 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds } 59218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds if (fl1 == NULL) 59560e8fbc4SBenjamin Thery fl1 = fl_lookup(net, freq.flr_label); 5961da177e4SLinus Torvalds if (fl1) { 59778c2e502SPavel Emelyanov recheck: 5981da177e4SLinus Torvalds err = -EEXIST; 5991da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) 6001da177e4SLinus Torvalds goto release; 6011da177e4SLinus Torvalds err = -EPERM; 6021da177e4SLinus Torvalds if (fl1->share == IPV6_FL_S_EXCL || 6031da177e4SLinus Torvalds fl1->share != fl->share || 6044f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_PROCESS) && 6054f82f457SEric W. Biederman (fl1->owner.pid == fl->owner.pid)) || 6064f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_USER) && 6074f82f457SEric W. Biederman uid_eq(fl1->owner.uid, fl->owner.uid))) 6081da177e4SLinus Torvalds goto release; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds err = -ENOMEM; 6111da177e4SLinus Torvalds if (sfl1 == NULL) 6121da177e4SLinus Torvalds goto release; 6131da177e4SLinus Torvalds if (fl->linger > fl1->linger) 6141da177e4SLinus Torvalds fl1->linger = fl->linger; 6151da177e4SLinus Torvalds if ((long)(fl->expires - fl1->expires) > 0) 6161da177e4SLinus Torvalds fl1->expires = fl->expires; 61704028045SPavel Emelyanov fl_link(np, sfl1, fl1); 6181da177e4SLinus Torvalds fl_free(fl); 6191da177e4SLinus Torvalds return 0; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds release: 6221da177e4SLinus Torvalds fl_release(fl1); 6231da177e4SLinus Torvalds goto done; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds err = -ENOENT; 6271da177e4SLinus Torvalds if (!(freq.flr_flags&IPV6_FL_F_CREATE)) 6281da177e4SLinus Torvalds goto done; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds err = -ENOMEM; 6311da177e4SLinus Torvalds if (sfl1 == NULL || (err = mem_check(sk)) != 0) 6321da177e4SLinus Torvalds goto done; 6331da177e4SLinus Torvalds 63460e8fbc4SBenjamin Thery fl1 = fl_intern(net, fl, freq.flr_label); 63578c2e502SPavel Emelyanov if (fl1 != NULL) 63678c2e502SPavel Emelyanov goto recheck; 6371da177e4SLinus Torvalds 6386c94d361SDavid S. Miller if (!freq.flr_label) { 6396c94d361SDavid S. Miller if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, 6406c94d361SDavid S. Miller &fl->label, sizeof(fl->label))) { 6416c94d361SDavid S. Miller /* Intentionally ignore fault. */ 6426c94d361SDavid S. Miller } 6436c94d361SDavid S. Miller } 6441da177e4SLinus Torvalds 64504028045SPavel Emelyanov fl_link(np, sfl1, fl); 6461da177e4SLinus Torvalds return 0; 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds default: 6491da177e4SLinus Torvalds return -EINVAL; 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds done: 6531da177e4SLinus Torvalds fl_free(fl); 6541da177e4SLinus Torvalds kfree(sfl1); 6551da177e4SLinus Torvalds return err; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds struct ip6fl_iter_state { 6615983a3dfSBenjamin Thery struct seq_net_private p; 6624f82f457SEric W. Biederman struct pid_namespace *pid_ns; 6631da177e4SLinus Torvalds int bucket; 6641da177e4SLinus Torvalds }; 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds #define ip6fl_seq_private(seq) ((struct ip6fl_iter_state *)(seq)->private) 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) 6691da177e4SLinus Torvalds { 6701da177e4SLinus Torvalds struct ip6_flowlabel *fl = NULL; 6711da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 6725983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { 675d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 676d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 677d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 6781da177e4SLinus Torvalds } 679d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 680d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 681d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 6821da177e4SLinus Torvalds return fl; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 6851da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl) 6861da177e4SLinus Torvalds { 6871da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 6885983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 6891da177e4SLinus Torvalds 690d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_continue_rcu(fl) { 691d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 692d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 6931da177e4SLinus Torvalds } 694d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 695d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again: 696d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (++state->bucket <= FL_HASH_MASK) { 697d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 698d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 699d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 700d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 701d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto try_again; 702d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 703d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 704d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 705d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7061da177e4SLinus Torvalds return fl; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos) 7101da177e4SLinus Torvalds { 7111da177e4SLinus Torvalds struct ip6_flowlabel *fl = ip6fl_get_first(seq); 7121da177e4SLinus Torvalds if (fl) 7131da177e4SLinus Torvalds while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL) 7141da177e4SLinus Torvalds --pos; 7151da177e4SLinus Torvalds return pos ? NULL : fl; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos) 719d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __acquires(RCU) 7201da177e4SLinus Torvalds { 721d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 7221da177e4SLinus Torvalds return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 7231da177e4SLinus Torvalds } 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos) 7261da177e4SLinus Torvalds { 7271da177e4SLinus Torvalds struct ip6_flowlabel *fl; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 7301da177e4SLinus Torvalds fl = ip6fl_get_first(seq); 7311da177e4SLinus Torvalds else 7321da177e4SLinus Torvalds fl = ip6fl_get_next(seq, v); 7331da177e4SLinus Torvalds ++*pos; 7341da177e4SLinus Torvalds return fl; 7351da177e4SLinus Torvalds } 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v) 738d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __releases(RCU) 7391da177e4SLinus Torvalds { 740d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 7411da177e4SLinus Torvalds } 7421da177e4SLinus Torvalds 7431b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v) 7441da177e4SLinus Torvalds { 7454f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7461b7c2dbcSJames Morris if (v == SEQ_START_TOKEN) 7471b7c2dbcSJames Morris seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n", 7481b7c2dbcSJames Morris "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt"); 7491b7c2dbcSJames Morris else { 7501b7c2dbcSJames Morris struct ip6_flowlabel *fl = v; 7511da177e4SLinus Torvalds seq_printf(seq, 7524b7a4274SHarvey Harrison "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", 75395c96174SEric Dumazet (unsigned int)ntohl(fl->label), 7541da177e4SLinus Torvalds fl->share, 7554f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_PROCESS) ? 7564f82f457SEric W. Biederman pid_nr_ns(fl->owner.pid, state->pid_ns) : 7574f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_USER) ? 7584f82f457SEric W. Biederman from_kuid_munged(seq_user_ns(seq), fl->owner.uid) : 7594f82f457SEric W. Biederman 0)), 7601da177e4SLinus Torvalds atomic_read(&fl->users), 7611da177e4SLinus Torvalds fl->linger/HZ, 7621da177e4SLinus Torvalds (long)(fl->expires - jiffies)/HZ, 763b071195dSHarvey Harrison &fl->dst, 7641da177e4SLinus Torvalds fl->opt ? fl->opt->opt_nflen : 0); 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds return 0; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 76956b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = { 7701da177e4SLinus Torvalds .start = ip6fl_seq_start, 7711da177e4SLinus Torvalds .next = ip6fl_seq_next, 7721da177e4SLinus Torvalds .stop = ip6fl_seq_stop, 7731da177e4SLinus Torvalds .show = ip6fl_seq_show, 7741da177e4SLinus Torvalds }; 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds static int ip6fl_seq_open(struct inode *inode, struct file *file) 7771da177e4SLinus Torvalds { 7784f82f457SEric W. Biederman struct seq_file *seq; 7794f82f457SEric W. Biederman struct ip6fl_iter_state *state; 7804f82f457SEric W. Biederman int err; 7814f82f457SEric W. Biederman 7824f82f457SEric W. Biederman err = seq_open_net(inode, file, &ip6fl_seq_ops, 783cf7732e4SPavel Emelyanov sizeof(struct ip6fl_iter_state)); 7844f82f457SEric W. Biederman 7854f82f457SEric W. Biederman if (!err) { 7864f82f457SEric W. Biederman seq = file->private_data; 7874f82f457SEric W. Biederman state = ip6fl_seq_private(seq); 7884f82f457SEric W. Biederman rcu_read_lock(); 7894f82f457SEric W. Biederman state->pid_ns = get_pid_ns(task_active_pid_ns(current)); 7904f82f457SEric W. Biederman rcu_read_unlock(); 7914f82f457SEric W. Biederman } 7924f82f457SEric W. Biederman return err; 7934f82f457SEric W. Biederman } 7944f82f457SEric W. Biederman 7954f82f457SEric W. Biederman static int ip6fl_seq_release(struct inode *inode, struct file *file) 7964f82f457SEric W. Biederman { 7974f82f457SEric W. Biederman struct seq_file *seq = file->private_data; 7984f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7994f82f457SEric W. Biederman put_pid_ns(state->pid_ns); 8004f82f457SEric W. Biederman return seq_release_net(inode, file); 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 8039a32144eSArjan van de Ven static const struct file_operations ip6fl_seq_fops = { 8041da177e4SLinus Torvalds .owner = THIS_MODULE, 8051da177e4SLinus Torvalds .open = ip6fl_seq_open, 8061da177e4SLinus Torvalds .read = seq_read, 8071da177e4SLinus Torvalds .llseek = seq_lseek, 8084f82f457SEric W. Biederman .release = ip6fl_seq_release, 8091da177e4SLinus Torvalds }; 8101da177e4SLinus Torvalds 8112c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net) 8121da177e4SLinus Torvalds { 813d4beaa66SGao feng if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net, 814d4beaa66SGao feng &ip6fl_seq_fops)) 8150a3e78acSDaniel Lezcano return -ENOMEM; 8160a3e78acSDaniel Lezcano return 0; 8170a3e78acSDaniel Lezcano } 8180a3e78acSDaniel Lezcano 8192c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net) 8200a3e78acSDaniel Lezcano { 821ece31ffdSGao feng remove_proc_entry("ip6_flowlabel", net->proc_net); 8220a3e78acSDaniel Lezcano } 8230a3e78acSDaniel Lezcano #else 8240a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net) 8250a3e78acSDaniel Lezcano { 8260a3e78acSDaniel Lezcano return 0; 8270a3e78acSDaniel Lezcano } 8280a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net) 8290a3e78acSDaniel Lezcano { 8300a3e78acSDaniel Lezcano } 8311da177e4SLinus Torvalds #endif 8320a3e78acSDaniel Lezcano 8332c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net) 83460e8fbc4SBenjamin Thery { 83560e8fbc4SBenjamin Thery ip6_fl_purge(net); 8365983a3dfSBenjamin Thery ip6_flowlabel_proc_fini(net); 83760e8fbc4SBenjamin Thery } 83860e8fbc4SBenjamin Thery 83960e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = { 8405983a3dfSBenjamin Thery .init = ip6_flowlabel_proc_init, 84160e8fbc4SBenjamin Thery .exit = ip6_flowlabel_net_exit, 84260e8fbc4SBenjamin Thery }; 84360e8fbc4SBenjamin Thery 8440a3e78acSDaniel Lezcano int ip6_flowlabel_init(void) 8450a3e78acSDaniel Lezcano { 8465983a3dfSBenjamin Thery return register_pernet_subsys(&ip6_flowlabel_net_ops); 8471da177e4SLinus Torvalds } 8481da177e4SLinus Torvalds 8491da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void) 8501da177e4SLinus Torvalds { 8511da177e4SLinus Torvalds del_timer(&ip6_fl_gc_timer); 85260e8fbc4SBenjamin Thery unregister_pernet_subsys(&ip6_flowlabel_net_ops); 8531da177e4SLinus Torvalds } 854