xref: /openbmc/linux/net/ipv6/ip6_flowlabel.c (revision 4c5c496a)
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)				\
61*4c5c496aSEric Dumazet 	for (fl = rcu_dereference(fl_ht[(hash)]);		\
62d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	     fl != NULL;					\
63*4c5c496aSEric Dumazet 	     fl = rcu_dereference(fl->next))
64d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 #define for_each_fl_continue_rcu(fl)				\
65*4c5c496aSEric Dumazet 	for (fl = rcu_dereference(fl->next);			\
66d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	     fl != NULL;					\
67*4c5c496aSEric Dumazet 	     fl = rcu_dereference(fl->next))
681da177e4SLinus Torvalds 
6918367681SYOSHIFUJI Hideaki / 吉藤英明 #define for_each_sk_fl_rcu(np, sfl)				\
70*4c5c496aSEric Dumazet 	for (sfl = rcu_dereference(np->ipv6_fl_list);	\
7118367681SYOSHIFUJI Hideaki / 吉藤英明 	     sfl != NULL;					\
72*4c5c496aSEric Dumazet 	     sfl = rcu_dereference(sfl->next))
7318367681SYOSHIFUJI Hideaki / 吉藤英明 
__fl_lookup(struct net * net,__be32 label)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 
fl_lookup(struct net * net,__be32 label)8560e8fbc4SBenjamin Thery static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds 	struct ip6_flowlabel *fl;
881da177e4SLinus Torvalds 
89*4c5c496aSEric Dumazet 	rcu_read_lock();
9060e8fbc4SBenjamin Thery 	fl = __fl_lookup(net, label);
91d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	if (fl && !atomic_inc_not_zero(&fl->users))
92d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		fl = NULL;
93*4c5c496aSEric Dumazet 	rcu_read_unlock();
941da177e4SLinus Torvalds 	return fl;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
fl_shared_exclusive(struct ip6_flowlabel * fl)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 
fl_free_rcu(struct rcu_head * head)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 
fl_free(struct ip6_flowlabel * fl)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 
fl_release(struct ip6_flowlabel * fl)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 
ip6_fl_gc(struct timer_list * unused)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 
ip6_fl_purge(struct net * net)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 
fl_intern(struct net * net,struct ip6_flowlabel * fl,__be32 label)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 
220*4c5c496aSEric Dumazet 	rcu_read_lock();
221d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_fl_lock);
2221da177e4SLinus Torvalds 	if (label == 0) {
2231da177e4SLinus Torvalds 		for (;;) {
224a251c17aSJason A. Donenfeld 			fl->label = htonl(get_random_u32())&IPV6_FLOWLABEL_MASK;
2251da177e4SLinus Torvalds 			if (fl->label) {
22660e8fbc4SBenjamin Thery 				lfl = __fl_lookup(net, fl->label);
22763159f29SIan Morris 				if (!lfl)
2281da177e4SLinus Torvalds 					break;
2291da177e4SLinus Torvalds 			}
2301da177e4SLinus Torvalds 		}
23178c2e502SPavel Emelyanov 	} else {
23278c2e502SPavel Emelyanov 		/*
23378c2e502SPavel Emelyanov 		 * we dropper the ip6_fl_lock, so this entry could reappear
23478c2e502SPavel Emelyanov 		 * and we need to recheck with it.
23578c2e502SPavel Emelyanov 		 *
23678c2e502SPavel Emelyanov 		 * OTOH no need to search the active socket first, like it is
23778c2e502SPavel Emelyanov 		 * done in ipv6_flowlabel_opt - sock is locked, so new entry
23878c2e502SPavel Emelyanov 		 * with the same label can only appear on another sock
23978c2e502SPavel Emelyanov 		 */
24060e8fbc4SBenjamin Thery 		lfl = __fl_lookup(net, fl->label);
24153b24b8fSIan Morris 		if (lfl) {
24278c2e502SPavel Emelyanov 			atomic_inc(&lfl->users);
243d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			spin_unlock_bh(&ip6_fl_lock);
244*4c5c496aSEric Dumazet 			rcu_read_unlock();
24578c2e502SPavel Emelyanov 			return lfl;
24678c2e502SPavel Emelyanov 		}
2471da177e4SLinus Torvalds 	}
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	fl->lastuse = jiffies;
2501da177e4SLinus Torvalds 	fl->next = fl_ht[FL_HASH(fl->label)];
251d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
2521da177e4SLinus Torvalds 	atomic_inc(&fl_size);
253d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_fl_lock);
254*4c5c496aSEric Dumazet 	rcu_read_unlock();
25578c2e502SPavel Emelyanov 	return NULL;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds /* Socket flowlabel lists */
2611da177e4SLinus Torvalds 
__fl6_sock_lookup(struct sock * sk,__be32 label)26259c820b2SWillem de Bruijn struct ip6_flowlabel *__fl6_sock_lookup(struct sock *sk, __be32 label)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl;
2651da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	label &= IPV6_FLOWLABEL_MASK;
2681da177e4SLinus Torvalds 
269*4c5c496aSEric Dumazet 	rcu_read_lock();
27018367681SYOSHIFUJI Hideaki / 吉藤英明 	for_each_sk_fl_rcu(np, sfl) {
2711da177e4SLinus Torvalds 		struct ip6_flowlabel *fl = sfl->fl;
27265a3c497SEric Dumazet 
27365a3c497SEric Dumazet 		if (fl->label == label && atomic_inc_not_zero(&fl->users)) {
2741da177e4SLinus Torvalds 			fl->lastuse = jiffies;
275*4c5c496aSEric Dumazet 			rcu_read_unlock();
2761da177e4SLinus Torvalds 			return fl;
2771da177e4SLinus Torvalds 		}
2781da177e4SLinus Torvalds 	}
279*4c5c496aSEric Dumazet 	rcu_read_unlock();
2801da177e4SLinus Torvalds 	return NULL;
2811da177e4SLinus Torvalds }
28259c820b2SWillem de Bruijn EXPORT_SYMBOL_GPL(__fl6_sock_lookup);
2833cf3dc6cSArnaldo Carvalho de Melo 
fl6_free_socklist(struct sock * sk)2841da177e4SLinus Torvalds void fl6_free_socklist(struct sock *sk)
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
2871da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl;
2881da177e4SLinus Torvalds 
28918367681SYOSHIFUJI Hideaki / 吉藤英明 	if (!rcu_access_pointer(np->ipv6_fl_list))
290f256dc59SYOSHIFUJI Hideaki / 吉藤英明 		return;
291f256dc59SYOSHIFUJI Hideaki / 吉藤英明 
29218367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_sk_fl_lock);
29318367681SYOSHIFUJI Hideaki / 吉藤英明 	while ((sfl = rcu_dereference_protected(np->ipv6_fl_list,
29418367681SYOSHIFUJI Hideaki / 吉藤英明 						lockdep_is_held(&ip6_sk_fl_lock))) != NULL) {
29518367681SYOSHIFUJI Hideaki / 吉藤英明 		np->ipv6_fl_list = sfl->next;
29618367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_unlock_bh(&ip6_sk_fl_lock);
297f256dc59SYOSHIFUJI Hideaki / 吉藤英明 
2981da177e4SLinus Torvalds 		fl_release(sfl->fl);
29918367681SYOSHIFUJI Hideaki / 吉藤英明 		kfree_rcu(sfl, rcu);
30018367681SYOSHIFUJI Hideaki / 吉藤英明 
30118367681SYOSHIFUJI Hideaki / 吉藤英明 		spin_lock_bh(&ip6_sk_fl_lock);
3021da177e4SLinus Torvalds 	}
30318367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_sk_fl_lock);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds /* Service routines */
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /*
3101da177e4SLinus Torvalds    It is the only difficult place. flowlabel enforces equal headers
3111da177e4SLinus Torvalds    before and including routing header, however user may supply options
3121da177e4SLinus Torvalds    following rthdr.
3131da177e4SLinus Torvalds  */
3141da177e4SLinus Torvalds 
fl6_merge_options(struct ipv6_txoptions * opt_space,struct ip6_flowlabel * fl,struct ipv6_txoptions * fopt)3151da177e4SLinus Torvalds struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
3161da177e4SLinus Torvalds 					 struct ip6_flowlabel *fl,
3171da177e4SLinus Torvalds 					 struct ipv6_txoptions *fopt)
3181da177e4SLinus Torvalds {
319df9890c3SYOSHIFUJI Hideaki 	struct ipv6_txoptions *fl_opt = fl->opt;
3201da177e4SLinus Torvalds 
32163159f29SIan Morris 	if (!fopt || fopt->opt_flen == 0)
3221da177e4SLinus Torvalds 		return fl_opt;
3231da177e4SLinus Torvalds 
32453b24b8fSIan Morris 	if (fl_opt) {
3251da177e4SLinus Torvalds 		opt_space->hopopt = fl_opt->hopopt;
326df9890c3SYOSHIFUJI Hideaki 		opt_space->dst0opt = fl_opt->dst0opt;
3271da177e4SLinus Torvalds 		opt_space->srcrt = fl_opt->srcrt;
3281da177e4SLinus Torvalds 		opt_space->opt_nflen = fl_opt->opt_nflen;
3291da177e4SLinus Torvalds 	} else {
3301da177e4SLinus Torvalds 		if (fopt->opt_nflen == 0)
3311da177e4SLinus Torvalds 			return fopt;
3321da177e4SLinus Torvalds 		opt_space->hopopt = NULL;
3331da177e4SLinus Torvalds 		opt_space->dst0opt = NULL;
3341da177e4SLinus Torvalds 		opt_space->srcrt = NULL;
3351da177e4SLinus Torvalds 		opt_space->opt_nflen = 0;
3361da177e4SLinus Torvalds 	}
3371da177e4SLinus Torvalds 	opt_space->dst1opt = fopt->dst1opt;
3381da177e4SLinus Torvalds 	opt_space->opt_flen = fopt->opt_flen;
339864e2a1fSEric Dumazet 	opt_space->tot_len = fopt->tot_len;
3401da177e4SLinus Torvalds 	return opt_space;
3411da177e4SLinus Torvalds }
342a495f836SChris Elston EXPORT_SYMBOL_GPL(fl6_merge_options);
3431da177e4SLinus Torvalds 
check_linger(unsigned long ttl)3441da177e4SLinus Torvalds static unsigned long check_linger(unsigned long ttl)
3451da177e4SLinus Torvalds {
3461da177e4SLinus Torvalds 	if (ttl < FL_MIN_LINGER)
3471da177e4SLinus Torvalds 		return FL_MIN_LINGER*HZ;
3481da177e4SLinus Torvalds 	if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))
3491da177e4SLinus Torvalds 		return 0;
3501da177e4SLinus Torvalds 	return ttl*HZ;
3511da177e4SLinus Torvalds }
3521da177e4SLinus Torvalds 
fl6_renew(struct ip6_flowlabel * fl,unsigned long linger,unsigned long expires)3531da177e4SLinus Torvalds static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires)
3541da177e4SLinus Torvalds {
3551da177e4SLinus Torvalds 	linger = check_linger(linger);
3561da177e4SLinus Torvalds 	if (!linger)
3571da177e4SLinus Torvalds 		return -EPERM;
3581da177e4SLinus Torvalds 	expires = check_linger(expires);
3591da177e4SLinus Torvalds 	if (!expires)
3601da177e4SLinus Torvalds 		return -EPERM;
361394055f6SFlorent Fourcot 
362394055f6SFlorent Fourcot 	spin_lock_bh(&ip6_fl_lock);
3631da177e4SLinus Torvalds 	fl->lastuse = jiffies;
3641da177e4SLinus Torvalds 	if (time_before(fl->linger, linger))
3651da177e4SLinus Torvalds 		fl->linger = linger;
3661da177e4SLinus Torvalds 	if (time_before(expires, fl->linger))
3671da177e4SLinus Torvalds 		expires = fl->linger;
3681da177e4SLinus Torvalds 	if (time_before(fl->expires, fl->lastuse + expires))
3691da177e4SLinus Torvalds 		fl->expires = fl->lastuse + expires;
370394055f6SFlorent Fourcot 	spin_unlock_bh(&ip6_fl_lock);
371394055f6SFlorent Fourcot 
3721da177e4SLinus Torvalds 	return 0;
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds static struct ip6_flowlabel *
fl_create(struct net * net,struct sock * sk,struct in6_flowlabel_req * freq,sockptr_t optval,int optlen,int * err_p)376ec0506dbSMaciej Żenczykowski fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
37786298285SChristoph Hellwig 	  sockptr_t optval, int optlen, int *err_p)
3781da177e4SLinus Torvalds {
379684de409SDavid S. Miller 	struct ip6_flowlabel *fl = NULL;
3801da177e4SLinus Torvalds 	int olen;
3811da177e4SLinus Torvalds 	int addr_type;
3821da177e4SLinus Torvalds 	int err;
3831da177e4SLinus Torvalds 
384684de409SDavid S. Miller 	olen = optlen - CMSG_ALIGN(sizeof(*freq));
385684de409SDavid S. Miller 	err = -EINVAL;
386684de409SDavid S. Miller 	if (olen > 64 * 1024)
387684de409SDavid S. Miller 		goto done;
388684de409SDavid S. Miller 
3891da177e4SLinus Torvalds 	err = -ENOMEM;
3900c600edaSIngo Oeser 	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
39163159f29SIan Morris 	if (!fl)
3921da177e4SLinus Torvalds 		goto done;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	if (olen > 0) {
3951da177e4SLinus Torvalds 		struct msghdr msg;
3964c9483b2SDavid S. Miller 		struct flowi6 flowi6;
39726879da5SWei Wang 		struct ipcm6_cookie ipc6;
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 		err = -ENOMEM;
4001da177e4SLinus Torvalds 		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
40163159f29SIan Morris 		if (!fl->opt)
4021da177e4SLinus Torvalds 			goto done;
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 		memset(fl->opt, 0, sizeof(*fl->opt));
4051da177e4SLinus Torvalds 		fl->opt->tot_len = sizeof(*fl->opt) + olen;
4061da177e4SLinus Torvalds 		err = -EFAULT;
407d3c48151SChristoph Hellwig 		if (copy_from_sockptr_offset(fl->opt + 1, optval,
408d3c48151SChristoph Hellwig 				CMSG_ALIGN(sizeof(*freq)), olen))
4091da177e4SLinus Torvalds 			goto done;
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 		msg.msg_controllen = olen;
4121da177e4SLinus Torvalds 		msg.msg_control = (void *)(fl->opt+1);
4134c9483b2SDavid S. Miller 		memset(&flowi6, 0, sizeof(flowi6));
4141da177e4SLinus Torvalds 
41526879da5SWei Wang 		ipc6.opt = fl->opt;
4165fdaa88dSWillem de Bruijn 		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &ipc6);
4171da177e4SLinus Torvalds 		if (err)
4181da177e4SLinus Torvalds 			goto done;
4191da177e4SLinus Torvalds 		err = -EINVAL;
4201da177e4SLinus Torvalds 		if (fl->opt->opt_flen)
4211da177e4SLinus Torvalds 			goto done;
4221da177e4SLinus Torvalds 		if (fl->opt->opt_nflen == 0) {
4231da177e4SLinus Torvalds 			kfree(fl->opt);
4241da177e4SLinus Torvalds 			fl->opt = NULL;
4251da177e4SLinus Torvalds 		}
4261da177e4SLinus Torvalds 	}
4271da177e4SLinus Torvalds 
428efd7ef1cSEric W. Biederman 	fl->fl_net = net;
4291da177e4SLinus Torvalds 	fl->expires = jiffies;
4301da177e4SLinus Torvalds 	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
4311da177e4SLinus Torvalds 	if (err)
4321da177e4SLinus Torvalds 		goto done;
4331da177e4SLinus Torvalds 	fl->share = freq->flr_share;
4341da177e4SLinus Torvalds 	addr_type = ipv6_addr_type(&freq->flr_dst);
43535700212SJoe Perches 	if ((addr_type & IPV6_ADDR_MAPPED) ||
43635700212SJoe Perches 	    addr_type == IPV6_ADDR_ANY) {
437c6817e4cSJames Morris 		err = -EINVAL;
4381da177e4SLinus Torvalds 		goto done;
439c6817e4cSJames Morris 	}
4404e3fd7a0SAlexey Dobriyan 	fl->dst = freq->flr_dst;
4411da177e4SLinus Torvalds 	atomic_set(&fl->users, 1);
4421da177e4SLinus Torvalds 	switch (fl->share) {
4431da177e4SLinus Torvalds 	case IPV6_FL_S_EXCL:
4441da177e4SLinus Torvalds 	case IPV6_FL_S_ANY:
4451da177e4SLinus Torvalds 		break;
4461da177e4SLinus Torvalds 	case IPV6_FL_S_PROCESS:
4474f82f457SEric W. Biederman 		fl->owner.pid = get_task_pid(current, PIDTYPE_PID);
4481da177e4SLinus Torvalds 		break;
4491da177e4SLinus Torvalds 	case IPV6_FL_S_USER:
4504f82f457SEric W. Biederman 		fl->owner.uid = current_euid();
4511da177e4SLinus Torvalds 		break;
4521da177e4SLinus Torvalds 	default:
4531da177e4SLinus Torvalds 		err = -EINVAL;
4541da177e4SLinus Torvalds 		goto done;
4551da177e4SLinus Torvalds 	}
4560b0dff5bSWillem de Bruijn 	if (fl_shared_exclusive(fl) || fl->opt) {
4570b0dff5bSWillem de Bruijn 		WRITE_ONCE(sock_net(sk)->ipv6.flowlabel_has_excl, 1);
458d44e3fa5SEric Dumazet 		static_branch_deferred_inc(&ipv6_flowlabel_exclusive);
4590b0dff5bSWillem de Bruijn 	}
4601da177e4SLinus Torvalds 	return fl;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds done:
463d44e3fa5SEric Dumazet 	if (fl) {
464d44e3fa5SEric Dumazet 		kfree(fl->opt);
465d44e3fa5SEric Dumazet 		kfree(fl);
466d44e3fa5SEric Dumazet 	}
4671da177e4SLinus Torvalds 	*err_p = err;
4681da177e4SLinus Torvalds 	return NULL;
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
mem_check(struct sock * sk)4711da177e4SLinus Torvalds static int mem_check(struct sock *sk)
4721da177e4SLinus Torvalds {
4731da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
4741da177e4SLinus Torvalds 	struct ipv6_fl_socklist *sfl;
4751da177e4SLinus Torvalds 	int room = FL_MAX_SIZE - atomic_read(&fl_size);
4761da177e4SLinus Torvalds 	int count = 0;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
4791da177e4SLinus Torvalds 		return 0;
4801da177e4SLinus Torvalds 
481*4c5c496aSEric Dumazet 	rcu_read_lock();
48218367681SYOSHIFUJI Hideaki / 吉藤英明 	for_each_sk_fl_rcu(np, sfl)
4831da177e4SLinus Torvalds 		count++;
484*4c5c496aSEric Dumazet 	rcu_read_unlock();
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	if (room <= 0 ||
4871da177e4SLinus Torvalds 	    ((count >= FL_MAX_PER_SOCK ||
48835700212SJoe Perches 	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
48935700212SJoe Perches 	     !capable(CAP_NET_ADMIN)))
4901da177e4SLinus Torvalds 		return -ENOBUFS;
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	return 0;
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
fl_link(struct ipv6_pinfo * np,struct ipv6_fl_socklist * sfl,struct ip6_flowlabel * fl)49504028045SPavel Emelyanov static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
49604028045SPavel Emelyanov 		struct ip6_flowlabel *fl)
49704028045SPavel Emelyanov {
49818367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_sk_fl_lock);
49904028045SPavel Emelyanov 	sfl->fl = fl;
50004028045SPavel Emelyanov 	sfl->next = np->ipv6_fl_list;
50118367681SYOSHIFUJI Hideaki / 吉藤英明 	rcu_assign_pointer(np->ipv6_fl_list, sfl);
50218367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_sk_fl_lock);
50304028045SPavel Emelyanov }
50404028045SPavel Emelyanov 
ipv6_flowlabel_opt_get(struct sock * sk,struct in6_flowlabel_req * freq,int flags)50546e5f401SFlorent Fourcot int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq,
50646e5f401SFlorent Fourcot 			   int flags)
5073fdfa5ffSFlorent Fourcot {
5083fdfa5ffSFlorent Fourcot 	struct ipv6_pinfo *np = inet6_sk(sk);
5093fdfa5ffSFlorent Fourcot 	struct ipv6_fl_socklist *sfl;
5103fdfa5ffSFlorent Fourcot 
51146e5f401SFlorent Fourcot 	if (flags & IPV6_FL_F_REMOTE) {
51246e5f401SFlorent Fourcot 		freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK;
51346e5f401SFlorent Fourcot 		return 0;
51446e5f401SFlorent Fourcot 	}
51546e5f401SFlorent Fourcot 
516df3687ffSFlorent Fourcot 	if (np->repflow) {
517df3687ffSFlorent Fourcot 		freq->flr_label = np->flow_label;
518df3687ffSFlorent Fourcot 		return 0;
519df3687ffSFlorent Fourcot 	}
520df3687ffSFlorent Fourcot 
521*4c5c496aSEric Dumazet 	rcu_read_lock();
5223fdfa5ffSFlorent Fourcot 
5233fdfa5ffSFlorent Fourcot 	for_each_sk_fl_rcu(np, sfl) {
5243fdfa5ffSFlorent Fourcot 		if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
5253fdfa5ffSFlorent Fourcot 			spin_lock_bh(&ip6_fl_lock);
5263fdfa5ffSFlorent Fourcot 			freq->flr_label = sfl->fl->label;
5273fdfa5ffSFlorent Fourcot 			freq->flr_dst = sfl->fl->dst;
5283fdfa5ffSFlorent Fourcot 			freq->flr_share = sfl->fl->share;
5293fdfa5ffSFlorent Fourcot 			freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
5303fdfa5ffSFlorent Fourcot 			freq->flr_linger = sfl->fl->linger / HZ;
5313fdfa5ffSFlorent Fourcot 
5323fdfa5ffSFlorent Fourcot 			spin_unlock_bh(&ip6_fl_lock);
533*4c5c496aSEric Dumazet 			rcu_read_unlock();
5343fdfa5ffSFlorent Fourcot 			return 0;
5353fdfa5ffSFlorent Fourcot 		}
5363fdfa5ffSFlorent Fourcot 	}
537*4c5c496aSEric Dumazet 	rcu_read_unlock();
5383fdfa5ffSFlorent Fourcot 
5393fdfa5ffSFlorent Fourcot 	return -ENOENT;
5403fdfa5ffSFlorent Fourcot }
5413fdfa5ffSFlorent Fourcot 
542ff6a4cf2SChristoph Hellwig #define socklist_dereference(__sflp) \
543ff6a4cf2SChristoph Hellwig 	rcu_dereference_protected(__sflp, lockdep_is_held(&ip6_sk_fl_lock))
544ff6a4cf2SChristoph Hellwig 
ipv6_flowlabel_put(struct sock * sk,struct in6_flowlabel_req * freq)545ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq)
5461da177e4SLinus Torvalds {
5471da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
5487f0e44acSEric Dumazet 	struct ipv6_fl_socklist __rcu **sflp;
549ff6a4cf2SChristoph Hellwig 	struct ipv6_fl_socklist *sfl;
55078c2e502SPavel Emelyanov 
551ff6a4cf2SChristoph Hellwig 	if (freq->flr_flags & IPV6_FL_F_REFLECT) {
552df3687ffSFlorent Fourcot 		if (sk->sk_protocol != IPPROTO_TCP)
553df3687ffSFlorent Fourcot 			return -ENOPROTOOPT;
554df3687ffSFlorent Fourcot 		if (!np->repflow)
555df3687ffSFlorent Fourcot 			return -ESRCH;
556df3687ffSFlorent Fourcot 		np->flow_label = 0;
557df3687ffSFlorent Fourcot 		np->repflow = 0;
558df3687ffSFlorent Fourcot 		return 0;
559df3687ffSFlorent Fourcot 	}
560ff6a4cf2SChristoph Hellwig 
56118367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_lock_bh(&ip6_sk_fl_lock);
56218367681SYOSHIFUJI Hideaki / 吉藤英明 	for (sflp = &np->ipv6_fl_list;
563ff6a4cf2SChristoph Hellwig 	     (sfl = socklist_dereference(*sflp)) != NULL;
56418367681SYOSHIFUJI Hideaki / 吉藤英明 	     sflp = &sfl->next) {
565ff6a4cf2SChristoph Hellwig 		if (sfl->fl->label == freq->flr_label)
566ff6a4cf2SChristoph Hellwig 			goto found;
567ff6a4cf2SChristoph Hellwig 	}
568ff6a4cf2SChristoph Hellwig 	spin_unlock_bh(&ip6_sk_fl_lock);
569ff6a4cf2SChristoph Hellwig 	return -ESRCH;
570ff6a4cf2SChristoph Hellwig found:
571ff6a4cf2SChristoph Hellwig 	if (freq->flr_label == (np->flow_label & IPV6_FLOWLABEL_MASK))
5721da177e4SLinus Torvalds 		np->flow_label &= ~IPV6_FLOWLABEL_MASK;
57344c3d0c1SEric Dumazet 	*sflp = sfl->next;
57418367681SYOSHIFUJI Hideaki / 吉藤英明 	spin_unlock_bh(&ip6_sk_fl_lock);
5751da177e4SLinus Torvalds 	fl_release(sfl->fl);
57618367681SYOSHIFUJI Hideaki / 吉藤英明 	kfree_rcu(sfl, rcu);
5771da177e4SLinus Torvalds 	return 0;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
ipv6_flowlabel_renew(struct sock * sk,struct in6_flowlabel_req * freq)580ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_renew(struct sock *sk, struct in6_flowlabel_req *freq)
581ff6a4cf2SChristoph Hellwig {
582ff6a4cf2SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
583ff6a4cf2SChristoph Hellwig 	struct net *net = sock_net(sk);
584ff6a4cf2SChristoph Hellwig 	struct ipv6_fl_socklist *sfl;
585ff6a4cf2SChristoph Hellwig 	int err;
586ff6a4cf2SChristoph Hellwig 
587*4c5c496aSEric Dumazet 	rcu_read_lock();
58818367681SYOSHIFUJI Hideaki / 吉藤英明 	for_each_sk_fl_rcu(np, sfl) {
589ff6a4cf2SChristoph Hellwig 		if (sfl->fl->label == freq->flr_label) {
590ff6a4cf2SChristoph Hellwig 			err = fl6_renew(sfl->fl, freq->flr_linger,
591ff6a4cf2SChristoph Hellwig 					freq->flr_expires);
592*4c5c496aSEric Dumazet 			rcu_read_unlock();
5931da177e4SLinus Torvalds 			return err;
5941da177e4SLinus Torvalds 		}
5951da177e4SLinus Torvalds 	}
596*4c5c496aSEric Dumazet 	rcu_read_unlock();
5971da177e4SLinus Torvalds 
598ff6a4cf2SChristoph Hellwig 	if (freq->flr_share == IPV6_FL_S_NONE &&
599af31f412SEric W. Biederman 	    ns_capable(net->user_ns, CAP_NET_ADMIN)) {
600ff6a4cf2SChristoph Hellwig 		struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label);
601ff6a4cf2SChristoph Hellwig 
6021da177e4SLinus Torvalds 		if (fl) {
603ff6a4cf2SChristoph Hellwig 			err = fl6_renew(fl, freq->flr_linger,
604ff6a4cf2SChristoph Hellwig 					freq->flr_expires);
6051da177e4SLinus Torvalds 			fl_release(fl);
6061da177e4SLinus Torvalds 			return err;
6071da177e4SLinus Torvalds 		}
6081da177e4SLinus Torvalds 	}
6091da177e4SLinus Torvalds 	return -ESRCH;
610ff6a4cf2SChristoph Hellwig }
6111da177e4SLinus Torvalds 
ipv6_flowlabel_get(struct sock * sk,struct in6_flowlabel_req * freq,sockptr_t optval,int optlen)612ff6a4cf2SChristoph Hellwig static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
61386298285SChristoph Hellwig 		sockptr_t optval, int optlen)
614ff6a4cf2SChristoph Hellwig {
615ff6a4cf2SChristoph Hellwig 	struct ipv6_fl_socklist *sfl, *sfl1 = NULL;
616ff6a4cf2SChristoph Hellwig 	struct ip6_flowlabel *fl, *fl1 = NULL;
617ff6a4cf2SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
6186444f72bSFlorent Fourcot 	struct net *net = sock_net(sk);
61947ec5303SLinus Torvalds 	int err;
620ff6a4cf2SChristoph Hellwig 
621ff6a4cf2SChristoph Hellwig 	if (freq->flr_flags & IPV6_FL_F_REFLECT) {
6226444f72bSFlorent Fourcot 		if (net->ipv6.sysctl.flowlabel_consistency) {
6236444f72bSFlorent Fourcot 			net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n");
6246444f72bSFlorent Fourcot 			return -EPERM;
6256444f72bSFlorent Fourcot 		}
6266444f72bSFlorent Fourcot 
627df3687ffSFlorent Fourcot 		if (sk->sk_protocol != IPPROTO_TCP)
628df3687ffSFlorent Fourcot 			return -ENOPROTOOPT;
629df3687ffSFlorent Fourcot 		np->repflow = 1;
630df3687ffSFlorent Fourcot 		return 0;
631df3687ffSFlorent Fourcot 	}
632df3687ffSFlorent Fourcot 
633ff6a4cf2SChristoph Hellwig 	if (freq->flr_label & ~IPV6_FLOWLABEL_MASK)
6341da177e4SLinus Torvalds 		return -EINVAL;
63582a584b7STom Herbert 	if (net->ipv6.sysctl.flowlabel_state_ranges &&
636ff6a4cf2SChristoph Hellwig 	    (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
63782a584b7STom Herbert 		return -ERANGE;
63882a584b7STom Herbert 
639ff6a4cf2SChristoph Hellwig 	fl = fl_create(net, sk, freq, optval, optlen, &err);
64063159f29SIan Morris 	if (!fl)
6411da177e4SLinus Torvalds 		return err;
642ff6a4cf2SChristoph Hellwig 
6431da177e4SLinus Torvalds 	sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
6441da177e4SLinus Torvalds 
645ff6a4cf2SChristoph Hellwig 	if (freq->flr_label) {
6461da177e4SLinus Torvalds 		err = -EEXIST;
647*4c5c496aSEric Dumazet 		rcu_read_lock();
64818367681SYOSHIFUJI Hideaki / 吉藤英明 		for_each_sk_fl_rcu(np, sfl) {
649ff6a4cf2SChristoph Hellwig 			if (sfl->fl->label == freq->flr_label) {
650ff6a4cf2SChristoph Hellwig 				if (freq->flr_flags & IPV6_FL_F_EXCL) {
651*4c5c496aSEric Dumazet 					rcu_read_unlock();
6521da177e4SLinus Torvalds 					goto done;
6531da177e4SLinus Torvalds 				}
6541da177e4SLinus Torvalds 				fl1 = sfl->fl;
65565a3c497SEric Dumazet 				if (!atomic_inc_not_zero(&fl1->users))
65665a3c497SEric Dumazet 					fl1 = NULL;
6571da177e4SLinus Torvalds 				break;
6581da177e4SLinus Torvalds 			}
6591da177e4SLinus Torvalds 		}
660*4c5c496aSEric Dumazet 		rcu_read_unlock();
6611da177e4SLinus Torvalds 
66263159f29SIan Morris 		if (!fl1)
663ff6a4cf2SChristoph Hellwig 			fl1 = fl_lookup(net, freq->flr_label);
6641da177e4SLinus Torvalds 		if (fl1) {
66578c2e502SPavel Emelyanov recheck:
6661da177e4SLinus Torvalds 			err = -EEXIST;
667ff6a4cf2SChristoph Hellwig 			if (freq->flr_flags&IPV6_FL_F_EXCL)
6681da177e4SLinus Torvalds 				goto release;
6691da177e4SLinus Torvalds 			err = -EPERM;
6701da177e4SLinus Torvalds 			if (fl1->share == IPV6_FL_S_EXCL ||
6711da177e4SLinus Torvalds 			    fl1->share != fl->share ||
6724f82f457SEric W. Biederman 			    ((fl1->share == IPV6_FL_S_PROCESS) &&
67395c16925SWillem de Bruijn 			     (fl1->owner.pid != fl->owner.pid)) ||
6744f82f457SEric W. Biederman 			    ((fl1->share == IPV6_FL_S_USER) &&
67595c16925SWillem de Bruijn 			     !uid_eq(fl1->owner.uid, fl->owner.uid)))
6761da177e4SLinus Torvalds 				goto release;
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds 			err = -ENOMEM;
67963159f29SIan Morris 			if (!sfl1)
6801da177e4SLinus Torvalds 				goto release;
6811da177e4SLinus Torvalds 			if (fl->linger > fl1->linger)
6821da177e4SLinus Torvalds 				fl1->linger = fl->linger;
6831da177e4SLinus Torvalds 			if ((long)(fl->expires - fl1->expires) > 0)
6841da177e4SLinus Torvalds 				fl1->expires = fl->expires;
68504028045SPavel Emelyanov 			fl_link(np, sfl1, fl1);
6861da177e4SLinus Torvalds 			fl_free(fl);
6871da177e4SLinus Torvalds 			return 0;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds release:
6901da177e4SLinus Torvalds 			fl_release(fl1);
6911da177e4SLinus Torvalds 			goto done;
6921da177e4SLinus Torvalds 		}
6931da177e4SLinus Torvalds 	}
6941da177e4SLinus Torvalds 	err = -ENOENT;
695ff6a4cf2SChristoph Hellwig 	if (!(freq->flr_flags & IPV6_FL_F_CREATE))
6961da177e4SLinus Torvalds 		goto done;
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	err = -ENOMEM;
69963159f29SIan Morris 	if (!sfl1)
700e5d08d71SIan Morris 		goto done;
701e5d08d71SIan Morris 
702e5d08d71SIan Morris 	err = mem_check(sk);
703e5d08d71SIan Morris 	if (err != 0)
7041da177e4SLinus Torvalds 		goto done;
7051da177e4SLinus Torvalds 
706ff6a4cf2SChristoph Hellwig 	fl1 = fl_intern(net, fl, freq->flr_label);
70753b24b8fSIan Morris 	if (fl1)
70878c2e502SPavel Emelyanov 		goto recheck;
7091da177e4SLinus Torvalds 
710ff6a4cf2SChristoph Hellwig 	if (!freq->flr_label) {
711d3c48151SChristoph Hellwig 		size_t offset = offsetof(struct in6_flowlabel_req, flr_label);
712d3c48151SChristoph Hellwig 
713d3c48151SChristoph Hellwig 		if (copy_to_sockptr_offset(optval, offset, &fl->label,
714d3c48151SChristoph Hellwig 				sizeof(fl->label))) {
7156c94d361SDavid S. Miller 			/* Intentionally ignore fault. */
7166c94d361SDavid S. Miller 		}
7176c94d361SDavid S. Miller 	}
7181da177e4SLinus Torvalds 
71904028045SPavel Emelyanov 	fl_link(np, sfl1, fl);
7201da177e4SLinus Torvalds 	return 0;
7211da177e4SLinus Torvalds done:
7221da177e4SLinus Torvalds 	fl_free(fl);
7231da177e4SLinus Torvalds 	kfree(sfl1);
7241da177e4SLinus Torvalds 	return err;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
ipv6_flowlabel_opt(struct sock * sk,sockptr_t optval,int optlen)72786298285SChristoph Hellwig int ipv6_flowlabel_opt(struct sock *sk, sockptr_t optval, int optlen)
728ff6a4cf2SChristoph Hellwig {
729ff6a4cf2SChristoph Hellwig 	struct in6_flowlabel_req freq;
730ff6a4cf2SChristoph Hellwig 
731ff6a4cf2SChristoph Hellwig 	if (optlen < sizeof(freq))
732ff6a4cf2SChristoph Hellwig 		return -EINVAL;
73386298285SChristoph Hellwig 	if (copy_from_sockptr(&freq, optval, sizeof(freq)))
734ff6a4cf2SChristoph Hellwig 		return -EFAULT;
735ff6a4cf2SChristoph Hellwig 
736ff6a4cf2SChristoph Hellwig 	switch (freq.flr_action) {
737ff6a4cf2SChristoph Hellwig 	case IPV6_FL_A_PUT:
738ff6a4cf2SChristoph Hellwig 		return ipv6_flowlabel_put(sk, &freq);
739ff6a4cf2SChristoph Hellwig 	case IPV6_FL_A_RENEW:
740ff6a4cf2SChristoph Hellwig 		return ipv6_flowlabel_renew(sk, &freq);
741ff6a4cf2SChristoph Hellwig 	case IPV6_FL_A_GET:
742ff6a4cf2SChristoph Hellwig 		return ipv6_flowlabel_get(sk, &freq, optval, optlen);
743ff6a4cf2SChristoph Hellwig 	default:
744ff6a4cf2SChristoph Hellwig 		return -EINVAL;
745ff6a4cf2SChristoph Hellwig 	}
746ff6a4cf2SChristoph Hellwig }
747ff6a4cf2SChristoph Hellwig 
7481da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds struct ip6fl_iter_state {
7515983a3dfSBenjamin Thery 	struct seq_net_private p;
7524f82f457SEric W. Biederman 	struct pid_namespace *pid_ns;
7531da177e4SLinus Torvalds 	int bucket;
7541da177e4SLinus Torvalds };
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds #define ip6fl_seq_private(seq)	((struct ip6fl_iter_state *)(seq)->private)
7571da177e4SLinus Torvalds 
ip6fl_get_first(struct seq_file * seq)7581da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq)
7591da177e4SLinus Torvalds {
7601da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = NULL;
7611da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7625983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
765d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
766d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
767d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
7681da177e4SLinus Torvalds 		}
769d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
770d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
771d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
7721da177e4SLinus Torvalds 	return fl;
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds 
ip6fl_get_next(struct seq_file * seq,struct ip6_flowlabel * fl)7751da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl)
7761da177e4SLinus Torvalds {
7771da177e4SLinus Torvalds 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
7785983a3dfSBenjamin Thery 	struct net *net = seq_file_net(seq);
7791da177e4SLinus Torvalds 
780d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	for_each_fl_continue_rcu(fl) {
781d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		if (net_eq(fl->fl_net, net))
782d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
7831da177e4SLinus Torvalds 	}
784d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
785d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 try_again:
786d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	if (++state->bucket <= FL_HASH_MASK) {
787d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		for_each_fl_rcu(state->bucket, fl) {
788d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 			if (net_eq(fl->fl_net, net))
789d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 				goto out;
790d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		}
791d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 		goto try_again;
792d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	}
793d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	fl = NULL;
794d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 
795d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 out:
7961da177e4SLinus Torvalds 	return fl;
7971da177e4SLinus Torvalds }
7981da177e4SLinus Torvalds 
ip6fl_get_idx(struct seq_file * seq,loff_t pos)7991da177e4SLinus Torvalds static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos)
8001da177e4SLinus Torvalds {
8011da177e4SLinus Torvalds 	struct ip6_flowlabel *fl = ip6fl_get_first(seq);
8021da177e4SLinus Torvalds 	if (fl)
8031da177e4SLinus Torvalds 		while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL)
8041da177e4SLinus Torvalds 			--pos;
8051da177e4SLinus Torvalds 	return pos ? NULL : fl;
8061da177e4SLinus Torvalds }
8071da177e4SLinus Torvalds 
ip6fl_seq_start(struct seq_file * seq,loff_t * pos)8081da177e4SLinus Torvalds static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
809d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__acquires(RCU)
8101da177e4SLinus Torvalds {
811ad08978aSChristoph Hellwig 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
812ad08978aSChristoph Hellwig 
8139d78edeaSAlexey Gladkov 	state->pid_ns = proc_pid_ns(file_inode(seq->file)->i_sb);
814ad08978aSChristoph Hellwig 
815*4c5c496aSEric Dumazet 	rcu_read_lock();
8161da177e4SLinus Torvalds 	return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
8171da177e4SLinus Torvalds }
8181da177e4SLinus Torvalds 
ip6fl_seq_next(struct seq_file * seq,void * v,loff_t * pos)8191da177e4SLinus Torvalds static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
8201da177e4SLinus Torvalds {
8211da177e4SLinus Torvalds 	struct ip6_flowlabel *fl;
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN)
8241da177e4SLinus Torvalds 		fl = ip6fl_get_first(seq);
8251da177e4SLinus Torvalds 	else
8261da177e4SLinus Torvalds 		fl = ip6fl_get_next(seq, v);
8271da177e4SLinus Torvalds 	++*pos;
8281da177e4SLinus Torvalds 	return fl;
8291da177e4SLinus Torvalds }
8301da177e4SLinus Torvalds 
ip6fl_seq_stop(struct seq_file * seq,void * v)8311da177e4SLinus Torvalds static void ip6fl_seq_stop(struct seq_file *seq, void *v)
832d3aedd5eSYOSHIFUJI Hideaki / 吉藤英明 	__releases(RCU)
8331da177e4SLinus Torvalds {
834*4c5c496aSEric Dumazet 	rcu_read_unlock();
8351da177e4SLinus Torvalds }
8361da177e4SLinus Torvalds 
ip6fl_seq_show(struct seq_file * seq,void * v)8371b7c2dbcSJames Morris static int ip6fl_seq_show(struct seq_file *seq, void *v)
8381da177e4SLinus Torvalds {
8394f82f457SEric W. Biederman 	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
840869ba988SFlorent Fourcot 	if (v == SEQ_START_TOKEN) {
8411744bea1SJoe Perches 		seq_puts(seq, "Label S Owner  Users  Linger Expires  Dst                              Opt\n");
842869ba988SFlorent Fourcot 	} else {
8431b7c2dbcSJames Morris 		struct ip6_flowlabel *fl = v;
8441da177e4SLinus Torvalds 		seq_printf(seq,
8454b7a4274SHarvey Harrison 			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
84695c96174SEric Dumazet 			   (unsigned int)ntohl(fl->label),
8471da177e4SLinus Torvalds 			   fl->share,
8484f82f457SEric W. Biederman 			   ((fl->share == IPV6_FL_S_PROCESS) ?
8494f82f457SEric W. Biederman 			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
8504f82f457SEric W. Biederman 			    ((fl->share == IPV6_FL_S_USER) ?
8514f82f457SEric W. Biederman 			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
8524f82f457SEric W. Biederman 			     0)),
8531da177e4SLinus Torvalds 			   atomic_read(&fl->users),
8541da177e4SLinus Torvalds 			   fl->linger/HZ,
8551da177e4SLinus Torvalds 			   (long)(fl->expires - jiffies)/HZ,
856b071195dSHarvey Harrison 			   &fl->dst,
8571da177e4SLinus Torvalds 			   fl->opt ? fl->opt->opt_nflen : 0);
8581da177e4SLinus Torvalds 	}
8591da177e4SLinus Torvalds 	return 0;
8601da177e4SLinus Torvalds }
8611da177e4SLinus Torvalds 
86256b3d975SPhilippe De Muyter static const struct seq_operations ip6fl_seq_ops = {
8631da177e4SLinus Torvalds 	.start	=	ip6fl_seq_start,
8641da177e4SLinus Torvalds 	.next	=	ip6fl_seq_next,
8651da177e4SLinus Torvalds 	.stop	=	ip6fl_seq_stop,
8661da177e4SLinus Torvalds 	.show	=	ip6fl_seq_show,
8671da177e4SLinus Torvalds };
8681da177e4SLinus Torvalds 
ip6_flowlabel_proc_init(struct net * net)8692c8c1e72SAlexey Dobriyan static int __net_init ip6_flowlabel_proc_init(struct net *net)
8701da177e4SLinus Torvalds {
871c3506372SChristoph Hellwig 	if (!proc_create_net("ip6_flowlabel", 0444, net->proc_net,
872c3506372SChristoph Hellwig 			&ip6fl_seq_ops, sizeof(struct ip6fl_iter_state)))
8730a3e78acSDaniel Lezcano 		return -ENOMEM;
8740a3e78acSDaniel Lezcano 	return 0;
8750a3e78acSDaniel Lezcano }
8760a3e78acSDaniel Lezcano 
ip6_flowlabel_proc_fini(struct net * net)8772c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
8780a3e78acSDaniel Lezcano {
879ece31ffdSGao feng 	remove_proc_entry("ip6_flowlabel", net->proc_net);
8800a3e78acSDaniel Lezcano }
8810a3e78acSDaniel Lezcano #else
ip6_flowlabel_proc_init(struct net * net)8820a3e78acSDaniel Lezcano static inline int ip6_flowlabel_proc_init(struct net *net)
8830a3e78acSDaniel Lezcano {
8840a3e78acSDaniel Lezcano 	return 0;
8850a3e78acSDaniel Lezcano }
ip6_flowlabel_proc_fini(struct net * net)8860a3e78acSDaniel Lezcano static inline void ip6_flowlabel_proc_fini(struct net *net)
8870a3e78acSDaniel Lezcano {
8880a3e78acSDaniel Lezcano }
8891da177e4SLinus Torvalds #endif
8900a3e78acSDaniel Lezcano 
ip6_flowlabel_net_exit(struct net * net)8912c8c1e72SAlexey Dobriyan static void __net_exit ip6_flowlabel_net_exit(struct net *net)
89260e8fbc4SBenjamin Thery {
89360e8fbc4SBenjamin Thery 	ip6_fl_purge(net);
8945983a3dfSBenjamin Thery 	ip6_flowlabel_proc_fini(net);
89560e8fbc4SBenjamin Thery }
89660e8fbc4SBenjamin Thery 
89760e8fbc4SBenjamin Thery static struct pernet_operations ip6_flowlabel_net_ops = {
8985983a3dfSBenjamin Thery 	.init = ip6_flowlabel_proc_init,
89960e8fbc4SBenjamin Thery 	.exit = ip6_flowlabel_net_exit,
90060e8fbc4SBenjamin Thery };
90160e8fbc4SBenjamin Thery 
ip6_flowlabel_init(void)9020a3e78acSDaniel Lezcano int ip6_flowlabel_init(void)
9030a3e78acSDaniel Lezcano {
9045983a3dfSBenjamin Thery 	return register_pernet_subsys(&ip6_flowlabel_net_ops);
9051da177e4SLinus Torvalds }
9061da177e4SLinus Torvalds 
ip6_flowlabel_cleanup(void)9071da177e4SLinus Torvalds void ip6_flowlabel_cleanup(void)
9081da177e4SLinus Torvalds {
90959c820b2SWillem de Bruijn 	static_key_deferred_flush(&ipv6_flowlabel_exclusive);
9101da177e4SLinus Torvalds 	del_timer(&ip6_fl_gc_timer);
91160e8fbc4SBenjamin Thery 	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
9121da177e4SLinus Torvalds }
913