aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/netfilter/nf_nat_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/netfilter/nf_nat_core.c')
-rw-r--r--net/ipv4/netfilter/nf_nat_core.c127
1 files changed, 87 insertions, 40 deletions
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
index ea02f00d2dac..e848d8d6292f 100644
--- a/net/ipv4/netfilter/nf_nat_core.c
+++ b/net/ipv4/netfilter/nf_nat_core.c
@@ -12,7 +12,6 @@
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
-#include <linux/vmalloc.h>
#include <net/checksum.h>
#include <net/icmp.h>
#include <net/ip.h>
@@ -32,20 +31,15 @@
#include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_l4proto.h>
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
static DEFINE_RWLOCK(nf_nat_lock);
static struct nf_conntrack_l3proto *l3proto = NULL;
/* Calculated at init based on memory size */
static unsigned int nf_nat_htable_size;
+static int nf_nat_vmalloced;
-static struct list_head *bysource;
+static struct hlist_head *bysource;
#define MAX_IP_NAT_PROTO 256
static struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO];
@@ -87,19 +81,6 @@ hash_by_src(const struct nf_conntrack_tuple *tuple)
tuple->dst.protonum, 0) % nf_nat_htable_size;
}
-/* Noone using conntrack by the time this called. */
-static void nf_nat_cleanup_conntrack(struct nf_conn *conn)
-{
- struct nf_conn_nat *nat;
- if (!(conn->status & IPS_NAT_DONE_MASK))
- return;
-
- nat = nfct_nat(conn);
- write_lock_bh(&nf_nat_lock);
- list_del(&nat->info.bysource);
- write_unlock_bh(&nf_nat_lock);
-}
-
/* Is this tuple already taken? (not by us) */
int
nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
@@ -166,10 +147,11 @@ find_appropriate_src(const struct nf_conntrack_tuple *tuple,
unsigned int h = hash_by_src(tuple);
struct nf_conn_nat *nat;
struct nf_conn *ct;
+ struct hlist_node *n;
read_lock_bh(&nf_nat_lock);
- list_for_each_entry(nat, &bysource[h], info.bysource) {
- ct = (struct nf_conn *)((char *)nat - offsetof(struct nf_conn, data));
+ hlist_for_each_entry(nat, n, &bysource[h], bysource) {
+ ct = nat->ct;
if (same_src(ct, tuple)) {
/* Copy source part from reply tuple. */
nf_ct_invert_tuplepr(result,
@@ -254,7 +236,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
manips not an issue. */
if (maniptype == IP_NAT_MANIP_SRC) {
if (find_appropriate_src(orig_tuple, tuple, range)) {
- DEBUGP("get_unique_tuple: Found current src map\n");
+ pr_debug("get_unique_tuple: Found current src map\n");
if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))
if (!nf_nat_used_tuple(tuple, ct))
return;
@@ -296,11 +278,20 @@ nf_nat_setup_info(struct nf_conn *ct,
unsigned int hooknum)
{
struct nf_conntrack_tuple curr_tuple, new_tuple;
- struct nf_conn_nat *nat = nfct_nat(ct);
- struct nf_nat_info *info = &nat->info;
+ struct nf_conn_nat *nat;
int have_to_hash = !(ct->status & IPS_NAT_DONE_MASK);
enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
+ /* nat helper or nfctnetlink also setup binding */
+ nat = nfct_nat(ct);
+ if (!nat) {
+ nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
+ if (nat == NULL) {
+ pr_debug("failed to add NAT extension\n");
+ return NF_ACCEPT;
+ }
+ }
+
NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING ||
hooknum == NF_IP_POST_ROUTING ||
hooknum == NF_IP_LOCAL_IN ||
@@ -337,7 +328,10 @@ nf_nat_setup_info(struct nf_conn *ct,
srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
write_lock_bh(&nf_nat_lock);
- list_add(&info->bysource, &bysource[srchash]);
+ /* nf_conntrack_alter_reply might re-allocate exntension aera */
+ nat = nfct_nat(ct);
+ nat->ct = ct;
+ hlist_add_head(&nat->bysource, &bysource[srchash]);
write_unlock_bh(&nf_nat_lock);
}
@@ -462,8 +456,9 @@ int nf_nat_icmp_reply_translation(struct nf_conn *ct,
return 0;
}
- DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n",
- *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
+ pr_debug("icmp_reply_translation: translating error %p manip %u "
+ "dir %s\n", *pskb, manip,
+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
/* rcu_read_lock()ed by nf_hook_slow */
l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
@@ -590,17 +585,69 @@ nf_nat_port_nfattr_to_range(struct nfattr *tb[], struct nf_nat_range *range)
EXPORT_SYMBOL_GPL(nf_nat_port_range_to_nfattr);
#endif
+/* Noone using conntrack by the time this called. */
+static void nf_nat_cleanup_conntrack(struct nf_conn *ct)
+{
+ struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT);
+
+ if (nat == NULL || nat->ct == NULL)
+ return;
+
+ NF_CT_ASSERT(nat->ct->status & IPS_NAT_DONE_MASK);
+
+ write_lock_bh(&nf_nat_lock);
+ hlist_del(&nat->bysource);
+ nat->ct = NULL;
+ write_unlock_bh(&nf_nat_lock);
+}
+
+static void nf_nat_move_storage(struct nf_conn *conntrack, void *old)
+{
+ struct nf_conn_nat *new_nat = nf_ct_ext_find(conntrack, NF_CT_EXT_NAT);
+ struct nf_conn_nat *old_nat = (struct nf_conn_nat *)old;
+ struct nf_conn *ct = old_nat->ct;
+ unsigned int srchash;
+
+ if (!(ct->status & IPS_NAT_DONE_MASK))
+ return;
+
+ srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+
+ write_lock_bh(&nf_nat_lock);
+ hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource);
+ new_nat->ct = ct;
+ write_unlock_bh(&nf_nat_lock);
+}
+
+static struct nf_ct_ext_type nat_extend __read_mostly = {
+ .len = sizeof(struct nf_conn_nat),
+ .align = __alignof__(struct nf_conn_nat),
+ .destroy = nf_nat_cleanup_conntrack,
+ .move = nf_nat_move_storage,
+ .id = NF_CT_EXT_NAT,
+ .flags = NF_CT_EXT_F_PREALLOC,
+};
+
static int __init nf_nat_init(void)
{
size_t i;
+ int ret;
+
+ ret = nf_ct_extend_register(&nat_extend);
+ if (ret < 0) {
+ printk(KERN_ERR "nf_nat_core: Unable to register extension\n");
+ return ret;
+ }
/* Leave them the same for the moment. */
nf_nat_htable_size = nf_conntrack_htable_size;
- /* One vmalloc for both hash tables */
- bysource = vmalloc(sizeof(struct list_head) * nf_nat_htable_size);
- if (!bysource)
- return -ENOMEM;
+ bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size,
+ &nf_nat_vmalloced);
+ if (!bysource) {
+ ret = -ENOMEM;
+ goto cleanup_extend;
+ }
/* Sew in builtin protocols. */
write_lock_bh(&nf_nat_lock);
@@ -612,18 +659,18 @@ static int __init nf_nat_init(void)
write_unlock_bh(&nf_nat_lock);
for (i = 0; i < nf_nat_htable_size; i++) {
- INIT_LIST_HEAD(&bysource[i]);
+ INIT_HLIST_HEAD(&bysource[i]);
}
- /* FIXME: Man, this is a hack. <SIGH> */
- NF_CT_ASSERT(rcu_dereference(nf_conntrack_destroyed) == NULL);
- rcu_assign_pointer(nf_conntrack_destroyed, nf_nat_cleanup_conntrack);
-
/* Initialize fake conntrack so that NAT will skip it */
nf_conntrack_untracked.status |= IPS_NAT_DONE_MASK;
l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET);
return 0;
+
+ cleanup_extend:
+ nf_ct_extend_unregister(&nat_extend);
+ return ret;
}
/* Clear NAT section of all conntracks, in case we're loaded again. */
@@ -641,10 +688,10 @@ static int clean_nat(struct nf_conn *i, void *data)
static void __exit nf_nat_cleanup(void)
{
nf_ct_iterate_cleanup(&clean_nat, NULL);
- rcu_assign_pointer(nf_conntrack_destroyed, NULL);
synchronize_rcu();
- vfree(bysource);
+ nf_ct_free_hashtable(bysource, nf_nat_vmalloced, nf_nat_htable_size);
nf_ct_l3proto_put(l3proto);
+ nf_ct_extend_unregister(&nat_extend);
}
MODULE_LICENSE("GPL");