[PATCH] m68knommu: FEC driver support for the ColdFire 523x CPU family
[safe/jmp/linux-2.6] / net / netfilter / nf_conntrack_netlink.c
index 4f2e509..b8c7c56 100644 (file)
@@ -2,9 +2,9 @@
  * protocol helpers and general trouble making from userspace.
  *
  * (C) 2001 by Jay Schulist <jschlst@samba.org>
- * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org>
  * (C) 2003 by Patrick Mchardy <kaber@trash.net>
- * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
  *
  * I've reworked this stuff to use attributes instead of conntrack 
  * structures. 5.44 am. I need more tea. --pablo 05/07/11.
@@ -44,7 +44,7 @@
 
 MODULE_LICENSE("GPL");
 
-static char __initdata version[] = "0.92";
+static char __initdata version[] = "0.93";
 
 #if 0
 #define DEBUGP printk
@@ -55,20 +55,18 @@ static char __initdata version[] = "0.92";
 
 static inline int
 ctnetlink_dump_tuples_proto(struct sk_buff *skb, 
-                           const struct nf_conntrack_tuple *tuple)
+                           const struct nf_conntrack_tuple *tuple,
+                           struct nf_conntrack_protocol *proto)
 {
-       struct nf_conntrack_protocol *proto;
        int ret = 0;
+       struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
 
        NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
 
-       /* If no protocol helper is found, this function will return the
-        * generic protocol helper, so proto won't *ever* be NULL */
-       proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
        if (likely(proto->tuple_to_nfattr))
                ret = proto->tuple_to_nfattr(skb, tuple);
        
-       nf_ct_proto_put(proto);
+       NFA_NEST_END(skb, nest_parms);
 
        return ret;
 
@@ -77,33 +75,44 @@ nfattr_failure:
 }
 
 static inline int
-ctnetlink_dump_tuples(struct sk_buff *skb, 
-                     const struct nf_conntrack_tuple *tuple)
+ctnetlink_dump_tuples_ip(struct sk_buff *skb,
+                        const struct nf_conntrack_tuple *tuple,
+                        struct nf_conntrack_l3proto *l3proto)
 {
-       struct nfattr *nest_parms;
-       struct nf_conntrack_l3proto *l3proto;
        int ret = 0;
-       
-       l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
-       
-       nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
+       struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
+
        if (likely(l3proto->tuple_to_nfattr))
                ret = l3proto->tuple_to_nfattr(skb, tuple);
+
        NFA_NEST_END(skb, nest_parms);
 
+       return ret;
+
+nfattr_failure:
+       return -1;
+}
+
+static inline int
+ctnetlink_dump_tuples(struct sk_buff *skb,
+                     const struct nf_conntrack_tuple *tuple)
+{
+       int ret;
+       struct nf_conntrack_l3proto *l3proto;
+       struct nf_conntrack_protocol *proto;
+
+       l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+       ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
        nf_ct_l3proto_put(l3proto);
 
        if (unlikely(ret < 0))
                return ret;
 
-       nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
-       ret = ctnetlink_dump_tuples_proto(skb, tuple);
-       NFA_NEST_END(skb, nest_parms);
+       proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+       ret = ctnetlink_dump_tuples_proto(skb, tuple, proto);
+       nf_ct_proto_put(proto);
 
        return ret;
-
-nfattr_failure:
-       return -1;
 }
 
 static inline int
@@ -165,15 +174,16 @@ static inline int
 ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
 {
        struct nfattr *nest_helper;
+       const struct nf_conn_help *help = nfct_help(ct);
 
-       if (!ct->helper)
+       if (!help || !help->helper)
                return 0;
                
        nest_helper = NFA_NEST(skb, CTA_HELP);
-       NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name);
+       NFA_PUT(skb, CTA_HELP_NAME, strlen(help->helper->name), help->helper->name);
 
-       if (ct->helper->to_nfattr)
-               ct->helper->to_nfattr(skb, ct);
+       if (help->helper->to_nfattr)
+               help->helper->to_nfattr(skb, ct);
 
        NFA_NEST_END(skb, nest_helper);
 
@@ -337,9 +347,10 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
                group = NFNLGRP_CONNTRACK_UPDATE;
        } else
                return NOTIFY_DONE;
