Blame view

net/ipv6/ip6_flowlabel.c 17.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   *	ip6_flowlabel.c		IPv6 flowlabel manager.
   *
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   *
   *	Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
11
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/if_arp.h>
  #include <linux/in6.h>
  #include <linux/route.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
22
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
23
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
25
  #include <net/net_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  #include <net/sock.h>
  
  #include <net/ipv6.h>
  #include <net/ndisc.h>
  #include <net/protocol.h>
  #include <net/ip6_route.h>
  #include <net/addrconf.h>
  #include <net/rawv6.h>
  #include <net/icmp.h>
  #include <net/transp_v6.h>
  
  #include <asm/uaccess.h>
  
  #define FL_MIN_LINGER	6	/* Minimal linger. It is set to 6sec specified
  				   in old IPv6 RFC. Well, it was reasonable value.
  				 */
  #define FL_MAX_LINGER	60	/* Maximal linger timeout */
  
  /* 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);
  static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1];
  
  static void ip6_fl_gc(unsigned long dummy);
8d06afab7   Ingo Molnar   [PATCH] timer ini...
55
  static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
  
  /* FL hash table lock: it protects only of GC */
  
  static DEFINE_RWLOCK(ip6_fl_lock);
  
  /* Big socket sock */
  
  static DEFINE_RWLOCK(ip6_sk_fl_lock);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
