xref: /openbmc/linux/net/ipv6/ip6_flowlabel.c (revision f8c31c8f)
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;
348394055f6SFlorent Fourcot 
349394055f6SFlorent Fourcot 	spin_lock_bh(&ip6_fl_lock);
3501da177e4SLinus Torvalds 	fl->lastuse = jiffies;
3511da177e4SLinus Torvalds 	if (time_before(fl->linger, linger))
3521da177e4SLinus Torvalds 		fl->linger = linger;
3531da177e4SLinus Torvalds 	if (time_before(expires, fl->linger))
3541da177e4SLinus Torvalds 		expires = fl->linger;
3551da177e4SLinus Torvalds 	if (time_before(fl->expires, fl->lastuse + expires))
3561da177e4SLinus Torvalds 		fl->expires = fl->lastuse + expires;
357394055f6SFlorent Fourcot 	spin_unlock_bh(&ip6_fl_lock);
358394055f6SFlorent Fourcot 
3591da177e4SLinus Torvalds 	return 0;
3601da177e4SLinus Torvalds }
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds static struct ip6_flowlabel *
363ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
364ec0506dbSMaciej Żenczykowski 	  char __user *optval, int optlen, int *err_p)
3651da177e4SLinus Torvalds {
366684de409SDavid S. Miller 	struct ip6_flowlabel *fl = NULL;
3671da177e4SLinus Torvalds 	int olen;
3681da177e4SLinus Torvalds 	int addr_type;
3691da177e4SLinus Torvalds 	int err;
3701da177e4SLinus Torvalds 
371684de409SDavid S. Miller 	olen = optlen - CMSG_ALIGN(sizeof(*freq));
372684de409SDavid S. Miller 	err = -EINVAL;
373684de409SDavid S. Miller 	if (olen > 64 * 1024)
374684de409SDavid S. Miller 		goto done;
375684de409SDavid S. Miller 
3761da177e4SLinus Torvalds 	err = -ENOMEM;
3770c600edaSIngo Oeser 	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
3781da177e4SLinus Torvalds 	if (fl == NULL)
3791da177e4SLinus Torvalds 		goto done;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	if (olen > 0) {
3821da177e4SLinus Torvalds 		struct msghdr msg;
3834c9483b2SDavid S. Miller 		struct flowi6 flowi6;
3841da177e4SLinus Torvalds 		int junk;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 		err = -ENOMEM;
3871da177e4SLinus Torvalds 		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
3881da177e4SLinus Torvalds 		if (fl->opt == NULL)
3891da177e4SLinus Torvalds 			goto done;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 		memset(fl->opt, 0, sizeof(*fl->opt));
3921da177e4SLinus Torvalds 		fl->opt->tot_len = sizeof(*fl->opt) + olen;
3931da177e4SLinus Torvalds 		err = -EFAULT;
3941da177e4SLinus Torvalds 		if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
3951da177e4SLinus Torvalds 			goto done;
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 		msg.msg_controllen = olen;
3981da177e4SLinus Torvalds 		msg.msg_control = (void*)(fl->opt+1);
3994c9483b2SDavid S. Miller 		memset(&flowi6, 0, sizeof(flowi6));
4001da177e4SLinus Torvalds 
40173df66f8STom Parkin 		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt,
40273df66f8STom Parkin 					    &junk, &junk, &junk);
4031da177e4SLinus Torvalds 		if (err)
4041da177e4SLinus Torvalds 			goto done;
4051da177e4SLinus Torvalds 		err = -EINVAL;
4061da177e4SLinus Torvalds 		if (fl->opt->opt_flen)
4071da177e4SLinus Torvalds 			goto done;
4081da177e4SLinus Torvalds 		if (fl->opt->opt_nflen == 0) {
4091da177e4SLinus Torvalds 			kfree(fl->opt);
4101da177e4SLinus Torvalds 			fl->opt = NULL;
4111da177e4SLinus Torvalds 		}
4121da177e4SLinus Torvalds 	}
4131da177e4SLinus Torvalds 
41460e8fbc4SBenjamin Thery 	fl->fl_net = hold_net(net);
4151da177e4SLinus Torvalds 	fl->expires = jiffies;
4161da177e4SLinus Torvalds 	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
4171da177e4SLinus Torvalds 	if (err)
4181da177e4SLinus Torvalds 		goto done;
4191da177e4SLinus Torvalds 	fl->share = freq->flr_share;
4201da177e4SLinus Torvalds 	addr_type = ipv6_addr_type(&freq->flr_dst);
42135700212SJoe Perches 	if ((addr_type & IPV6_ADDR_MAPPED) ||
42235700212SJoe Perches 	    addr_type == IPV6_ADDR_ANY) {
423c6817e4cSJames Morris 		err = -EINVAL;
4241da177e4SLinus Torvalds 		goto done;
425c6817e4cSJames Morris 	}
4264e3fd7a0SAlexey Dobriyan 	fl->dst = freq->flr_dst;
4271da177e4SLinus Torvalds 	atomic_set(&fl->users, 1);
4281da177e4SLinus Torvalds 	switch (fl->share) {
4291da177e4SLinus Torvalds 	case IPV6_FL_S_EXCL:
4301da177e4SLinus Torvalds 	case IPV6_FL_S_ANY:
4311da177e4SLinus Torvalds 		break;
4321da177e4SLinus Torvalds 	case IPV6_FL_S_PROCESS:
4334f82f457SEric W. Biederman 		fl->owner.pid = get_task_pid(current, PIDTYPE_PID);
4341da177e4SLinus Torvalds 		break;
4351da177e4SLinus Torvalds 	case IPV6_FL_S_USER:
4364f82f457SEric W. Biederman 		fl->owner.uid = current_euid();
4371da177e4SLinus Torvalds 		break;
4381da177e4SLinus Torvalds 	default:
4391da177e4SLinus Torvalds 		err = -EINVAL;
4401da177e4SLinus Torvalds 		goto done;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 	return fl;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds done:
4451da177e4SLinus Torvalds 	fl_free(fl);
4461da177e4SLinus Torvalds 	*err_p = err;
4471da177e4SLinus Torvalds 	return NULL;
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds static int mem_check(struct sock *sk)
4511da177e4SLinus Torvalds {
4521da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
4531da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl;
4541da177e4SLinus Torvalds 	int room = FL_MAX_SIZE - atomic_read(&fl_size);
4551da177e4SLinus Torvalds 	int count = 0;
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
4581da177e4SLinus Torvalds 		return 0;
4591da177e4SLinus Torvalds 
460f8c31c8fSHannes Frederic Sowa 	rcu_read_lock_bh();
46118367681SYOSHIFUJI Hideaki / 吉藤英明 	for_each_sk_fl_rcu(np, sfl)
4621da177e4SLinus Torvalds 		count++;
463f8c31c8fSHannes Frederic Sowa 	rcu_read_unlock_bh();
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	if (room <= 0 ||
4661da177e4SLinus Torvalds 	    ((count >= FL_MAX_PER_SOCK ||
46735700212SJoe Perches 	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
46835700212SJoe Perches 	     !capable(CAP_NET_ADMIN)))
4691da177e4SLinus Torvalds 		return -ENOBUFS;
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
47404028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
47504028045SPavel Emelyanov 		struct ip6_flowlabel *fl)
47604028045SPavel Emelyanov {
47718367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_sk_fl_lock);
47804028045SPavel Emelyanov 	sfl->fl = fl;
47904028045SPavel Emelyanov 	sfl->next = np->ipv6_fl_list;
48018367681SYOSHIFUJI Hideaki / 吉藤英明 	rcu_assign_pointer(np->ipv6_fl_list, sfl);
48118367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_sk_fl_lock);
48204028045SPavel Emelyanov }
48304028045SPavel Emelyanov 
4843fdfa5ffSFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq)
4853fdfa5ffSFlorent Fourcot {
4863fdfa5ffSFlorent Fourcot 	struct ipv6_pinfo *np = inet6_sk(sk);
4873fdfa5ffSFlorent Fourcot 	struct ipv6_fl_socklist *sfl;
4883fdfa5ffSFlorent Fourcot 
4893fdfa5ffSFlorent Fourcot 	rcu_read_lock_bh();
4903fdfa5ffSFlorent Fourcot 
4913fdfa5ffSFlorent Fourcot 	for_each_sk_fl_rcu(np, sfl) {
4923fdfa5ffSFlorent Fourcot 		if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
4933fdfa5ffSFlorent Fourcot 			spin_lock_bh(&ip6_fl_lock);
4943fdfa5ffSFlorent Fourcot 			freq->flr_label = sfl->fl->label;
4953fdfa5ffSFlorent Fourcot 			freq->flr_dst = sfl->fl->dst;
4963fdfa5ffSFlorent Fourcot 			freq->flr_share = sfl->fl->share;
4973fdfa5ffSFlorent Fourcot 			freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
4983fdfa5ffSFlorent Fourcot 			freq->flr_linger = sfl->fl->linger / HZ;
4993fdfa5ffSFlorent Fourcot 
5003fdfa5ffSFlorent Fourcot 			spin_unlock_bh(&ip6_fl_lock);
5013fdfa5ffSFlorent Fourcot 			rcu_read_unlock_bh();
5023fdfa5ffSFlorent Fourcot 			return 0;
5033fdfa5ffSFlorent Fourcot 		}
5043fdfa5ffSFlorent Fourcot 	}
5053fdfa5ffSFlorent Fourcot 	rcu_read_unlock_bh();
5063fdfa5ffSFlorent Fourcot 
5073fdfa5ffSFlorent Fourcot 	return -ENOENT;
5083fdfa5ffSFlorent Fourcot }
5093fdfa5ffSFlorent Fourcot 
5101da177e4SLinus Torvalds int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
5111da177e4SLinus Torvalds {
51255205d40SIngo Molnar 	int uninitialized_var(err);
51360e8fbc4SBenjamin Thery 	struct net *net = sock_net(sk);
5141da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
5151da177e4SLinus Torvalds 	struct in6_flowlabel_req freq;
5161da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl1=NULL;
5177f0e44acSEric Dumazet 	struct ipv6_fl_socklist *sfl;
5187f0e44acSEric Dumazet 	struct ipv6_fl_socklist __rcu **sflp;
51978c2e502SPavel Emelyanov 	struct ip6_flowlabel *fl, *fl1 = NULL;
52078c2e502SPavel Emelyanov 
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	if (optlen < sizeof(freq))
5231da177e4SLinus Torvalds 		return -EINVAL;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	if (copy_from_user(&freq, optval, sizeof(freq)))
5261da177e4SLinus Torvalds 		return -EFAULT;
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 	switch (freq.flr_action) {
5291da177e4SLinus Torvalds 	case IPV6_FL_A_PUT:
53018367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_lock_bh(&ip6_sk_fl_lock);
53118367681SYOSHIFUJI Hideaki / 吉藤英明 		for (sflp = &np->ipv6_fl_list;
53218367681SYOSHIFUJI Hideaki / 吉藤英明 		     (sfl = rcu_dereference(*sflp))!=NULL;
53318367681SYOSHIFUJI Hideaki / 吉藤英明 		     sflp = &sfl->next) {
5341da177e4SLinus Torvalds 			if (sfl->fl->label == freq.flr_label) {
5351da177e4SLinus Torvalds 				if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
5361da177e4SLinus Torvalds 					np->flow_label &= ~IPV6_FLOWLABEL_MASK;
53718367681SYOSHIFUJI Hideaki / 吉藤英明 				*sflp = rcu_dereference(sfl->next);
53818367681SYOSHIFUJI Hideaki / 吉藤英明 				spin_unlock_bh(&ip6_sk_fl_lock);
5391da177e4SLinus Torvalds 				fl_release(sfl->fl);
54018367681SYOSHIFUJI Hideaki / 吉藤英明 				kfree_rcu(sfl, rcu);
5411da177e4SLinus Torvalds 				return 0;
5421da177e4SLinus Torvalds 			}
5431da177e4SLinus Torvalds 		}
54418367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_unlock_bh(&ip6_sk_fl_lock);
5451da177e4SLinus Torvalds 		return -ESRCH;
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	case IPV6_FL_A_RENEW:
54818367681SYOSHIFUJI Hideaki / 吉藤英明 		rcu_read_lock_bh();
54918367681SYOSHIFUJI Hideaki / 吉藤英明 		for_each_sk_fl_rcu(np, sfl) {
5501da177e4SLinus Torvalds 			if (sfl->fl->label == freq.flr_label) {
5511da177e4SLinus Torvalds 				err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
55218367681SYOSHIFUJI Hideaki / 吉藤英明 				rcu_read_unlock_bh();
5531da177e4SLinus Torvalds 				return err;
5541da177e4SLinus Torvalds 			}
5551da177e4SLinus Torvalds 		}
55618367681SYOSHIFUJI Hideaki / 吉藤英明 		rcu_read_unlock_bh();
5571da177e4SLinus Torvalds 
558af31f412SEric W. Biederman 		if (freq.flr_share == IPV6_FL_S_NONE &&
559af31f412SEric W. Biederman 		    ns_capable(net->user_ns, CAP_NET_ADMIN)) {
56060e8fbc4SBenjamin Thery 			fl = fl_lookup(net, freq.flr_label);
5611da177e4SLinus Torvalds 			if (fl) {
5621da177e4SLinus Torvalds 				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
5631da177e4SLinus Torvalds 				fl_release(fl);
5641da177e4SLinus Torvalds 				return err;
5651da177e4SLinus Torvalds 			}
5661da177e4SLinus Torvalds 		}
5671da177e4SLinus Torvalds 		return -ESRCH;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	case IPV6_FL_A_GET:
5701da177e4SLinus Torvalds 		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
5711da177e4SLinus Torvalds 			return -EINVAL;
5721da177e4SLinus Torvalds 
573ec0506dbSMaciej Żenczykowski 		fl = fl_create(net, sk, &freq, optval, optlen, &err);
5741da177e4SLinus Torvalds 		if (fl == NULL)
5751da177e4SLinus Torvalds 			return err;
5761da177e4SLinus Torvalds 		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 		if (freq.flr_label) {
5791da177e4SLinus Torvalds 			err = -EEXIST;
58018367681SYOSHIFUJI Hideaki / 吉藤英明 			rcu_read_lock_bh();
58118367681SYOSHIFUJI Hideaki / 吉藤英明 			for_each_sk_fl_rcu(np, sfl) {
5821da177e4SLinus Torvalds 				if (sfl->fl->label == freq.flr_label) {
5831da177e4SLinus Torvalds 					if (freq.flr_flags&IPV6_FL_F_EXCL) {
58418367681SYOSHIFUJI Hideaki / 吉藤英明 						rcu_read_unlock_bh();
5851da177e4SLinus Torvalds 						goto done;
5861da177e4SLinus Torvalds 					}
5871da177e4SLinus Torvalds 					fl1 = sfl->fl;
5884ea6a804SYan Zheng 					atomic_inc(&fl1->users);
5891da177e4SLinus Torvalds 					break;
5901da177e4SLinus Torvalds 				}
5911da177e4SLinus Torvalds 			}
59218367681SYOSHIFUJI Hideaki / 吉藤英明 			rcu_read_unlock_bh();
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 			if (fl1 == NULL)
59560e8fbc4SBenjamin Thery 				fl1 = fl_lookup(net, freq.flr_label);
5961da177e4SLinus Torvalds 			if (fl1) {
59778c2e502SPavel Emelyanov recheck:
5981da177e4SLinus Torvalds 				err = -EEXIST;
5991da177e4SLinus Torvalds 				if (freq.flr_flags&IPV6_FL_F_EXCL)
6001da177e4SLinus Torvalds 					goto release;
6011da177e4SLinus Torvalds 				err = -EPERM;
6021da177e4SLinus Torvalds 				if (fl1->share == IPV6_FL_S_EXCL ||
6031da177e4SLinus Torvalds 				    fl1->share != fl->share ||
6044f82f457SEric W. Biederman 				    ((fl1->share == IPV6_FL_S_PROCESS) &&
6054f82f457SEric W. Biederman 				     (fl1->owner.pid == fl->owner.pid)) ||
6064f82f457SEric W. Biederman 				    ((fl1->share == IPV6_FL_S_USER) &&
6074f82f457SEric W. Biederman 				     uid_eq(fl1->owner.uid, fl->owner.uid)))
6081da177e4SLinus Torvalds 					goto release;
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 				err = -ENOMEM;
6111da177e4SLinus Torvalds 				if (sfl1 == NULL)
6121da177e4SLinus Torvalds 					goto release;
6131da177e4SLinus Torvalds 				if (fl->linger > fl1->linger)
6141da177e4SLinus Torvalds 					fl1->linger = fl->linger;
6151da177e4SLinus Torvalds 				if ((long)(fl->expires - fl1->expires) > 0)
6161da177e4SLinus Torvalds 					fl1->expires = fl->expires;
61704028045SPavel Emelyanov 				fl_link(np, sfl1, fl1);
6181da177e4SLinus Torvalds 				fl_free(fl);
6191da177e4SLinus Torvalds 				return 0;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds release:
6221da177e4SLinus Torvalds 				fl_release(fl1);
6231da177e4SLinus Torvalds 				goto done;
6241da177e4SLinus Torvalds 			}
6251da177e4SLinus Torvalds 		}
6261da177e4SLinus Torvalds 		err = -ENOENT;
6271da177e4SLinus Torvalds 		if (!(freq.flr_flags&IPV6_FL_F_CREATE))
6281da177e4SLinus Torvalds 			goto done;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 		err = -ENOMEM;
6311da177e4SLinus Torvalds 		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
6321da177e4SLinus Torvalds 			goto done;
6331da177e4SLinus Torvalds 
63460e8fbc4SBenjamin Thery 		fl1 = fl_intern(net, fl, freq.flr_label);
63578c2e502SPavel Emelyanov 		if (fl1 != NULL)
63678c2e502SPavel Emelyanov 			goto recheck;
6371da177e4SLinus Torvalds 
6386c94d361SDavid S. Miller 		if (!freq.flr_label) {
6396c94d361SDavid S. Miller 			if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
6406c94d361SDavid S. Miller 					 &fl->label, sizeof(fl->label))) {
6416c94d361SDavid S. Miller 				/* Intentionally ignore fault. */
6426c94d361SDavid S. Miller 			}
6436c94d361SDavid S. Miller 		}
6441da177e4SLinus Torvalds 
64504028045SPavel Emelyanov 		fl_link(np, sfl1, fl);
6461da177e4SLinus Torvalds 		return 0;
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	default:
6491da177e4SLinus Torvalds 		return -EINVAL;
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds done:
6531da177e4SLinus Torvalds 	fl_free(fl);
6541da177e4SLinus Torvalds 	kfree(sfl1);
6551da177e4SLinus Torvalds 	return err;
6561da177e4SLinus Torvalds }
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds struct ip6fl_iter_state {
6615983a3dfSBenjamin Thery 	struct seq_net_private p;
6624f82f457SEric W. Biederman 	struct pid_namespace *pid_ns;
6631da177e4SLinus Torvalds 	int bucket;
6641da177e4SLinus Torvalds };
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds #define ip6fl_seq_private(seq)	((struct ip6fl_iter_state *)(seq)->private)
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq)
6691da177e4SLinus Torvalds {
6701da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = NULL;
6711da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
6725983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
675d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
676d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
677d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
6781da177e4SLinus Torvalds 		}
679d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
680d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
681d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
6821da177e4SLinus Torvalds 	return fl;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl)
6861da177e4SLinus Torvalds {
6871da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
6885983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
6891da177e4SLinus Torvalds 
690d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	for_each_fl_continue_rcu(fl) {
691d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		if (net_eq(fl->fl_net, net))
692d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
6931da177e4SLinus Torvalds 	}
694d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
695d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again:
696d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	if (++state->bucket <= FL_HASH_MASK) {
697d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
698d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
699d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
700d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		}
701d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		goto try_again;
702d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
703d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
704d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
705d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
7061da177e4SLinus Torvalds 	return fl;
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos)
7101da177e4SLinus Torvalds {
7111da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = ip6fl_get_first(seq);
7121da177e4SLinus Torvalds 	if (fl)
7131da177e4SLinus Torvalds 		while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL)
7141da177e4SLinus Torvalds 			--pos;
7151da177e4SLinus Torvalds 	return pos ? NULL : fl;
7161da177e4SLinus Torvalds }
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
719d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__acquires(RCU)
7201da177e4SLinus Torvalds {
721d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
7221da177e4SLinus Torvalds 	return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	struct ip6_flowlabel *fl;
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN)
7301da177e4SLinus Torvalds 		fl = ip6fl_get_first(seq);
7311da177e4SLinus Torvalds 	else
7321da177e4SLinus Torvalds 		fl = ip6fl_get_next(seq, v);
7331da177e4SLinus Torvalds 	++*pos;
7341da177e4SLinus Torvalds 	return fl;
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v)
738d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__releases(RCU)
7391da177e4SLinus Torvalds {
740d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
7411da177e4SLinus Torvalds }
7421da177e4SLinus Torvalds 
7431b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v)
7441da177e4SLinus Torvalds {
7454f82f457SEric W. Biederman 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7461b7c2dbcSJames Morris 	if (v == SEQ_START_TOKEN)
7471b7c2dbcSJames Morris 		seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n",
7481b7c2dbcSJames Morris 			   "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt");
7491b7c2dbcSJames Morris 	else {
7501b7c2dbcSJames Morris 		struct ip6_flowlabel *fl = v;
7511da177e4SLinus Torvalds 		seq_printf(seq,
7524b7a4274SHarvey Harrison 			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
75395c96174SEric Dumazet 			   (unsigned int)ntohl(fl->label),
7541da177e4SLinus Torvalds 			   fl->share,
7554f82f457SEric W. Biederman 			   ((fl->share == IPV6_FL_S_PROCESS) ?
7564f82f457SEric W. Biederman 			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
7574f82f457SEric W. Biederman 			    ((fl->share == IPV6_FL_S_USER) ?
7584f82f457SEric W. Biederman 			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
7594f82f457SEric W. Biederman 			     0)),
7601da177e4SLinus Torvalds 			   atomic_read(&fl->users),
7611da177e4SLinus Torvalds 			   fl->linger/HZ,
7621da177e4SLinus Torvalds 			   (long)(fl->expires - jiffies)/HZ,
763b071195dSHarvey Harrison 			   &fl->dst,
7641da177e4SLinus Torvalds 			   fl->opt ? fl->opt->opt_nflen : 0);
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds 	return 0;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
76956b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = {
7701da177e4SLinus Torvalds 	.start	=	ip6fl_seq_start,
7711da177e4SLinus Torvalds 	.next	=	ip6fl_seq_next,
7721da177e4SLinus Torvalds 	.stop	=	ip6fl_seq_stop,
7731da177e4SLinus Torvalds 	.show	=	ip6fl_seq_show,
7741da177e4SLinus Torvalds };
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds static int ip6fl_seq_open(struct inode *inode, struct file *file)
7771da177e4SLinus Torvalds {
7784f82f457SEric W. Biederman 	struct seq_file *seq;
7794f82f457SEric W. Biederman 	struct ip6fl_iter_state *state;
7804f82f457SEric W. Biederman 	int err;
7814f82f457SEric W. Biederman 
7824f82f457SEric W. Biederman 	err = seq_open_net(inode, file, &ip6fl_seq_ops,
783cf7732e4SPavel Emelyanov 			   sizeof(struct ip6fl_iter_state));
7844f82f457SEric W. Biederman 
7854f82f457SEric W. Biederman 	if (!err) {
7864f82f457SEric W. Biederman 		seq = file->private_data;
7874f82f457SEric W. Biederman 		state = ip6fl_seq_private(seq);
7884f82f457SEric W. Biederman 		rcu_read_lock();
7894f82f457SEric W. Biederman 		state->pid_ns = get_pid_ns(task_active_pid_ns(current));
7904f82f457SEric W. Biederman 		rcu_read_unlock();
7914f82f457SEric W. Biederman 	}
7924f82f457SEric W. Biederman 	return err;
7934f82f457SEric W. Biederman }
7944f82f457SEric W. Biederman 
7954f82f457SEric W. Biederman static int ip6fl_seq_release(struct inode *inode, struct file *file)
7964f82f457SEric W. Biederman {
7974f82f457SEric W. Biederman 	struct seq_file *seq = file->private_data;
7984f82f457SEric W. Biederman 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7994f82f457SEric W. Biederman 	put_pid_ns(state->pid_ns);
8004f82f457SEric W. Biederman 	return seq_release_net(inode, file);
8011da177e4SLinus Torvalds }
8021da177e4SLinus Torvalds 
8039a32144eSArjan van de Ven static const struct file_operations ip6fl_seq_fops = {
8041da177e4SLinus Torvalds 	.owner		=	THIS_MODULE,
8051da177e4SLinus Torvalds 	.open		=	ip6fl_seq_open,
8061da177e4SLinus Torvalds 	.read		=	seq_read,
8071da177e4SLinus Torvalds 	.llseek		=	seq_lseek,
8084f82f457SEric W. Biederman 	.release	=	ip6fl_seq_release,
8091da177e4SLinus Torvalds };
8101da177e4SLinus Torvalds 
8112c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net)
8121da177e4SLinus Torvalds {
813d4beaa66SGao feng 	if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net,
814d4beaa66SGao feng 			 &ip6fl_seq_fops))
8150a3e78acSDaniel Lezcano 		return -ENOMEM;
8160a3e78acSDaniel Lezcano 	return 0;
8170a3e78acSDaniel Lezcano }
8180a3e78acSDaniel Lezcano 
8192c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
8200a3e78acSDaniel Lezcano {
821ece31ffdSGao feng 	remove_proc_entry("ip6_flowlabel", net->proc_net);
8220a3e78acSDaniel Lezcano }
8230a3e78acSDaniel Lezcano #else
8240a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net)
8250a3e78acSDaniel Lezcano {
8260a3e78acSDaniel Lezcano 	return 0;
8270a3e78acSDaniel Lezcano }
8280a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net)
8290a3e78acSDaniel Lezcano {
8300a3e78acSDaniel Lezcano }
8311da177e4SLinus Torvalds #endif
8320a3e78acSDaniel Lezcano 
8332c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net)
83460e8fbc4SBenjamin Thery {
83560e8fbc4SBenjamin Thery 	ip6_fl_purge(net);
8365983a3dfSBenjamin Thery 	ip6_flowlabel_proc_fini(net);
83760e8fbc4SBenjamin Thery }
83860e8fbc4SBenjamin Thery 
83960e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = {
8405983a3dfSBenjamin Thery 	.init = ip6_flowlabel_proc_init,
84160e8fbc4SBenjamin Thery 	.exit = ip6_flowlabel_net_exit,
84260e8fbc4SBenjamin Thery };
84360e8fbc4SBenjamin Thery 
8440a3e78acSDaniel Lezcano int ip6_flowlabel_init(void)
8450a3e78acSDaniel Lezcano {
8465983a3dfSBenjamin Thery 	return register_pernet_subsys(&ip6_flowlabel_net_ops);
8471da177e4SLinus Torvalds }
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	del_timer(&ip6_fl_gc_timer);
85260e8fbc4SBenjamin Thery 	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
8531da177e4SLinus Torvalds }
854