Merge branch 'upstream-fixes' of git://lost.foo-projects.org/~ahkok/git/netdev-2...
[pandora-kernel.git] / net / netfilter / nf_conntrack_netlink.c
index bd10eb9..6527d4e 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/errno.h>
 #include <linux/netlink.h>
 #include <linux/spinlock.h>
+#include <linux/interrupt.h>
 #include <linux/notifier.h>
 
 #include <linux/netfilter.h>
@@ -407,6 +408,8 @@ nfattr_failure:
 
 static int ctnetlink_done(struct netlink_callback *cb)
 {
+       if (cb->args[1])
+               nf_ct_put((struct nf_conn *)cb->args[1]);
        DEBUGP("entered %s\n", __FUNCTION__);
        return 0;
 }
@@ -416,10 +419,9 @@ static int ctnetlink_done(struct netlink_callback *cb)
 static int
 ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct nf_conn *ct = NULL;
+       struct nf_conn *ct, *last;
        struct nf_conntrack_tuple_hash *h;
        struct list_head *i;
-       u_int32_t *id = (u_int32_t *) &cb->args[1];
        struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
 
@@ -427,7 +429,9 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
                        cb->args[0], *id);
 
        read_lock_bh(&nf_conntrack_lock);
-       for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) {
+       last = (struct nf_conn *)cb->args[1];
+       for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
+restart:
                list_for_each_prev(i, &nf_conntrack_hash[cb->args[0]]) {
                        h = (struct nf_conntrack_tuple_hash *) i;
                        if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
@@ -438,21 +442,31 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
                         * then dump everything. */
                        if (l3proto && L3PROTO(ct) != l3proto)
                                continue;
-                       if (ct->id <= *id)
-                               continue;
+                       if (cb->args[1]) {
+                               if (ct != last)
+                                       continue;
+                               cb->args[1] = 0;
+                       }
                        if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
                                                cb->nlh->nlmsg_seq,
                                                IPCTNL_MSG_CT_NEW,
-                                               1, ct) < 0)
+                                               1, ct) < 0) {
+                               nf_conntrack_get(&ct->ct_general);
+                               cb->args[1] = (unsigned long)ct;
                                goto out;
-                       *id = ct->id;
+                       }
+               }
+               if (cb->args[1]) {
+                       cb->args[1] = 0;
+                       goto restart;
                }
        }
-out:   
+out:
        read_unlock_bh(&nf_conntrack_lock);
+       if (last)
+               nf_ct_put(last);
 
        DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
-
        return skb->len;
 }
 
@@ -641,7 +655,7 @@ static const size_t cta_min_nat[CTA_NAT_MAX] = {
 };
 
 static inline int
-ctnetlink_parse_nat(struct nfattr *cda[],
+ctnetlink_parse_nat(struct nfattr *nat,
                    const struct nf_conn *ct, struct ip_nat_range *range)
 {
        struct nfattr *tb[CTA_NAT_MAX];
@@ -651,7 +665,7 @@ ctnetlink_parse_nat(struct nfattr *cda[],
 
        memset(range, 0, sizeof(*range));
        
-       nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]);
+       nfattr_parse_nested(tb, CTA_NAT_MAX, nat);
 
        if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
                return -EINVAL;
@@ -866,39 +880,30 @@ ctnetlink_change_status(struct nf_conn *ct, struct nfattr *cda[])
                /* ASSURED bit can only be set */
                return -EINVAL;
 
-       if (cda[CTA_NAT-1]) {
+       if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
 #ifndef CONFIG_IP_NF_NAT_NEEDED
                return -EINVAL;
 #else
-               unsigned int hooknum;
                struct ip_nat_range range;
 
-               if (ctnetlink_parse_nat(cda, ct, &range) < 0)
-                       return -EINVAL;
-
-               DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n", 
-                      NIPQUAD(range.min_ip), NIPQUAD(range.max_ip),
-                      htons(range.min.all), htons(range.max.all));
-               
-               /* This is tricky but it works. ip_nat_setup_info needs the
-                * hook number as parameter, so let's do the correct 
-                * conversion and run away */
-               if (status & IPS_SRC_NAT_DONE)
-                       hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */
-               else if (status & IPS_DST_NAT_DONE)
-                       hooknum = NF_IP_PRE_ROUTING;  /* IP_NAT_MANIP_DST */
-               else 
-                       return -EINVAL; /* Missing NAT flags */
-
-               DEBUGP("NAT status: %lu\n", 
-                      status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
-               
-               if (ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
-                       return -EEXIST;
-               ip_nat_setup_info(ct, &range, hooknum);
-
-                DEBUGP("NAT status after setup_info: %lu\n",
-                       ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
+               if (cda[CTA_NAT_DST-1]) {
+                       if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct,
+                                               &range) < 0)
+                               return -EINVAL;
+                       if (ip_nat_initialized(ct,
+                                              HOOK2MANIP(NF_IP_PRE_ROUTING)))
+                               return -EEXIST;
+                       ip_nat_setup_info(ct, &range, hooknum);
+               }
+               if (cda[CTA_NAT_SRC-1]) {
+                       if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct,
+                                               &range) < 0)
+                               return -EINVAL;
+                       if (ip_nat_initialized(ct,
+                                              HOOK2MANIP(NF_IP_POST_ROUTING)))
+                               return -EEXIST;
+                       ip_nat_setup_info(ct, &range, hooknum);
+               }
 #endif
        }
 
@@ -1122,7 +1127,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        /* implicit 'else' */
 
        /* we only allow nat config for new conntracks */
-       if (cda[CTA_NAT-1]) {
+       if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
                err = -EINVAL;
                goto out_unlock;
        }