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

[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
...@@ -202,6 +202,7 @@ struct ip6_flowlabel ...@@ -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)
......
...@@ -62,23 +62,23 @@ static DEFINE_RWLOCK(ip6_fl_lock); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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); write_unlock(&ip6_fl_lock);
} }
static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label) 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);
}
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) ...@@ -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) ...@@ -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 ...@@ -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 * ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment