xref: /openbmc/linux/net/ipv6/ip6_flowlabel.c (revision 53b47106)
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 (;;) {
2131da177e4SLinus Torvalds 			fl->label = htonl(net_random())&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;
3481da177e4SLinus Torvalds 	fl->lastuse = jiffies;
3491da177e4SLinus Torvalds 	if (time_before(fl->linger, linger))
3501da177e4SLinus Torvalds 		fl->linger = linger;
3511da177e4SLinus Torvalds 	if (time_before(expires, fl->linger))
3521da177e4SLinus Torvalds 		expires = fl->linger;
3531da177e4SLinus Torvalds 	if (time_before(fl->expires, fl->lastuse + expires))
3541da177e4SLinus Torvalds 		fl->expires = fl->lastuse + expires;
3551da177e4SLinus Torvalds 	return 0;
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds static struct ip6_flowlabel *
359ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
360ec0506dbSMaciej Żenczykowski 	  char __user *optval, int optlen, int *err_p)
3611da177e4SLinus Torvalds {
362684de409SDavid S. Miller 	struct ip6_flowlabel *fl = NULL;
3631da177e4SLinus Torvalds 	int olen;
3641da177e4SLinus Torvalds 	int addr_type;
3651da177e4SLinus Torvalds 	int err;
3661da177e4SLinus Torvalds 
367684de409SDavid S. Miller 	olen = optlen - CMSG_ALIGN(sizeof(*freq));
368684de409SDavid S. Miller 	err = -EINVAL;
369684de409SDavid S. Miller 	if (olen > 64 * 1024)
370684de409SDavid S. Miller 		goto done;
371684de409SDavid S. Miller 
3721da177e4SLinus Torvalds 	err = -ENOMEM;
3730c600edaSIngo Oeser 	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
3741da177e4SLinus Torvalds 	if (fl == NULL)
3751da177e4SLinus Torvalds 		goto done;
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	if (olen > 0) {
3781da177e4SLinus Torvalds 		struct msghdr msg;
3794c9483b2SDavid S. Miller 		struct flowi6 flowi6;
3801da177e4SLinus Torvalds 		int junk;
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 		err = -ENOMEM;
3831da177e4SLinus Torvalds 		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
3841da177e4SLinus Torvalds 		if (fl->opt == NULL)
3851da177e4SLinus Torvalds 			goto done;
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 		memset(fl->opt, 0, sizeof(*fl->opt));
3881da177e4SLinus Torvalds 		fl->opt->tot_len = sizeof(*fl->opt) + olen;
3891da177e4SLinus Torvalds 		err = -EFAULT;
3901da177e4SLinus Torvalds 		if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
3911da177e4SLinus Torvalds 			goto done;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 		msg.msg_controllen = olen;
3941da177e4SLinus Torvalds 		msg.msg_control = (void*)(fl->opt+1);
3954c9483b2SDavid S. Miller 		memset(&flowi6, 0, sizeof(flowi6));
3961da177e4SLinus Torvalds 
39773df66f8STom Parkin 		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt,
39873df66f8STom Parkin 					    &junk, &junk, &junk);
3991da177e4SLinus Torvalds 		if (err)
4001da177e4SLinus Torvalds 			goto done;
4011da177e4SLinus Torvalds 		err = -EINVAL;
4021da177e4SLinus Torvalds 		if (fl->opt->opt_flen)
4031da177e4SLinus Torvalds 			goto done;
4041da177e4SLinus Torvalds 		if (fl->opt->opt_nflen == 0) {
4051da177e4SLinus Torvalds 			kfree(fl->opt);
4061da177e4SLinus Torvalds 			fl->opt = NULL;
4071da177e4SLinus Torvalds 		}
4081da177e4SLinus Torvalds 	}
4091da177e4SLinus Torvalds 
41060e8fbc4SBenjamin Thery 	fl->fl_net = hold_net(net);
4111da177e4SLinus Torvalds 	fl->expires = jiffies;
4121da177e4SLinus Torvalds 	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
4131da177e4SLinus Torvalds 	if (err)
4141da177e4SLinus Torvalds 		goto done;
4151da177e4SLinus Torvalds 	fl->share = freq->flr_share;
4161da177e4SLinus Torvalds 	addr_type = ipv6_addr_type(&freq->flr_dst);
41735700212SJoe Perches 	if ((addr_type & IPV6_ADDR_MAPPED) ||
41835700212SJoe Perches 	    addr_type == IPV6_ADDR_ANY) {
419c6817e4cSJames Morris 		err = -EINVAL;
4201da177e4SLinus Torvalds 		goto done;
421c6817e4cSJames Morris 	}
4224e3fd7a0SAlexey Dobriyan 	fl->dst = freq->flr_dst;
4231da177e4SLinus Torvalds 	atomic_set(&fl->users, 1);
4241da177e4SLinus Torvalds 	switch (fl->share) {
4251da177e4SLinus Torvalds 	case IPV6_FL_S_EXCL:
4261da177e4SLinus Torvalds 	case IPV6_FL_S_ANY:
4271da177e4SLinus Torvalds 		break;
4281da177e4SLinus Torvalds 	case IPV6_FL_S_PROCESS:
4294f82f457SEric W. Biederman 		fl->owner.pid = get_task_pid(current, PIDTYPE_PID);
4301da177e4SLinus Torvalds 		break;
4311da177e4SLinus Torvalds 	case IPV6_FL_S_USER:
4324f82f457SEric W. Biederman 		fl->owner.uid = current_euid();
4331da177e4SLinus Torvalds 		break;
4341da177e4SLinus Torvalds 	default:
4351da177e4SLinus Torvalds 		err = -EINVAL;
4361da177e4SLinus Torvalds 		goto done;
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds 	return fl;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds done:
4411da177e4SLinus Torvalds 	fl_free(fl);
4421da177e4SLinus Torvalds 	*err_p = err;
4431da177e4SLinus Torvalds 	return NULL;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds static int mem_check(struct sock *sk)
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
4491da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl;
4501da177e4SLinus Torvalds 	int room = FL_MAX_SIZE - atomic_read(&fl_size);
4511da177e4SLinus Torvalds 	int count = 0;
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
4541da177e4SLinus Torvalds 		return 0;
4551da177e4SLinus Torvalds 
45618367681SYOSHIFUJI Hideaki / 吉藤英明 	for_each_sk_fl_rcu(np, sfl)
4571da177e4SLinus Torvalds 		count++;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	if (room <= 0 ||
4601da177e4SLinus Torvalds 	    ((count >= FL_MAX_PER_SOCK ||
46135700212SJoe Perches 	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
46235700212SJoe Perches 	     !capable(CAP_NET_ADMIN)))
4631da177e4SLinus Torvalds 		return -ENOBUFS;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	return 0;
4661da177e4SLinus Torvalds }
4671da177e4SLinus Torvalds 
46804028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
46904028045SPavel Emelyanov 		struct ip6_flowlabel *fl)
47004028045SPavel Emelyanov {
47118367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_sk_fl_lock);
47204028045SPavel Emelyanov 	sfl->fl = fl;
47304028045SPavel Emelyanov 	sfl->next = np->ipv6_fl_list;
47418367681SYOSHIFUJI Hideaki / 吉藤英明 	rcu_assign_pointer(np->ipv6_fl_list, sfl);
47518367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_sk_fl_lock);
47604028045SPavel Emelyanov }
47704028045SPavel Emelyanov 
4783fdfa5ffSFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq)
4793fdfa5ffSFlorent Fourcot {
4803fdfa5ffSFlorent Fourcot 	struct ipv6_pinfo *np = inet6_sk(sk);
4813fdfa5ffSFlorent Fourcot 	struct ipv6_fl_socklist *sfl;
4823fdfa5ffSFlorent Fourcot 
4833fdfa5ffSFlorent Fourcot 	rcu_read_lock_bh();
4843fdfa5ffSFlorent Fourcot 
4853fdfa5ffSFlorent Fourcot 	for_each_sk_fl_rcu(np, sfl) {
4863fdfa5ffSFlorent Fourcot 		if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
4873fdfa5ffSFlorent Fourcot 			spin_lock_bh(&ip6_fl_lock);
4883fdfa5ffSFlorent Fourcot 			freq->flr_label = sfl->fl->label;
4893fdfa5ffSFlorent Fourcot 			freq->flr_dst = sfl->fl->dst;
4903fdfa5ffSFlorent Fourcot 			freq->flr_share = sfl->fl->share;
4913fdfa5ffSFlorent Fourcot 			freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
4923fdfa5ffSFlorent Fourcot 			freq->flr_linger = sfl->fl->linger / HZ;
4933fdfa5ffSFlorent Fourcot 
4943fdfa5ffSFlorent Fourcot 			spin_unlock_bh(&ip6_fl_lock);
4953fdfa5ffSFlorent Fourcot 			rcu_read_unlock_bh();
4963fdfa5ffSFlorent Fourcot 			return 0;
4973fdfa5ffSFlorent Fourcot 		}
4983fdfa5ffSFlorent Fourcot 	}
4993fdfa5ffSFlorent Fourcot 	rcu_read_unlock_bh();
5003fdfa5ffSFlorent Fourcot 
5013fdfa5ffSFlorent Fourcot 	return -ENOENT;
5023fdfa5ffSFlorent Fourcot }
5033fdfa5ffSFlorent Fourcot 
5041da177e4SLinus Torvalds int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
5051da177e4SLinus Torvalds {
50655205d40SIngo Molnar 	int uninitialized_var(err);
50760e8fbc4SBenjamin Thery 	struct net *net = sock_net(sk);
5081da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
5091da177e4SLinus Torvalds 	struct in6_flowlabel_req freq;
5101da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl1=NULL;
5117f0e44acSEric Dumazet 	struct ipv6_fl_socklist *sfl;
5127f0e44acSEric Dumazet 	struct ipv6_fl_socklist __rcu **sflp;
51378c2e502SPavel Emelyanov 	struct ip6_flowlabel *fl, *fl1 = NULL;
51478c2e502SPavel Emelyanov 
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	if (optlen < sizeof(freq))
5171da177e4SLinus Torvalds 		return -EINVAL;
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 	if (copy_from_user(&freq, optval, sizeof(freq)))
5201da177e4SLinus Torvalds 		return -EFAULT;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	switch (freq.flr_action) {
5231da177e4SLinus Torvalds 	case IPV6_FL_A_PUT:
52418367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_lock_bh(&ip6_sk_fl_lock);
52518367681SYOSHIFUJI Hideaki / 吉藤英明 		for (sflp = &np->ipv6_fl_list;
52618367681SYOSHIFUJI Hideaki / 吉藤英明 		     (sfl = rcu_dereference(*sflp))!=NULL;
52718367681SYOSHIFUJI Hideaki / 吉藤英明 		     sflp = &sfl->next) {
5281da177e4SLinus Torvalds 			if (sfl->fl->label == freq.flr_label) {
5291da177e4SLinus Torvalds 				if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
5301da177e4SLinus Torvalds 					np->flow_label &= ~IPV6_FLOWLABEL_MASK;
53118367681SYOSHIFUJI Hideaki / 吉藤英明 				*sflp = rcu_dereference(sfl->next);
53218367681SYOSHIFUJI Hideaki / 吉藤英明 				spin_unlock_bh(&ip6_sk_fl_lock);
5331da177e4SLinus Torvalds 				fl_release(sfl->fl);
53418367681SYOSHIFUJI Hideaki / 吉藤英明 				kfree_rcu(sfl, rcu);
5351da177e4SLinus Torvalds 				return 0;
5361da177e4SLinus Torvalds 			}
5371da177e4SLinus Torvalds 		}
53818367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_unlock_bh(&ip6_sk_fl_lock);
5391da177e4SLinus Torvalds 		return -ESRCH;
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds 	case IPV6_FL_A_RENEW:
54218367681SYOSHIFUJI Hideaki / 吉藤英明 		rcu_read_lock_bh();
54318367681SYOSHIFUJI Hideaki / 吉藤英明 		for_each_sk_fl_rcu(np, sfl) {
5441da177e4SLinus Torvalds 			if (sfl->fl->label == freq.flr_label) {
5451da177e4SLinus Torvalds 				err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
54618367681SYOSHIFUJI Hideaki / 吉藤英明 				rcu_read_unlock_bh();
5471da177e4SLinus Torvalds 				return err;
5481da177e4SLinus Torvalds 			}
5491da177e4SLinus Torvalds 		}
55018367681SYOSHIFUJI Hideaki / 吉藤英明 		rcu_read_unlock_bh();
5511da177e4SLinus Torvalds 
552af31f412SEric W. Biederman 		if (freq.flr_share == IPV6_FL_S_NONE &&
553af31f412SEric W. Biederman 		    ns_capable(net->user_ns, CAP_NET_ADMIN)) {
55460e8fbc4SBenjamin Thery 			fl = fl_lookup(net, freq.flr_label);
5551da177e4SLinus Torvalds 			if (fl) {
5561da177e4SLinus Torvalds 				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
5571da177e4SLinus Torvalds 				fl_release(fl);
5581da177e4SLinus Torvalds 				return err;
5591da177e4SLinus Torvalds 			}
5601da177e4SLinus Torvalds 		}
5611da177e4SLinus Torvalds 		return -ESRCH;
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	case IPV6_FL_A_GET:
5641da177e4SLinus Torvalds 		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
5651da177e4SLinus Torvalds 			return -EINVAL;
5661da177e4SLinus Torvalds 
567ec0506dbSMaciej Żenczykowski 		fl = fl_create(net, sk, &freq, optval, optlen, &err);
5681da177e4SLinus Torvalds 		if (fl == NULL)
5691da177e4SLinus Torvalds 			return err;
5701da177e4SLinus Torvalds 		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 		if (freq.flr_label) {
5731da177e4SLinus Torvalds 			err = -EEXIST;
57418367681SYOSHIFUJI Hideaki / 吉藤英明 			rcu_read_lock_bh();
57518367681SYOSHIFUJI Hideaki / 吉藤英明 			for_each_sk_fl_rcu(np, sfl) {
5761da177e4SLinus Torvalds 				if (sfl->fl->label == freq.flr_label) {
5771da177e4SLinus Torvalds 					if (freq.flr_flags&IPV6_FL_F_EXCL) {
57818367681SYOSHIFUJI Hideaki / 吉藤英明 						rcu_read_unlock_bh();
5791da177e4SLinus Torvalds 						goto done;
5801da177e4SLinus Torvalds 					}
5811da177e4SLinus Torvalds 					fl1 = sfl->fl;
5824ea6a804SYan Zheng 					atomic_inc(&fl1->users);
5831da177e4SLinus Torvalds 					break;
5841da177e4SLinus Torvalds 				}
5851da177e4SLinus Torvalds 			}
58618367681SYOSHIFUJI Hideaki / 吉藤英明 			rcu_read_unlock_bh();
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 			if (fl1 == NULL)
58960e8fbc4SBenjamin Thery 				fl1 = fl_lookup(net, freq.flr_label);
5901da177e4SLinus Torvalds 			if (fl1) {
59178c2e502SPavel Emelyanov recheck:
5921da177e4SLinus Torvalds 				err = -EEXIST;
5931da177e4SLinus Torvalds 				if (freq.flr_flags&IPV6_FL_F_EXCL)
5941da177e4SLinus Torvalds 					goto release;
5951da177e4SLinus Torvalds 				err = -EPERM;
5961da177e4SLinus Torvalds 				if (fl1->share == IPV6_FL_S_EXCL ||
5971da177e4SLinus Torvalds 				    fl1->share != fl->share ||
5984f82f457SEric W. Biederman 				    ((fl1->share == IPV6_FL_S_PROCESS) &&
5994f82f457SEric W. Biederman 				     (fl1->owner.pid == fl->owner.pid)) ||
6004f82f457SEric W. Biederman 				    ((fl1->share == IPV6_FL_S_USER) &&
6014f82f457SEric W. Biederman 				     uid_eq(fl1->owner.uid, fl->owner.uid)))
6021da177e4SLinus Torvalds 					goto release;
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 				err = -ENOMEM;
6051da177e4SLinus Torvalds 				if (sfl1 == NULL)
6061da177e4SLinus Torvalds 					goto release;
6071da177e4SLinus Torvalds 				if (fl->linger > fl1->linger)
6081da177e4SLinus Torvalds 					fl1->linger = fl->linger;
6091da177e4SLinus Torvalds 				if ((long)(fl->expires - fl1->expires) > 0)
6101da177e4SLinus Torvalds 					fl1->expires = fl->expires;
61104028045SPavel Emelyanov 				fl_link(np, sfl1, fl1);
6121da177e4SLinus Torvalds 				fl_free(fl);
6131da177e4SLinus Torvalds 				return 0;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds release:
6161da177e4SLinus Torvalds 				fl_release(fl1);
6171da177e4SLinus Torvalds 				goto done;
6181da177e4SLinus Torvalds 			}
6191da177e4SLinus Torvalds 		}
6201da177e4SLinus Torvalds 		err = -ENOENT;
6211da177e4SLinus Torvalds 		if (!(freq.flr_flags&IPV6_FL_F_CREATE))
6221da177e4SLinus Torvalds 			goto done;
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 		err = -ENOMEM;
6251da177e4SLinus Torvalds 		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
6261da177e4SLinus Torvalds 			goto done;
6271da177e4SLinus Torvalds 
62860e8fbc4SBenjamin Thery 		fl1 = fl_intern(net, fl, freq.flr_label);
62978c2e502SPavel Emelyanov 		if (fl1 != NULL)
63078c2e502SPavel Emelyanov 			goto recheck;
6311da177e4SLinus Torvalds 
6326c94d361SDavid S. Miller 		if (!freq.flr_label) {
6336c94d361SDavid S. Miller 			if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
6346c94d361SDavid S. Miller 					 &fl->label, sizeof(fl->label))) {
6356c94d361SDavid S. Miller 				/* Intentionally ignore fault. */
6366c94d361SDavid S. Miller 			}
6376c94d361SDavid S. Miller 		}
6381da177e4SLinus Torvalds 
63904028045SPavel Emelyanov 		fl_link(np, sfl1, fl);
6401da177e4SLinus Torvalds 		return 0;
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 	default:
6431da177e4SLinus Torvalds 		return -EINVAL;
6441da177e4SLinus Torvalds 	}
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds done:
6471da177e4SLinus Torvalds 	fl_free(fl);
6481da177e4SLinus Torvalds 	kfree(sfl1);
6491da177e4SLinus Torvalds 	return err;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds struct ip6fl_iter_state {
6555983a3dfSBenjamin Thery 	struct seq_net_private p;
6564f82f457SEric W. Biederman 	struct pid_namespace *pid_ns;
6571da177e4SLinus Torvalds 	int bucket;
6581da177e4SLinus Torvalds };
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds #define ip6fl_seq_private(seq)	((struct ip6fl_iter_state *)(seq)->private)
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq)
6631da177e4SLinus Torvalds {
6641da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = NULL;
6651da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
6665983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
669d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
670d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
671d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
6721da177e4SLinus Torvalds 		}
673d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
674d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
675d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
6761da177e4SLinus Torvalds 	return fl;
6771da177e4SLinus Torvalds }
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl)
6801da177e4SLinus Torvalds {
6811da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
6825983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
6831da177e4SLinus Torvalds 
684d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	for_each_fl_continue_rcu(fl) {
685d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		if (net_eq(fl->fl_net, net))
686d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
6871da177e4SLinus Torvalds 	}
688d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
689d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again:
690d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	if (++state->bucket <= FL_HASH_MASK) {
691d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
692d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
693d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
694d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		}
695d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		goto try_again;
696d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
697d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
698d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
699d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
7001da177e4SLinus Torvalds 	return fl;
7011da177e4SLinus Torvalds }
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos)
7041da177e4SLinus Torvalds {
7051da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = ip6fl_get_first(seq);
7061da177e4SLinus Torvalds 	if (fl)
7071da177e4SLinus Torvalds 		while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL)
7081da177e4SLinus Torvalds 			--pos;
7091da177e4SLinus Torvalds 	return pos ? NULL : fl;
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
713d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__acquires(RCU)
7141da177e4SLinus Torvalds {
715d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
7161da177e4SLinus Torvalds 	return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
7201da177e4SLinus Torvalds {
7211da177e4SLinus Torvalds 	struct ip6_flowlabel *fl;
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN)
7241da177e4SLinus Torvalds 		fl = ip6fl_get_first(seq);
7251da177e4SLinus Torvalds 	else
7261da177e4SLinus Torvalds 		fl = ip6fl_get_next(seq, v);
7271da177e4SLinus Torvalds 	++*pos;
7281da177e4SLinus Torvalds 	return fl;
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v)
732d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__releases(RCU)
7331da177e4SLinus Torvalds {
734d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v)
7381da177e4SLinus Torvalds {
7394f82f457SEric W. Biederman 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7401b7c2dbcSJames Morris 	if (v == SEQ_START_TOKEN)
7411b7c2dbcSJames Morris 		seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n",
7421b7c2dbcSJames Morris 			   "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt");
7431b7c2dbcSJames Morris 	else {
7441b7c2dbcSJames Morris 		struct ip6_flowlabel *fl = v;
7451da177e4SLinus Torvalds 		seq_printf(seq,
7464b7a4274SHarvey Harrison 			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
74795c96174SEric Dumazet 			   (unsigned int)ntohl(fl->label),
7481da177e4SLinus Torvalds 			   fl->share,
7494f82f457SEric W. Biederman 			   ((fl->share == IPV6_FL_S_PROCESS) ?
7504f82f457SEric W. Biederman 			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
7514f82f457SEric W. Biederman 			    ((fl->share == IPV6_FL_S_USER) ?
7524f82f457SEric W. Biederman 			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
7534f82f457SEric W. Biederman 			     0)),
7541da177e4SLinus Torvalds 			   atomic_read(&fl->users),
7551da177e4SLinus Torvalds 			   fl->linger/HZ,
7561da177e4SLinus Torvalds 			   (long)(fl->expires - jiffies)/HZ,
757b071195dSHarvey Harrison 			   &fl->dst,
7581da177e4SLinus Torvalds 			   fl->opt ? fl->opt->opt_nflen : 0);
7591da177e4SLinus Torvalds 	}
7601da177e4SLinus Torvalds 	return 0;
7611da177e4SLinus Torvalds }
7621da177e4SLinus Torvalds 
76356b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = {
7641da177e4SLinus Torvalds 	.start	=	ip6fl_seq_start,
7651da177e4SLinus Torvalds 	.next	=	ip6fl_seq_next,
7661da177e4SLinus Torvalds 	.stop	=	ip6fl_seq_stop,
7671da177e4SLinus Torvalds 	.show	=	ip6fl_seq_show,
7681da177e4SLinus Torvalds };
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds static int ip6fl_seq_open(struct inode *inode, struct file *file)
7711da177e4SLinus Torvalds {
7724f82f457SEric W. Biederman 	struct seq_file *seq;
7734f82f457SEric W. Biederman 	struct ip6fl_iter_state *state;
7744f82f457SEric W. Biederman 	int err;
7754f82f457SEric W. Biederman 
7764f82f457SEric W. Biederman 	err = seq_open_net(inode, file, &ip6fl_seq_ops,
777cf7732e4SPavel Emelyanov 			   sizeof(struct ip6fl_iter_state));
7784f82f457SEric W. Biederman 
7794f82f457SEric W. Biederman 	if (!err) {
7804f82f457SEric W. Biederman 		seq = file->private_data;
7814f82f457SEric W. Biederman 		state = ip6fl_seq_private(seq);
7824f82f457SEric W. Biederman 		rcu_read_lock();
7834f82f457SEric W. Biederman 		state->pid_ns = get_pid_ns(task_active_pid_ns(current));
7844f82f457SEric W. Biederman 		rcu_read_unlock();
7854f82f457SEric W. Biederman 	}
7864f82f457SEric W. Biederman 	return err;
7874f82f457SEric W. Biederman }
7884f82f457SEric W. Biederman 
7894f82f457SEric W. Biederman static int ip6fl_seq_release(struct inode *inode, struct file *file)
7904f82f457SEric W. Biederman {
7914f82f457SEric W. Biederman 	struct seq_file *seq = file->private_data;
7924f82f457SEric W. Biederman 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7934f82f457SEric W. Biederman 	put_pid_ns(state->pid_ns);
7944f82f457SEric W. Biederman 	return seq_release_net(inode, file);
7951da177e4SLinus Torvalds }
7961da177e4SLinus Torvalds 
7979a32144eSArjan van de Ven static const struct file_operations ip6fl_seq_fops = {
7981da177e4SLinus Torvalds 	.owner		=	THIS_MODULE,
7991da177e4SLinus Torvalds 	.open		=	ip6fl_seq_open,
8001da177e4SLinus Torvalds 	.read		=	seq_read,
8011da177e4SLinus Torvalds 	.llseek		=	seq_lseek,
8024f82f457SEric W. Biederman 	.release	=	ip6fl_seq_release,
8031da177e4SLinus Torvalds };
8041da177e4SLinus Torvalds 
8052c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net)
8061da177e4SLinus Torvalds {
807d4beaa66SGao feng 	if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net,
808d4beaa66SGao feng 			 &ip6fl_seq_fops))
8090a3e78acSDaniel Lezcano 		return -ENOMEM;
8100a3e78acSDaniel Lezcano 	return 0;
8110a3e78acSDaniel Lezcano }
8120a3e78acSDaniel Lezcano 
8132c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
8140a3e78acSDaniel Lezcano {
815ece31ffdSGao feng 	remove_proc_entry("ip6_flowlabel", net->proc_net);
8160a3e78acSDaniel Lezcano }
8170a3e78acSDaniel Lezcano #else
8180a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net)
8190a3e78acSDaniel Lezcano {
8200a3e78acSDaniel Lezcano 	return 0;
8210a3e78acSDaniel Lezcano }
8220a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net)
8230a3e78acSDaniel Lezcano {
8240a3e78acSDaniel Lezcano }
8251da177e4SLinus Torvalds #endif
8260a3e78acSDaniel Lezcano 
8272c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net)
82860e8fbc4SBenjamin Thery {
82960e8fbc4SBenjamin Thery 	ip6_fl_purge(net);
8305983a3dfSBenjamin Thery 	ip6_flowlabel_proc_fini(net);
83160e8fbc4SBenjamin Thery }
83260e8fbc4SBenjamin Thery 
83360e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = {
8345983a3dfSBenjamin Thery 	.init = ip6_flowlabel_proc_init,
83560e8fbc4SBenjamin Thery 	.exit = ip6_flowlabel_net_exit,
83660e8fbc4SBenjamin Thery };
83760e8fbc4SBenjamin Thery 
8380a3e78acSDaniel Lezcano int ip6_flowlabel_init(void)
8390a3e78acSDaniel Lezcano {
8405983a3dfSBenjamin Thery 	return register_pernet_subsys(&ip6_flowlabel_net_ops);
8411da177e4SLinus Torvalds }
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void)
8441da177e4SLinus Torvalds {
8451da177e4SLinus Torvalds 	del_timer(&ip6_fl_gc_timer);
84660e8fbc4SBenjamin Thery 	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
8471da177e4SLinus Torvalds }
848