64
  static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
  {
  	struct ip6_flowlabel *fl;
  
  	for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
69
  		if (fl->label == label && net_eq(fl->fl_net, net))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
  			return fl;
  	}
  	return NULL;
  }
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
74
  static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
  {
  	struct ip6_flowlabel *fl;
  
  	read_lock_bh(&ip6_fl_lock);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
79
  	fl = __fl_lookup(net, label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
  	if (fl)
  		atomic_inc(&fl->users);
  	read_unlock_bh(&ip6_fl_lock);
  	return fl;
  }
  
  
  static void fl_free(struct ip6_flowlabel *fl)
  {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
89
90
  	if (fl) {
  		release_net(fl->fl_net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  		kfree(fl->opt);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
92
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  	kfree(fl);
  }
  
  static void fl_release(struct ip6_flowlabel *fl)
  {
  	write_lock_bh(&ip6_fl_lock);
  
  	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);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  	write_unlock_bh(&ip6_fl_lock);
  }
  
  static void ip6_fl_gc(unsigned long dummy)
  {
  	int i;
  	unsigned long now = jiffies;
  	unsigned long sched = 0;
  
  	write_lock(&ip6_fl_lock);
  
  	for (i=0; i<=FL_HASH_MASK; i++) {
  		struct ip6_flowlabel *fl, **flp;
  		flp = &fl_ht[i];
  		while ((fl=*flp) != NULL) {
  			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...
150
  		mod_timer(&ip6_fl_gc_timer, sched);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
  	}
  	write_unlock(&ip6_fl_lock);
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
154
  static void __net_exit ip6_fl_purge(struct net *net)
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
155
156
157
158
159
160
161
162
  {
  	int i;
  
  	write_lock(&ip6_fl_lock);
  	for (i = 0; i <= FL_HASH_MASK; i++) {
  		struct ip6_flowlabel *fl, **flp;
  		flp = &fl_ht[i];
  		while ((fl = *flp) != NULL) {
09ad9bc75   Octavian Purdila   net: use net_eq t...
163
164
  			if (net_eq(fl->fl_net, net) &&
  			    atomic_read(&fl->users) == 0) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
165
166
167
168
169
170
171
172
173
174
175
176
177
  				*flp = fl->next;
  				fl_free(fl);
  				atomic_dec(&fl_size);
  				continue;
  			}
  			flp = &fl->next;
  		}
  	}
  	write_unlock(&ip6_fl_lock);
  }
  
  static struct ip6_flowlabel *fl_intern(struct net *net,
  				       struct ip6_flowlabel *fl, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  {
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
179
  	struct ip6_flowlabel *lfl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
186
  	fl->label = label & IPV6_FLOWLABEL_MASK;
  
  	write_lock_bh(&ip6_fl_lock);
  	if (label == 0) {
  		for (;;) {
  			fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
  			if (fl->label) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
187
  				lfl = __fl_lookup(net, fl->label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
  				if (lfl == NULL)
  					break;
  			}
  		}
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
192
193
194
195
196
197
198
199
200
  	} 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...
201
  		lfl = __fl_lookup(net, fl->label);
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
202
203
204
205
206
  		if (lfl != NULL) {
  			atomic_inc(&lfl->users);
  			write_unlock_bh(&ip6_fl_lock);
  			return lfl;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
211
212
213
  	}
  
  	fl->lastuse = jiffies;
  	fl->next = fl_ht[FL_HASH(fl->label)];
  	fl_ht[FL_HASH(fl->label)] = fl;
  	atomic_inc(&fl_size);
  	write_unlock_bh(&ip6_fl_lock);
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
214
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
  }
  
  
  
  /* Socket flowlabel lists */
90bcaf7b4   Al Viro   [IPV6]: flowlabel...
220
  struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
  {
  	struct ipv6_fl_socklist *sfl;
  	struct ipv6_pinfo *np = inet6_sk(sk);
  
  	label &= IPV6_FLOWLABEL_MASK;
bd0bf5770   Pavel Emelyanov   [IPV6]: Lost lock...
226
  	read_lock_bh(&ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
  	for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) {
  		struct ip6_flowlabel *fl = sfl->fl;
  		if (fl->label == label) {
  			fl->lastuse = jiffies;
  			atomic_inc(&fl->users);
52f095ee8   Pavel Emelyanov   [IPV6]: Fix again...
232
  			read_unlock_bh(&ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
  			return fl;
  		}
  	}
bd0bf5770   Pavel Emelyanov   [IPV6]: Lost lock...
236
  	read_unlock_bh(&ip6_sk_fl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
  	return NULL;
  }
3cf3dc6c2   Arnaldo Carvalho de Melo   [IPV6]: Export so...
239
  EXPORT_SYMBOL_GPL(fl6_sock_lookup);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  void fl6_free_socklist(struct sock *sk)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6_fl_socklist *sfl;
  
  	while ((sfl = np->ipv6_fl_list) != NULL) {
  		np->ipv6_fl_list = sfl->next;
  		fl_release(sfl->fl);
  		kfree(sfl);
  	}
  }
  
  /* Service routines */
  
  
  /*
     It is the only difficult place. flowlabel enforces equal headers
     before and including routing header, however user may supply options
     following rthdr.
   */
  
  struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
  					 struct ip6_flowlabel * fl,
  					 struct ipv6_txoptions * fopt)
  {
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
265
  	struct ipv6_txoptions * fl_opt = fl->opt;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
266

df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
267
268
  	if (fopt == NULL || fopt->opt_flen == 0)
  		return fl_opt;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
269

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
  	if (fl_opt != NULL) {
  		opt_space->hopopt = fl_opt->hopopt;
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
272
  		opt_space->dst0opt = fl_opt->dst0opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
280
281
282
283
  		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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  	opt_space->opt_flen = fopt->opt_flen;
  	return opt_space;
  }
  
  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;
  	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;
  	return 0;
  }
  
  static struct ip6_flowlabel *
ec0506dbe   Maciej Żenczykowski   net: relax PKTINF...
316
317
  fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
  	  char __user *optval, int optlen, int *err_p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  {
684de409a   David S. Miller   ipv6: Disallow re...
319
  	struct ip6_flowlabel *fl = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
  	int olen;
  	int addr_type;
  	int err;
684de409a   David S. Miller   ipv6: Disallow re...
323
324
325
326
  	olen = optlen - CMSG_ALIGN(sizeof(*freq));
  	err = -EINVAL;
  	if (olen > 64 * 1024)
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  	err = -ENOMEM;
0c600eda4   Ingo Oeser   [IPV6]: Nearly co...
328
  	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  	if (fl == NULL)
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
  	if (olen > 0) {
  		struct msghdr msg;
4c9483b2f   David S. Miller   ipv6: Convert to ...
334
  		struct flowi6 flowi6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  		int junk;
  
  		err = -ENOMEM;
  		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
  		if (fl->opt == NULL)
  			goto done;
  
  		memset(fl->opt, 0, sizeof(*fl->opt));
  		fl->opt->tot_len = sizeof(*fl->opt) + olen;
  		err = -EFAULT;
  		if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
  			goto done;
  
  		msg.msg_controllen = olen;
  		msg.msg_control = (void*)(fl->opt+1);
4c9483b2f   David S. Miller   ipv6: Convert to ...
350
  		memset(&flowi6, 0, sizeof(flowi6));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

ec0506dbe   Maciej Żenczykowski   net: relax PKTINF...
352
  		err = datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, &junk,
13b52cd44   Brian Haley   IPv6: Add dontfra...
353
  					&junk, &junk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
360
361
362
363
  		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;
  		}
  	}
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
364
  	fl->fl_net = hold_net(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
  	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 &&...
371
372
  	if ((addr_type & IPV6_ADDR_MAPPED) ||
  	    addr_type == IPV6_ADDR_ANY) {
c6817e4c3   James Morris   [IPV6]: return EI...
373
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  		goto done;
c6817e4c3   James Morris   [IPV6]: return EI...
375
  	}
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
376
  	fl->dst = freq->flr_dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
385
  	atomic_set(&fl->users, 1);
  	switch (fl->share) {
  	case IPV6_FL_S_EXCL:
  	case IPV6_FL_S_ANY:
  		break;
  	case IPV6_FL_S_PROCESS:
  		fl->owner = current->pid;
  		break;
  	case IPV6_FL_S_USER:
f82b35902   David Howells   CRED: Wrap task c...
386
  		fl->owner = current_euid();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  		break;
  	default:
  		err = -EINVAL;
  		goto done;
  	}
  	return fl;
  
  done:
  	fl_free(fl);
  	*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;
  
  	for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next)
  		count++;
  
  	if (room <= 0 ||
  	    ((count >= FL_MAX_PER_SOCK ||
35700212b   Joe Perches   net/ipv6: Move &&...
415
416
  	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
  	     !capable(CAP_NET_ADMIN)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  		return -ENOBUFS;
  
  	return 0;
  }
  
  static int ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2)
  {
  	if (h1 == h2)
  		return 0;
  	if (h1 == NULL || h2 == NULL)
  		return 1;
  	if (h1->hdrlen != h2->hdrlen)
  		return 1;
  	return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));
  }
  
  static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
  {
  	if (o1 == o2)
  		return 0;
  	if (o1 == NULL || o2 == NULL)
  		return 1;
  	if (o1->opt_nflen != o2->opt_nflen)
  		return 1;
  	if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt))
  		return 1;
  	if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt))
  		return 1;
  	if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
  		return 1;
  	return 0;
  }
