]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/netfilter/nf_conntrack_netlink.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-next-2.6
[linux-2.6-omap-h63xx.git] / net / netfilter / nf_conntrack_netlink.c
index 735ea9c1a96fff2f64c5ba8291ec68c534c57dc2..c6439c77953c49cf705ac07fe758764d1cc82a9d 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/rculist.h>
+#include <linux/rculist_nulls.h>
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/skbuff.h>
@@ -404,6 +405,78 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
+/*
+ * The general structure of a ctnetlink event is
+ *
+ *  CTA_TUPLE_ORIG
+ *    <l3/l4-proto-attributes>
+ *  CTA_TUPLE_REPLY
+ *    <l3/l4-proto-attributes>
+ *  CTA_ID
+ *  ...
+ *  CTA_PROTOINFO
+ *    <l4-proto-attributes>
+ *  CTA_TUPLE_MASTER
+ *    <l3/l4-proto-attributes>
+ *
+ * Therefore the formular is
+ *
+ *   size = sizeof(headers) + sizeof(generic_nlas) + 3 * sizeof(tuple_nlas)
+ *             + sizeof(protoinfo_nlas)
+ */
+static struct sk_buff *
+ctnetlink_alloc_skb(const struct nf_conntrack_tuple *tuple, gfp_t gfp)
+{
+       struct nf_conntrack_l3proto *l3proto;
+       struct nf_conntrack_l4proto *l4proto;
+       int len;
+
+#define NLA_TYPE_SIZE(type)            nla_total_size(sizeof(type))
+
+       /* proto independant part */
+       len = NLMSG_SPACE(sizeof(struct nfgenmsg))
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_ORIG|REPL|MASTER */
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_IP */
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_PROTO */
+               + 3 * NLA_TYPE_SIZE(u_int8_t)   /* CTA_PROTO_NUM */
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_ID */
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_STATUS */
+#ifdef CONFIG_NF_CT_ACCT
+               + 2 * nla_total_size(0)         /* CTA_COUNTERS_ORIG|REPL */
+               + 2 * NLA_TYPE_SIZE(uint64_t)   /* CTA_COUNTERS_PACKETS */
+               + 2 * NLA_TYPE_SIZE(uint64_t)   /* CTA_COUNTERS_BYTES */
+#endif
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_TIMEOUT */
+               + nla_total_size(0)             /* CTA_PROTOINFO */
+               + nla_total_size(0)             /* CTA_HELP */
+               + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_SECMARK */
+#endif
+#ifdef CONFIG_NF_NAT_NEEDED
+               + 2 * nla_total_size(0)         /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_POS */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_BEFORE */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_AFTER */
+#endif
+#ifdef CONFIG_NF_CONNTRACK_MARK
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_MARK */
+#endif
+               ;
+
+#undef NLA_TYPE_SIZE
+
+       rcu_read_lock();
+       l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
+       len += l3proto->nla_size;
+
+       l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
+       len += l4proto->nla_size;
+       rcu_read_unlock();
+
+       return alloc_skb(len, gfp);
+}
+
 static int ctnetlink_conntrack_event(struct notifier_block *this,
                                     unsigned long events, void *ptr)
 {
@@ -437,7 +510,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        if (!item->report && !nfnetlink_has_listeners(group))
                return NOTIFY_DONE;
 
-       skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
+       skb = ctnetlink_alloc_skb(tuple(ct, IP_CT_DIR_ORIGINAL), GFP_ATOMIC);
        if (!skb)
                return NOTIFY_DONE;
 
@@ -518,6 +591,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
 nla_put_failure:
        rcu_read_unlock();
 nlmsg_failure:
+       nfnetlink_set_err(0, group, -ENOBUFS);
        kfree_skb(skb);
        return NOTIFY_DONE;
 }
@@ -535,7 +609,7 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct nf_conn *ct, *last;
        struct nf_conntrack_tuple_hash *h;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
 
@@ -543,27 +617,27 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        last = (struct nf_conn *)cb->args[1];
        for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
 restart:
