Blame view

net/ipv6/ip6_flowlabel.c 20.4 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   *	ip6_flowlabel.c		IPv6 flowlabel manager.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
   *	Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
7
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/in6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
17
  #include <linux/export.h>
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
18
  #include <linux/pid_namespace.h>
59c820b23   Willem de Bruijn   ipv6: elide flowl...
19
  #include <linux/jump_label_ratelimit.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
21
  #include <net/net_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
  #include <net/sock.h>
  
  #include <net/ipv6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <net/rawv6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  #include <net/transp_v6.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
27
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  
  #define FL_MIN_LINGER	6	/* Minimal linger. It is set to 6sec specified
  				   in old IPv6 RFC. Well, it was reasonable value.
  				 */
53b47106c   Florent Fourcot   ipv6: increase ma...
32
  #define FL_MAX_LINGER	150	/* Maximal linger timeout */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
  
  /* FL hash table */
  
  #define FL_MAX_PER_SOCK	32
  #define FL_MAX_SIZE	4096
  #define FL_HASH_MASK	255
  #define FL_HASH(l)	(ntohl(l)&FL_HASH_MASK)
  
  static atomic_t fl_size = ATOMIC_INIT(0);
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
42
  static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

24ed960ab   Kees Cook   treewide: Switch ...
44
  static void ip6_fl_gc(struct timer_list *unused);
