]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/netfilter/nf_nat_core.c
netfilter: nf_conntrack: add support for "conntrack zones"
[net-next-2.6.git] / net / ipv4 / netfilter / nf_nat_core.c
index fe1a64479dd088de80cd51226db941609bfff658..4595281c28635b30d4522d75aafdc57e82828b4a 100644 (file)
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_l3proto.h>
 #include <net/netfilter/nf_conntrack_l4proto.h>
+#include <net/netfilter/nf_conntrack_zones.h>
 
 static DEFINE_SPINLOCK(nf_nat_lock);
 
 static struct nf_conntrack_l3proto *l3proto __read_mostly;
 
-/* Calculated at init based on memory size */
-static unsigned int nf_nat_htable_size __read_mostly;
-
 #define MAX_IP_NAT_PROTO 256
 static const struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO]
                                                __read_mostly;
@@ -72,15 +70,16 @@ EXPORT_SYMBOL_GPL(nf_nat_proto_put);
 
 /* We keep an extra hash for each conntrack, for fast searching. */
 static inline unsigned int
-hash_by_src(const struct nf_conntrack_tuple *tuple)
+hash_by_src(const struct net *net, u16 zone,
+           const struct nf_conntrack_tuple *tuple)
 {
        unsigned int hash;
 
        /* Original src, to ensure we map it consistently if poss. */
        hash = jhash_3words((__force u32)tuple->src.u3.ip,
-                           (__force u32)tuple->src.u.all,
+                           (__force u32)tuple->src.u.all ^ zone,
                            tuple->dst.protonum, 0);
-       return ((u64)hash * nf_nat_htable_size) >> 32;
+       return ((u64)hash * net->ipv4.nat_htable_size) >> 32;
 }
 
 /* Is this tuple already taken? (not by us) */
@@ -142,12 +141,12 @@ same_src(const struct nf_conn *ct,
 
 /* Only called for SRC manip */
 static int
-find_appropriate_src(struct net *net,
+find_appropriate_src(struct net *net, u16 zone,
                     const struct nf_conntrack_tuple *tuple,
                     struct nf_conntrack_tuple *result,
                     const struct nf_nat_range *range)
 {
-       unsigned int h = hash_by_src(tuple);
+       unsigned int h = hash_by_src(net, zone, tuple);
        const struct nf_conn_nat *nat;
        const struct nf_conn *ct;
        const struct hlist_node *n;
@@ -155,7 +154,7 @@ find_appropriate_src(struct net *net,
        rcu_read_lock();
        hlist_for_each_entry_rcu(nat, n, &net->ipv4.nat_bysource[h], bysource) {
                ct = nat->ct;
-               if (same_src(ct, tuple)) {
+               if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) {
                        /* Copy source part from reply tuple. */
                        nf_ct_invert_tuplepr(result,
                                       &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
@@ -178,7 +177,7 @@ find_appropriate_src(struct net *net,
    the ip with the lowest src-ip/dst-ip/proto usage.
 */
 static void
-find_best_ips_proto(struct nf_conntrack_tuple *tuple,
+find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple,
                    const struct nf_nat_range *range,
                    const struct nf_conn *ct,
                    enum nf_nat_manip_type maniptype)
@@ -212,7 +211,7 @@ find_best_ips_proto(struct nf_conntrack_tuple *tuple,
        maxip = ntohl(range->max_ip);
        j = jhash_2words((__force u32)tuple->src.u3.ip,
                         range->flags & IP_NAT_RANGE_PERSISTENT ?
-                               0 : (__force u32)tuple->dst.u3.ip, 0);
+                               0 : (__force u32)tuple->dst.u3.ip ^ zone, 0);
        j = ((u64)j * (maxip - minip + 1)) >> 32;
        *var_ipp = htonl(minip + j);
 }
@@ -232,6 +231,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
 {
        struct net *net = nf_ct_net(ct);
        const struct nf_nat_protocol *proto;
+       u16 zone = nf_ct_zone(ct);
 
        /* 1) If this srcip/proto/src-proto-part is currently mapped,
           and that same mapping gives a unique tuple within the given
@@ -242,7 +242,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
           manips not an issue.  */
        if (maniptype == IP_NAT_MANIP_SRC &&
            !(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) {
-               if (find_appropriate_src(net, orig_tuple, tuple, range)) {
+               if (find_appropriate_src(net, zone, orig_tuple, tuple, range)) {
                        pr_debug("get_unique_tuple: Found current src map\n");
                        if (!nf_nat_used_tuple(tuple, ct))
                                return;
@@ -252,7 +252,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
        /* 2) Select the least-used IP/proto combination in the given
           range. */
        *tuple = *orig_tuple;
-       find_best_ips_proto(tuple, range, ct, maniptype);
+       find_best_ips_proto(zone, tuple, range, ct, maniptype);
 
        /* 3) The per-protocol part of the manip is made to map into
           the range to make a unique tuple. */
@@ -330,7 +330,8 @@ nf_nat_setup_info(struct nf_conn *ct,
        if (have_to_hash) {
                unsigned int srchash;
 
-               srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+               srchash = hash_by_src(net, nf_ct_zone(ct),
+                                     &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
                spin_lock_bh(&nf_nat_lock);
                /* nf_conntrack_alter_reply might re-allocate exntension aera */
                nat = nfct_nat(ct);
@@ -679,8 +680,10 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct,
 
 static int __net_init nf_nat_net_init(struct net *net)
 {
-       net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size,
-                                                     &net->ipv4.nat_vmalloced, 0);
+       /* Leave them the same for the moment. */
+       net->ipv4.nat_htable_size = net->ct.htable_size;
+       net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size,
+                                                      &net->ipv4.nat_vmalloced, 0);
        if (!net->ipv4.nat_bysource)
                return -ENOMEM;
        return 0;
@@ -703,7 +706,7 @@ static void __net_exit nf_nat_net_exit(struct net *net)
        nf_ct_iterate_cleanup(net, &clean_nat, NULL);
        synchronize_rcu();
        nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_vmalloced,
-                            nf_nat_htable_size);
+                            net->ipv4.nat_htable_size);
 }
 
 static struct pernet_operations nf_nat_net_ops = {
@@ -724,9 +727,6 @@ static int __init nf_nat_init(void)
                return ret;
        }
 
-       /* Leave them the same for the moment. */
-       nf_nat_htable_size = nf_conntrack_htable_size;
-
        ret = register_pernet_subsys(&nf_nat_net_ops);
        if (ret < 0)
                goto cleanup_extend;