-               hlist_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
-                                        hnode) {
+               hlist_nulls_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
+                                        hnnode) {
                        if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
+                       if (!atomic_inc_not_zero(&ct->ct_general.use))
+                               continue;
                        /* Dump entries of a given L3 protocol number.
                         * If it is not specified, ie. l3proto == 0,
                         * then dump everything. */
                        if (l3proto && nf_ct_l3num(ct) != l3proto)
-                               continue;
+                               goto releasect;
                        if (cb->args[1]) {
                                if (ct != last)
-                                       continue;
+                                       goto releasect;
                                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) {
-                               if (!atomic_inc_not_zero(&ct->ct_general.use))
-                                       continue;
                                cb->args[1] = (unsigned long)ct;
                                goto out;
                        }
@@ -576,6 +650,8 @@ restart:
                                if (acct)
                                        memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]));
                        }
+releasect:
+               nf_ct_put(ct);
                }
                if (cb->args[1]) {
                        cb->args[1] = 0;
@@ -1146,7 +1222,7 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
                return ERR_PTR(-ENOMEM);
 
        if (!cda[CTA_TIMEOUT])
-               goto err;
+               goto err1;
        ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
@@ -1157,10 +1233,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
                char *helpname;
  
                err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
 
                helper = __nf_conntrack_helper_find_byname(helpname);
                if (helper == NULL) {
@@ -1168,28 +1242,26 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 #ifdef CONFIG_MODULES
                        if (request_module("nfct-helper-%s", helpname) < 0) {
                                err = -EOPNOTSUPP;
-                               goto err;
+                               goto err1;
                        }
 
                        rcu_read_lock();
                        helper = __nf_conntrack_helper_find_byname(helpname);
                        if (helper) {
-                               rcu_read_unlock();
                                err = -EAGAIN;
-                               goto err;
+                               goto err2;
                        }
                        rcu_read_unlock();
 #endif
                        err = -EOPNOTSUPP;
-                       goto err;
+                       goto err1;
                } else {
                        struct nf_conn_help *help;
 
                        help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
                        if (help == NULL) {
-                               rcu_read_unlock();
                                err = -ENOMEM;
-                               goto err;
+                               goto err2;
                        }
 
                        /* not in hash table yet so not strictly necessary */
@@ -1198,44 +1270,34 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
        } else {
                /* try an implicit helper assignation */
                err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
        }
 
        if (cda[CTA_STATUS]) {
                err = ctnetlink_change_status(ct, cda);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
        }
 
        if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
                err = ctnetlink_change_nat(ct, cda);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
        }
 
 #ifdef CONFIG_NF_NAT_NEEDED
        if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) {
                err = ctnetlink_change_nat_seq_adj(ct, cda);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
        }
 #endif
 
        if (cda[CTA_PROTOINFO]) {
                err = ctnetlink_change_protoinfo(ct, cda);
-               if (err < 0) {
-                       rcu_read_unlock();
-                       goto err;
-               }
+               if (err < 0)
+                       goto err2;
        }
 
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
@@ -1253,15 +1315,14 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 
                err = ctnetlink_parse_tuple(cda, &master, CTA_TUPLE_MASTER, u3);
                if (err < 0)
-                       goto err;
+                       goto err2;
 
-               master_h = __nf_conntrack_find(&init_net, &master);
+               master_h = nf_conntrack_find_get(&init_net, &master);
                if (master_h == NULL) {
                        err = -ENOENT;
-                       goto err;
+                       goto err2;
                }
                master_ct = nf_ct_tuplehash_to_ctrack(master_h);
-               nf_conntrack_get(&master_ct->ct_general);
                __set_bit(IPS_EXPECTED_BIT, &ct->status);
                ct->master = master_ct;
        }
@@ -1271,7 +1332,10 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
        rcu_read_unlock();
 
        return ct;
-err:
+
+err2:
+       rcu_read_unlock();
+err1:
        nf_conntrack_free(ct);
        return ERR_PTR(err);
 }
@@ -1525,6 +1589,7 @@ static int ctnetlink_expect_event(struct notifier_block *this,
 nla_put_failure:
        rcu_read_unlock();
 nlmsg_failure:
+       nfnetlink_set_err(0, 0, -ENOBUFS);
        kfree_skb(skb);
        return NOTIFY_DONE;
 }
@@ -1772,6 +1837,7 @@ ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3, u32 pid, int report)
                goto out;
        }
 
+       exp->class = 0;
        exp->expectfn = NULL;
        exp->flags = 0;
        exp->master = ct;