04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
449
450
451
452
453
454
455
456
457
  static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
  		struct ip6_flowlabel *fl)
  {
  	write_lock_bh(&ip6_sk_fl_lock);
  	sfl->fl = fl;
  	sfl->next = np->ipv6_fl_list;
  	np->ipv6_fl_list = sfl;
  	write_unlock_bh(&ip6_sk_fl_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
  int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
  {
55205d400   Ingo Molnar   ipv6: fix warning...
460
  	int uninitialized_var(err);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
461
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct in6_flowlabel_req freq;
  	struct ipv6_fl_socklist *sfl1=NULL;
  	struct ipv6_fl_socklist *sfl, **sflp;
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
466
  	struct ip6_flowlabel *fl, *fl1 = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  
  	if (optlen < sizeof(freq))
  		return -EINVAL;
  
  	if (copy_from_user(&freq, optval, sizeof(freq)))
  		return -EFAULT;
  
  	switch (freq.flr_action) {
  	case IPV6_FL_A_PUT:
  		write_lock_bh(&ip6_sk_fl_lock);
  		for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {
  			if (sfl->fl->label == freq.flr_label) {
  				if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
  					np->flow_label &= ~IPV6_FLOWLABEL_MASK;
  				*sflp = sfl->next;
  				write_unlock_bh(&ip6_sk_fl_lock);
  				fl_release(sfl->fl);
  				kfree(sfl);
  				return 0;
  			}
  		}
  		write_unlock_bh(&ip6_sk_fl_lock);
  		return -ESRCH;
  
  	case IPV6_FL_A_RENEW:
  		read_lock_bh(&ip6_sk_fl_lock);
  		for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
  			if (sfl->fl->label == freq.flr_label) {
  				err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
  				read_unlock_bh(&ip6_sk_fl_lock);
  				return err;
  			}
  		}
  		read_unlock_bh(&ip6_sk_fl_lock);
  
  		if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
503
  			fl = fl_lookup(net, freq.flr_label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
507
508
509
510
511
512
513
514
  			if (fl) {
  				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
  				fl_release(fl);
  				return err;
  			}
  		}
  		return -ESRCH;
  
  	case IPV6_FL_A_GET:
  		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
  			return -EINVAL;
ec0506dbe   Maciej Żenczykowski   net: relax PKTINF...
515
  		fl = fl_create(net, sk, &freq, optval, optlen, &err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
517
518
519
520
  		if (fl == NULL)
  			return err;
  		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
  
  		if (freq.flr_label) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
523
524
525
526
527
528
529
  			err = -EEXIST;
  			read_lock_bh(&ip6_sk_fl_lock);
  			for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
  				if (sfl->fl->label == freq.flr_label) {
  					if (freq.flr_flags&IPV6_FL_F_EXCL) {
  						read_unlock_bh(&ip6_sk_fl_lock);
  						goto done;
  					}
  					fl1 = sfl->fl;
4ea6a8046   Yan Zheng   [IPV6]: Fix refcn...
530
  					atomic_inc(&fl1->users);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
536
  					break;
  				}
  			}
  			read_unlock_bh(&ip6_sk_fl_lock);
  
  			if (fl1 == NULL)
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
537
  				fl1 = fl_lookup(net, freq.flr_label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  			if (fl1) {
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
539
  recheck:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
  				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->owner != fl->owner)
  					goto release;
  
  				err = -EINVAL;
  				if (!ipv6_addr_equal(&fl1->dst, &fl->dst) ||
  				    ipv6_opt_cmp(fl1->opt, fl->opt))
  					goto release;
  
  				err = -ENOMEM;
  				if (sfl1 == NULL)
  					goto release;
  				if (fl->linger > fl1->linger)
  					fl1->linger = fl->linger;
  				if ((long)(fl->expires - fl1->expires) > 0)
  					fl1->expires = fl->expires;
04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
561
  				fl_link(np, sfl1, fl1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  				fl_free(fl);
  				return 0;
  
  release:
  				fl_release(fl1);
  				goto done;
  			}
  		}
  		err = -ENOENT;
  		if (!(freq.flr_flags&IPV6_FL_F_CREATE))
  			goto done;
  
  		err = -ENOMEM;
  		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
  			goto done;
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
577
  		fl1 = fl_intern(net, fl, freq.flr_label);
78c2e5025   Pavel Emelyanov   [IPV6]: Fix race ...
578
579
  		if (fl1 != NULL)
  			goto recheck;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580

6c94d3611   David S. Miller   [IPV6]: Clear up ...
581
582
583
584
585
586
  		if (!freq.flr_label) {
  			if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
  					 &fl->label, sizeof(fl->label))) {
  				/* Intentionally ignore fault. */
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587

04028045a   Pavel Emelyanov   [IPV6]: Lost lock...
588
  		fl_link(np, sfl1, fl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  		return 0;
  
  	default:
  		return -EINVAL;
  	}
  
  done:
  	fl_free(fl);
  	kfree(sfl1);
  	return err;
  }
  
  #ifdef CONFIG_PROC_FS
  
  struct ip6fl_iter_state {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
604
  	struct seq_net_private p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
611
612
613
  	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...
614
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
  
  	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
617
  		fl = fl_ht[state->bucket];
09ad9bc75   Octavian Purdila   net: use net_eq t...
618
  		while (fl && !net_eq(fl->fl_net, net))
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
619
620
  			fl = fl->next;
  		if (fl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
627
628
  	}
  	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...
629
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
  
  	fl = fl->next;
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
632
  try_again:
09ad9bc75   Octavian Purdila   net: use net_eq t...
633
  	while (fl && !net_eq(fl->fl_net, net))
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
634
  		fl = fl->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  	while (!fl) {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
636
  		if (++state->bucket <= FL_HASH_MASK) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
  			fl = fl_ht[state->bucket];
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
638
639
  			goto try_again;
  		} else
bcd620757   James Morris   [IPV6]: fix locku...
640
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  	}
  	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)
9a429c498   Eric Dumazet   [NET]: Add some a...
655
  	__acquires(ip6_fl_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
  {
  	read_lock_bh(&ip6_fl_lock);
  	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)
9a429c498   Eric Dumazet   [NET]: Add some a...
674
  	__releases(ip6_fl_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
  {
  	read_unlock_bh(&ip6_fl_lock);
  }
1b7c2dbc0   James Morris   [IPV6]: fix flowl...
678
  static int ip6fl_seq_show(struct seq_file *seq, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
  {
1b7c2dbc0   James Morris   [IPV6]: fix flowl...
680
681
682
683
684
685
  	if (v == SEQ_START_TOKEN)
  		seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s
  ",
  			   "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt");
  	else {
  		struct ip6_flowlabel *fl = v;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  		seq_printf(seq,
4b7a4274c   Harvey Harrison   net: replace %#p6...
687
688
  			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
692
693
694
  			   (unsigned)ntohl(fl->label),
  			   fl->share,
  			   (unsigned)fl->owner,
  			   atomic_read(&fl->users),
  			   fl->linger/HZ,
  			   (long)(fl->expires - jiffies)/HZ,
b071195de   Harvey Harrison   net: replace all ...
695
  			   &fl->dst,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
  			   fl->opt ? fl->opt->opt_nflen : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
  	return 0;
  }
56b3d975b   Philippe De Muyter   [NET]: Make all i...
700
  static const struct seq_operations ip6fl_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
704
705
706
707
708
  	.start	=	ip6fl_seq_start,
  	.next	=	ip6fl_seq_next,
  	.stop	=	ip6fl_seq_stop,
  	.show	=	ip6fl_seq_show,
  };
  
  static int ip6fl_seq_open(struct inode *inode, struct file *file)
  {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
709
710
  	return seq_open_net(inode, file, &ip6fl_seq_ops,
  			    sizeof(struct ip6fl_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
712
  static const struct file_operations ip6fl_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
714
715
716
  	.owner		=	THIS_MODULE,
  	.open		=	ip6fl_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
717
  	.release	=	seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719

2c8c1e729   Alexey Dobriyan   net: spread __net...
720
  static int __net_init ip6_flowlabel_proc_init(struct net *net)
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
721
  {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
722
723
  	if (!proc_net_fops_create(net, "ip6_flowlabel",
  				  S_IRUGO, &ip6fl_seq_fops))
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
724
725
726
  		return -ENOMEM;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727

2c8c1e729   Alexey Dobriyan   net: spread __net...
728
  static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
  {
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
730
731
732
733
734
735
736
737
738
  	proc_net_remove(net, "ip6_flowlabel");
  }
  #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...
739
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  #endif
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
741

2c8c1e729   Alexey Dobriyan   net: spread __net...
742
  static void __net_exit ip6_flowlabel_net_exit(struct net *net)
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
743
744
  {
  	ip6_fl_purge(net);
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
745
  	ip6_flowlabel_proc_fini(net);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
746
747
748
  }
  
  static struct pernet_operations ip6_flowlabel_net_ops = {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
749
  	.init = ip6_flowlabel_proc_init,
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
750
751
  	.exit = ip6_flowlabel_net_exit,
  };
0a3e78ac2   Daniel Lezcano   [IPV6]: make flow...
752
753
  int ip6_flowlabel_init(void)
  {
5983a3dff   Benjamin Thery   [NETNS][IPV6] flo...
754
  	return register_pernet_subsys(&ip6_flowlabel_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
  }
  
  void ip6_flowlabel_cleanup(void)
  {
  	del_timer(&ip6_fl_gc_timer);
60e8fbc4c   Benjamin Thery   [NETNS][IPV6] flo...
760
  	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
  }