Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60e8fbc4 authored by Benjamin Thery's avatar Benjamin Thery Committed by David S. Miller
Browse files

[NETNS][IPV6] flowlabels - make flowlabels per namespace



This patch introduces a new member, fl_net, in struct ip6_flowlabel.
This allows to create labels with the same value in different namespaces.

Signed-off-by: default avatarBenjamin Thery <benjamin.thery@bull.net>
Signed-off-by: default avatarDaniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6ab57e7e
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -202,6 +202,7 @@ struct ip6_flowlabel
	u32			owner;
	u32			owner;
	unsigned long		lastuse;
	unsigned long		lastuse;
	unsigned long		expires;
	unsigned long		expires;
	struct net		*fl_net;
};
};


#define IPV6_FLOWINFO_MASK	__constant_htonl(0x0FFFFFFF)
#define IPV6_FLOWINFO_MASK	__constant_htonl(0x0FFFFFFF)
+56 −16
Original line number Original line Diff line number Diff line
@@ -62,23 +62,23 @@ static DEFINE_RWLOCK(ip6_fl_lock);
static DEFINE_RWLOCK(ip6_sk_fl_lock);
static DEFINE_RWLOCK(ip6_sk_fl_lock);




static __inline__ struct ip6_flowlabel * __fl_lookup(__be32 label)
static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label)
{
{
	struct ip6_flowlabel *fl;
	struct ip6_flowlabel *fl;


	for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
	for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
		if (fl->label == label)
		if (fl->label == label && fl->fl_net == net)
			return fl;
			return fl;
	}
	}
	return NULL;
	return NULL;
}
}


static struct ip6_flowlabel * fl_lookup(__be32 label)
static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label)
{
{
	struct ip6_flowlabel *fl;
	struct ip6_flowlabel *fl;


	read_lock_bh(&ip6_fl_lock);
	read_lock_bh(&ip6_fl_lock);
	fl = __fl_lookup(label);
	fl = __fl_lookup(net, label);
	if (fl)
	if (fl)
		atomic_inc(&fl->users);
		atomic_inc(&fl->users);
	read_unlock_bh(&ip6_fl_lock);
	read_unlock_bh(&ip6_fl_lock);
@@ -88,8 +88,10 @@ static struct ip6_flowlabel * fl_lookup(__be32 label)


static void fl_free(struct ip6_flowlabel *fl)
static void fl_free(struct ip6_flowlabel *fl)
{
{
	if (fl)
	if (fl) {
		release_net(fl->fl_net);
		kfree(fl->opt);
		kfree(fl->opt);
	}
	kfree(fl);
	kfree(fl);
}
}


@@ -112,7 +114,6 @@ static void fl_release(struct ip6_flowlabel *fl)
		    time_after(ip6_fl_gc_timer.expires, ttd))
		    time_after(ip6_fl_gc_timer.expires, ttd))
			mod_timer(&ip6_fl_gc_timer, ttd);
			mod_timer(&ip6_fl_gc_timer, ttd);
	}
	}

	write_unlock_bh(&ip6_fl_lock);
	write_unlock_bh(&ip6_fl_lock);
}
}


@@ -148,13 +149,34 @@ static void ip6_fl_gc(unsigned long dummy)
	if (!sched && atomic_read(&fl_size))
	if (!sched && atomic_read(&fl_size))
		sched = now + FL_MAX_LINGER;
		sched = now + FL_MAX_LINGER;
	if (sched) {
	if (sched) {
		ip6_fl_gc_timer.expires = sched;
		mod_timer(&ip6_fl_gc_timer, sched);
		add_timer(&ip6_fl_gc_timer);
	}
	write_unlock(&ip6_fl_lock);
}

static void ip6_fl_purge(struct net *net)
{
	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) {
			if (fl->fl_net == net && atomic_read(&fl->users) == 0) {
				*flp = fl->next;
				fl_free(fl);
				atomic_dec(&fl_size);
				continue;
			}
			flp = &fl->next;
		}
	}
	}
	write_unlock(&ip6_fl_lock);
	write_unlock(&ip6_fl_lock);
}
}