-       
-  /* FIXME: Check if there are any listeners before, don't hurt performance */
-       
+
+       if (!nfnetlink_has_listeners(group))
+               return NOTIFY_DONE;
+
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!skb)
                return NOTIFY_DONE;
@@ -396,39 +407,64 @@ 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;
 }
 
+#define L3PROTO(ct) ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num
+
 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;
 
        DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__, 
                        cb->args[0], *id);
 
        read_lock_bh(&nf_conntrack_lock);
-       for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) {
+       for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
+restart:
+               last = (struct nf_conn *)cb->args[1];
                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)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
-                       if (ct->id <= *id)
+                       /* Dump entries of a given L3 protocol number.
+                        * If it is not specified, ie. l3proto == 0,
+                        * then dump everything. */
+                       if (l3proto && L3PROTO(ct) != l3proto)
                                continue;
+                       if (last != NULL) {
+                               if (ct == last) {
+                                       nf_ct_put(last);
+                                       cb->args[1] = 0;
+                                       last = NULL;
+                               } else
+                                       continue;
+                       }
                        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 (last != NULL) {
+                       nf_ct_put(last);
+                       cb->args[1] = 0;
+                       goto restart;
                }
        }
-out:   
+out:
        read_unlock_bh(&nf_conntrack_lock);
 
        DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
@@ -444,6 +480,8 @@ ctnetlink_dump_table_w(struct sk_buff *skb, struct netlink_callback *cb)
        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; 
 
        DEBUGP("entered %s, last bucket=%u id=%u\n", __FUNCTION__, 
                        cb->args[0], *id);
@@ -455,6 +493,8 @@ ctnetlink_dump_table_w(struct sk_buff *skb, struct netlink_callback *cb)
                        if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
+                       if (l3proto && L3PROTO(ct) != l3proto)
+                               continue;
                        if (ct->id <= *id)
                                continue;
                        if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
@@ -617,7 +657,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];
@@ -627,7 +667,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;
@@ -750,9 +790,6 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                u32 rlen;
 
-               if (nfmsg->nfgen_family != AF_INET)
-                       return -EAFNOSUPPORT;
-
                if (NFNL_MSG_TYPE(nlh->nlmsg_type) ==
                                        IPCTNL_MSG_CT_GET_CTRZERO) {
 #ifdef CONFIG_NF_CT_ACCT
@@ -845,39 +882,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
        }
 
@@ -893,11 +921,17 @@ static inline int
 ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[])
 {
        struct nf_conntrack_helper *helper;
+       struct nf_conn_help *help = nfct_help(ct);
        char *helpname;
        int err;
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
+       if (!help) {
+               /* FIXME: we need to reallocate and rehash */
+               return -EBUSY;
+       }
+
        /* don't change helper of sibling connections */
        if (ct->master)
                return -EINVAL;
@@ -914,18 +948,18 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[])
                        return -EINVAL;
        }
 
-       if (ct->helper) {
+       if (help->helper) {
                if (!helper) {
                        /* we had a helper before ... */
                        nf_ct_remove_expectations(ct);
-                       ct->helper = NULL;
+                       help->helper = NULL;
                } else {
                        /* need to zero data of old helper */
-                       memset(&ct->help, 0, sizeof(ct->help));
+                       memset(&help->help, 0, sizeof(help->help));
                }
        }
        
-       ct->helper = helper;
+       help->helper = helper;
 
        return 0;
 }
@@ -995,7 +1029,7 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nfattr *cda[])
                        return err;
        }
 
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+#if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK-1])
                ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
 #endif
@@ -1035,19 +1069,14 @@ ctnetlink_create_conntrack(struct nfattr *cda[],
                        return err;
        }
 
-#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+#if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK-1])
                ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
 #endif
 
-       ct->helper = nf_ct_helper_find_get(rtuple);
-
        add_timer(&ct->timeout);
        nf_conntrack_hash_insert(ct);
 
-       if (ct->helper)
-               nf_ct_helper_put(ct->helper);
-
        DEBUGP("conntrack with id %u inserted\n", ct->id);
        return 0;
 
@@ -1100,7 +1129,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;
        }
@@ -1140,6 +1169,37 @@ nfattr_failure:
 }                      
 
 static inline int