1d27e3e22   Kees Cook   timer: Remove exp...
45
  static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
  
  /* FL hash table lock: it protects only of GC */
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
48
  static DEFINE_SPINLOCK(ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
  
  /* Big socket sock */
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
51
  static DEFINE_SPINLOCK(ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52

59c820b23   Willem de Bruijn   ipv6: elide flowl...
53
54
  DEFINE_STATIC_KEY_DEFERRED_FALSE(ipv6_flowlabel_exclusive, HZ);
  EXPORT_SYMBOL(ipv6_flowlabel_exclusive);
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
55
  #define for_each_fl_rcu(hash, fl)				\
6a98dcf03   Amerigo Wang   ipv6: fix a RCU w...
56
  	for (fl = rcu_dereference_bh(fl_ht[(hash)]);		\
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
57
  	     fl != NULL;					\
6a98dcf03   Amerigo Wang   ipv6: fix a RCU w...
58
  	     fl = rcu_dereference_bh(fl->next))
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
59
  #define for_each_fl_continue_rcu(fl)				\
6a98dcf03   Amerigo Wang   ipv6: fix a RCU w...
60
  	for (fl = rcu_dereference_bh(fl->next);			\
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
61
  	     fl != NULL;					\
6a98dcf03   Amerigo Wang   ipv6: fix a RCU w...
62
  	     fl = rcu_dereference_bh(fl->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
64
65
66
67
  #define for_each_sk_fl_rcu(np, sfl)				\
  	for (sfl = rcu_dereference_bh(np->ipv6_fl_list);	\
  	     sfl != NULL;					\
  	     sfl = rcu_dereference_bh(sfl->next))
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
68
  static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  {
  	struct ip6_flowlabel *fl;
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
71
  	for_each_fl_rcu(FL_HASH(label), fl) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
72
  		if (fl->label == label && net_eq(fl->fl_net, net))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  			return fl;
  	}
  	return NULL;
  }
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
77
  static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
  {
  	struct ip6_flowlabel *fl;
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
80
  	rcu_read_lock_bh();
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
81
  	fl = __fl_lookup(net, label);
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
82
83
84
  	if (fl && !atomic_inc_not_zero(&fl->users))
  		fl = NULL;
  	rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  	return fl;
  }
59c820b23   Willem de Bruijn   ipv6: elide flowl...
87
88
89
90
91
92
  static bool fl_shared_exclusive(struct ip6_flowlabel *fl)
  {
  	return fl->share == IPV6_FL_S_EXCL ||
  	       fl->share == IPV6_FL_S_PROCESS ||
  	       fl->share == IPV6_FL_S_USER;
  }
6c0afef5f   Eric Dumazet   ipv6/flowlabel: w...
93
94
95
96
97
98
99
100
101
  static void fl_free_rcu(struct rcu_head *head)
  {
  	struct ip6_flowlabel *fl = container_of(head, struct ip6_flowlabel, rcu);
  
  	if (fl->share == IPV6_FL_S_PROCESS)
  		put_pid(fl->owner.pid);
  	kfree(fl->opt);
  	kfree(fl);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  
  static void fl_free(struct ip6_flowlabel *fl)
  {
59c820b23   Willem de Bruijn   ipv6: elide flowl...
105
106
107
108
109
110
111
  	if (!fl)
  		return;
  
  	if (fl_shared_exclusive(fl) || fl->opt)
  		static_branch_slow_dec_deferred(&ipv6_flowlabel_exclusive);
  
  	call_rcu(&fl->rcu, fl_free_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
  }
  
  static void fl_release(struct ip6_flowlabel *fl)
  {
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
116
  	spin_lock_bh(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  
  	fl->lastuse = jiffies;
  	if (atomic_dec_and_test(&fl->users)) {
  		unsigned long ttd = fl->lastuse + fl->linger;
  		if (time_after(ttd, fl->expires))
  			fl->expires = ttd;
  		ttd = fl->expires;
  		if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
  			struct ipv6_txoptions *opt = fl->opt;
  			fl->opt = NULL;
  			kfree(opt);
  		}
  		if (!timer_pending(&ip6_fl_gc_timer) ||
  		    time_after(ip6_fl_gc_timer.expires, ttd))
  			mod_timer(&ip6_fl_gc_timer, ttd);
  	}
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
133
  	spin_unlock_bh(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  }
24ed960ab   Kees Cook   treewide: Switch ...
135
  static void ip6_fl_gc(struct timer_list *unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
  {
  	int i;
  	unsigned long now = jiffies;
  	unsigned long sched = 0;
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
140
  	spin_lock(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141

67ba4152e   Ian Morris   ipv6: White-space...
142
  	for (i = 0; i <= FL_HASH_MASK; i++) {
7f0e44ac9   Eric Dumazet   ipv6 flowlabel: a...
143
144
  		struct ip6_flowlabel *fl;
  		struct ip6_flowlabel __rcu **flp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  		flp = &fl_ht[i];
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
146
147
  		while ((fl = rcu_dereference_protected(*flp,
  						       lockdep_is_held(&ip6_fl_lock))) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  			if (atomic_read(&fl->users) == 0) {
  				unsigned long ttd = fl->lastuse + fl->linger;
  				if (time_after(ttd, fl->expires))
  					fl->expires = ttd;
  				ttd = fl->expires;
  				if (time_after_eq(now, ttd)) {
  					*flp = fl->next;
  					fl_free(fl);
  					atomic_dec(&fl_size);
  					continue;
  				}
  				if (!sched || time_before(ttd, sched))
  					sched = ttd;
  			}
  			flp = &fl->next;
  		}
  	}
  	if (!sched && atomic_read(&fl_size))
  		sched = now + FL_MAX_LINGER;
  	if (sched) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
168
  		mod_timer(&ip6_fl_gc_timer, sched);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  	}
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
170
  	spin_unlock(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
172
  static void __net_exit ip6_fl_purge(struct net *net)
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
173
174
  {
  	int i;
4762fb980   Jan Stancek   ipv6: fix possibl...
175
  	spin_lock_bh(&ip6_fl_lock);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
176
  	for (i = 0; i <= FL_HASH_MASK; i++) {
7f0e44ac9   Eric Dumazet   ipv6 flowlabel: a...
177
178
  		struct ip6_flowlabel *fl;
  		struct ip6_flowlabel __rcu **flp;
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
179
  		flp = &fl_ht[i];
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
180
181
  		while ((fl = rcu_dereference_protected(*flp,
  						       lockdep_is_held(&ip6_fl_lock))) != NULL) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
182
183
  			if (net_eq(fl->fl_net, net) &&
  			    atomic_read(&fl->users) == 0) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
184
185
186
187
188
189
190
191
  				*flp = fl->next;
  				fl_free(fl);
  				atomic_dec(&fl_size);
  				continue;
  			}
  			flp = &fl->next;
  		}
  	}
4762fb980   Jan Stancek   ipv6: fix possibl...
192
  	spin_unlock_bh(&ip6_fl_lock);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
193
194
195
196
  }
  
  static struct ip6_flowlabel *fl_intern(struct net *net,
  				       struct ip6_flowlabel *fl, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  {
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
198
  	struct ip6_flowlabel *lfl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  	fl->label = label & IPV6_FLOWLABEL_MASK;
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
200
  	spin_lock_bh(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  	if (label == 0) {
  		for (;;) {
63862b5be   Aruna-Hewapathirane   net: replace macr...
203
  			fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
  			if (fl->label) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
205
  				lfl = __fl_lookup(net, fl->label);
63159f29b   Ian Morris   ipv6: coding styl...
206
  				if (!lfl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
  					break;
  			}
  		}
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
210
211
212
213
214
215
216
217
218
  	} else {
  		/*
  		 * we dropper the ip6_fl_lock, so this entry could reappear
  		 * and we need to recheck with it.
  		 *
  		 * OTOH no need to search the active socket first, like it is
  		 * done in ipv6_flowlabel_opt - sock is locked, so new entry
  		 * with the same label can only appear on another sock
  		 */
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
219
  		lfl = __fl_lookup(net, fl->label);
53b24b8f9   Ian Morris   ipv6: coding styl...
220
  		if (lfl) {
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
221
  			atomic_inc(&lfl->users);
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
222
  			spin_unlock_bh(&ip6_fl_lock);
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
223
224
  			return lfl;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
  	}
  
  	fl->lastuse = jiffies;
  	fl->next = fl_ht[FL_HASH(fl->label)];
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
229
  	rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	atomic_inc(&fl_size);
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
231
  	spin_unlock_bh(&ip6_fl_lock);
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
232
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
  }
  
  
  
  /* Socket flowlabel lists */
59c820b23   Willem de Bruijn   ipv6: elide flowl...
238
  struct ip6_flowlabel *__fl6_sock_lookup(struct sock *sk, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
  {
  	struct ipv6_fl_socklist *sfl;
  	struct ipv6_pinfo *np = inet6_sk(sk);
  
  	label &= IPV6_FLOWLABEL_MASK;
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
244
245
  	rcu_read_lock_bh();
  	for_each_sk_fl_rcu(np, sfl) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  		struct ip6_flowlabel *fl = sfl->fl;
65a3c497c   Eric Dumazet   ipv6: flowlabel: ...
247
248
  
  		if (fl->label == label && atomic_inc_not_zero(&fl->users)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  			fl->lastuse = jiffies;
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
250
  			rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
  			return fl;
  		}
  	}
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
254
  	rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
  	return NULL;
  }
59c820b23   Willem de Bruijn   ipv6: elide flowl...
257
  EXPORT_SYMBOL_GPL(__fl6_sock_lookup);
3cf3dc6c2   Arnaldo Carvalho de Melo   [IPV6]: Export so...
258

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
  void fl6_free_socklist(struct sock *sk)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6_fl_socklist *sfl;
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
263
  	if (!rcu_access_pointer(np->ipv6_fl_list))
f256dc59d   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: E...
264
  		return;
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
265
266
267
268
269
  	spin_lock_bh(&ip6_sk_fl_lock);
  	while ((sfl = rcu_dereference_protected(np->ipv6_fl_list,
  						lockdep_is_held(&ip6_sk_fl_lock))) != NULL) {
  		np->ipv6_fl_list = sfl->next;
  		spin_unlock_bh(&ip6_sk_fl_lock);
f256dc59d   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: E...
270

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  		fl_release(sfl->fl);
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
272
273
274
  		kfree_rcu(sfl, rcu);
  
  		spin_lock_bh(&ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	}
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
276
  	spin_unlock_bh(&ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
283
284
285
286
  }
  
  /* Service routines */
  
  
  /*
     It is the only difficult place. flowlabel enforces equal headers
     before and including routing header, however user may supply options
     following rthdr.
   */
67ba4152e   Ian Morris   ipv6: White-space...
287
288
289
  struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
  					 struct ip6_flowlabel *fl,
  					 struct ipv6_txoptions *fopt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  {
67ba4152e   Ian Morris   ipv6: White-space...
291
  	struct ipv6_txoptions *fl_opt = fl->opt;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
292

63159f29b   Ian Morris   ipv6: coding styl...
293
  	if (!fopt || fopt->opt_flen == 0)
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
294
  		return fl_opt;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
295

53b24b8f9   Ian Morris   ipv6: coding styl...
296
  	if (fl_opt) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  		opt_space->hopopt = fl_opt->hopopt;
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
298
  		opt_space->dst0opt = fl_opt->dst0opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
303
304
305
306
307
308
309
  		opt_space->srcrt = fl_opt->srcrt;
  		opt_space->opt_nflen = fl_opt->opt_nflen;
  	} else {
  		if (fopt->opt_nflen == 0)
  			return fopt;
  		opt_space->hopopt = NULL;
  		opt_space->dst0opt = NULL;
  		opt_space->srcrt = NULL;
  		opt_space->opt_nflen = 0;
  	}
  	opt_space->dst1opt = fopt->dst1opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	opt_space->opt_flen = fopt->opt_flen;
864e2a1f8   Eric Dumazet   ipv6: flowlabel: ...
311
  	opt_space->tot_len = fopt->tot_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
  	return opt_space;
  }
a495f8364   Chris Elston   ipv6: Export ipv6...
314
  EXPORT_SYMBOL_GPL(fl6_merge_options);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  
  static unsigned long check_linger(unsigned long ttl)
  {
  	if (ttl < FL_MIN_LINGER)
  		return FL_MIN_LINGER*HZ;
  	if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))
  		return 0;
  	return ttl*HZ;
  }
  
  static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires)
  {
  	linger = check_linger(linger);
  	if (!linger)
  		return -EPERM;
  	expires = check_linger(expires);
  	if (!expires)
  		return -EPERM;
394055f6f   Florent Fourcot   ipv6: protect flo...
333
334
  
  	spin_lock_bh(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
  	fl->lastuse = jiffies;
  	if (time_before(fl->linger, linger))
  		fl->linger = linger;
  	if (time_before(expires, fl->linger))
  		expires = fl->linger;
  	if (time_before(fl->expires, fl->lastuse + expires))
  		fl->expires = fl->lastuse + expires;
394055f6f   Florent Fourcot   ipv6: protect flo...
342
  	spin_unlock_bh(&ip6_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
  	return 0;
  }
  
  static struct ip6_flowlabel *
ec0506dbe   Maciej Żenczykowski   net: relax PKTINF...
347
  fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
86298285c   Christoph Hellwig   net/ipv6: switch ...
348
  	  sockptr_t optval, int optlen, int *err_p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  {
684de409a   David S. Miller   ipv6: Disallow re...
350
  	struct ip6_flowlabel *fl = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
  	int olen;
  	int addr_type;
  	int err;
684de409a   David S. Miller   ipv6: Disallow re...
354
355
356
357
  	olen = optlen - CMSG_ALIGN(sizeof(*freq));
  	err = -EINVAL;
  	if (olen > 64 * 1024)
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  	err = -ENOMEM;
0c600eda4   Ingo Oeser   [IPV6]: Nearly co...
359
  	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
63159f29b   Ian Morris   ipv6: coding styl...
360
  	if (!fl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  	if (olen > 0) {
  		struct msghdr msg;
4c9483b2f   David S. Miller   ipv6: Convert to ...
365
  		struct flowi6 flowi6;
26879da58   Wei Wang   ipv6: add new str...
366
  		struct ipcm6_cookie ipc6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
  
  		err = -ENOMEM;
  		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
63159f29b   Ian Morris   ipv6: coding styl...
370
  		if (!fl->opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
375
  			goto done;
  
  		memset(fl->opt, 0, sizeof(*fl->opt));
  		fl->opt->tot_len = sizeof(*fl->opt) + olen;
  		err = -EFAULT;
d3c481515   Christoph Hellwig   net: remove sockp...
376
377
  		if (copy_from_sockptr_offset(fl->opt + 1, optval,
  				CMSG_ALIGN(sizeof(*freq)), olen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
  			goto done;
  
  		msg.msg_controllen = olen;
67ba4152e   Ian Morris   ipv6: White-space...
381
  		msg.msg_control = (void *)(fl->opt+1);
4c9483b2f   David S. Miller   ipv6: Convert to ...
382
  		memset(&flowi6, 0, sizeof(flowi6));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383

26879da58   Wei Wang   ipv6: add new str...
384
  		ipc6.opt = fl->opt;
5fdaa88df   Willem de Bruijn   ipv6: fold sockcm...
385
  		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &ipc6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
394
395
  		if (err)
  			goto done;
  		err = -EINVAL;
  		if (fl->opt->opt_flen)
  			goto done;
  		if (fl->opt->opt_nflen == 0) {
  			kfree(fl->opt);
  			fl->opt = NULL;
  		}
  	}
efd7ef1c1   Eric W. Biederman   net: Kill hold_ne...
396
  	fl->fl_net = net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
402
  	fl->expires = jiffies;
  	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
  	if (err)
  		goto done;
  	fl->share = freq->flr_share;
  	addr_type = ipv6_addr_type(&freq->flr_dst);
35700212b   Joe Perches   net/ipv6: Move &&...
403
404
  	if ((addr_type & IPV6_ADDR_MAPPED) ||
  	    addr_type == IPV6_ADDR_ANY) {
c6817e4c3   James Morris   [IPV6]: return EI...
405
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  		goto done;
c6817e4c3   James Morris   [IPV6]: return EI...
407
  	}
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
408
  	fl->dst = freq->flr_dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
  	atomic_set(&fl->users, 1);
  	switch (fl->share) {
  	case IPV6_FL_S_EXCL:
  	case IPV6_FL_S_ANY:
  		break;
  	case IPV6_FL_S_PROCESS:
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
415
  		fl->owner.pid = get_task_pid(current, PIDTYPE_PID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
  		break;
  	case IPV6_FL_S_USER:
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
418
  		fl->owner.uid = current_euid();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
423
  		break;
  	default:
  		err = -EINVAL;
  		goto done;
  	}
d44e3fa5d   Eric Dumazet   ipv6: fix static ...
424
425
  	if (fl_shared_exclusive(fl) || fl->opt)
  		static_branch_deferred_inc(&ipv6_flowlabel_exclusive);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
  	return fl;
  
  done:
d44e3fa5d   Eric Dumazet   ipv6: fix static ...
429
430
431
432
  	if (fl) {
  		kfree(fl->opt);
  		kfree(fl);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
445
  	*err_p = err;
  	return NULL;
  }
  
  static int mem_check(struct sock *sk)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6_fl_socklist *sfl;
  	int room = FL_MAX_SIZE - atomic_read(&fl_size);
  	int count = 0;
  
  	if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
  		return 0;
f8c31c8f8   Hannes Frederic Sowa   ipv6: protect for...
446
  	rcu_read_lock_bh();
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
447
  	for_each_sk_fl_rcu(np, sfl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
  		count++;
f8c31c8f8   Hannes Frederic Sowa   ipv6: protect for...
449
  	rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
  
  	if (room <= 0 ||
  	    ((count >= FL_MAX_PER_SOCK ||
35700212b   Joe Perches   net/ipv6: Move &&...
453
454
  	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
  	     !capable(CAP_NET_ADMIN)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
458
  		return -ENOBUFS;
  
  	return 0;
  }
04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
459
460
461
  static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
  		struct ip6_flowlabel *fl)
  {
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
462
  	spin_lock_bh(&ip6_sk_fl_lock);
04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
463
464
  	sfl->fl = fl;
  	sfl->next = np->ipv6_fl_list;
18367681a   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
465
466
  	rcu_assign_pointer(np->ipv6_fl_list, sfl);
  	spin_unlock_bh(&ip6_sk_fl_lock);
04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
467
  }
46e5f4017   Florent Fourcot   ipv6: add a flag ...
468
469
  int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq,
  			   int flags)
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
470
471
472
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6_fl_socklist *sfl;
46e5f4017   Florent Fourcot   ipv6: add a flag ...
473
474
475
476
  	if (flags & IPV6_FL_F_REMOTE) {
  		freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK;
  		return 0;
  	}
df3687ffc   Florent Fourcot   ipv6: add the IPV...
477
478
479
480
  	if (np->repflow) {
  		freq->flr_label = np->flow_label;
  		return 0;
  	}
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  	rcu_read_lock_bh();
  
  	for_each_sk_fl_rcu(np, sfl) {
  		if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
  			spin_lock_bh(&ip6_fl_lock);
  			freq->flr_label = sfl->fl->label;
  			freq->flr_dst = sfl->fl->dst;
  			freq->flr_share = sfl->fl->share;
  			freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
  			freq->flr_linger = sfl->fl->linger / HZ;
  
  			spin_unlock_bh(&ip6_fl_lock);
  			rcu_read_unlock_bh();
  			return 0;
  		}
  	}
  	rcu_read_unlock_bh();
  
  	return -ENOENT;
  }
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
501
502
503
504
  #define socklist_dereference(__sflp) \
  	rcu_dereference_protected(__sflp, lockdep_is_held(&ip6_sk_fl_lock))
  
  static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  	struct ipv6_pinfo *np = inet6_sk(sk);
7f0e44ac9   Eric Dumazet   ipv6 flowlabel: a...
507
  	struct ipv6_fl_socklist __rcu **sflp;
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
508
  	struct ipv6_fl_socklist *sfl;
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
509

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
510
511
512
513
514
515
516
517
518
  	if (freq->flr_flags & IPV6_FL_F_REFLECT) {
  		if (sk->sk_protocol != IPPROTO_TCP)
  			return -ENOPROTOOPT;
  		if (!np->repflow)
  			return -ESRCH;
  		np->flow_label = 0;
  		np->repflow = 0;
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  	spin_lock_bh(&ip6_sk_fl_lock);
  	for (sflp = &np->ipv6_fl_list;
  	     (sfl = socklist_dereference(*sflp)) != NULL;
  	     sflp = &sfl->next) {
  		if (sfl->fl->label == freq->flr_label)
  			goto found;
  	}
  	spin_unlock_bh(&ip6_sk_fl_lock);
  	return -ESRCH;
  found:
  	if (freq->flr_label == (np->flow_label & IPV6_FLOWLABEL_MASK))
  		np->flow_label &= ~IPV6_FLOWLABEL_MASK;
  	*sflp = sfl->next;
  	spin_unlock_bh(&ip6_sk_fl_lock);
  	fl_release(sfl->fl);
  	kfree_rcu(sfl, rcu);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
539
540
541
542
543
544
  static int ipv6_flowlabel_renew(struct sock *sk, struct in6_flowlabel_req *freq)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct net *net = sock_net(sk);
  	struct ipv6_fl_socklist *sfl;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
546
547
548
549
550
551
552
  	rcu_read_lock_bh();
  	for_each_sk_fl_rcu(np, sfl) {
  		if (sfl->fl->label == freq->flr_label) {
  			err = fl6_renew(sfl->fl, freq->flr_linger,
  					freq->flr_expires);
  			rcu_read_unlock_bh();
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  		}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
554
555
  	}
  	rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
557
558
559
  	if (freq->flr_share == IPV6_FL_S_NONE &&
  	    ns_capable(net->user_ns, CAP_NET_ADMIN)) {
  		struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
561
562
563
564
565
  		if (fl) {
  			err = fl6_renew(fl, freq->flr_linger,
  					freq->flr_expires);
  			fl_release(fl);
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  		}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
567
568
569
  	}
  	return -ESRCH;
  }
6444f72b4   Florent Fourcot   ipv6: add flowlab...
570

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
571
  static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
86298285c   Christoph Hellwig   net/ipv6: switch ...
572
  		sockptr_t optval, int optlen)
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
573
574
575
576
577
  {
  	struct ipv6_fl_socklist *sfl, *sfl1 = NULL;
  	struct ip6_flowlabel *fl, *fl1 = NULL;
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct net *net = sock_net(sk);
47ec5303d   Linus Torvalds   Merge git://git.k...
578
  	int err;
6444f72b4   Florent Fourcot   ipv6: add flowlab...
579

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
580
581
582
583
584
  	if (freq->flr_flags & IPV6_FL_F_REFLECT) {
  		if (net->ipv6.sysctl.flowlabel_consistency) {
  			net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable
  ");
  			return -EPERM;
df3687ffc   Florent Fourcot   ipv6: add the IPV...
585
  		}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
586
587
588
589
590
  		if (sk->sk_protocol != IPPROTO_TCP)
  			return -ENOPROTOOPT;
  		np->repflow = 1;
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
592
593
594
595
596
  	if (freq->flr_label & ~IPV6_FLOWLABEL_MASK)
  		return -EINVAL;
  	if (net->ipv6.sysctl.flowlabel_state_ranges &&
  	    (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
  		return -ERANGE;
82a584b7c   Tom Herbert   ipv6: Flow label ...
597

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
598
599
600
  	fl = fl_create(net, sk, freq, optval, optlen, &err);
  	if (!fl)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
602
603
604
605
606
607
608
609
610
611
  	sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
  
  	if (freq->flr_label) {
  		err = -EEXIST;
  		rcu_read_lock_bh();
  		for_each_sk_fl_rcu(np, sfl) {
  			if (sfl->fl->label == freq->flr_label) {
  				if (freq->flr_flags & IPV6_FL_F_EXCL) {
  					rcu_read_unlock_bh();
  					goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  				}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
613
614
615
616
  				fl1 = sfl->fl;
  				if (!atomic_inc_not_zero(&fl1->users))
  					fl1 = NULL;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  			}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
618
619
  		}
  		rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
621
622
623
  		if (!fl1)
  			fl1 = fl_lookup(net, freq->flr_label);
  		if (fl1) {
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
624
  recheck:
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  			err = -EEXIST;
  			if (freq->flr_flags&IPV6_FL_F_EXCL)
  				goto release;
  			err = -EPERM;
  			if (fl1->share == IPV6_FL_S_EXCL ||
  			    fl1->share != fl->share ||
  			    ((fl1->share == IPV6_FL_S_PROCESS) &&
  			     (fl1->owner.pid != fl->owner.pid)) ||
  			    ((fl1->share == IPV6_FL_S_USER) &&
  			     !uid_eq(fl1->owner.uid, fl->owner.uid)))
  				goto release;
  
  			err = -ENOMEM;
  			if (!sfl1)
  				goto release;
  			if (fl->linger > fl1->linger)
  				fl1->linger = fl->linger;
  			if ((long)(fl->expires - fl1->expires) > 0)
  				fl1->expires = fl->expires;
  			fl_link(np, sfl1, fl1);
  			fl_free(fl);
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
  
  release:
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
649
  			fl_release(fl1);
e5d08d718   Ian Morris   ipv6: coding styl...
650
  			goto done;
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
651
652
653
654
655
  		}
  	}
  	err = -ENOENT;
  	if (!(freq->flr_flags & IPV6_FL_F_CREATE))
  		goto done;
e5d08d718   Ian Morris   ipv6: coding styl...
656

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
657
658
659
  	err = -ENOMEM;
  	if (!sfl1)
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
661
662
663
  	err = mem_check(sk);
  	if (err != 0)
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
665
666
667
  	fl1 = fl_intern(net, fl, freq->flr_label);
  	if (fl1)
  		goto recheck;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668

ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
669
  	if (!freq->flr_label) {
d3c481515   Christoph Hellwig   net: remove sockp...
670
  		size_t offset = offsetof(struct in6_flowlabel_req, flr_label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671

d3c481515   Christoph Hellwig   net: remove sockp...
672
673
  		if (copy_to_sockptr_offset(optval, offset, &fl->label,
  				sizeof(fl->label))) {
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
674
  			/* Intentionally ignore fault. */
6c94d3611   David S. Miller   [IPV6]: Clear up ...
675
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
  	}
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
677
678
  	fl_link(np, sfl1, fl);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
681
682
683
  done:
  	fl_free(fl);
  	kfree(sfl1);
  	return err;
  }
86298285c   Christoph Hellwig   net/ipv6: switch ...
684
  int ipv6_flowlabel_opt(struct sock *sk, sockptr_t optval, int optlen)
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
685
686
687
688
689
  {
  	struct in6_flowlabel_req freq;
  
  	if (optlen < sizeof(freq))
  		return -EINVAL;
86298285c   Christoph Hellwig   net/ipv6: switch ...
690
  	if (copy_from_sockptr(&freq, optval, sizeof(freq)))
ff6a4cf21   Christoph Hellwig   net/ipv6: split u...
691
692
693
694
695
696
697
698
699
700
701
702
703
  		return -EFAULT;
  
  	switch (freq.flr_action) {
  	case IPV6_FL_A_PUT:
  		return ipv6_flowlabel_put(sk, &freq);
  	case IPV6_FL_A_RENEW:
  		return ipv6_flowlabel_renew(sk, &freq);
  	case IPV6_FL_A_GET:
  		return ipv6_flowlabel_get(sk, &freq, optval, optlen);
  	default:
  		return -EINVAL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
  #ifdef CONFIG_PROC_FS
  
  struct ip6fl_iter_state {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
707
  	struct seq_net_private p;
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
708
  	struct pid_namespace *pid_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
713
714
715
716
717
  	int bucket;
  };
  
  #define ip6fl_seq_private(seq)	((struct ip6fl_iter_state *)(seq)->private)
  
  static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq)
  {
  	struct ip6_flowlabel *fl = NULL;
  	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
718
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
  
  	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
721
722
723
724
  		for_each_fl_rcu(state->bucket, fl) {
  			if (net_eq(fl->fl_net, net))
  				goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
  	}
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
726
727
  	fl = NULL;
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
  	return fl;
  }
  
  static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl)
  {
  	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
734
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735

d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
736
737
738
739
  	for_each_fl_continue_rcu(fl) {
  		if (net_eq(fl->fl_net, net))
  			goto out;
  	}
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
740
  try_again:
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
741
742
743
744
745
746
  	if (++state->bucket <= FL_HASH_MASK) {
  		for_each_fl_rcu(state->bucket, fl) {
  			if (net_eq(fl->fl_net, net))
  				goto out;
  		}
  		goto try_again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
  	}
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
748
749
750
  	fl = NULL;
  
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
756
757
758
759
760
761
762
763
  	return fl;
  }
  
  static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct ip6_flowlabel *fl = ip6fl_get_first(seq);
  	if (fl)
  		while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL)
  			--pos;
  	return pos ? NULL : fl;
  }
  
  static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos)
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
764
  	__acquires(RCU)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
  {
ad08978ab   Christoph Hellwig   ipv6/flowlabel: s...
766
  	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
9d78edeae   Alexey Gladkov   proc: proc_pid_ns...
767
  	state->pid_ns = proc_pid_ns(file_inode(seq->file)->i_sb);
ad08978ab   Christoph Hellwig   ipv6/flowlabel: s...
768

d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
769
  	rcu_read_lock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
  	return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ip6_flowlabel *fl;
  
  	if (v == SEQ_START_TOKEN)
  		fl = ip6fl_get_first(seq);
  	else
  		fl = ip6fl_get_next(seq, v);
  	++*pos;
  	return fl;
  }
  
  static void ip6fl_seq_stop(struct seq_file *seq, void *v)
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
786
  	__releases(RCU)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
  {
d3aedd5eb   YOSHIFUJI Hideaki / 吉藤英明   ipv6 flowlabel: C...
788
  	rcu_read_unlock_bh();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
  }
1b7c2dbc0   James Morris   [IPV6]: fix flowl...
790
  static int ip6fl_seq_show(struct seq_file *seq, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  {
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
792
  	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
869ba988f   Florent Fourcot   ipv6: trivial, ad...
793
  	if (v == SEQ_START_TOKEN) {
1744bea1f   Joe Perches   net: Convert SEQ_...
794
795
  		seq_puts(seq, "Label S Owner  Users  Linger Expires  Dst                              Opt
  ");
869ba988f   Florent Fourcot   ipv6: trivial, ad...
796
  	} else {
1b7c2dbc0   James Morris   [IPV6]: fix flowl...
797
  		struct ip6_flowlabel *fl = v;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
  		seq_printf(seq,
4b7a4274c   Harvey Harrison   net: replace %#p6...
799
800
  			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d
  ",
95c961747   Eric Dumazet   net: cleanup unsi...
801
  			   (unsigned int)ntohl(fl->label),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  			   fl->share,
4f82f4573   Eric W. Biederman   net ip6 flowlabel...
803
804
805
806
807
  			   ((fl->share == IPV6_FL_S_PROCESS) ?
  			    pid_nr_ns(fl->owner.pid, state->pid_ns) :
  			    ((fl->share == IPV6_FL_S_USER) ?
  			     from_kuid_munged(seq_user_ns(seq), fl->owner.uid) :
  			     0)),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
810
  			   atomic_read(&fl->users),
  			   fl->linger/HZ,
  			   (long)(fl->expires - jiffies)/HZ,
b071195de   Harvey Harrison   net: replace all ...
811
  			   &fl->dst,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  			   fl->opt ? fl->opt->opt_nflen : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
  	return 0;
  }
56b3d975b   Philippe De Muyter   [NET]: Make all i...
816
  static const struct seq_operations ip6fl_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
819
820
821
  	.start	=	ip6fl_seq_start,
  	.next	=	ip6fl_seq_next,
  	.stop	=	ip6fl_seq_stop,
  	.show	=	ip6fl_seq_show,
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
822
  static int __net_init ip6_flowlabel_proc_init(struct net *net)
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
823
  {
c35063722   Christoph Hellwig   proc: introduce p...
824
825
  	if (!proc_create_net("ip6_flowlabel", 0444, net->proc_net,
  			&ip6fl_seq_ops, sizeof(struct ip6fl_iter_state)))
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
826
827
828
  		return -ENOMEM;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829

2c8c1e729   Alexey Dobriyan   net: spread __net...
830
  static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
  {
ece31ffd5   Gao feng   net: proc: change...
832
  	remove_proc_entry("ip6_flowlabel", net->proc_net);
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
833
834
835
836
837
838
839
840
  }
  #else
  static inline int ip6_flowlabel_proc_init(struct net *net)
  {
  	return 0;
  }
  static inline void ip6_flowlabel_proc_fini(struct net *net)
  {
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
841
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
  #endif
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
843

2c8c1e729   Alexey Dobriyan   net: spread __net...
844
  static void __net_exit ip6_flowlabel_net_exit(struct net *net)
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
845
846
  {
  	ip6_fl_purge(net);
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
847
  	ip6_flowlabel_proc_fini(net);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
848
849
850
  }
  
  static struct pernet_operations ip6_flowlabel_net_ops = {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
851
  	.init = ip6_flowlabel_proc_init,
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
852
853
  	.exit = ip6_flowlabel_net_exit,
  };
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
854
855
  int ip6_flowlabel_init(void)
  {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
856
  	return register_pernet_subsys(&ip6_flowlabel_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
857
858
859
860
  }
  
  void ip6_flowlabel_cleanup(void)
  {
59c820b23   Willem de Bruijn   ipv6: elide flowl...
861
  	static_key_deferred_flush(&ipv6_flowlabel_exclusive);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  	del_timer(&ip6_fl_gc_timer);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
863
  	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  }