static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label)
static struct ip6_flowlabel *fl_intern(struct net *net,
				       struct ip6_flowlabel *fl, __be32 label)
{
{
	struct ip6_flowlabel *lfl;
	struct ip6_flowlabel *lfl;


@@ -165,7 +187,7 @@ static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label)
		for (;;) {
		for (;;) {
			fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
			fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
			if (fl->label) {
			if (fl->label) {
				lfl = __fl_lookup(fl->label);
				lfl = __fl_lookup(net, fl->label);
				if (lfl == NULL)
				if (lfl == NULL)
					break;
					break;
			}
			}
@@ -179,7 +201,7 @@ static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label)
		 * done in ipv6_flowlabel_opt - sock is locked, so new entry
		 * done in ipv6_flowlabel_opt - sock is locked, so new entry
		 * with the same label can only appear on another sock
		 * with the same label can only appear on another sock
		 */
		 */
		lfl = __fl_lookup(fl->label);
		lfl = __fl_lookup(net, fl->label);
		if (lfl != NULL) {
		if (lfl != NULL) {
			atomic_inc(&lfl->users);
			atomic_inc(&lfl->users);
			write_unlock_bh(&ip6_fl_lock);
			write_unlock_bh(&ip6_fl_lock);
@@ -298,7 +320,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo
}
}


static struct ip6_flowlabel *
static struct ip6_flowlabel *
fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *err_p)
fl_create(struct net *net, struct in6_flowlabel_req *freq, char __user *optval,
	  int optlen, int *err_p)
{
{
	struct ip6_flowlabel *fl;
	struct ip6_flowlabel *fl;
	int olen;
	int olen;
@@ -343,6 +366,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *
		}
		}
	}
	}


	fl->fl_net = hold_net(net);
	fl->expires = jiffies;
	fl->expires = jiffies;
	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
	if (err)
	if (err)
@@ -441,6 +465,7 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
{
{
	int err;
	int err;
	struct net *net = sock_net(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct in6_flowlabel_req freq;
	struct in6_flowlabel_req freq;
	struct ipv6_fl_socklist *sfl1=NULL;
	struct ipv6_fl_socklist *sfl1=NULL;
@@ -483,7 +508,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
		read_unlock_bh(&ip6_sk_fl_lock);
		read_unlock_bh(&ip6_sk_fl_lock);


		if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
		if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
			fl = fl_lookup(freq.flr_label);
			fl = fl_lookup(net, freq.flr_label);
			if (fl) {
			if (fl) {
				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
				fl_release(fl);
				fl_release(fl);
@@ -496,7 +521,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
			return -EINVAL;
			return -EINVAL;


		fl = fl_create(&freq, optval, optlen, &err);
		fl = fl_create(net, &freq, optval, optlen, &err);
		if (fl == NULL)
		if (fl == NULL)
			return err;
			return err;
		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
@@ -518,7 +543,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
			read_unlock_bh(&ip6_sk_fl_lock);
			read_unlock_bh(&ip6_sk_fl_lock);


			if (fl1 == NULL)
			if (fl1 == NULL)
				fl1 = fl_lookup(freq.flr_label);
				fl1 = fl_lookup(net, freq.flr_label);
			if (fl1) {
			if (fl1) {
recheck:
recheck:
				err = -EEXIST;
				err = -EEXIST;
@@ -559,7 +584,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
			goto done;
			goto done;


		fl1 = fl_intern(fl, freq.flr_label);
		fl1 = fl_intern(net, fl, freq.flr_label);
		if (fl1 != NULL)
		if (fl1 != NULL)
			goto recheck;
			goto recheck;


@@ -717,13 +742,28 @@ static inline void ip6_flowlabel_proc_fini(struct net *net)
}
}
#endif
#endif


static inline void ip6_flowlabel_net_exit(struct net *net)
{
	ip6_fl_purge(net);
}

static struct pernet_operations ip6_flowlabel_net_ops = {
	.exit = ip6_flowlabel_net_exit,
};

int ip6_flowlabel_init(void)
int ip6_flowlabel_init(void)
{
{
	int err;

	err = register_pernet_subsys(&ip6_flowlabel_net_ops);
	if (err)
		return err;
	return ip6_flowlabel_proc_init(&init_net);
	return ip6_flowlabel_proc_init(&init_net);
}
}


void ip6_flowlabel_cleanup(void)
void ip6_flowlabel_cleanup(void)
{
{
	del_timer(&ip6_fl_gc_timer);
	del_timer(&ip6_fl_gc_timer);
	unregister_pernet_subsys(&ip6_flowlabel_net_ops);
	ip6_flowlabel_proc_fini(&init_net);
	ip6_flowlabel_proc_fini(&init_net);
}
}