e1000: FIX: Stop raw interrupts disabled nag from RT
[pandora-kernel.git] / net / ipv4 / netfilter / ip_nat_core.c
index 4b6260a..40737fd 100644 (file)
@@ -50,7 +50,7 @@ static struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO];
 static inline struct ip_nat_protocol *
 __ip_nat_proto_find(u_int8_t protonum)
 {
-       return ip_nat_protos[protonum];
+       return rcu_dereference(ip_nat_protos[protonum]);
 }
 
 struct ip_nat_protocol *
@@ -58,13 +58,11 @@ ip_nat_proto_find_get(u_int8_t protonum)
 {
        struct ip_nat_protocol *p;
 
-       /* we need to disable preemption to make sure 'p' doesn't get
-        * removed until we've grabbed the reference */
-       preempt_disable();
+       rcu_read_lock();
        p = __ip_nat_proto_find(protonum);
        if (!try_module_get(p->me))
                p = &ip_nat_unknown_protocol;
-       preempt_enable();
+       rcu_read_unlock();
 
        return p;
 }
@@ -120,8 +118,8 @@ static int
 in_range(const struct ip_conntrack_tuple *tuple,
         const struct ip_nat_range *range)
 {
-       struct ip_nat_protocol *proto = 
-                               __ip_nat_proto_find(tuple->dst.protonum);
+       struct ip_nat_protocol *proto;
+       int ret = 0;
 
        /* If we are supposed to map IPs, then we must be in the
           range specified, otherwise let this drag us onto a new src IP. */
@@ -131,12 +129,15 @@ in_range(const struct ip_conntrack_tuple *tuple,
                        return 0;
        }
 
+       rcu_read_lock();
+       proto = __ip_nat_proto_find(tuple->dst.protonum);
        if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
            || proto->in_range(tuple, IP_NAT_MANIP_SRC,
                               &range->min, &range->max))
-               return 1;
+               ret = 1;
+       rcu_read_unlock();
 
-       return 0;
+       return ret;
 }
 
 static inline int
@@ -246,8 +247,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;
                }
        }
 
@@ -259,20 +261,25 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple,
        /* 3) The per-protocol part of the manip is made to map into
           the range to make a unique tuple. */
 
-       proto = ip_nat_proto_find_get(orig_tuple->dst.protonum);
+       rcu_read_lock();
+       proto = __ip_nat_proto_find(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);
+               goto out;
+       }
 
        /* 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))
-           && !ip_nat_used_tuple(tuple, conntrack)) {
-               ip_nat_proto_put(proto);
-               return;
-       }
+           && !ip_nat_used_tuple(tuple, conntrack))
+               goto out;
 
        /* Last change: get protocol to try to obtain unique tuple. */
        proto->unique_tuple(tuple, range, maniptype, conntrack);
-
-       ip_nat_proto_put(proto);
+out:
+       rcu_read_unlock();
 }
 
 unsigned int
@@ -352,22 +359,19 @@ manip_pkt(u_int16_t proto,
        iph = (void *)(*pskb)->data + iphdroff;
 
        /* Manipulate protcol part. */
-       p = ip_nat_proto_find_get(proto);
-       if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) {
-               ip_nat_proto_put(p);
+
+       /* rcu_read_lock()ed by nf_hook_slow */
+       p = __ip_nat_proto_find(proto);
+       if (!p->manip_pkt(pskb, iphdroff, target, maniptype))
                return 0;
-       }
-       ip_nat_proto_put(p);
 
        iph = (void *)(*pskb)->data + iphdroff;
 
        if (maniptype == IP_NAT_MANIP_SRC) {
-               iph->check = nf_csum_update(~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 = nf_csum_update(~iph->daddr, target->dst.ip,
-                                           iph->check);
+               nf_csum_replace4(&iph->check, iph->daddr, target->dst.ip);
                iph->daddr = target->dst.ip;
        }
        return 1;
@@ -416,6 +420,7 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
                struct icmphdr icmp;
                struct iphdr ip;
        } *inside;
+       struct ip_conntrack_protocol *proto;
        struct ip_conntrack_tuple inner, target;
        int hdrlen = (*pskb)->nh.iph->ihl * 4;
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@@ -437,8 +442,8 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
                     (*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)
@@ -451,10 +456,11 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
        DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n",
               *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
 
+       /* rcu_read_lock()ed by nf_hook_slow */
+       proto = __ip_conntrack_proto_find(inside->ip.protocol);
        if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
-                            sizeof(struct icmphdr) + inside->ip.ihl*4,
-                            &inner,
-                            __ip_conntrack_proto_find(inside->ip.protocol)))
+                            sizeof(struct icmphdr) + inside->ip.ihl*4,
+                            &inner, proto))
                return 0;
 
        /* Change inner back to look like incoming packet.  We do the
@@ -509,7 +515,7 @@ int ip_nat_protocol_register(struct ip_nat_protocol *proto)
                ret = -EBUSY;
                goto out;
        }
-       ip_nat_protos[proto->protonum] = proto;
+       rcu_assign_pointer(ip_nat_protos[proto->protonum], proto);
  out:
        write_unlock_bh(&ip_nat_lock);
        return ret;
@@ -520,18 +526,17 @@ EXPORT_SYMBOL(ip_nat_protocol_register);
 void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
 {
        write_lock_bh(&ip_nat_lock);
-       ip_nat_protos[proto->protonum] = &ip_nat_unknown_protocol;
+       rcu_assign_pointer(ip_nat_protos[proto->protonum],
+                          &ip_nat_unknown_protocol);
        write_unlock_bh(&ip_nat_lock);
-
-       /* Someone could be still looking at the proto in a bh. */
-       synchronize_net();
+       synchronize_rcu();
 }
 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(__be16),
@@ -549,21 +554,21 @@ 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 = 
+               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 = 
+               range->max.tcp.port =
                        *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]);
        }
 
@@ -588,10 +593,10 @@ static int __init ip_nat_init(void)
        /* Sew in builtin protocols. */
        write_lock_bh(&ip_nat_lock);
        for (i = 0; i < MAX_IP_NAT_PROTO; i++)
-               ip_nat_protos[i] = &ip_nat_unknown_protocol;
-       ip_nat_protos[IPPROTO_TCP] = &ip_nat_protocol_tcp;
-       ip_nat_protos[IPPROTO_UDP] = &ip_nat_protocol_udp;
-       ip_nat_protos[IPPROTO_ICMP] = &ip_nat_protocol_icmp;
+               rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol);
+       rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp);
+       rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp);
+       rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp);
        write_unlock_bh(&ip_nat_lock);
 
        for (i = 0; i < ip_nat_htable_size; i++) {
@@ -599,8 +604,8 @@ static int __init ip_nat_init(void)
        }
 
        /* FIXME: Man, this is a hack.  <SIGH> */
-       IP_NF_ASSERT(ip_conntrack_destroyed == NULL);
-       ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;
+       IP_NF_ASSERT(rcu_dereference(ip_conntrack_destroyed) == NULL);
+       rcu_assign_pointer(ip_conntrack_destroyed, ip_nat_cleanup_conntrack);
 
        /* Initialize fake conntrack so that NAT will skip it */
        ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK;
@@ -618,7 +623,8 @@ static int clean_nat(struct ip_conntrack *i, void *data)
 static void __exit ip_nat_cleanup(void)
 {
        ip_ct_iterate_cleanup(&clean_nat, NULL);
-       ip_conntrack_destroyed = NULL;
+       rcu_assign_pointer(ip_conntrack_destroyed, NULL);
+       synchronize_rcu();
        vfree(bysource);
 }