12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * ip6_flowlabel.c IPv6 flowlabel manager. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 84fc268d2SRandy Dunlap #include <linux/capability.h> 91da177e4SLinus Torvalds #include <linux/errno.h> 101da177e4SLinus Torvalds #include <linux/types.h> 111da177e4SLinus Torvalds #include <linux/socket.h> 121da177e4SLinus Torvalds #include <linux/net.h> 131da177e4SLinus Torvalds #include <linux/netdevice.h> 141da177e4SLinus Torvalds #include <linux/in6.h> 151da177e4SLinus Torvalds #include <linux/proc_fs.h> 161da177e4SLinus Torvalds #include <linux/seq_file.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 18bc3b2d7fSPaul Gortmaker #include <linux/export.h> 194f82f457SEric W. Biederman #include <linux/pid_namespace.h> 2059c820b2SWillem de Bruijn #include <linux/jump_label_ratelimit.h> 211da177e4SLinus Torvalds 22457c4cbcSEric W. Biederman #include <net/net_namespace.h> 231da177e4SLinus Torvalds #include <net/sock.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include <net/ipv6.h> 261da177e4SLinus Torvalds #include <net/rawv6.h> 271da177e4SLinus Torvalds #include <net/transp_v6.h> 281da177e4SLinus Torvalds 297c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define FL_MIN_LINGER 6 /* Minimal linger. It is set to 6sec specified 321da177e4SLinus Torvalds in old IPv6 RFC. Well, it was reasonable value. 331da177e4SLinus Torvalds */ 3453b47106SFlorent Fourcot #define FL_MAX_LINGER 150 /* Maximal linger timeout */ 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds /* FL hash table */ 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds #define FL_MAX_PER_SOCK 32 391da177e4SLinus Torvalds #define FL_MAX_SIZE 4096 401da177e4SLinus Torvalds #define FL_HASH_MASK 255 411da177e4SLinus Torvalds #define FL_HASH(l) (ntohl(l)&FL_HASH_MASK) 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds static atomic_t fl_size = ATOMIC_INIT(0); 44d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1]; 451da177e4SLinus Torvalds 4624ed960aSKees Cook static void ip6_fl_gc(struct timer_list *unused); 471d27e3e2SKees Cook static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc); 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* FL hash table lock: it protects only of GC */ 501da177e4SLinus Torvalds 51d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_fl_lock); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds /* Big socket sock */ 541da177e4SLinus Torvalds 5518367681SYOSHIFUJI Hideaki / 吉藤英明 static DEFINE_SPINLOCK(ip6_sk_fl_lock); 561da177e4SLinus Torvalds 5759c820b2SWillem de Bruijn DEFINE_STATIC_KEY_DEFERRED_FALSE(ipv6_flowlabel_exclusive, HZ); 5859c820b2SWillem de Bruijn EXPORT_SYMBOL(ipv6_flowlabel_exclusive); 5959c820b2SWillem de Bruijn 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 9759c820b2SWillem de Bruijn static bool fl_shared_exclusive(struct ip6_flowlabel *fl) 9859c820b2SWillem de Bruijn { 9959c820b2SWillem de Bruijn return fl->share == IPV6_FL_S_EXCL || 10059c820b2SWillem de Bruijn fl->share == IPV6_FL_S_PROCESS || 10159c820b2SWillem de Bruijn fl->share == IPV6_FL_S_USER; 10259c820b2SWillem de Bruijn } 10359c820b2SWillem de Bruijn 1046c0afef5SEric Dumazet static void fl_free_rcu(struct rcu_head *head) 1051da177e4SLinus Torvalds { 1066c0afef5SEric Dumazet struct ip6_flowlabel *fl = container_of(head, struct ip6_flowlabel, rcu); 1076c0afef5SEric Dumazet 108898132aeSDan Carpenter if (fl->share == IPV6_FL_S_PROCESS) 109898132aeSDan Carpenter put_pid(fl->owner.pid); 1101da177e4SLinus Torvalds kfree(fl->opt); 1116c0afef5SEric Dumazet kfree(fl); 11260e8fbc4SBenjamin Thery } 1136c0afef5SEric Dumazet 1146c0afef5SEric Dumazet 1156c0afef5SEric Dumazet static void fl_free(struct ip6_flowlabel *fl) 1166c0afef5SEric Dumazet { 11759c820b2SWillem de Bruijn if (!fl) 11859c820b2SWillem de Bruijn return; 11959c820b2SWillem de Bruijn 12059c820b2SWillem de Bruijn if (fl_shared_exclusive(fl) || fl->opt) 12159c820b2SWillem de Bruijn static_branch_slow_dec_deferred(&ipv6_flowlabel_exclusive); 12259c820b2SWillem de Bruijn 1236c0afef5SEric Dumazet call_rcu(&fl->rcu, fl_free_rcu); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds static void fl_release(struct ip6_flowlabel *fl) 1271da177e4SLinus Torvalds { 128d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds fl->lastuse = jiffies; 1311da177e4SLinus Torvalds if (atomic_dec_and_test(&fl->users)) { 1321da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1331da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1341da177e4SLinus Torvalds fl->expires = ttd; 1351da177e4SLinus Torvalds ttd = fl->expires; 1361da177e4SLinus Torvalds if (fl->opt && fl->share == IPV6_FL_S_EXCL) { 1371da177e4SLinus Torvalds struct ipv6_txoptions *opt = fl->opt; 1381da177e4SLinus Torvalds fl->opt = NULL; 1391da177e4SLinus Torvalds kfree(opt); 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds if (!timer_pending(&ip6_fl_gc_timer) || 1421da177e4SLinus Torvalds time_after(ip6_fl_gc_timer.expires, ttd)) 1431da177e4SLinus Torvalds mod_timer(&ip6_fl_gc_timer, ttd); 1441da177e4SLinus Torvalds } 145d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 14824ed960aSKees Cook static void ip6_fl_gc(struct timer_list *unused) 1491da177e4SLinus Torvalds { 1501da177e4SLinus Torvalds int i; 1511da177e4SLinus Torvalds unsigned long now = jiffies; 1521da177e4SLinus Torvalds unsigned long sched = 0; 1531da177e4SLinus Torvalds 154d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock(&ip6_fl_lock); 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds for (i = 0; i <= FL_HASH_MASK; i++) { 1577f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1587f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1597f0e44acSEric Dumazet 1601da177e4SLinus Torvalds flp = &fl_ht[i]; 161d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 162d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 1631da177e4SLinus Torvalds if (atomic_read(&fl->users) == 0) { 1641da177e4SLinus Torvalds unsigned long ttd = fl->lastuse + fl->linger; 1651da177e4SLinus Torvalds if (time_after(ttd, fl->expires)) 1661da177e4SLinus Torvalds fl->expires = ttd; 1671da177e4SLinus Torvalds ttd = fl->expires; 1681da177e4SLinus Torvalds if (time_after_eq(now, ttd)) { 1691da177e4SLinus Torvalds *flp = fl->next; 1701da177e4SLinus Torvalds fl_free(fl); 1711da177e4SLinus Torvalds atomic_dec(&fl_size); 1721da177e4SLinus Torvalds continue; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds if (!sched || time_before(ttd, sched)) 1751da177e4SLinus Torvalds sched = ttd; 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds flp = &fl->next; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds if (!sched && atomic_read(&fl_size)) 1811da177e4SLinus Torvalds sched = now + FL_MAX_LINGER; 1821da177e4SLinus Torvalds if (sched) { 18360e8fbc4SBenjamin Thery mod_timer(&ip6_fl_gc_timer, sched); 1841da177e4SLinus Torvalds } 185d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock(&ip6_fl_lock); 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1882c8c1e72SAlexey Dobriyan static void __net_exit ip6_fl_purge(struct net *net) 18960e8fbc4SBenjamin Thery { 19060e8fbc4SBenjamin Thery int i; 19160e8fbc4SBenjamin Thery 1924762fb98SJan Stancek spin_lock_bh(&ip6_fl_lock); 19360e8fbc4SBenjamin Thery for (i = 0; i <= FL_HASH_MASK; i++) { 1947f0e44acSEric Dumazet struct ip6_flowlabel *fl; 1957f0e44acSEric Dumazet struct ip6_flowlabel __rcu **flp; 1967f0e44acSEric Dumazet 19760e8fbc4SBenjamin Thery flp = &fl_ht[i]; 198d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 while ((fl = rcu_dereference_protected(*flp, 199d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_fl_lock))) != NULL) { 20009ad9bc7SOctavian Purdila if (net_eq(fl->fl_net, net) && 20109ad9bc7SOctavian Purdila atomic_read(&fl->users) == 0) { 20260e8fbc4SBenjamin Thery *flp = fl->next; 20360e8fbc4SBenjamin Thery fl_free(fl); 20460e8fbc4SBenjamin Thery atomic_dec(&fl_size); 20560e8fbc4SBenjamin Thery continue; 20660e8fbc4SBenjamin Thery } 20760e8fbc4SBenjamin Thery flp = &fl->next; 20860e8fbc4SBenjamin Thery } 20960e8fbc4SBenjamin Thery } 2104762fb98SJan Stancek spin_unlock_bh(&ip6_fl_lock); 21160e8fbc4SBenjamin Thery } 21260e8fbc4SBenjamin Thery 21360e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_intern(struct net *net, 21460e8fbc4SBenjamin Thery struct ip6_flowlabel *fl, __be32 label) 2151da177e4SLinus Torvalds { 21678c2e502SPavel Emelyanov struct ip6_flowlabel *lfl; 21778c2e502SPavel Emelyanov 2181da177e4SLinus Torvalds fl->label = label & IPV6_FLOWLABEL_MASK; 2191da177e4SLinus Torvalds 220d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_fl_lock); 2211da177e4SLinus Torvalds if (label == 0) { 2221da177e4SLinus Torvalds for (;;) { 22363862b5bSAruna-Hewapathirane fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK; 2241da177e4SLinus Torvalds if (fl->label) { 22560e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 22663159f29SIan Morris if (!lfl) 2271da177e4SLinus Torvalds break; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds } 23078c2e502SPavel Emelyanov } else { 23178c2e502SPavel Emelyanov /* 23278c2e502SPavel Emelyanov * we dropper the ip6_fl_lock, so this entry could reappear 23378c2e502SPavel Emelyanov * and we need to recheck with it. 23478c2e502SPavel Emelyanov * 23578c2e502SPavel Emelyanov * OTOH no need to search the active socket first, like it is 23678c2e502SPavel Emelyanov * done in ipv6_flowlabel_opt - sock is locked, so new entry 23778c2e502SPavel Emelyanov * with the same label can only appear on another sock 23878c2e502SPavel Emelyanov */ 23960e8fbc4SBenjamin Thery lfl = __fl_lookup(net, fl->label); 24053b24b8fSIan Morris if (lfl) { 24178c2e502SPavel Emelyanov atomic_inc(&lfl->users); 242d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 24378c2e502SPavel Emelyanov return lfl; 24478c2e502SPavel Emelyanov } 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds fl->lastuse = jiffies; 2481da177e4SLinus Torvalds fl->next = fl_ht[FL_HASH(fl->label)]; 249d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); 2501da177e4SLinus Torvalds atomic_inc(&fl_size); 251d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_fl_lock); 25278c2e502SPavel Emelyanov return NULL; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds /* Socket flowlabel lists */ 2581da177e4SLinus Torvalds 25959c820b2SWillem de Bruijn struct ip6_flowlabel *__fl6_sock_lookup(struct sock *sk, __be32 label) 2601da177e4SLinus Torvalds { 2611da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2621da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds label &= IPV6_FLOWLABEL_MASK; 2651da177e4SLinus Torvalds 26618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 26718367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 2681da177e4SLinus Torvalds struct ip6_flowlabel *fl = sfl->fl; 26965a3c497SEric Dumazet 27065a3c497SEric Dumazet if (fl->label == label && atomic_inc_not_zero(&fl->users)) { 2711da177e4SLinus Torvalds fl->lastuse = jiffies; 27218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2731da177e4SLinus Torvalds return fl; 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds } 27618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 2771da177e4SLinus Torvalds return NULL; 2781da177e4SLinus Torvalds } 27959c820b2SWillem de Bruijn EXPORT_SYMBOL_GPL(__fl6_sock_lookup); 2803cf3dc6cSArnaldo Carvalho de Melo 2811da177e4SLinus Torvalds void fl6_free_socklist(struct sock *sk) 2821da177e4SLinus Torvalds { 2831da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 2841da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 2851da177e4SLinus Torvalds 28618367681SYOSHIFUJI Hideaki / 吉藤英明 if (!rcu_access_pointer(np->ipv6_fl_list)) 287f256dc59SYOSHIFUJI Hideaki / 吉藤英明 return; 288f256dc59SYOSHIFUJI Hideaki / 吉藤英明 28918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 29018367681SYOSHIFUJI Hideaki / 吉藤英明 while ((sfl = rcu_dereference_protected(np->ipv6_fl_list, 29118367681SYOSHIFUJI Hideaki / 吉藤英明 lockdep_is_held(&ip6_sk_fl_lock))) != NULL) { 29218367681SYOSHIFUJI Hideaki / 吉藤英明 np->ipv6_fl_list = sfl->next; 29318367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 294f256dc59SYOSHIFUJI Hideaki / 吉藤英明 2951da177e4SLinus Torvalds fl_release(sfl->fl); 29618367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 29718367681SYOSHIFUJI Hideaki / 吉藤英明 29818367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 2991da177e4SLinus Torvalds } 30018367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds /* Service routines */ 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds /* 3071da177e4SLinus Torvalds It is the only difficult place. flowlabel enforces equal headers 3081da177e4SLinus Torvalds before and including routing header, however user may supply options 3091da177e4SLinus Torvalds following rthdr. 3101da177e4SLinus Torvalds */ 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, 3131da177e4SLinus Torvalds struct ip6_flowlabel *fl, 3141da177e4SLinus Torvalds struct ipv6_txoptions *fopt) 3151da177e4SLinus Torvalds { 316df9890c3SYOSHIFUJI Hideaki struct ipv6_txoptions *fl_opt = fl->opt; 3171da177e4SLinus Torvalds 31863159f29SIan Morris if (!fopt || fopt->opt_flen == 0) 3191da177e4SLinus Torvalds return fl_opt; 3201da177e4SLinus Torvalds 32153b24b8fSIan Morris if (fl_opt) { 3221da177e4SLinus Torvalds opt_space->hopopt = fl_opt->hopopt; 323df9890c3SYOSHIFUJI Hideaki opt_space->dst0opt = fl_opt->dst0opt; 3241da177e4SLinus Torvalds opt_space->srcrt = fl_opt->srcrt; 3251da177e4SLinus Torvalds opt_space->opt_nflen = fl_opt->opt_nflen; 3261da177e4SLinus Torvalds } else { 3271da177e4SLinus Torvalds if (fopt->opt_nflen == 0) 3281da177e4SLinus Torvalds return fopt; 3291da177e4SLinus Torvalds opt_space->hopopt = NULL; 3301da177e4SLinus Torvalds opt_space->dst0opt = NULL; 3311da177e4SLinus Torvalds opt_space->srcrt = NULL; 3321da177e4SLinus Torvalds opt_space->opt_nflen = 0; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds opt_space->dst1opt = fopt->dst1opt; 3351da177e4SLinus Torvalds opt_space->opt_flen = fopt->opt_flen; 336864e2a1fSEric Dumazet opt_space->tot_len = fopt->tot_len; 3371da177e4SLinus Torvalds return opt_space; 3381da177e4SLinus Torvalds } 339a495f836SChris Elston EXPORT_SYMBOL_GPL(fl6_merge_options); 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds static unsigned long check_linger(unsigned long ttl) 3421da177e4SLinus Torvalds { 3431da177e4SLinus Torvalds if (ttl < FL_MIN_LINGER) 3441da177e4SLinus Torvalds return FL_MIN_LINGER*HZ; 3451da177e4SLinus Torvalds if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN)) 3461da177e4SLinus Torvalds return 0; 3471da177e4SLinus Torvalds return ttl*HZ; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires) 3511da177e4SLinus Torvalds { 3521da177e4SLinus Torvalds linger = check_linger(linger); 3531da177e4SLinus Torvalds if (!linger) 3541da177e4SLinus Torvalds return -EPERM; 3551da177e4SLinus Torvalds expires = check_linger(expires); 3561da177e4SLinus Torvalds if (!expires) 3571da177e4SLinus Torvalds return -EPERM; 358394055f6SFlorent Fourcot 359394055f6SFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 3601da177e4SLinus Torvalds fl->lastuse = jiffies; 3611da177e4SLinus Torvalds if (time_before(fl->linger, linger)) 3621da177e4SLinus Torvalds fl->linger = linger; 3631da177e4SLinus Torvalds if (time_before(expires, fl->linger)) 3641da177e4SLinus Torvalds expires = fl->linger; 3651da177e4SLinus Torvalds if (time_before(fl->expires, fl->lastuse + expires)) 3661da177e4SLinus Torvalds fl->expires = fl->lastuse + expires; 367394055f6SFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 368394055f6SFlorent Fourcot 3691da177e4SLinus Torvalds return 0; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds static struct ip6_flowlabel * 373ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, 37486298285SChristoph Hellwig sockptr_t optval, int optlen, int *err_p) 3751da177e4SLinus Torvalds { 376684de409SDavid S. Miller struct ip6_flowlabel *fl = NULL; 3771da177e4SLinus Torvalds int olen; 3781da177e4SLinus Torvalds int addr_type; 3791da177e4SLinus Torvalds int err; 3801da177e4SLinus Torvalds 381684de409SDavid S. Miller olen = optlen - CMSG_ALIGN(sizeof(*freq)); 382684de409SDavid S. Miller err = -EINVAL; 383684de409SDavid S. Miller if (olen > 64 * 1024) 384684de409SDavid S. Miller goto done; 385684de409SDavid S. Miller 3861da177e4SLinus Torvalds err = -ENOMEM; 3870c600edaSIngo Oeser fl = kzalloc(sizeof(*fl), GFP_KERNEL); 38863159f29SIan Morris if (!fl) 3891da177e4SLinus Torvalds goto done; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds if (olen > 0) { 3921da177e4SLinus Torvalds struct msghdr msg; 3934c9483b2SDavid S. Miller struct flowi6 flowi6; 39426879da5SWei Wang struct ipcm6_cookie ipc6; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds err = -ENOMEM; 3971da177e4SLinus Torvalds fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL); 39863159f29SIan Morris if (!fl->opt) 3991da177e4SLinus Torvalds goto done; 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds memset(fl->opt, 0, sizeof(*fl->opt)); 4021da177e4SLinus Torvalds fl->opt->tot_len = sizeof(*fl->opt) + olen; 4031da177e4SLinus Torvalds err = -EFAULT; 404d3c48151SChristoph Hellwig if (copy_from_sockptr_offset(fl->opt + 1, optval, 405d3c48151SChristoph Hellwig CMSG_ALIGN(sizeof(*freq)), olen)) 4061da177e4SLinus Torvalds goto done; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds msg.msg_controllen = olen; 4091da177e4SLinus Torvalds msg.msg_control = (void *)(fl->opt+1); 4104c9483b2SDavid S. Miller memset(&flowi6, 0, sizeof(flowi6)); 4111da177e4SLinus Torvalds 41226879da5SWei Wang ipc6.opt = fl->opt; 4135fdaa88dSWillem de Bruijn err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &ipc6); 4141da177e4SLinus Torvalds if (err) 4151da177e4SLinus Torvalds goto done; 4161da177e4SLinus Torvalds err = -EINVAL; 4171da177e4SLinus Torvalds if (fl->opt->opt_flen) 4181da177e4SLinus Torvalds goto done; 4191da177e4SLinus Torvalds if (fl->opt->opt_nflen == 0) { 4201da177e4SLinus Torvalds kfree(fl->opt); 4211da177e4SLinus Torvalds fl->opt = NULL; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 425efd7ef1cSEric W. Biederman fl->fl_net = net; 4261da177e4SLinus Torvalds fl->expires = jiffies; 4271da177e4SLinus Torvalds err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); 4281da177e4SLinus Torvalds if (err) 4291da177e4SLinus Torvalds goto done; 4301da177e4SLinus Torvalds fl->share = freq->flr_share; 4311da177e4SLinus Torvalds addr_type = ipv6_addr_type(&freq->flr_dst); 43235700212SJoe Perches if ((addr_type & IPV6_ADDR_MAPPED) || 43335700212SJoe Perches addr_type == IPV6_ADDR_ANY) { 434c6817e4cSJames Morris err = -EINVAL; 4351da177e4SLinus Torvalds goto done; 436c6817e4cSJames Morris } 4374e3fd7a0SAlexey Dobriyan fl->dst = freq->flr_dst; 4381da177e4SLinus Torvalds atomic_set(&fl->users, 1); 4391da177e4SLinus Torvalds switch (fl->share) { 4401da177e4SLinus Torvalds case IPV6_FL_S_EXCL: 4411da177e4SLinus Torvalds case IPV6_FL_S_ANY: 4421da177e4SLinus Torvalds break; 4431da177e4SLinus Torvalds case IPV6_FL_S_PROCESS: 4444f82f457SEric W. Biederman fl->owner.pid = get_task_pid(current, PIDTYPE_PID); 4451da177e4SLinus Torvalds break; 4461da177e4SLinus Torvalds case IPV6_FL_S_USER: 4474f82f457SEric W. Biederman fl->owner.uid = current_euid(); 4481da177e4SLinus Torvalds break; 4491da177e4SLinus Torvalds default: 4501da177e4SLinus Torvalds err = -EINVAL; 4511da177e4SLinus Torvalds goto done; 4521da177e4SLinus Torvalds } 453d44e3fa5SEric Dumazet if (fl_shared_exclusive(fl) || fl->opt) 454d44e3fa5SEric Dumazet static_branch_deferred_inc(&ipv6_flowlabel_exclusive); 4551da177e4SLinus Torvalds return fl; 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds done: 458d44e3fa5SEric Dumazet if (fl) { 459d44e3fa5SEric Dumazet kfree(fl->opt); 460d44e3fa5SEric Dumazet kfree(fl); 461d44e3fa5SEric Dumazet } 4621da177e4SLinus Torvalds *err_p = err; 4631da177e4SLinus Torvalds return NULL; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds static int mem_check(struct sock *sk) 4671da177e4SLinus Torvalds { 4681da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 4691da177e4SLinus Torvalds struct ipv6_fl_socklist *sfl; 4701da177e4SLinus Torvalds int room = FL_MAX_SIZE - atomic_read(&fl_size); 4711da177e4SLinus Torvalds int count = 0; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) 4741da177e4SLinus Torvalds return 0; 4751da177e4SLinus Torvalds 476f8c31c8fSHannes Frederic Sowa rcu_read_lock_bh(); 47718367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) 4781da177e4SLinus Torvalds count++; 479f8c31c8fSHannes Frederic Sowa rcu_read_unlock_bh(); 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds if (room <= 0 || 4821da177e4SLinus Torvalds ((count >= FL_MAX_PER_SOCK || 48335700212SJoe Perches (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && 48435700212SJoe Perches !capable(CAP_NET_ADMIN))) 4851da177e4SLinus Torvalds return -ENOBUFS; 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds return 0; 4881da177e4SLinus Torvalds } 4891da177e4SLinus Torvalds 49004028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, 49104028045SPavel Emelyanov struct ip6_flowlabel *fl) 49204028045SPavel Emelyanov { 49318367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 49404028045SPavel Emelyanov sfl->fl = fl; 49504028045SPavel Emelyanov sfl->next = np->ipv6_fl_list; 49618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_assign_pointer(np->ipv6_fl_list, sfl); 49718367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 49804028045SPavel Emelyanov } 49904028045SPavel Emelyanov 50046e5f401SFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, 50146e5f401SFlorent Fourcot int flags) 5023fdfa5ffSFlorent Fourcot { 5033fdfa5ffSFlorent Fourcot struct ipv6_pinfo *np = inet6_sk(sk); 5043fdfa5ffSFlorent Fourcot struct ipv6_fl_socklist *sfl; 5053fdfa5ffSFlorent Fourcot 50646e5f401SFlorent Fourcot if (flags & IPV6_FL_F_REMOTE) { 50746e5f401SFlorent Fourcot freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK; 50846e5f401SFlorent Fourcot return 0; 50946e5f401SFlorent Fourcot } 51046e5f401SFlorent Fourcot 511df3687ffSFlorent Fourcot if (np->repflow) { 512df3687ffSFlorent Fourcot freq->flr_label = np->flow_label; 513df3687ffSFlorent Fourcot return 0; 514df3687ffSFlorent Fourcot } 515df3687ffSFlorent Fourcot 5163fdfa5ffSFlorent Fourcot rcu_read_lock_bh(); 5173fdfa5ffSFlorent Fourcot 5183fdfa5ffSFlorent Fourcot for_each_sk_fl_rcu(np, sfl) { 5193fdfa5ffSFlorent Fourcot if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) { 5203fdfa5ffSFlorent Fourcot spin_lock_bh(&ip6_fl_lock); 5213fdfa5ffSFlorent Fourcot freq->flr_label = sfl->fl->label; 5223fdfa5ffSFlorent Fourcot freq->flr_dst = sfl->fl->dst; 5233fdfa5ffSFlorent Fourcot freq->flr_share = sfl->fl->share; 5243fdfa5ffSFlorent Fourcot freq->flr_expires = (sfl->fl->expires - jiffies) / HZ; 5253fdfa5ffSFlorent Fourcot freq->flr_linger = sfl->fl->linger / HZ; 5263fdfa5ffSFlorent Fourcot 5273fdfa5ffSFlorent Fourcot spin_unlock_bh(&ip6_fl_lock); 5283fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5293fdfa5ffSFlorent Fourcot return 0; 5303fdfa5ffSFlorent Fourcot } 5313fdfa5ffSFlorent Fourcot } 5323fdfa5ffSFlorent Fourcot rcu_read_unlock_bh(); 5333fdfa5ffSFlorent Fourcot 5343fdfa5ffSFlorent Fourcot return -ENOENT; 5353fdfa5ffSFlorent Fourcot } 5363fdfa5ffSFlorent Fourcot 537ff6a4cf2SChristoph Hellwig #define socklist_dereference(__sflp) \ 538ff6a4cf2SChristoph Hellwig rcu_dereference_protected(__sflp, lockdep_is_held(&ip6_sk_fl_lock)) 539ff6a4cf2SChristoph Hellwig 540ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds struct ipv6_pinfo *np = inet6_sk(sk); 5437f0e44acSEric Dumazet struct ipv6_fl_socklist __rcu **sflp; 544ff6a4cf2SChristoph Hellwig struct ipv6_fl_socklist *sfl; 54578c2e502SPavel Emelyanov 546ff6a4cf2SChristoph Hellwig if (freq->flr_flags & IPV6_FL_F_REFLECT) { 547df3687ffSFlorent Fourcot if (sk->sk_protocol != IPPROTO_TCP) 548df3687ffSFlorent Fourcot return -ENOPROTOOPT; 549df3687ffSFlorent Fourcot if (!np->repflow) 550df3687ffSFlorent Fourcot return -ESRCH; 551df3687ffSFlorent Fourcot np->flow_label = 0; 552df3687ffSFlorent Fourcot np->repflow = 0; 553df3687ffSFlorent Fourcot return 0; 554df3687ffSFlorent Fourcot } 555ff6a4cf2SChristoph Hellwig 55618367681SYOSHIFUJI Hideaki / 吉藤英明 spin_lock_bh(&ip6_sk_fl_lock); 55718367681SYOSHIFUJI Hideaki / 吉藤英明 for (sflp = &np->ipv6_fl_list; 558ff6a4cf2SChristoph Hellwig (sfl = socklist_dereference(*sflp)) != NULL; 55918367681SYOSHIFUJI Hideaki / 吉藤英明 sflp = &sfl->next) { 560ff6a4cf2SChristoph Hellwig if (sfl->fl->label == freq->flr_label) 561ff6a4cf2SChristoph Hellwig goto found; 562ff6a4cf2SChristoph Hellwig } 563ff6a4cf2SChristoph Hellwig spin_unlock_bh(&ip6_sk_fl_lock); 564ff6a4cf2SChristoph Hellwig return -ESRCH; 565ff6a4cf2SChristoph Hellwig found: 566ff6a4cf2SChristoph Hellwig if (freq->flr_label == (np->flow_label & IPV6_FLOWLABEL_MASK)) 5671da177e4SLinus Torvalds np->flow_label &= ~IPV6_FLOWLABEL_MASK; 56844c3d0c1SEric Dumazet *sflp = sfl->next; 56918367681SYOSHIFUJI Hideaki / 吉藤英明 spin_unlock_bh(&ip6_sk_fl_lock); 5701da177e4SLinus Torvalds fl_release(sfl->fl); 57118367681SYOSHIFUJI Hideaki / 吉藤英明 kfree_rcu(sfl, rcu); 5721da177e4SLinus Torvalds return 0; 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds 575ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_renew(struct sock *sk, struct in6_flowlabel_req *freq) 576ff6a4cf2SChristoph Hellwig { 577ff6a4cf2SChristoph Hellwig struct ipv6_pinfo *np = inet6_sk(sk); 578ff6a4cf2SChristoph Hellwig struct net *net = sock_net(sk); 579ff6a4cf2SChristoph Hellwig struct ipv6_fl_socklist *sfl; 580ff6a4cf2SChristoph Hellwig int err; 581ff6a4cf2SChristoph Hellwig 58218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 58318367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 584ff6a4cf2SChristoph Hellwig if (sfl->fl->label == freq->flr_label) { 585ff6a4cf2SChristoph Hellwig err = fl6_renew(sfl->fl, freq->flr_linger, 586ff6a4cf2SChristoph Hellwig freq->flr_expires); 58718367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5881da177e4SLinus Torvalds return err; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds } 59118367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 5921da177e4SLinus Torvalds 593ff6a4cf2SChristoph Hellwig if (freq->flr_share == IPV6_FL_S_NONE && 594af31f412SEric W. Biederman ns_capable(net->user_ns, CAP_NET_ADMIN)) { 595ff6a4cf2SChristoph Hellwig struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label); 596ff6a4cf2SChristoph Hellwig 5971da177e4SLinus Torvalds if (fl) { 598ff6a4cf2SChristoph Hellwig err = fl6_renew(fl, freq->flr_linger, 599ff6a4cf2SChristoph Hellwig freq->flr_expires); 6001da177e4SLinus Torvalds fl_release(fl); 6011da177e4SLinus Torvalds return err; 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds return -ESRCH; 605ff6a4cf2SChristoph Hellwig } 6061da177e4SLinus Torvalds 607ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, 60886298285SChristoph Hellwig sockptr_t optval, int optlen) 609ff6a4cf2SChristoph Hellwig { 610ff6a4cf2SChristoph Hellwig struct ipv6_fl_socklist *sfl, *sfl1 = NULL; 611ff6a4cf2SChristoph Hellwig struct ip6_flowlabel *fl, *fl1 = NULL; 612ff6a4cf2SChristoph Hellwig struct ipv6_pinfo *np = inet6_sk(sk); 6136444f72bSFlorent Fourcot struct net *net = sock_net(sk); 61447ec5303SLinus Torvalds int err; 615ff6a4cf2SChristoph Hellwig 616ff6a4cf2SChristoph Hellwig if (freq->flr_flags & IPV6_FL_F_REFLECT) { 6176444f72bSFlorent Fourcot if (net->ipv6.sysctl.flowlabel_consistency) { 6186444f72bSFlorent Fourcot net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); 6196444f72bSFlorent Fourcot return -EPERM; 6206444f72bSFlorent Fourcot } 6216444f72bSFlorent Fourcot 622df3687ffSFlorent Fourcot if (sk->sk_protocol != IPPROTO_TCP) 623df3687ffSFlorent Fourcot return -ENOPROTOOPT; 624df3687ffSFlorent Fourcot np->repflow = 1; 625df3687ffSFlorent Fourcot return 0; 626df3687ffSFlorent Fourcot } 627df3687ffSFlorent Fourcot 628ff6a4cf2SChristoph Hellwig if (freq->flr_label & ~IPV6_FLOWLABEL_MASK) 6291da177e4SLinus Torvalds return -EINVAL; 63082a584b7STom Herbert if (net->ipv6.sysctl.flowlabel_state_ranges && 631ff6a4cf2SChristoph Hellwig (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) 63282a584b7STom Herbert return -ERANGE; 63382a584b7STom Herbert 634ff6a4cf2SChristoph Hellwig fl = fl_create(net, sk, freq, optval, optlen, &err); 63563159f29SIan Morris if (!fl) 6361da177e4SLinus Torvalds return err; 637ff6a4cf2SChristoph Hellwig 6381da177e4SLinus Torvalds sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); 6391da177e4SLinus Torvalds 640ff6a4cf2SChristoph Hellwig if (freq->flr_label) { 6411da177e4SLinus Torvalds err = -EEXIST; 64218367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 64318367681SYOSHIFUJI Hideaki / 吉藤英明 for_each_sk_fl_rcu(np, sfl) { 644ff6a4cf2SChristoph Hellwig if (sfl->fl->label == freq->flr_label) { 645ff6a4cf2SChristoph Hellwig if (freq->flr_flags & IPV6_FL_F_EXCL) { 64618367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 6471da177e4SLinus Torvalds goto done; 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds fl1 = sfl->fl; 65065a3c497SEric Dumazet if (!atomic_inc_not_zero(&fl1->users)) 65165a3c497SEric Dumazet fl1 = NULL; 6521da177e4SLinus Torvalds break; 6531da177e4SLinus Torvalds } 6541da177e4SLinus Torvalds } 65518367681SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 6561da177e4SLinus Torvalds 65763159f29SIan Morris if (!fl1) 658ff6a4cf2SChristoph Hellwig fl1 = fl_lookup(net, freq->flr_label); 6591da177e4SLinus Torvalds if (fl1) { 66078c2e502SPavel Emelyanov recheck: 6611da177e4SLinus Torvalds err = -EEXIST; 662ff6a4cf2SChristoph Hellwig if (freq->flr_flags&IPV6_FL_F_EXCL) 6631da177e4SLinus Torvalds goto release; 6641da177e4SLinus Torvalds err = -EPERM; 6651da177e4SLinus Torvalds if (fl1->share == IPV6_FL_S_EXCL || 6661da177e4SLinus Torvalds fl1->share != fl->share || 6674f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_PROCESS) && 66895c16925SWillem de Bruijn (fl1->owner.pid != fl->owner.pid)) || 6694f82f457SEric W. Biederman ((fl1->share == IPV6_FL_S_USER) && 67095c16925SWillem de Bruijn !uid_eq(fl1->owner.uid, fl->owner.uid))) 6711da177e4SLinus Torvalds goto release; 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds err = -ENOMEM; 67463159f29SIan Morris if (!sfl1) 6751da177e4SLinus Torvalds goto release; 6761da177e4SLinus Torvalds if (fl->linger > fl1->linger) 6771da177e4SLinus Torvalds fl1->linger = fl->linger; 6781da177e4SLinus Torvalds if ((long)(fl->expires - fl1->expires) > 0) 6791da177e4SLinus Torvalds fl1->expires = fl->expires; 68004028045SPavel Emelyanov fl_link(np, sfl1, fl1); 6811da177e4SLinus Torvalds fl_free(fl); 6821da177e4SLinus Torvalds return 0; 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds release: 6851da177e4SLinus Torvalds fl_release(fl1); 6861da177e4SLinus Torvalds goto done; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds err = -ENOENT; 690ff6a4cf2SChristoph Hellwig if (!(freq->flr_flags & IPV6_FL_F_CREATE)) 6911da177e4SLinus Torvalds goto done; 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds err = -ENOMEM; 69463159f29SIan Morris if (!sfl1) 695e5d08d71SIan Morris goto done; 696e5d08d71SIan Morris 697e5d08d71SIan Morris err = mem_check(sk); 698e5d08d71SIan Morris if (err != 0) 6991da177e4SLinus Torvalds goto done; 7001da177e4SLinus Torvalds 701ff6a4cf2SChristoph Hellwig fl1 = fl_intern(net, fl, freq->flr_label); 70253b24b8fSIan Morris if (fl1) 70378c2e502SPavel Emelyanov goto recheck; 7041da177e4SLinus Torvalds 705ff6a4cf2SChristoph Hellwig if (!freq->flr_label) { 706d3c48151SChristoph Hellwig size_t offset = offsetof(struct in6_flowlabel_req, flr_label); 707d3c48151SChristoph Hellwig 708d3c48151SChristoph Hellwig if (copy_to_sockptr_offset(optval, offset, &fl->label, 709d3c48151SChristoph Hellwig sizeof(fl->label))) { 7106c94d361SDavid S. Miller /* Intentionally ignore fault. */ 7116c94d361SDavid S. Miller } 7126c94d361SDavid S. Miller } 7131da177e4SLinus Torvalds 71404028045SPavel Emelyanov fl_link(np, sfl1, fl); 7151da177e4SLinus Torvalds return 0; 7161da177e4SLinus Torvalds done: 7171da177e4SLinus Torvalds fl_free(fl); 7181da177e4SLinus Torvalds kfree(sfl1); 7191da177e4SLinus Torvalds return err; 7201da177e4SLinus Torvalds } 7211da177e4SLinus Torvalds 72286298285SChristoph Hellwig int ipv6_flowlabel_opt(struct sock *sk, sockptr_t optval, int optlen) 723ff6a4cf2SChristoph Hellwig { 724ff6a4cf2SChristoph Hellwig struct in6_flowlabel_req freq; 725ff6a4cf2SChristoph Hellwig 726ff6a4cf2SChristoph Hellwig if (optlen < sizeof(freq)) 727ff6a4cf2SChristoph Hellwig return -EINVAL; 72886298285SChristoph Hellwig if (copy_from_sockptr(&freq, optval, sizeof(freq))) 729ff6a4cf2SChristoph Hellwig return -EFAULT; 730ff6a4cf2SChristoph Hellwig 731ff6a4cf2SChristoph Hellwig switch (freq.flr_action) { 732ff6a4cf2SChristoph Hellwig case IPV6_FL_A_PUT: 733ff6a4cf2SChristoph Hellwig return ipv6_flowlabel_put(sk, &freq); 734ff6a4cf2SChristoph Hellwig case IPV6_FL_A_RENEW: 735ff6a4cf2SChristoph Hellwig return ipv6_flowlabel_renew(sk, &freq); 736ff6a4cf2SChristoph Hellwig case IPV6_FL_A_GET: 737ff6a4cf2SChristoph Hellwig return ipv6_flowlabel_get(sk, &freq, optval, optlen); 738ff6a4cf2SChristoph Hellwig default: 739ff6a4cf2SChristoph Hellwig return -EINVAL; 740ff6a4cf2SChristoph Hellwig } 741ff6a4cf2SChristoph Hellwig } 742ff6a4cf2SChristoph Hellwig 7431da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds struct ip6fl_iter_state { 7465983a3dfSBenjamin Thery struct seq_net_private p; 7474f82f457SEric W. Biederman struct pid_namespace *pid_ns; 7481da177e4SLinus Torvalds int bucket; 7491da177e4SLinus Torvalds }; 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds #define ip6fl_seq_private(seq) ((struct ip6fl_iter_state *)(seq)->private) 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) 7541da177e4SLinus Torvalds { 7551da177e4SLinus Torvalds struct ip6_flowlabel *fl = NULL; 7561da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7575983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { 760d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 761d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 762d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 7631da177e4SLinus Torvalds } 764d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 765d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 766d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7671da177e4SLinus Torvalds return fl; 7681da177e4SLinus Torvalds } 7691da177e4SLinus Torvalds 7701da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl) 7711da177e4SLinus Torvalds { 7721da177e4SLinus Torvalds struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 7735983a3dfSBenjamin Thery struct net *net = seq_file_net(seq); 7741da177e4SLinus Torvalds 775d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_continue_rcu(fl) { 776d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 777d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 7781da177e4SLinus Torvalds } 779d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 780d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again: 781d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (++state->bucket <= FL_HASH_MASK) { 782d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 for_each_fl_rcu(state->bucket, fl) { 783d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 if (net_eq(fl->fl_net, net)) 784d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto out; 785d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 786d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 goto try_again; 787d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 } 788d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 fl = NULL; 789d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 790d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out: 7911da177e4SLinus Torvalds return fl; 7921da177e4SLinus Torvalds } 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos) 7951da177e4SLinus Torvalds { 7961da177e4SLinus Torvalds struct ip6_flowlabel *fl = ip6fl_get_first(seq); 7971da177e4SLinus Torvalds if (fl) 7981da177e4SLinus Torvalds while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL) 7991da177e4SLinus Torvalds --pos; 8001da177e4SLinus Torvalds return pos ? NULL : fl; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos) 804d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __acquires(RCU) 8051da177e4SLinus Torvalds { 806ad08978aSChristoph Hellwig struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 807ad08978aSChristoph Hellwig 8089d78edeaSAlexey Gladkov state->pid_ns = proc_pid_ns(file_inode(seq->file)->i_sb); 809ad08978aSChristoph Hellwig 810d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 8111da177e4SLinus Torvalds return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 8121da177e4SLinus Torvalds } 8131da177e4SLinus Torvalds 8141da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos) 8151da177e4SLinus Torvalds { 8161da177e4SLinus Torvalds struct ip6_flowlabel *fl; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 8191da177e4SLinus Torvalds fl = ip6fl_get_first(seq); 8201da177e4SLinus Torvalds else 8211da177e4SLinus Torvalds fl = ip6fl_get_next(seq, v); 8221da177e4SLinus Torvalds ++*pos; 8231da177e4SLinus Torvalds return fl; 8241da177e4SLinus Torvalds } 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v) 827d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 __releases(RCU) 8281da177e4SLinus Torvalds { 829d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds 8321b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v) 8331da177e4SLinus Torvalds { 8344f82f457SEric W. Biederman struct ip6fl_iter_state *state = ip6fl_seq_private(seq); 835869ba988SFlorent Fourcot if (v == SEQ_START_TOKEN) { 8361744bea1SJoe Perches seq_puts(seq, "Label S Owner Users Linger Expires Dst Opt\n"); 837869ba988SFlorent Fourcot } else { 8381b7c2dbcSJames Morris struct ip6_flowlabel *fl = v; 8391da177e4SLinus Torvalds seq_printf(seq, 8404b7a4274SHarvey Harrison "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", 84195c96174SEric Dumazet (unsigned int)ntohl(fl->label), 8421da177e4SLinus Torvalds fl->share, 8434f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_PROCESS) ? 8444f82f457SEric W. Biederman pid_nr_ns(fl->owner.pid, state->pid_ns) : 8454f82f457SEric W. Biederman ((fl->share == IPV6_FL_S_USER) ? 8464f82f457SEric W. Biederman from_kuid_munged(seq_user_ns(seq), fl->owner.uid) : 8474f82f457SEric W. Biederman 0)), 8481da177e4SLinus Torvalds atomic_read(&fl->users), 8491da177e4SLinus Torvalds fl->linger/HZ, 8501da177e4SLinus Torvalds (long)(fl->expires - jiffies)/HZ, 851b071195dSHarvey Harrison &fl->dst, 8521da177e4SLinus Torvalds fl->opt ? fl->opt->opt_nflen : 0); 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds return 0; 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds 85756b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = { 8581da177e4SLinus Torvalds .start = ip6fl_seq_start, 8591da177e4SLinus Torvalds .next = ip6fl_seq_next, 8601da177e4SLinus Torvalds .stop = ip6fl_seq_stop, 8611da177e4SLinus Torvalds .show = ip6fl_seq_show, 8621da177e4SLinus Torvalds }; 8631da177e4SLinus Torvalds 8642c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net) 8651da177e4SLinus Torvalds { 866c3506372SChristoph Hellwig if (!proc_create_net("ip6_flowlabel", 0444, net->proc_net, 867c3506372SChristoph Hellwig &ip6fl_seq_ops, sizeof(struct ip6fl_iter_state))) 8680a3e78acSDaniel Lezcano return -ENOMEM; 8690a3e78acSDaniel Lezcano return 0; 8700a3e78acSDaniel Lezcano } 8710a3e78acSDaniel Lezcano 8722c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net) 8730a3e78acSDaniel Lezcano { 874ece31ffdSGao feng remove_proc_entry("ip6_flowlabel", net->proc_net); 8750a3e78acSDaniel Lezcano } 8760a3e78acSDaniel Lezcano #else 8770a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net) 8780a3e78acSDaniel Lezcano { 8790a3e78acSDaniel Lezcano return 0; 8800a3e78acSDaniel Lezcano } 8810a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net) 8820a3e78acSDaniel Lezcano { 8830a3e78acSDaniel Lezcano } 8841da177e4SLinus Torvalds #endif 8850a3e78acSDaniel Lezcano 8862c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net) 88760e8fbc4SBenjamin Thery { 88860e8fbc4SBenjamin Thery ip6_fl_purge(net); 8895983a3dfSBenjamin Thery ip6_flowlabel_proc_fini(net); 89060e8fbc4SBenjamin Thery } 89160e8fbc4SBenjamin Thery 89260e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = { 8935983a3dfSBenjamin Thery .init = ip6_flowlabel_proc_init, 89460e8fbc4SBenjamin Thery .exit = ip6_flowlabel_net_exit, 89560e8fbc4SBenjamin Thery }; 89660e8fbc4SBenjamin Thery 8970a3e78acSDaniel Lezcano int ip6_flowlabel_init(void) 8980a3e78acSDaniel Lezcano { 8995983a3dfSBenjamin Thery return register_pernet_subsys(&ip6_flowlabel_net_ops); 9001da177e4SLinus Torvalds } 9011da177e4SLinus Torvalds 9021da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void) 9031da177e4SLinus Torvalds { 90459c820b2SWillem de Bruijn static_key_deferred_flush(&ipv6_flowlabel_exclusive); 9051da177e4SLinus Torvalds del_timer(&ip6_fl_gc_timer); 90660e8fbc4SBenjamin Thery unregister_pernet_subsys(&ip6_flowlabel_net_ops); 9071da177e4SLinus Torvalds } 908