ip6_flowlabel.c 19.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 *	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>
 */

12
#include <linux/capability.h>
L
Linus Torvalds 已提交
13 14 15 16 17 18 19 20
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/in6.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
21
#include <linux/slab.h>
22
#include <linux/export.h>
23
#include <linux/pid_namespace.h>
L
Linus Torvalds 已提交
24

25
#include <net/net_namespace.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31
#include <net/sock.h>

#include <net/ipv6.h>
#include <net/rawv6.h>
#include <net/transp_v6.h>

32
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
33 34 35 36

#define FL_MIN_LINGER	6	/* Minimal linger. It is set to 6sec specified
				   in old IPv6 RFC. Well, it was reasonable value.
				 */
37
#define FL_MAX_LINGER	150	/* Maximal linger timeout */
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45 46

/* 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);
47
static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1];
L
Linus Torvalds 已提交
48

49
static void ip6_fl_gc(struct timer_list *unused);
50
static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc);
L
Linus Torvalds 已提交
51 52 53

/* FL hash table lock: it protects only of GC */

54
static DEFINE_SPINLOCK(ip6_fl_lock);
L
Linus Torvalds 已提交
55 56 57

/* Big socket sock */

58
static DEFINE_SPINLOCK(ip6_sk_fl_lock);
L
Linus Torvalds 已提交
59

60
#define for_each_fl_rcu(hash, fl)				\
61
	for (fl = rcu_dereference_bh(fl_ht[(hash)]);		\
62
	     fl != NULL;					\
63
	     fl = rcu_dereference_bh(fl->next))
64
#define for_each_fl_continue_rcu(fl)				\
65
	for (fl = rcu_dereference_bh(fl->next);			\
66
	     fl != NULL;					\
67
	     fl = rcu_dereference_bh(fl->next))
L
Linus Torvalds 已提交
68

69 70 71 72 73
#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))

74
static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label)
L
Linus Torvalds 已提交
75 76 77
{
	struct ip6_flowlabel *fl;

78
	for_each_fl_rcu(FL_HASH(label), fl) {
O
Octavian Purdila 已提交
79
		if (fl->label == label && net_eq(fl->fl_net, net))
L
Linus Torvalds 已提交
80 81 82 83 84
			return fl;
	}
	return NULL;
}

85
static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
L
Linus Torvalds 已提交
86 87 88
{
	struct ip6_flowlabel *fl;

89
	rcu_read_lock_bh();
90
	fl = __fl_lookup(net, label);
91 92 93
	if (fl && !atomic_inc_not_zero(&fl->users))
		fl = NULL;
	rcu_read_unlock_bh();
L
Linus Torvalds 已提交
94 95 96 97 98 99
	return fl;
}


static void fl_free(struct ip6_flowlabel *fl)
{
100
	if (fl) {
101 102
		if (fl->share == IPV6_FL_S_PROCESS)
			put_pid(fl->owner.pid);
L
Linus Torvalds 已提交
103
		kfree(fl->opt);
104
		kfree_rcu(fl, rcu);
105
	}
L
Linus Torvalds 已提交
106 107 108 109
}

static void fl_release(struct ip6_flowlabel *fl)
{
110
	spin_lock_bh(&ip6_fl_lock);
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

	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);
	}
127
	spin_unlock_bh(&ip6_fl_lock);
L
Linus Torvalds 已提交
128 129
}

