Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-drm-fsl-dcu.git] / net / ipv4 / netfilter / ip_nat_core.c
index 1741d555ad0dd5e29ab17305aa0ddc77065fd89e..275a4d3faf0af65d0da5ed7470d71d137dba30f5 100644 (file)
@@ -22,9 +22,6 @@
 #include <linux/udp.h>
 #include <linux/jhash.h>
 
-#define ASSERT_READ_LOCK(x)
-#define ASSERT_WRITE_LOCK(x)
-
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
@@ -33,7 +30,6 @@
 #include <linux/netfilter_ipv4/ip_nat_core.h>
 #include <linux/netfilter_ipv4/ip_nat_helper.h>
 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-#include <linux/netfilter_ipv4/listhelp.h>
 
 #if 0
 #define DEBUGP printk
@@ -86,7 +82,7 @@ static inline unsigned int
 hash_by_src(const struct ip_conntrack_tuple *tuple)
 {
        /* Original src, to ensure we map it consistently if poss. */
-       return jhash_3words(tuple->src.ip, tuple->src.u.all,
+       return jhash_3words((__force u32)tuple->src.ip, tuple->src.u.all,
                            tuple->dst.protonum, 0) % ip_nat_htable_size;
 }
 
@@ -101,18 +97,6 @@ static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn)
        write_unlock_bh(&ip_nat_lock);
 }
 
-/* We do checksum mangling, so if they were wrong before they're still
- * wrong.  Also works for incomplete packets (eg. ICMP dest
- * unreachables.) */
-u_int16_t
-ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
-{
-       u_int32_t diffs[] = { oldvalinv, newval };
-       return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
-                                     oldcheck^0xFFFF));
-}
-EXPORT_SYMBOL(ip_nat_cheat_check);
-
 /* Is this tuple already taken? (not by us) */
 int
 ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
@@ -136,7 +120,7 @@ static int
 in_range(const struct ip_conntrack_tuple *tuple,
         const struct ip_nat_range *range)
 {
-       struct ip_nat_protocol *proto = 
+       struct ip_nat_protocol *proto =
                                __ip_nat_proto_find(tuple->dst.protonum);
 
        /* If we are supposed to map IPs, then we must be in the
@@ -206,7 +190,7 @@ find_best_ips_proto(struct ip_conntrack_tuple *tuple,
                    const struct ip_conntrack *conntrack,
                    enum ip_nat_manip_type maniptype)
 {
-       u_int32_t *var_ipp;
+       __be32 *var_ipp;
        /* Host order */
        u_int32_t minip, maxip, j;
 
@@ -233,7 +217,7 @@ find_best_ips_proto(struct ip_conntrack_tuple *tuple,
         * like this), even across reboots. */
        minip = ntohl(range->min_ip);
        maxip = ntohl(range->max_ip);
-       j = jhash_2words(tuple->src.ip, tuple->dst.ip, 0);
+       j = jhash_2words((__force u32)tuple->src.ip, (__force u32)tuple->dst.ip, 0);
        *var_ipp = htonl(minip + j % (maxip - minip + 1));
 }
 
@@ -262,8 +246,9 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
        if (maniptype == IP_NAT_MANIP_SRC) {
                if (find_appropriate_src(orig_tuple, tuple, range)) {
                        DEBUGP("get_unique_tuple: Found current src map\n");
-                       if (!ip_nat_used_tuple(tuple, conntrack))
-                               return;
+                       if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))
+                               if (!ip_nat_used_tuple(tuple, conntrack))
+                                       return;
                }
        }
 
@@ -277,6 +262,13 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
 
        proto = ip_nat_proto_find_get(orig_tuple->dst.protonum);
 
+       /* Change protocol info to have some randomization */
+       if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) {
+               proto->unique_tuple(tuple, range, maniptype, conntrack);
+               ip_nat_proto_put(proto);
+               return;
+       }
+
        /* Only bother mapping if it's not already in range and unique */
        if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
             || proto->in_range(tuple, maniptype, &range->min, &range->max))