+ctnetlink_exp_dump_mask(struct sk_buff *skb,
+                       const struct nf_conntrack_tuple *tuple,
+                       const struct nf_conntrack_tuple *mask)
+{
+       int ret;
+       struct nf_conntrack_l3proto *l3proto;
+       struct nf_conntrack_protocol *proto;
+       struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK);
+
+       l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+       ret = ctnetlink_dump_tuples_ip(skb, mask, l3proto);
+       nf_ct_l3proto_put(l3proto);
+
+       if (unlikely(ret < 0))
+               goto nfattr_failure;
+
+       proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+       ret = ctnetlink_dump_tuples_proto(skb, mask, proto);
+       nf_ct_proto_put(proto);
+       if (unlikely(ret < 0))
+               goto nfattr_failure;
+
+       NFA_NEST_END(skb, nest_parms);
+
+       return 0;
+
+nfattr_failure:
+       return -1;
+}
+
+static inline int
 ctnetlink_exp_dump_expect(struct sk_buff *skb,
                           const struct nf_conntrack_expect *exp)
 {
@@ -1149,7 +1209,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 
        if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
                goto nfattr_failure;
-       if (ctnetlink_exp_dump_tuple(skb, &exp->mask, CTA_EXPECT_MASK) < 0)
+       if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
                goto nfattr_failure;
        if (ctnetlink_exp_dump_tuple(skb,
                                 &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
@@ -1222,7 +1282,7 @@ static int ctnetlink_expect_event(struct notifier_block *this,
 
        b = skb->tail;
 
-       type |= NFNL_SUBSYS_CTNETLINK << 8;
+       type |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
        nlh   = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
        nfmsg = NLMSG_DATA(nlh);
 
@@ -1251,12 +1311,16 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        struct nf_conntrack_expect *exp = NULL;
        struct list_head *i;
        u_int32_t *id = (u_int32_t *) &cb->args[0];
+       struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
+       u_int8_t l3proto = nfmsg->nfgen_family;
 
        DEBUGP("entered %s, last id=%llu\n", __FUNCTION__, *id);
 
        read_lock_bh(&nf_conntrack_lock);
        list_for_each_prev(i, &nf_conntrack_expect_list) {
                exp = (struct nf_conntrack_expect *) i;
+               if (l3proto && exp->tuple.src.l3num != l3proto)
+                       continue;
                if (exp->id <= *id)
                        continue;
                if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid,
@@ -1298,9 +1362,6 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                u32 rlen;
 
-               if (nfmsg->nfgen_family != AF_INET)
-                       return -EAFNOSUPPORT;
-
                if ((*errp = netlink_dump_start(ctnl, skb, nlh,
                                                ctnetlink_exp_dump_table,
                                                ctnetlink_done)) != 0)
@@ -1406,7 +1467,8 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                }
                list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list,
                                         list) {
-                       if (exp->master->helper == h 
+                       struct nf_conn_help *m_help = nfct_help(exp->master);
+                       if (m_help->helper == h
                            && del_timer(&exp->timeout)) {
                                nf_ct_unlink_expect(exp);
                                nf_conntrack_expect_put(exp);
@@ -1441,6 +1503,7 @@ ctnetlink_create_expect(struct nfattr *cda[], u_int8_t u3)
        struct nf_conntrack_tuple_hash *h = NULL;
        struct nf_conntrack_expect *exp;
        struct nf_conn *ct;
+       struct nf_conn_help *help;
        int err = 0;
 
        DEBUGP("entered %s\n", __FUNCTION__);
@@ -1461,8 +1524,9 @@ ctnetlink_create_expect(struct nfattr *cda[], u_int8_t u3)
        if (!h)
                return -ENOENT;
        ct = nf_ct_tuplehash_to_ctrack(h);
+       help = nfct_help(ct);
 
-       if (!ct->helper) {
+       if (!help || !help->helper) {
                /* such conntrack hasn't got any helper, abort */
                err = -EINVAL;
                goto out;
@@ -1578,6 +1642,7 @@ static struct nfnetlink_subsystem ctnl_exp_subsys = {
 };
 
 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP);
 
 static int __init ctnetlink_init(void)
 {
@@ -1629,7 +1694,7 @@ static void __exit ctnetlink_exit(void)
        printk("ctnetlink: unregistering from nfnetlink.\n");
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-       nf_conntrack_unregister_notifier(&ctnl_notifier_exp);
+       nf_conntrack_expect_unregister_notifier(&ctnl_notifier_exp);
        nf_conntrack_unregister_notifier(&ctnl_notifier);
 #endif