130
static void ip6_fl_gc(struct timer_list *unused)
L
Linus Torvalds 已提交
131 132 133 134 135
{
	int i;
	unsigned long now = jiffies;
	unsigned long sched = 0;

136
	spin_lock(&ip6_fl_lock);
L
Linus Torvalds 已提交
137

138
	for (i = 0; i <= FL_HASH_MASK; i++) {
139 140 141
		struct ip6_flowlabel *fl;
		struct ip6_flowlabel __rcu **flp;

L
Linus Torvalds 已提交
142
		flp = &fl_ht[i];
143 144
		while ((fl = rcu_dereference_protected(*flp,
						       lockdep_is_held(&ip6_fl_lock))) != NULL) {
L
Linus Torvalds 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
			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) {
165
		mod_timer(&ip6_fl_gc_timer, sched);
L
Linus Torvalds 已提交
166
	}
167
	spin_unlock(&ip6_fl_lock);
L
Linus Torvalds 已提交
168 169
}

170
static void __net_exit ip6_fl_purge(struct net *net)
171 172 173
{
	int i;

174
	spin_lock_bh(&ip6_fl_lock);
175
	for (i = 0; i <= FL_HASH_MASK; i++) {
176 177 178
		struct ip6_flowlabel *fl;
		struct ip6_flowlabel __rcu **flp;

179
		flp = &fl_ht[i];
180 181
		while ((fl = rcu_dereference_protected(*flp,
						       lockdep_is_held(&ip6_fl_lock))) != NULL) {
O
Octavian Purdila 已提交
182 183
			if (net_eq(fl->fl_net, net) &&
			    atomic_read(&fl->users) == 0) {
184 185 186 187 188 189 190 191
				*flp = fl->next;
				fl_free(fl);
				atomic_dec(&fl_size);
				continue;
			}
			flp = &fl->next;
		}
	}
192
	spin_unlock_bh(&ip6_fl_lock);
193 194 195 196
}

static struct ip6_flowlabel *fl_intern(struct net *net,
				       struct ip6_flowlabel *fl, __be32 label)
L
Linus Torvalds 已提交
197
{
198 199
	struct ip6_flowlabel *lfl;

L
Linus Torvalds 已提交
200 201
	fl->label = label & IPV6_FLOWLABEL_MASK;

202
	spin_lock_bh(&ip6_fl_lock);
L
Linus Torvalds 已提交
203 204
	if (label == 0) {
		for (;;) {
205
			fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK;
L
Linus Torvalds 已提交
206
			if (fl->label) {
207
				lfl = __fl_lookup(net, fl->label);
208
				if (!lfl)
L
Linus Torvalds 已提交
209 210 211
					break;
			}
		}
212 213 214 215 216 217 218 219 220
	} 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
		 */
221
		lfl = __fl_lookup(net, fl->label);
222
		if (lfl) {
223
			atomic_inc(&lfl->users);
224
			spin_unlock_bh(&ip6_fl_lock);
225 226
			return lfl;
		}
L
Linus Torvalds 已提交
227 228 229 230
	}

	fl->lastuse = jiffies;
	fl->next = fl_ht[FL_HASH(fl->label)];
231
	rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
L
Linus Torvalds 已提交
232
	atomic_inc(&fl_size);
233
	spin_unlock_bh(&ip6_fl_lock);
234
	return NULL;
L
Linus Torvalds 已提交
235 236 237 238 239 240
}



/* Socket flowlabel lists */

241
struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label)
L
Linus Torvalds 已提交
242 243 244 245 246 247
{
	struct ipv6_fl_socklist *sfl;
	struct ipv6_pinfo *np = inet6_sk(sk);

	label &= IPV6_FLOWLABEL_MASK;

248 249
	rcu_read_lock_bh();
	for_each_sk_fl_rcu(np, sfl) {
L
Linus Torvalds 已提交
250 251 252 253
		struct ip6_flowlabel *fl = sfl->fl;
		if (fl->label == label) {
			fl->lastuse = jiffies;
			atomic_inc(&fl->users);
254
			rcu_read_unlock_bh();
L
Linus Torvalds 已提交
255 256 257
			return fl;
		}
	}
258
	rcu_read_unlock_bh();
L
Linus Torvalds 已提交
259 260
	return NULL;
}
261 262
EXPORT_SYMBOL_GPL(fl6_sock_lookup);

L
Linus Torvalds 已提交
263 264 265 266 267
void fl6_free_socklist(struct sock *sk)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct ipv6_fl_socklist *sfl;

268
	if (!rcu_access_pointer(np->ipv6_fl_list))
269 270
		return;

271 272 273 274 275
	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);
276

L
Linus Torvalds 已提交
277
		fl_release(sfl->fl);