@@ -378,12 +370,10 @@ manip_pkt(u_int16_t proto,
        iph = (void *)(*pskb)->data + iphdroff;
 
        if (maniptype == IP_NAT_MANIP_SRC) {
-               iph->check = ip_nat_cheat_check(~iph->saddr, target->src.ip,
-                                               iph->check);
+               nf_csum_replace4(&iph->check, iph->saddr, target->src.ip);
                iph->saddr = target->src.ip;
        } else {
-               iph->check = ip_nat_cheat_check(~iph->daddr, target->dst.ip,
-                                               iph->check);
+               nf_csum_replace4(&iph->check, iph->daddr, target->dst.ip);
                iph->daddr = target->dst.ip;
        }
        return 1;
@@ -423,10 +413,10 @@ unsigned int ip_nat_packet(struct ip_conntrack *ct,
 EXPORT_SYMBOL_GPL(ip_nat_packet);
 
 /* Dir is direction ICMP is coming from (opposite to packet it contains) */
-int ip_nat_icmp_reply_translation(struct sk_buff **pskb,
-                                 struct ip_conntrack *ct,
-                                 enum ip_nat_manip_type manip,
-                                 enum ip_conntrack_dir dir)
+int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
+                                 enum ip_conntrack_info ctinfo,
+                                 unsigned int hooknum,
+                                 struct sk_buff **pskb)
 {
        struct {
                struct icmphdr icmp;
@@ -434,7 +424,9 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb,
        } *inside;
        struct ip_conntrack_tuple inner, target;
        int hdrlen = (*pskb)->nh.iph->ihl * 4;
+       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
        unsigned long statusbit;
+       enum ip_nat_manip_type manip = HOOK2MANIP(hooknum);
 
        if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
                return 0;
@@ -443,20 +435,16 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb,
 
        /* We're actually going to mangle it beyond trivial checksum
           adjustment, so make sure the current checksum is correct. */
-       if ((*pskb)->ip_summed != CHECKSUM_UNNECESSARY) {
-               hdrlen = (*pskb)->nh.iph->ihl * 4;
-               if ((u16)csum_fold(skb_checksum(*pskb, hdrlen,
-                                               (*pskb)->len - hdrlen, 0)))
-                       return 0;
-       }
+       if (nf_ip_checksum(*pskb, hooknum, hdrlen, 0))
+               return 0;
 
        /* Must be RELATED */
        IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED ||
                     (*pskb)->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY);
 
        /* Redirects on non-null nats must be dropped, else they'll
-           start talking to each other without our translation, and be
-           confused... --RR */
+          start talking to each other without our translation, and be
+          confused... --RR */
        if (inside->icmp.type == ICMP_REDIRECT) {
                /* If NAT isn't finished, assume it and drop. */
                if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
@@ -470,8 +458,8 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb,
               *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
 
        if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
-                            sizeof(struct icmphdr) + inside->ip.ihl*4,
-                            &inner,
+                            sizeof(struct icmphdr) + inside->ip.ihl*4,
+                            &inner,
                             __ip_conntrack_proto_find(inside->ip.protocol)))
                return 0;
 
@@ -487,12 +475,14 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb,
                       !manip))
                return 0;
 
-       /* Reloading "inside" here since manip_pkt inner. */
-       inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
-       inside->icmp.checksum = 0;
-       inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen,
-                                                      (*pskb)->len - hdrlen,
-                                                      0));
+       if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) {
+               /* Reloading "inside" here since manip_pkt inner. */
+               inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
+               inside->icmp.checksum = 0;
+               inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen,
+                                                              (*pskb)->len - hdrlen,
+                                                              0));
+       }
 
        /* Change outer to look the reply to an incoming packet
         * (proto 0 means don't invert per-proto part). */
@@ -547,12 +537,12 @@ EXPORT_SYMBOL(ip_nat_protocol_unregister);
 #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
     defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
 int
-ip_nat_port_range_to_nfattr(struct sk_buff *skb, 
+ip_nat_port_range_to_nfattr(struct sk_buff *skb,
                            const struct ip_nat_range *range)
 {
-       NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(u_int16_t),
+       NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(__be16),
                &range->min.tcp.port);
-       NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(u_int16_t),
+       NFA_PUT(skb, CTA_PROTONAT_PORT_MAX, sizeof(__be16),
                &range->max.tcp.port);
 
        return 0;
@@ -565,22 +555,22 @@ int
 ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range)
 {
        int ret = 0;
-       
+
        /* we have to return whether we actually parsed something or not */
 
        if (tb[CTA_PROTONAT_PORT_MIN-1]) {
                ret = 1;
-               range->min.tcp.port = 
-                       *(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]);
+               range->min.tcp.port =
+                       *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]);
        }
-       
+
        if (!tb[CTA_PROTONAT_PORT_MAX-1]) {
-               if (ret) 
+               if (ret)
                        range->max.tcp.port = range->min.tcp.port;
        } else {
                ret = 1;
-               range->max.tcp.port = 
-                       *(u_int16_t *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]);
+               range->max.tcp.port =
+                       *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]);
        }
 
        return ret;