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 */ 441da177e4SLinus Torvalds #define FL_MAX_LINGER 60 /* 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++) { 1471da177e4SLinus Torvalds struct ip6_flowlabel *fl, **flp; 1481da177e4SLinus Torvalds flp = &fl_ht[i]; 149d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 150d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 1511da177e4SLinus Torvalds if (atomic_read(&fl->users) == 0) { 1521da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1531da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1541da177e4SLinus Torvalds fl->expires = ttd; 1551da177e4SLinus Torvalds ttd = fl->expires; 1561da177e4SLinus Torvalds if (time_after_eq(now, ttd)) { 1571da177e4SLinus Torvalds *flp = fl->next; 1581da177e4SLinus Torvalds fl_free(fl); 1591da177e4SLinus Torvalds atomic_dec(&fl_size); 1601da177e4SLinus Torvalds continue; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds if (!sched || time_before(ttd, sched)) 1631da177e4SLinus Torvalds sched = ttd; 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds flp = &fl->next; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds if (!sched && atomic_read(&fl_size)) 1691da177e4SLinus Torvalds sched = now + FL_MAX_LINGER; 1701da177e4SLinus Torvalds if (sched) { 17160e8fbc4SBenjamin Thery mod_timer(&ip6_fl_gc_timer, sched); 1721da177e4SLinus Torvalds } 173d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 1762c8c1e72SAlexey Dobriyan static void __net_exit ip6_fl_purge(struct net *net) 17760e8fbc4SBenjamin Thery { 17860e8fbc4SBenjamin Thery int i; 17960e8fbc4SBenjamin Thery 180d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock(&ip6_fl_lock); 18160e8fbc4SBenjamin Thery for (i = 0; i <= FL_HASH_MASK; i++) { 18260e8fbc4SBenjamin Thery struct ip6_flowlabel *fl, **flp; 18360e8fbc4SBenjamin Thery flp = &fl_ht[i]; 184d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 185d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 18609ad9bc7SOctavian Purdila if (net_eq(fl->fl_net, net) && 18709ad9bc7SOctavian Purdila atomic_read(&fl->users) == 0) { 18860e8fbc4SBenjamin Thery *flp = fl->next; 18960e8fbc4SBenjamin Thery fl_free(fl); 19060e8fbc4SBenjamin Thery atomic_dec(&fl_size); 19160e8fbc4SBenjamin Thery continue; 19260e8fbc4SBenjamin Thery } 19360e8fbc4SBenjamin Thery flp = &fl->next; 19460e8fbc4SBenjamin Thery } 19560e8fbc4SBenjamin Thery } 196d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 19760e8fbc4SBenjamin Thery } 19860e8fbc4SBenjamin Thery 19960e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_intern(struct net *net, 20060e8fbc4SBenjamin Thery struct ip6_flowlabel *fl, __be32 label) 2011da177e4SLinus Torvalds { 20278c2e502SPavel Emelyanov struct ip6_flowlabel *lfl; 20378c2e502SPavel Emelyanov 2041da177e4SLinus Torvalds fl->label = label & IPV6_FLOWLABEL_MASK; 2051da177e4SLinus Torvalds 206d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 2071da177e4SLinus Torvalds if (label == 0) { 2081da177e4SLinus Torvalds for (;;) { 2091da177e4SLinus Torvalds fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; 2101da177e4SLinus Torvalds if (fl->label) { 21160e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 2121da177e4SLinus Torvalds if (lfl == NULL) 2131da177e4SLinus Torvalds break; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds } 21678c2e502SPavel Emelyanov } else { 21778c2e502SPavel Emelyanov /* 21878c2e502SPavel Emelyanov * we dropper the ip6_fl_lock, so this entry could reappear 21978c2e502SPavel Emelyanov * and we need to recheck with it. 22078c2e502SPavel Emelyanov * 22178c2e502SPavel Emelyanov * OTOH no need to search the active socket first, like it is 22278c2e502SPavel Emelyanov * done in ipv6_flowlabel_opt - sock is locked, so new entry 22378c2e502SPavel Emelyanov * with the same label can only appear on another sock 22478c2e502SPavel Emelyanov */ 22560e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 22678c2e502SPavel Emelyanov if (lfl != NULL) { 22778c2e502SPavel Emelyanov atomic_inc(&lfl->users); 228d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 22978c2e502SPavel Emelyanov return lfl; 23078c2e502SPavel Emelyanov } 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds fl->lastuse = jiffies; 2341da177e4SLinus Torvalds fl->next = fl_ht[FL_HASH(fl->label)]; 235d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); 2361da177e4SLinus Torvalds atomic_inc(&fl_size); 237d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 23878c2e502SPavel Emelyanov return NULL; 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds /* Socket flowlabel lists */ 2441da177e4SLinus Torvalds 24590bcaf7bSAl Viro struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label) 2461da177e4SLinus Torvalds { 2471da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2481da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds label &= IPV6_FLOWLABEL_MASK; 2511da177e4SLinus Torvalds 25218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 25318367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 2541da177e4SLinus Torvalds struct ip6_flowlabel *fl = sfl->fl; 2551da177e4SLinus Torvalds if (fl->label == label) { 2561da177e4SLinus Torvalds fl->lastuse = jiffies; 2571da177e4SLinus Torvalds atomic_inc(&fl->users); 25818367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2591da177e4SLinus Torvalds return fl; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds } 26218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2631da177e4SLinus Torvalds return NULL; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2663cf3dc6cSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(fl6_sock_lookup); 2673cf3dc6cSArnaldo Carvalho de Melo 2681da177e4SLinus Torvalds void fl6_free_socklist(struct sock *sk) 2691da177e4SLinus Torvalds { 2701da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2711da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2721da177e4SLinus Torvalds 27318367681SYOSHIFUJI Hideaki / 吉藤英明 if (!rcu_access_pointer(np->ipv6_fl_list)) 274f256dc59SYOSHIFUJI Hideaki / 吉藤英明 return; 275f256dc59SYOSHIFUJI Hideaki / 吉藤英明 27618367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 27718367681SYOSHIFUJI Hideaki / 吉藤英明 while ((sfl = rcu_dereference_protected(np->ipv6_fl_list, 27818367681SYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_sk_fl_lock))) != NULL) { 27918367681SYOSHIFUJI Hideaki / 吉藤英明 np->ipv6_fl_list = sfl->next; 28018367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 281f256dc59SYOSHIFUJI Hideaki / 吉藤英明 2821da177e4SLinus Torvalds fl_release(sfl->fl); 28318367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 28418367681SYOSHIFUJI Hideaki / 吉藤英明 28518367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 2861da177e4SLinus Torvalds } 28718367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds /* Service routines */ 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds /* 2941da177e4SLinus Torvalds It is the only difficult place. flowlabel enforces equal headers 2951da177e4SLinus Torvalds before and including routing header, however user may supply options 2961da177e4SLinus Torvalds following rthdr. 2971da177e4SLinus Torvalds */ 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, 3001da177e4SLinus Torvalds struct ip6_flowlabel * fl, 3011da177e4SLinus Torvalds struct ipv6_txoptions * fopt) 3021da177e4SLinus Torvalds { 303df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions * fl_opt = fl->opt; 3041da177e4SLinus Torvalds 305df9890c3SYOSHIFUJI Hideaki if (fopt == NULL || fopt->opt_flen == 0) 3061da177e4SLinus Torvalds return fl_opt; 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds if (fl_opt != NULL) { 3091da177e4SLinus Torvalds opt_space->hopopt = fl_opt->hopopt; 310df9890c3SYOSHIFUJI Hideaki opt_space->dst0opt = fl_opt->dst0opt; 3111da177e4SLinus Torvalds opt_space->srcrt = fl_opt->srcrt; 3121da177e4SLinus Torvalds opt_space->opt_nflen = fl_opt->opt_nflen; 3131da177e4SLinus Torvalds } else { 3141da177e4SLinus Torvalds if (fopt->opt_nflen == 0) 3151da177e4SLinus Torvalds return fopt; 3161da177e4SLinus Torvalds opt_space->hopopt = NULL; 3171da177e4SLinus Torvalds opt_space->dst0opt = NULL; 3181da177e4SLinus Torvalds opt_space->srcrt = NULL; 3191da177e4SLinus Torvalds opt_space->opt_nflen = 0; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds opt_space->dst1opt = fopt->dst1opt; 3221da177e4SLinus Torvalds opt_space->opt_flen = fopt->opt_flen; 3231da177e4SLinus Torvalds return opt_space; 3241da177e4SLinus Torvalds } 325a495f836SChris Elston EXPORT_SYMBOL_GPL(fl6_merge_options); 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds static unsigned long check_linger(unsigned long ttl) 3281da177e4SLinus Torvalds { 3291da177e4SLinus Torvalds if (ttl < FL_MIN_LINGER) 3301da177e4SLinus Torvalds return FL_MIN_LINGER*HZ; 3311da177e4SLinus Torvalds if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN)) 3321da177e4SLinus Torvalds return 0; 3331da177e4SLinus Torvalds return ttl*HZ; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds linger = check_linger(linger); 3391da177e4SLinus Torvalds if (!linger) 3401da177e4SLinus Torvalds return -EPERM; 3411da177e4SLinus Torvalds expires = check_linger(expires); 3421da177e4SLinus Torvalds if (!expires) 3431da177e4SLinus Torvalds return -EPERM; 3441da177e4SLinus Torvalds fl->lastuse = jiffies; 3451da177e4SLinus Torvalds if (time_before(fl->linger, linger)) 3461da177e4SLinus Torvalds fl->linger = linger; 3471da177e4SLinus Torvalds if (time_before(expires, fl->linger)) 3481da177e4SLinus Torvalds expires = fl->linger; 3491da177e4SLinus Torvalds if (time_before(fl->expires, fl->lastuse + expires)) 3501da177e4SLinus Torvalds fl->expires = fl->lastuse + expires; 3511da177e4SLinus Torvalds return 0; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds static struct ip6_flowlabel * 355ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, 356ec0506dbSMaciej Żenczykowski char __user *optval, int optlen, int *err_p) 3571da177e4SLinus Torvalds { 358684de409SDavid S. Miller struct ip6_flowlabel *fl = NULL; 3591da177e4SLinus Torvalds int olen; 3601da177e4SLinus Torvalds int addr_type; 3611da177e4SLinus Torvalds int err; 3621da177e4SLinus Torvalds 363684de409SDavid S. Miller olen = optlen - CMSG_ALIGN(sizeof(*freq)); 364684de409SDavid S. Miller err = -EINVAL; 365684de409SDavid S. Miller if (olen > 64 * 1024) 366684de409SDavid S. Miller goto done; 367684de409SDavid S. Miller 3681da177e4SLinus Torvalds err = -ENOMEM; 3690c600edaSIngo Oeser fl = kzalloc(sizeof(*fl), GFP_KERNEL); 3701da177e4SLinus Torvalds if (fl == NULL) 3711da177e4SLinus Torvalds goto done; 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds if (olen > 0) { 3741da177e4SLinus Torvalds struct msghdr msg; 3754c9483b2SDavid S. Miller struct flowi6 flowi6; 3761da177e4SLinus Torvalds int junk; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds err = -ENOMEM; 3791da177e4SLinus Torvalds fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL); 3801da177e4SLinus Torvalds if (fl->opt == NULL) 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, 39473df66f8STom Parkin &junk, &junk, &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 40660e8fbc4SBenjamin Thery fl->fl_net = hold_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 45218367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) 4531da177e4SLinus Torvalds count++; 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds if (room <= 0 || 4561da177e4SLinus Torvalds ((count >= FL_MAX_PER_SOCK || 45735700212SJoe Perches (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && 45835700212SJoe Perches !capable(CAP_NET_ADMIN))) 4591da177e4SLinus Torvalds return -ENOBUFS; 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds return 0; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 464a50feda5SEric Dumazet static bool ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2) 4651da177e4SLinus Torvalds { 4661da177e4SLinus Torvalds if (h1 == h2) 467a50feda5SEric Dumazet return false; 4681da177e4SLinus Torvalds if (h1 == NULL || h2 == NULL) 469a50feda5SEric Dumazet return true; 4701da177e4SLinus Torvalds if (h1->hdrlen != h2->hdrlen) 471a50feda5SEric Dumazet return true; 4721da177e4SLinus Torvalds return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1)); 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 475a50feda5SEric Dumazet static bool ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2) 4761da177e4SLinus Torvalds { 4771da177e4SLinus Torvalds if (o1 == o2) 478a50feda5SEric Dumazet return false; 4791da177e4SLinus Torvalds if (o1 == NULL || o2 == NULL) 480a50feda5SEric Dumazet return true; 4811da177e4SLinus Torvalds if (o1->opt_nflen != o2->opt_nflen) 482a50feda5SEric Dumazet return true; 4831da177e4SLinus Torvalds if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt)) 484a50feda5SEric Dumazet return true; 4851da177e4SLinus Torvalds if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt)) 486a50feda5SEric Dumazet return true; 4871da177e4SLinus Torvalds if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt)) 488a50feda5SEric Dumazet return true; 489a50feda5SEric Dumazet return false; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49204028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, 49304028045SPavel Emelyanov struct ip6_flowlabel *fl) 49404028045SPavel Emelyanov { 49518367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 49604028045SPavel Emelyanov sfl->fl = fl; 49704028045SPavel Emelyanov sfl->next = np->ipv6_fl_list; 49818367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(np->ipv6_fl_list, sfl); 49918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 50004028045SPavel Emelyanov } 50104028045SPavel Emelyanov 5021da177e4SLinus Torvalds int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 5031da177e4SLinus Torvalds { 50455205d40SIngo Molnar int uninitialized_var(err); 50560e8fbc4SBenjamin Thery struct net *net = sock_net(sk); 5061da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 5071da177e4SLinus Torvalds struct in6_flowlabel_req freq; 5081da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl1=NULL; 5091da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl, **sflp; 51078c2e502SPavel Emelyanov struct ip6_flowlabel *fl, *fl1 = NULL; 51178c2e502SPavel Emelyanov 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds if (optlen < sizeof(freq)) 5141da177e4SLinus Torvalds return -EINVAL; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds if (copy_from_user(&freq, optval, sizeof(freq))) 5171da177e4SLinus Torvalds return -EFAULT; 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds switch (freq.flr_action) { 5201da177e4SLinus Torvalds case IPV6_FL_A_PUT: 52118367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 52218367681SYOSHIFUJI Hideaki / 吉藤英明 for (sflp = &np->ipv6_fl_list; 52318367681SYOSHIFUJI Hideaki / 吉藤英明 (sfl = rcu_dereference(*sflp))!=NULL; 52418367681SYOSHIFUJI Hideaki / 吉藤英明 sflp = &sfl->next) { 5251da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5261da177e4SLinus Torvalds if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) 5271da177e4SLinus Torvalds np->flow_label &= ~IPV6_FLOWLABEL_MASK; 52818367681SYOSHIFUJI Hideaki / 吉藤英明 *sflp = rcu_dereference(sfl->next); 52918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5301da177e4SLinus Torvalds fl_release(sfl->fl); 53118367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 5321da177e4SLinus Torvalds return 0; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds } 53518367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5361da177e4SLinus Torvalds return -ESRCH; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds case IPV6_FL_A_RENEW: 53918367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 54018367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 5411da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5421da177e4SLinus Torvalds err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); 54318367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5441da177e4SLinus Torvalds return err; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds } 54718367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5481da177e4SLinus Torvalds 549af31f412SEric W. Biederman if (freq.flr_share == IPV6_FL_S_NONE && 550af31f412SEric W. Biederman ns_capable(net->user_ns, CAP_NET_ADMIN)) { 55160e8fbc4SBenjamin Thery fl = fl_lookup(net, freq.flr_label); 5521da177e4SLinus Torvalds if (fl) { 5531da177e4SLinus Torvalds err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); 5541da177e4SLinus Torvalds fl_release(fl); 5551da177e4SLinus Torvalds return err; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds return -ESRCH; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds case IPV6_FL_A_GET: 5611da177e4SLinus Torvalds if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) 5621da177e4SLinus Torvalds return -EINVAL; 5631da177e4SLinus Torvalds 564ec0506dbSMaciej Żenczykowski fl = fl_create(net, sk, &freq, optval, optlen, &err); 5651da177e4SLinus Torvalds if (fl == NULL) 5661da177e4SLinus Torvalds return err; 5671da177e4SLinus Torvalds sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds if (freq.flr_label) { 5701da177e4SLinus Torvalds err = -EEXIST; 57118367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 57218367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 5731da177e4SLinus Torvalds if (sfl->fl->label == freq.flr_label) { 5741da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) { 57518367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5761da177e4SLinus Torvalds goto done; 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds fl1 = sfl->fl; 5794ea6a804SYan Zheng atomic_inc(&fl1->users); 5801da177e4SLinus Torvalds break; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds } 58318367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds if (fl1 == NULL) 58660e8fbc4SBenjamin Thery fl1 = fl_lookup(net, freq.flr_label); 5871da177e4SLinus Torvalds if (fl1) { 58878c2e502SPavel Emelyanov recheck: 5891da177e4SLinus Torvalds err = -EEXIST; 5901da177e4SLinus Torvalds if (freq.flr_flags&IPV6_FL_F_EXCL) 5911da177e4SLinus Torvalds goto release; 5921da177e4SLinus Torvalds err = -EPERM; 5931da177e4SLinus Torvalds if (fl1->share == IPV6_FL_S_EXCL || 5941da177e4SLinus Torvalds fl1->share != fl->share || 5954f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_PROCESS) && 5964f82f457SEric W. Biederman (fl1->owner.pid == fl->owner.pid)) || 5974f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_USER) && 5984f82f457SEric W. Biederman uid_eq(fl1->owner.uid, fl->owner.uid))) 5991da177e4SLinus Torvalds goto release; 6001da177e4SLinus Torvalds 6011da177e4SLinus Torvalds err = -EINVAL; 6021da177e4SLinus Torvalds if (!ipv6_addr_equal(&fl1->dst, &fl->dst) || 6031da177e4SLinus Torvalds ipv6_opt_cmp(fl1->opt, fl->opt)) 6041da177e4SLinus Torvalds goto release; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds err = -ENOMEM; 6071da177e4SLinus Torvalds if (sfl1 == NULL) 6081da177e4SLinus Torvalds goto release; 6091da177e4SLinus Torvalds if (fl->linger > fl1->linger) 6101da177e4SLinus Torvalds fl1->linger = fl->linger; 6111da177e4SLinus Torvalds if ((long)(fl->expires - fl1->expires) > 0) 6121da177e4SLinus Torvalds fl1->expires = fl->expires; 61304028045SPavel Emelyanov fl_link(np, sfl1, fl1); 6141da177e4SLinus Torvalds fl_free(fl); 6151da177e4SLinus Torvalds return 0; 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds release: 6181da177e4SLinus Torvalds fl_release(fl1); 6191da177e4SLinus Torvalds goto done; 6201da177e4SLinus Torvalds } 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds err = -ENOENT; 6231da177e4SLinus Torvalds if (!(freq.flr_flags&IPV6_FL_F_CREATE)) 6241da177e4SLinus Torvalds goto done; 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds err = -ENOMEM; 6271da177e4SLinus Torvalds if (sfl1 == NULL || (err = mem_check(sk)) != 0) 6281da177e4SLinus Torvalds goto done; 6291da177e4SLinus Torvalds 63060e8fbc4SBenjamin Thery fl1 = fl_intern(net, fl, freq.flr_label); 63178c2e502SPavel Emelyanov if (fl1 != NULL) 63278c2e502SPavel Emelyanov goto recheck; 6331da177e4SLinus Torvalds 6346c94d361SDavid S. Miller if (!freq.flr_label) { 6356c94d361SDavid S. Miller if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, 6366c94d361SDavid S. Miller &fl->label, sizeof(fl->label))) { 6376c94d361SDavid S. Miller /* Intentionally ignore fault. */ 6386c94d361SDavid S. Miller } 6396c94d361SDavid S. Miller } 6401da177e4SLinus Torvalds 64104028045SPavel Emelyanov fl_link(np, sfl1, fl); 6421da177e4SLinus Torvalds return 0; 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds default: 6451da177e4SLinus Torvalds return -EINVAL; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds done: 6491da177e4SLinus Torvalds fl_free(fl); 6501da177e4SLinus Torvalds kfree(sfl1); 6511da177e4SLinus Torvalds return err; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds struct ip6fl_iter_state { 6575983a3dfSBenjamin Thery struct seq_net_private p; 6584f82f457SEric W. Biederman struct pid_namespace *pid_ns; 6591da177e4SLinus Torvalds int bucket; 6601da177e4SLinus Torvalds }; 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds #define ip6fl_seq_private(seq) ((struct ip6fl_iter_state *)(seq)->private) 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) 6651da177e4SLinus Torvalds { 6661da177e4SLinus Torvalds struct ip6_flowlabel *fl = NULL; 6671da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 6685983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { 671d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 672d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 673d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 6741da177e4SLinus Torvalds } 675d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 676d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 677d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 6781da177e4SLinus Torvalds return fl; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl) 6821da177e4SLinus Torvalds { 6831da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 6845983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 6851da177e4SLinus Torvalds 686d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_continue_rcu(fl) { 687d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 688d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 6891da177e4SLinus Torvalds } 690d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 691d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again: 692d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (++state->bucket <= FL_HASH_MASK) { 693d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 694d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 695d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 696d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 697d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto try_again; 698d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 699d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 700d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 701d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7021da177e4SLinus Torvalds return fl; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds struct ip6_flowlabel *fl = ip6fl_get_first(seq); 7081da177e4SLinus Torvalds if (fl) 7091da177e4SLinus Torvalds while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL) 7101da177e4SLinus Torvalds --pos; 7111da177e4SLinus Torvalds return pos ? NULL : fl; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos) 715d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __acquires(RCU) 7161da177e4SLinus Torvalds { 717d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 7181da177e4SLinus Torvalds return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 7191da177e4SLinus Torvalds } 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos) 7221da177e4SLinus Torvalds { 7231da177e4SLinus Torvalds struct ip6_flowlabel *fl; 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 7261da177e4SLinus Torvalds fl = ip6fl_get_first(seq); 7271da177e4SLinus Torvalds else 7281da177e4SLinus Torvalds fl = ip6fl_get_next(seq, v); 7291da177e4SLinus Torvalds ++*pos; 7301da177e4SLinus Torvalds return fl; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v) 734d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __releases(RCU) 7351da177e4SLinus Torvalds { 736d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds 7391b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v) 7401da177e4SLinus Torvalds { 7414f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7421b7c2dbcSJames Morris if (v == SEQ_START_TOKEN) 7431b7c2dbcSJames Morris seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n", 7441b7c2dbcSJames Morris "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt"); 7451b7c2dbcSJames Morris else { 7461b7c2dbcSJames Morris struct ip6_flowlabel *fl = v; 7471da177e4SLinus Torvalds seq_printf(seq, 7484b7a4274SHarvey Harrison "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", 74995c96174SEric Dumazet (unsigned int)ntohl(fl->label), 7501da177e4SLinus Torvalds fl->share, 7514f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_PROCESS) ? 7524f82f457SEric W. Biederman pid_nr_ns(fl->owner.pid, state->pid_ns) : 7534f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_USER) ? 7544f82f457SEric W. Biederman from_kuid_munged(seq_user_ns(seq), fl->owner.uid) : 7554f82f457SEric W. Biederman 0)), 7561da177e4SLinus Torvalds atomic_read(&fl->users), 7571da177e4SLinus Torvalds fl->linger/HZ, 7581da177e4SLinus Torvalds (long)(fl->expires - jiffies)/HZ, 759b071195dSHarvey Harrison &fl->dst, 7601da177e4SLinus Torvalds fl->opt ? fl->opt->opt_nflen : 0); 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds return 0; 7631da177e4SLinus Torvalds } 7641da177e4SLinus Torvalds 76556b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = { 7661da177e4SLinus Torvalds .start = ip6fl_seq_start, 7671da177e4SLinus Torvalds .next = ip6fl_seq_next, 7681da177e4SLinus Torvalds .stop = ip6fl_seq_stop, 7691da177e4SLinus Torvalds .show = ip6fl_seq_show, 7701da177e4SLinus Torvalds }; 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds static int ip6fl_seq_open(struct inode *inode, struct file *file) 7731da177e4SLinus Torvalds { 7744f82f457SEric W. Biederman struct seq_file *seq; 7754f82f457SEric W. Biederman struct ip6fl_iter_state *state; 7764f82f457SEric W. Biederman int err; 7774f82f457SEric W. Biederman 7784f82f457SEric W. Biederman err = seq_open_net(inode, file, &ip6fl_seq_ops, 779cf7732e4SPavel Emelyanov sizeof(struct ip6fl_iter_state)); 7804f82f457SEric W. Biederman 7814f82f457SEric W. Biederman if (!err) { 7824f82f457SEric W. Biederman seq = file->private_data; 7834f82f457SEric W. Biederman state = ip6fl_seq_private(seq); 7844f82f457SEric W. Biederman rcu_read_lock(); 7854f82f457SEric W. Biederman state->pid_ns = get_pid_ns(task_active_pid_ns(current)); 7864f82f457SEric W. Biederman rcu_read_unlock(); 7874f82f457SEric W. Biederman } 7884f82f457SEric W. Biederman return err; 7894f82f457SEric W. Biederman } 7904f82f457SEric W. Biederman 7914f82f457SEric W. Biederman static int ip6fl_seq_release(struct inode *inode, struct file *file) 7924f82f457SEric W. Biederman { 7934f82f457SEric W. Biederman struct seq_file *seq = file->private_data; 7944f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7954f82f457SEric W. Biederman put_pid_ns(state->pid_ns); 7964f82f457SEric W. Biederman return seq_release_net(inode, file); 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 7999a32144eSArjan van de Ven static const struct file_operations ip6fl_seq_fops = { 8001da177e4SLinus Torvalds .owner = THIS_MODULE, 8011da177e4SLinus Torvalds .open = ip6fl_seq_open, 8021da177e4SLinus Torvalds .read = seq_read, 8031da177e4SLinus Torvalds .llseek = seq_lseek, 8044f82f457SEric W. Biederman .release = ip6fl_seq_release, 8051da177e4SLinus Torvalds }; 8061da177e4SLinus Torvalds 8072c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net) 8081da177e4SLinus Torvalds { 809d4beaa66SGao feng if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net, 810d4beaa66SGao feng &ip6fl_seq_fops)) 8110a3e78acSDaniel Lezcano return -ENOMEM; 8120a3e78acSDaniel Lezcano return 0; 8130a3e78acSDaniel Lezcano } 8140a3e78acSDaniel Lezcano 8152c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net) 8160a3e78acSDaniel Lezcano { 8170a3e78acSDaniel Lezcano proc_net_remove(net, "ip6_flowlabel"); 8180a3e78acSDaniel Lezcano } 8190a3e78acSDaniel Lezcano #else 8200a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net) 8210a3e78acSDaniel Lezcano { 8220a3e78acSDaniel Lezcano return 0; 8230a3e78acSDaniel Lezcano } 8240a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net) 8250a3e78acSDaniel Lezcano { 8260a3e78acSDaniel Lezcano } 8271da177e4SLinus Torvalds #endif 8280a3e78acSDaniel Lezcano 8292c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net) 83060e8fbc4SBenjamin Thery { 83160e8fbc4SBenjamin Thery ip6_fl_purge(net); 8325983a3dfSBenjamin Thery ip6_flowlabel_proc_fini(net); 83360e8fbc4SBenjamin Thery } 83460e8fbc4SBenjamin Thery 83560e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = { 8365983a3dfSBenjamin Thery .init = ip6_flowlabel_proc_init, 83760e8fbc4SBenjamin Thery .exit = ip6_flowlabel_net_exit, 83860e8fbc4SBenjamin Thery }; 83960e8fbc4SBenjamin Thery 8400a3e78acSDaniel Lezcano int ip6_flowlabel_init(void) 8410a3e78acSDaniel Lezcano { 8425983a3dfSBenjamin Thery return register_pernet_subsys(&ip6_flowlabel_net_ops); 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void) 8461da177e4SLinus Torvalds { 8471da177e4SLinus Torvalds del_timer(&ip6_fl_gc_timer); 84860e8fbc4SBenjamin Thery unregister_pernet_subsys(&ip6_flowlabel_net_ops); 8491da177e4SLinus Torvalds } 850