|
@@ -30,6 +30,7 @@
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/socket.h>
|
|
|
#include <linux/mm.h>
|
|
|
+#include <linux/nsproxy.h>
|
|
|
#include <linux/rculist_nulls.h>
|
|
|
|
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
@@ -63,8 +64,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_max);
|
|
|
struct nf_conn nf_conntrack_untracked __read_mostly;
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_untracked);
|
|
|
|
|
|
-static struct kmem_cache *nf_conntrack_cachep __read_mostly;
|
|
|
-
|
|
|
static int nf_conntrack_hash_rnd_initted;
|
|
|
static unsigned int nf_conntrack_hash_rnd;
|
|
|
|
|
@@ -86,9 +85,10 @@ static u_int32_t __hash_conntrack(const struct nf_conntrack_tuple *tuple,
|
|
|
return ((u64)h * size) >> 32;
|
|
|
}
|
|
|
|
|
|
-static inline u_int32_t hash_conntrack(const struct nf_conntrack_tuple *tuple)
|
|
|
+static inline u_int32_t hash_conntrack(const struct net *net,
|
|
|
+ const struct nf_conntrack_tuple *tuple)
|
|
|
{
|
|
|
- return __hash_conntrack(tuple, nf_conntrack_htable_size,
|
|
|
+ return __hash_conntrack(tuple, net->ct.htable_size,
|
|
|
nf_conntrack_hash_rnd);
|
|
|
}
|
|
|
|
|
@@ -296,7 +296,7 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple)
|
|
|
{
|
|
|
struct nf_conntrack_tuple_hash *h;
|
|
|
struct hlist_nulls_node *n;
|
|
|
- unsigned int hash = hash_conntrack(tuple);
|
|
|
+ unsigned int hash = hash_conntrack(net, tuple);
|
|
|
|
|
|
/* Disable BHs the entire time since we normally need to disable them
|
|
|
* at least once for the stats anyway.
|
|
@@ -366,10 +366,11 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct,
|
|
|
|
|
|
void nf_conntrack_hash_insert(struct nf_conn *ct)
|
|
|
{
|
|
|
+ struct net *net = nf_ct_net(ct);
|
|
|
unsigned int hash, repl_hash;
|
|
|
|
|
|
- hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
|
|
- repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
|
|
+ hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
|
|
+ repl_hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
|
|
|
|
|
__nf_conntrack_hash_insert(ct, hash, repl_hash);
|
|
|
}
|
|
@@ -397,8 +398,8 @@ __nf_conntrack_confirm(struct sk_buff *skb)
|
|
|
if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
- hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
|
|
- repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
|
|
+ hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
|
|
+ repl_hash = hash_conntrack(net, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
|
|
|
|
|
/* We're not in hash table, and we refuse to set up related
|
|
|
connections for unconfirmed conns. But packet copies and
|
|
@@ -468,7 +469,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
|
|
|
struct net *net = nf_ct_net(ignored_conntrack);
|
|
|
struct nf_conntrack_tuple_hash *h;
|
|
|
struct hlist_nulls_node *n;
|
|
|
- unsigned int hash = hash_conntrack(tuple);
|
|
|
+ unsigned int hash = hash_conntrack(net, tuple);
|
|
|
|
|
|
/* Disable BHs the entire time since we need to disable them at
|
|
|
* least once for the stats anyway.
|
|
@@ -503,7 +504,7 @@ static noinline int early_drop(struct net *net, unsigned int hash)
|
|
|
int dropped = 0;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- for (i = 0; i < nf_conntrack_htable_size; i++) {
|
|
|
+ for (i = 0; i < net->ct.htable_size; i++) {
|
|
|
hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash],
|
|
|
hnnode) {
|
|
|
tmp = nf_ct_tuplehash_to_ctrack(h);
|
|
@@ -523,7 +524,7 @@ static noinline int early_drop(struct net *net, unsigned int hash)
|
|
|
if (cnt >= NF_CT_EVICTION_RANGE)
|
|
|
break;
|
|
|
|
|
|
- hash = (hash + 1) % nf_conntrack_htable_size;
|
|
|
+ hash = (hash + 1) % net->ct.htable_size;
|
|
|
}
|
|
|
rcu_read_unlock();
|
|
|
|
|
@@ -557,7 +558,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
|
|
|
|
|
|
if (nf_conntrack_max &&
|
|
|
unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) {
|
|
|
- unsigned int hash = hash_conntrack(orig);
|
|
|
+ unsigned int hash = hash_conntrack(net, orig);
|
|
|
if (!early_drop(net, hash)) {
|
|
|
atomic_dec(&net->ct.count);
|
|
|
if (net_ratelimit())
|
|
@@ -572,7 +573,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
|
|
|
* Do not use kmem_cache_zalloc(), as this cache uses
|
|
|
* SLAB_DESTROY_BY_RCU.
|
|
|
*/
|
|
|
- ct = kmem_cache_alloc(nf_conntrack_cachep, gfp);
|
|
|
+ ct = kmem_cache_alloc(net->ct.nf_conntrack_cachep, gfp);
|
|
|
if (ct == NULL) {
|
|
|
pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");
|
|
|
atomic_dec(&net->ct.count);
|
|
@@ -611,7 +612,7 @@ void nf_conntrack_free(struct nf_conn *ct)
|
|
|
nf_ct_ext_destroy(ct);
|
|
|
atomic_dec(&net->ct.count);
|
|
|
nf_ct_ext_free(ct);
|
|
|
- kmem_cache_free(nf_conntrack_cachep, ct);
|
|
|
+ kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_free);
|
|
|
|
|
@@ -1014,7 +1015,7 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data),
|
|
|
struct hlist_nulls_node *n;
|
|
|
|
|
|
spin_lock_bh(&nf_conntrack_lock);
|
|
|
- for (; *bucket < nf_conntrack_htable_size; (*bucket)++) {
|
|
|
+ for (; *bucket < net->ct.htable_size; (*bucket)++) {
|
|
|
hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) {
|
|
|
ct = nf_ct_tuplehash_to_ctrack(h);
|
|
|
if (iter(ct, data))
|
|
@@ -1113,9 +1114,12 @@ static void nf_ct_release_dying_list(struct net *net)
|
|
|
|
|
|
static void nf_conntrack_cleanup_init_net(void)
|
|
|
{
|
|
|
+ /* wait until all references to nf_conntrack_untracked are dropped */
|
|
|
+ while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
|
|
|
+ schedule();
|
|
|
+
|
|
|
nf_conntrack_helper_fini();
|
|
|
nf_conntrack_proto_fini();
|
|
|
- kmem_cache_destroy(nf_conntrack_cachep);
|
|
|
}
|
|
|
|
|
|
static void nf_conntrack_cleanup_net(struct net *net)
|
|
@@ -1127,15 +1131,14 @@ static void nf_conntrack_cleanup_net(struct net *net)
|
|
|
schedule();
|
|
|
goto i_see_dead_people;
|
|
|
}
|
|
|
- /* wait until all references to nf_conntrack_untracked are dropped */
|
|
|
- while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
|
|
|
- schedule();
|
|
|
|
|
|
nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc,
|
|
|
- nf_conntrack_htable_size);
|
|
|
+ net->ct.htable_size);
|
|
|
nf_conntrack_ecache_fini(net);
|
|
|
nf_conntrack_acct_fini(net);
|
|
|
nf_conntrack_expect_fini(net);
|
|
|
+ kmem_cache_destroy(net->ct.nf_conntrack_cachep);
|
|
|
+ kfree(net->ct.slabname);
|
|
|
free_percpu(net->ct.stat);
|
|
|
}
|
|
|
|
|
@@ -1190,10 +1193,12 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
|
|
|
{
|
|
|
int i, bucket, vmalloced, old_vmalloced;
|
|
|
unsigned int hashsize, old_size;
|
|
|
- int rnd;
|
|
|
struct hlist_nulls_head *hash, *old_hash;
|
|
|
struct nf_conntrack_tuple_hash *h;
|
|
|
|
|
|
+ if (current->nsproxy->net_ns != &init_net)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
/* On boot, we can set this without any fancy locking. */
|
|
|
if (!nf_conntrack_htable_size)
|
|
|
return param_set_uint(val, kp);
|
|
@@ -1206,33 +1211,29 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
|
|
|
if (!hash)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- /* We have to rehahs for the new table anyway, so we also can
|
|
|
- * use a newrandom seed */
|
|
|
- get_random_bytes(&rnd, sizeof(rnd));
|
|
|
-
|
|
|
/* Lookups in the old hash might happen in parallel, which means we
|
|
|
* might get false negatives during connection lookup. New connections
|
|
|
* created because of a false negative won't make it into the hash
|
|
|
* though since that required taking the lock.
|
|
|
*/
|
|
|
spin_lock_bh(&nf_conntrack_lock);
|
|
|
- for (i = 0; i < nf_conntrack_htable_size; i++) {
|
|
|
+ for (i = 0; i < init_net.ct.htable_size; i++) {
|
|
|
while (!hlist_nulls_empty(&init_net.ct.hash[i])) {
|
|
|
h = hlist_nulls_entry(init_net.ct.hash[i].first,
|
|
|
struct nf_conntrack_tuple_hash, hnnode);
|
|
|
hlist_nulls_del_rcu(&h->hnnode);
|
|
|
- bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
|
|
|
+ bucket = __hash_conntrack(&h->tuple, hashsize,
|
|
|
+ nf_conntrack_hash_rnd);
|
|
|
hlist_nulls_add_head_rcu(&h->hnnode, &hash[bucket]);
|
|
|
}
|
|
|
}
|
|
|
- old_size = nf_conntrack_htable_size;
|
|
|
+ old_size = init_net.ct.htable_size;
|
|
|
old_vmalloced = init_net.ct.hash_vmalloc;
|
|
|
old_hash = init_net.ct.hash;
|
|
|
|
|
|
- nf_conntrack_htable_size = hashsize;
|
|
|
+ init_net.ct.htable_size = nf_conntrack_htable_size = hashsize;
|
|
|
init_net.ct.hash_vmalloc = vmalloced;
|
|
|
init_net.ct.hash = hash;
|
|
|
- nf_conntrack_hash_rnd = rnd;
|
|
|
spin_unlock_bh(&nf_conntrack_lock);
|
|
|
|
|
|
nf_ct_free_hashtable(old_hash, old_vmalloced, old_size);
|
|
@@ -1271,15 +1272,6 @@ static int nf_conntrack_init_init_net(void)
|
|
|
NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
|
|
|
nf_conntrack_max);
|
|
|
|
|
|
- nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
|
|
|
- sizeof(struct nf_conn),
|
|
|
- 0, SLAB_DESTROY_BY_RCU, NULL);
|
|
|
- if (!nf_conntrack_cachep) {
|
|
|
- printk(KERN_ERR "Unable to create nf_conn slab cache\n");
|
|
|
- ret = -ENOMEM;
|
|
|
- goto err_cache;
|
|
|
- }
|
|
|
-
|
|
|
ret = nf_conntrack_proto_init();
|
|
|
if (ret < 0)
|
|
|
goto err_proto;
|
|
@@ -1288,13 +1280,19 @@ static int nf_conntrack_init_init_net(void)
|
|
|
if (ret < 0)
|
|
|
goto err_helper;
|
|
|
|
|
|
+ /* Set up fake conntrack: to never be deleted, not in any hashes */
|
|
|
+#ifdef CONFIG_NET_NS
|
|
|
+ nf_conntrack_untracked.ct_net = &init_net;
|
|
|
+#endif
|
|
|
+ atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
|
|
|
+ /* - and look it like as a confirmed connection */
|
|
|
+ set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
err_helper:
|
|
|
nf_conntrack_proto_fini();
|
|
|
err_proto:
|
|
|
- kmem_cache_destroy(nf_conntrack_cachep);
|
|
|
-err_cache:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1316,7 +1314,24 @@ static int nf_conntrack_init_net(struct net *net)
|
|
|
ret = -ENOMEM;
|
|
|
goto err_stat;
|
|
|
}
|
|
|
- net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size,
|
|
|
+
|
|
|
+ net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net);
|
|
|
+ if (!net->ct.slabname) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_slabname;
|
|
|
+ }
|
|
|
+
|
|
|
+ net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname,
|
|
|
+ sizeof(struct nf_conn), 0,
|
|
|
+ SLAB_DESTROY_BY_RCU, NULL);
|
|
|
+ if (!net->ct.nf_conntrack_cachep) {
|
|
|
+ printk(KERN_ERR "Unable to create nf_conn slab cache\n");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_cache;
|
|
|
+ }
|
|
|
+
|
|
|
+ net->ct.htable_size = nf_conntrack_htable_size;
|
|
|
+ net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size,
|
|
|
&net->ct.hash_vmalloc, 1);
|
|
|
if (!net->ct.hash) {
|
|
|
ret = -ENOMEM;
|
|
@@ -1333,15 +1348,6 @@ static int nf_conntrack_init_net(struct net *net)
|
|
|
if (ret < 0)
|
|
|
goto err_ecache;
|
|
|
|
|
|
- /* Set up fake conntrack:
|
|
|
- - to never be deleted, not in any hashes */
|
|
|
-#ifdef CONFIG_NET_NS
|
|
|
- nf_conntrack_untracked.ct_net = &init_net;
|
|
|
-#endif
|
|
|
- atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
|
|
|
- /* - and look it like as a confirmed connection */
|
|
|
- set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
err_ecache:
|
|
@@ -1350,8 +1356,12 @@ err_acct:
|
|
|
nf_conntrack_expect_fini(net);
|
|
|
err_expect:
|
|
|
nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc,
|
|
|
- nf_conntrack_htable_size);
|
|
|
+ net->ct.htable_size);
|
|
|
err_hash:
|
|
|
+ kmem_cache_destroy(net->ct.nf_conntrack_cachep);
|
|
|
+err_cache:
|
|
|
+ kfree(net->ct.slabname);
|
|
|
+err_slabname:
|
|
|
free_percpu(net->ct.stat);
|
|
|
err_stat:
|
|
|
return ret;
|