278 279 280
		kfree_rcu(sfl, rcu);

		spin_lock_bh(&ip6_sk_fl_lock);
L
Linus Torvalds 已提交
281
	}
282
	spin_unlock_bh(&ip6_sk_fl_lock);
L
Linus Torvalds 已提交
283 284 285 286 287 288 289 290 291 292 293
}

/* Service routines */


/*
   It is the only difficult place. flowlabel enforces equal headers
   before and including routing header, however user may supply options
   following rthdr.
 */

294 295 296
struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
					 struct ip6_flowlabel *fl,
					 struct ipv6_txoptions *fopt)
L
Linus Torvalds 已提交
297
{
298
	struct ipv6_txoptions *fl_opt = fl->opt;
299

300
	if (!fopt || fopt->opt_flen == 0)
301
		return fl_opt;
302

303
	if (fl_opt) {
L
Linus Torvalds 已提交
304
		opt_space->hopopt = fl_opt->hopopt;
305
		opt_space->dst0opt = fl_opt->dst0opt;
L
Linus Torvalds 已提交
306 307 308 309 310 311 312 313 314 315 316 317
		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;
	opt_space->opt_flen = fopt->opt_flen;
318
	opt_space->tot_len = fopt->tot_len;
L
Linus Torvalds 已提交
319 320
	return opt_space;
}
321
EXPORT_SYMBOL_GPL(fl6_merge_options);
L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

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;
340 341

	spin_lock_bh(&ip6_fl_lock);
L
Linus Torvalds 已提交
342 343 344 345 346 347 348
	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;
349 350
	spin_unlock_bh(&ip6_fl_lock);

L
Linus Torvalds 已提交
351 352 353 354
	return 0;
}

static struct ip6_flowlabel *
355 356
fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
	  char __user *optval, int optlen, int *err_p)
L
Linus Torvalds 已提交
357
{
358
	struct ip6_flowlabel *fl = NULL;
L
Linus Torvalds 已提交
359 360 361 362
	int olen;
	int addr_type;
	int err;

363 364 365 366 367
	olen = optlen - CMSG_ALIGN(sizeof(*freq));
	err = -EINVAL;
	if (olen > 64 * 1024)
		goto done;

L
Linus Torvalds 已提交
368
	err = -ENOMEM;
369
	fl = kzalloc(sizeof(*fl), GFP_KERNEL);
370
	if (!fl)
L
Linus Torvalds 已提交
371 372 373 374
		goto done;

	if (olen > 0) {
		struct msghdr msg;
375
		struct flowi6 flowi6;
W
Wei Wang 已提交
376
		struct ipcm6_cookie ipc6;
L
Linus Torvalds 已提交
377 378 379

		err = -ENOMEM;
		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
380
		if (!fl->opt)
L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388 389
			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;
390
		msg.msg_control = (void *)(fl->opt+1);
391
		memset(&flowi6, 0, sizeof(flowi6));
L
Linus Torvalds 已提交
392

W
Wei Wang 已提交
393
		ipc6.opt = fl->opt;
394
		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &ipc6);
L
Linus Torvalds 已提交
395 396 397 398 399 400 401 402 403 404 405
		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;
		}
	}

406
	fl->fl_net = net;
L
Linus Torvalds 已提交
407 408 409 410 411 412
	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);
413 414
	if ((addr_type & IPV6_ADDR_MAPPED) ||
	    addr_type == IPV6_ADDR_ANY) {
415
		err = -EINVAL;
L
Linus Torvalds 已提交
416
		goto done;
417
	}
A
Alexey Dobriyan 已提交
418
	fl->dst = freq->flr_dst;
L
Linus Torvalds 已提交
419 420 421 422 423 424
	atomic_set(&fl->users, 1);
	switch (fl->share) {
	case IPV6_FL_S_EXCL:
	case IPV6_FL_S_ANY:
		break;
	case IPV6_FL_S_PROCESS:
425
		fl->owner.pid = get_task_pid(current, PIDTYPE_PID);
L
Linus Torvalds 已提交
426 427
		break;
	case IPV6_FL_S_USER:
428
		fl->owner.uid = current_euid();
L
Linus Torvalds 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
		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;

452
	rcu_read_lock_bh();
453
	for_each_sk_fl_rcu(np, sfl)
L
Linus Torvalds 已提交
454
		count++;
455
	rcu_read_unlock_bh();
L
Linus Torvalds 已提交
456 457 458

	if (room <= 0 ||
	    ((count >= FL_MAX_PER_SOCK ||
459 460
	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
	     !capable(CAP_NET_ADMIN)))
L
Linus Torvalds 已提交
461 462 463 464 465
		return -ENOBUFS;

	return 0;
}

466 467 468
static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
		struct ip6_flowlabel *fl)
{
469
	spin_lock_bh(&ip6_sk_fl_lock);
470 471
	sfl->fl = fl;
	sfl->next = np->ipv6_fl_list;
472 473
	rcu_assign_pointer(np->ipv6_fl_list, sfl);
	spin_unlock_bh(&ip6_sk_fl_lock);
474 475
}

476 477
int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq,
			   int flags)
478 479 480 481
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct ipv6_fl_socklist *sfl;

482 483 484 485 486
	if (flags & IPV6_FL_F_REMOTE) {
		freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK;
		return 0;
	}

487 488 489 490 491
	if (np->repflow) {
		freq->flr_label = np->flow_label;
		return 0;
	}

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
	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;
}

L
Linus Torvalds 已提交
513 514
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
{
515
	int uninitialized_var(err);
516
	struct net *net = sock_net(sk);
L
Linus Torvalds 已提交
517 518
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct in6_flowlabel_req freq;
519
	struct ipv6_fl_socklist *sfl1 = NULL;
520 521
	struct ipv6_fl_socklist *sfl;
	struct ipv6_fl_socklist __rcu **sflp;
522 523
	struct ip6_flowlabel *fl, *fl1 = NULL;

L
Linus Torvalds 已提交
524 525 526 527 528 529 530 531 532

	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:
533 534 535 536 537 538 539 540 541
		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;
		}
542 543
		spin_lock_bh(&ip6_sk_fl_lock);
		for (sflp = &np->ipv6_fl_list;
E
Eric Dumazet 已提交
544 545
		     (sfl = rcu_dereference_protected(*sflp,
						      lockdep_is_held(&ip6_sk_fl_lock))) != NULL;
546
		     sflp = &sfl->next) {
L
Linus Torvalds 已提交
547 548 549
			if (sfl->fl->label == freq.flr_label) {
				if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
					np->flow_label &= ~IPV6_FLOWLABEL_MASK;
E
Eric Dumazet 已提交
550
				*sflp = sfl->next;
551
				spin_unlock_bh(&ip6_sk_fl_lock);
L
Linus Torvalds 已提交
552
				fl_release(sfl->fl);
553
				kfree_rcu(sfl, rcu);
L
Linus Torvalds 已提交
554 555 556
				return 0;
			}
		}
557
		spin_unlock_bh(&ip6_sk_fl_lock);
L
Linus Torvalds 已提交
558 559 560
		return -ESRCH;

	case IPV6_FL_A_RENEW:
561 562
		rcu_read_lock_bh();
		for_each_sk_fl_rcu(np, sfl) {
L
Linus Torvalds 已提交
563 564
			if (sfl->fl->label == freq.flr_label) {
				err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
565
				rcu_read_unlock_bh();
L
Linus Torvalds 已提交
566 567 568
				return err;
			}
		}
569
		rcu_read_unlock_bh();
L
Linus Torvalds 已提交
570

571 572
		if (freq.flr_share == IPV6_FL_S_NONE &&
		    ns_capable(net->user_ns, CAP_NET_ADMIN)) {
573
			fl = fl_lookup(net, freq.flr_label);
L
Linus Torvalds 已提交
574 575 576 577 578 579 580 581 582
			if (fl) {
				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
				fl_release(fl);
				return err;
			}
		}
		return -ESRCH;

	case IPV6_FL_A_GET:
583
		if (freq.flr_flags & IPV6_FL_F_REFLECT) {
584 585 586 587 588 589
			struct net *net = sock_net(sk);
			if (net->ipv6.sysctl.flowlabel_consistency) {
				net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n");
				return -EPERM;
			}

590 591
			if (sk->sk_protocol != IPPROTO_TCP)
				return -ENOPROTOOPT;
592

593 594 595 596
			np->repflow = 1;
			return 0;
		}

L
Linus Torvalds 已提交
597 598 599
		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
			return -EINVAL;

T
Tom Herbert 已提交
600 601 602 603
		if (net->ipv6.sysctl.flowlabel_state_ranges &&
		    (freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
			return -ERANGE;

604
		fl = fl_create(net, sk, &freq, optval, optlen, &err);
605
		if (!fl)
L
Linus Torvalds 已提交
606 607 608 609 610
			return err;
		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);

		if (freq.flr_label) {
			err = -EEXIST;
611 612
			rcu_read_lock_bh();
			for_each_sk_fl_rcu(np, sfl) {
L
Linus Torvalds 已提交
613 614
				if (sfl->fl->label == freq.flr_label) {
					if (freq.flr_flags&IPV6_FL_F_EXCL) {
615
						rcu_read_unlock_bh();
L
Linus Torvalds 已提交
616 617 618
						goto done;
					}
					fl1 = sfl->fl;
619
					atomic_inc(&fl1->users);
L
Linus Torvalds 已提交
620 621 622
					break;
				}
			}
623
			rcu_read_unlock_bh();
L
Linus Torvalds 已提交
624

625
			if (!fl1)
626
				fl1 = fl_lookup(net, freq.flr_label);
L
Linus Torvalds 已提交
627
			if (fl1) {
628
recheck:
L
Linus Torvalds 已提交
629 630 631 632 633 634
				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 ||
635
				    ((fl1->share == IPV6_FL_S_PROCESS) &&
636
				     (fl1->owner.pid != fl->owner.pid)) ||
637
				    ((fl1->share == IPV6_FL_S_USER) &&
638
				     !uid_eq(fl1->owner.uid, fl->owner.uid)))
L
Linus Torvalds 已提交
639 640 641
					goto release;

				err = -ENOMEM;
642
				if (!sfl1)
L
Linus Torvalds 已提交
643 644 645 646 647
					goto release;
				if (fl->linger > fl1->linger)
					fl1->linger = fl->linger;
				if ((long)(fl->expires - fl1->expires) > 0)
					fl1->expires = fl->expires;
648
				fl_link(np, sfl1, fl1);
L
Linus Torvalds 已提交
649 650 651 652 653 654 655 656 657 658 659 660 661
				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;
662
		if (!sfl1)
663 664 665 666
			goto done;

		err = mem_check(sk);
		if (err != 0)
L
Linus Torvalds 已提交
667 668
			goto done;

669
		fl1 = fl_intern(net, fl, freq.flr_label);
670
		if (fl1)
671
			goto recheck;
L
Linus Torvalds 已提交
672

673 674 675 676 677 678
		if (!freq.flr_label) {
			if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
					 &fl->label, sizeof(fl->label))) {
				/* Intentionally ignore fault. */
			}
		}
L
Linus Torvalds 已提交
679

680
		fl_link(np, sfl1, fl);
L
Linus Torvalds 已提交
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
		return 0;

	default:
		return -EINVAL;
	}

done:
	fl_free(fl);
	kfree(sfl1);
	return err;
}

#ifdef CONFIG_PROC_FS

struct ip6fl_iter_state {
696
	struct seq_net_private p;
697
	struct pid_namespace *pid_ns;
L
Linus Torvalds 已提交
698 699 700 701 702 703 704 705 706
	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);
707
	struct net *net = seq_file_net(seq);
L
Linus Torvalds 已提交
708 709

	for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) {
710 711 712 713
		for_each_fl_rcu(state->bucket, fl) {
			if (net_eq(fl->fl_net, net))
				goto out;
		}
L
Linus Torvalds 已提交
714
	}
715 716
	fl = NULL;
out:
L
Linus Torvalds 已提交
717 718 719 720 721 722
	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);
723
	struct net *net = seq_file_net(seq);
L
Linus Torvalds 已提交
724

725 726 727 728 729
	for_each_fl_continue_rcu(fl) {
		if (net_eq(fl->fl_net, net))
			goto out;
	}

730
try_again:
731 732 733 734 735 736
	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;
L
Linus Torvalds 已提交
737
	}
738 739 740
	fl = NULL;

out:
L
Linus Torvalds 已提交
741 742 743 744 745 746 747 748 749 750 751 752 753
	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)
754
	__acquires(RCU)
L
Linus Torvalds 已提交
755
{
756 757 758 759
	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);

	state->pid_ns = proc_pid_ns(file_inode(seq->file));

760
	rcu_read_lock_bh();
L
Linus Torvalds 已提交
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
	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)
777
	__releases(RCU)
L
Linus Torvalds 已提交
778
{
779
	rcu_read_unlock_bh();
L
Linus Torvalds 已提交
780 781
}

782
static int ip6fl_seq_show(struct seq_file *seq, void *v)
L
Linus Torvalds 已提交
783
{
784
	struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
785
	if (v == SEQ_START_TOKEN) {
786
		seq_puts(seq, "Label S Owner  Users  Linger Expires  Dst                              Opt\n");
787
	} else {
788
		struct ip6_flowlabel *fl = v;
L
Linus Torvalds 已提交
789
		seq_printf(seq,
790
			   "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
791
			   (unsigned int)ntohl(fl->label),
L
Linus Torvalds 已提交
792
			   fl->share,
793 794 795 796 797
			   ((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)),
L
Linus Torvalds 已提交
798 799 800
			   atomic_read(&fl->users),
			   fl->linger/HZ,
			   (long)(fl->expires - jiffies)/HZ,
801
			   &fl->dst,
L
Linus Torvalds 已提交
802 803 804 805 806
			   fl->opt ? fl->opt->opt_nflen : 0);
	}
	return 0;
}

807
static const struct seq_operations ip6fl_seq_ops = {
L
Linus Torvalds 已提交
808 809 810 811 812 813
	.start	=	ip6fl_seq_start,
	.next	=	ip6fl_seq_next,
	.stop	=	ip6fl_seq_stop,
	.show	=	ip6fl_seq_show,
};

814
static int __net_init ip6_flowlabel_proc_init(struct net *net)
815
{
816 817
	if (!proc_create_net("ip6_flowlabel", 0444, net->proc_net,
			&ip6fl_seq_ops, sizeof(struct ip6fl_iter_state)))
818 819 820
		return -ENOMEM;
	return 0;
}
L
Linus Torvalds 已提交
821

822
static void __net_exit ip6_flowlabel_proc_fini(struct net *net)
L
Linus Torvalds 已提交
823
{
824
	remove_proc_entry("ip6_flowlabel", net->proc_net);
825 826 827 828 829 830 831 832 833
}
#else
static inline int ip6_flowlabel_proc_init(struct net *net)
{
	return 0;
}
static inline void ip6_flowlabel_proc_fini(struct net *net)
{
}
L
Linus Torvalds 已提交
834
#endif
835

836
static void __net_exit ip6_flowlabel_net_exit(struct net *net)
837 838
{
	ip6_fl_purge(net);
839
	ip6_flowlabel_proc_fini(net);
840 841 842
}

static struct pernet_operations ip6_flowlabel_net_ops = {
843
	.init = ip6_flowlabel_proc_init,
844 845 846
	.exit = ip6_flowlabel_net_exit,
};

847 848
int ip6_flowlabel_init(void)
{
849
	return register_pernet_subsys(&ip6_flowlabel_net_ops);
L
Linus Torvalds 已提交
850 851 852 853 854
}

void ip6_flowlabel_cleanup(void)
{
	del_timer(&ip6_fl_gc_timer);
855
	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
L
Linus Torvalds 已提交
856
}