]> err.no Git - linux-2.6/blobdiff - net/ipv4/netfilter/ip_conntrack_netlink.c
[NETFILTER]: ctnetlink: propagate ctnetlink_dump_tuples_proto return value back
[linux-2.6] / net / ipv4 / netfilter / ip_conntrack_netlink.c
index 82a65043a8ef6a0c1b900a2ef5f282a12e7428ee..5e9affdd0dbee926ee0f00c31f80219193ef0aba 100644 (file)
 #include <linux/errno.h>
 #include <linux/netlink.h>
 #include <linux/spinlock.h>
+#include <linux/interrupt.h>
 #include <linux/notifier.h>
-#include <linux/rtnetlink.h>
 
 #include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
@@ -58,14 +56,19 @@ ctnetlink_dump_tuples_proto(struct sk_buff *skb,
                            const struct ip_conntrack_tuple *tuple)
 {
        struct ip_conntrack_protocol *proto;
+       int ret = 0;
 
        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 = ip_conntrack_proto_find_get(tuple->dst.protonum);
-       if (proto && proto->tuple_to_nfattr)
-               return proto->tuple_to_nfattr(skb, tuple);
+       if (likely(proto->tuple_to_nfattr))
+               ret = proto->tuple_to_nfattr(skb, tuple);
+       
+       ip_conntrack_proto_put(proto);
 
-       return 0;
+       return ret;
 
 nfattr_failure:
        return -1;
@@ -76,6 +79,7 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
                      const struct ip_conntrack_tuple *tuple)
 {
        struct nfattr *nest_parms;
+       int ret;
        
        nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
        NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(u_int32_t), &tuple->src.ip);
@@ -83,10 +87,10 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
        NFA_NEST_END(skb, nest_parms);
 
        nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
-       ctnetlink_dump_tuples_proto(skb, tuple);
+       ret = ctnetlink_dump_tuples_proto(skb, tuple);
        NFA_NEST_END(skb, nest_parms);
 
-       return 0;
+       return ret;
 
 nfattr_failure:
        return -1;
@@ -128,9 +132,11 @@ ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct)
 
        struct nfattr *nest_proto;
        int ret;
-       
-       if (!proto || !proto->to_nfattr)
+
+       if (!proto->to_nfattr) {
+               ip_conntrack_proto_put(proto);
                return 0;
+       }
        
        nest_proto = NFA_NEST(skb, CTA_PROTOINFO);
 
@@ -175,7 +181,7 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct,
 {
        enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
        struct nfattr *nest_count = NFA_NEST(skb, type);
-       u_int64_t tmp;
+       u_int32_t tmp;
 
        tmp = htonl(ct->counters[dir].packets);
        NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp);
@@ -467,7 +473,7 @@ out:
 }
 #endif
 
-static const int cta_min_ip[CTA_IP_MAX] = {
+static const size_t cta_min_ip[CTA_IP_MAX] = {
        [CTA_IP_V4_SRC-1]       = sizeof(u_int32_t),
        [CTA_IP_V4_DST-1]       = sizeof(u_int32_t),
 };
@@ -479,9 +485,7 @@ ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple)
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
-       
-       if (nfattr_parse_nested(tb, CTA_IP_MAX, attr) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_IP_MAX, attr);
 
        if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
                return -EINVAL;
@@ -497,13 +501,10 @@ ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple)
        DEBUGP("leaving\n");
 
        return 0;
-
-nfattr_failure:
-       return -1;
 }
 
-static const int cta_min_proto[CTA_PROTO_MAX] = {
-       [CTA_PROTO_NUM-1]       = sizeof(u_int16_t),
+static const size_t cta_min_proto[CTA_PROTO_MAX] = {
+       [CTA_PROTO_NUM-1]       = sizeof(u_int8_t),
        [CTA_PROTO_SRC_PORT-1]  = sizeof(u_int16_t),
        [CTA_PROTO_DST_PORT-1]  = sizeof(u_int16_t),
        [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t),
@@ -521,27 +522,23 @@ ctnetlink_parse_tuple_proto(struct nfattr *attr,
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
-       if (nfattr_parse_nested(tb, CTA_PROTO_MAX, attr) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_PROTO_MAX, attr);
 
        if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
                return -EINVAL;
 
        if (!tb[CTA_PROTO_NUM-1])
                return -EINVAL;
-       tuple->dst.protonum = *(u_int16_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
+       tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
 
        proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
 
-       if (likely(proto && proto->nfattr_to_tuple)) {
+       if (likely(proto->nfattr_to_tuple))
                ret = proto->nfattr_to_tuple(tb, tuple);
-               ip_conntrack_proto_put(proto);
-       }
+       
+       ip_conntrack_proto_put(proto);
        
        return ret;
-
-nfattr_failure:
-       return -1;
 }
 
 static inline int
@@ -555,8 +552,7 @@ ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple,
 
        memset(tuple, 0, sizeof(*tuple));
 
-       if (nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]);
 
        if (!tb[CTA_TUPLE_IP-1])
                return -EINVAL;
@@ -583,13 +579,10 @@ ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple,
        DEBUGP("leaving\n");
 
        return 0;
-
-nfattr_failure:
-       return -1;
 }
 
 #ifdef CONFIG_IP_NF_NAT_NEEDED
-static const int cta_min_protonat[CTA_PROTONAT_MAX] = {
+static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = {
        [CTA_PROTONAT_PORT_MIN-1]       = sizeof(u_int16_t),
        [CTA_PROTONAT_PORT_MAX-1]       = sizeof(u_int16_t),
 };
@@ -603,15 +596,12 @@ static int ctnetlink_parse_nat_proto(struct nfattr *attr,
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
-       if (nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr);
 
        if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat))
-               goto nfattr_failure;
+               return -EINVAL;
 
        npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
-       if (!npt)
-               return 0;
 
        if (!npt->nfattr_to_range) {
                ip_nat_proto_put(npt);
@@ -626,11 +616,13 @@ static int ctnetlink_parse_nat_proto(struct nfattr *attr,
 
        DEBUGP("leaving\n");
        return 0;
-
-nfattr_failure:
-       return -1;
 }
 
+static const size_t cta_min_nat[CTA_NAT_MAX] = {
+       [CTA_NAT_MINIP-1]       = sizeof(u_int32_t),
+       [CTA_NAT_MAXIP-1]       = sizeof(u_int32_t),
+};
+
 static inline int
 ctnetlink_parse_nat(struct nfattr *cda[],
                    const struct ip_conntrack *ct, struct ip_nat_range *range)
@@ -642,8 +634,10 @@ ctnetlink_parse_nat(struct nfattr *cda[],
 
        memset(range, 0, sizeof(*range));
        
-       if (nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]);
+
+       if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
+               return -EINVAL;
 
        if (tb[CTA_NAT_MINIP-1])
                range->min_ip = *(u_int32_t *)NFA_DATA(tb[CTA_NAT_MINIP-1]);
@@ -665,9 +659,6 @@ ctnetlink_parse_nat(struct nfattr *cda[],
 
        DEBUGP("leaving\n");
        return 0;
-
-nfattr_failure:
-       return -1;
 }
 #endif
 
@@ -678,8 +669,7 @@ ctnetlink_parse_help(struct nfattr *attr, char **helper_name)
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
-       if (nfattr_parse_nested(tb, CTA_HELP_MAX, attr) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_HELP_MAX, attr);
 
        if (!tb[CTA_HELP_NAME-1])
                return -EINVAL;
@@ -687,11 +677,16 @@ ctnetlink_parse_help(struct nfattr *attr, char **helper_name)
        *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]);
 
        return 0;
-
-nfattr_failure:
-       return -1;
 }
 
+static const size_t cta_min[CTA_MAX] = {
+       [CTA_STATUS-1]          = sizeof(u_int32_t),
+       [CTA_TIMEOUT-1]         = sizeof(u_int32_t),
+       [CTA_MARK-1]            = sizeof(u_int32_t),
+       [CTA_USE-1]             = sizeof(u_int32_t),
+       [CTA_ID-1]              = sizeof(u_int32_t)
+};
+
 static int
 ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, 
                        struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
@@ -703,6 +698,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
+       if (nfattr_bad_size(cda, CTA_MAX, cta_min))
+               return -EINVAL;
+
        if (cda[CTA_TUPLE_ORIG-1])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
        else if (cda[CTA_TUPLE_REPLY-1])
@@ -731,11 +729,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        return -ENOENT;
                }
        }       
-       if (del_timer(&ct->timeout)) {
-               ip_conntrack_put(ct);
+       if (del_timer(&ct->timeout))
                ct->timeout.function((unsigned long)ct);
-               return 0;
-       }
+
        ip_conntrack_put(ct);
        DEBUGP("leaving\n");
 
@@ -785,6 +781,9 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
                return 0;
        }
 
+       if (nfattr_bad_size(cda, CTA_MAX, cta_min))
+               return -EINVAL;
+
        if (cda[CTA_TUPLE_ORIG-1])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
        else if (cda[CTA_TUPLE_REPLY-1])
@@ -804,7 +803,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
        ct = tuplehash_to_ctrack(h);
 
        err = -ENOMEM;
-       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2) {
                ip_conntrack_put(ct);
                return -ENOMEM;
@@ -827,7 +826,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
 free:
        kfree_skb(skb2);
 out:
-       return -1;
+       return err;
 }
 
 static inline int
@@ -877,7 +876,7 @@ ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[])
                DEBUGP("NAT status: %lu\n", 
                       status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
                
-               if (ip_nat_initialized(ct, hooknum))
+               if (ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
                        return -EEXIST;
                ip_nat_setup_info(ct, &range, hooknum);
 
@@ -957,21 +956,15 @@ ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[])
        u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
        int err = 0;
 
-       if (nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr) < 0)
-               goto nfattr_failure;
+       nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr);
 
        proto = ip_conntrack_proto_find_get(npt);
-       if (!proto)
-               return -EINVAL;
 
        if (proto->from_nfattr)
                err = proto->from_nfattr(tb, ct);
        ip_conntrack_proto_put(proto); 
 
        return err;
-
-nfattr_failure:
-       return -ENOMEM;
 }
 
 static int
@@ -1005,6 +998,11 @@ ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[])
                        return err;
        }
 
+#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+       if (cda[CTA_MARK-1])
+               ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
+#endif
+
        DEBUGP("all done\n");
        return 0;
 }
@@ -1048,6 +1046,11 @@ ctnetlink_create_conntrack(struct nfattr *cda[],
        if (ct->helper)
                ip_conntrack_helper_put(ct->helper);
 
+#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
+       if (cda[CTA_MARK-1])
+               ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
+#endif
+
        DEBUGP("conntrack with id %u inserted\n", ct->id);
        return 0;
 
@@ -1066,6 +1069,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
+       if (nfattr_bad_size(cda, CTA_MAX, cta_min))
+               return -EINVAL;
+
        if (cda[CTA_TUPLE_ORIG-1]) {
                err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG);
                if (err < 0)
@@ -1271,6 +1277,11 @@ out:
        return skb->len;
 }
 
+static const size_t cta_min_exp[CTA_EXPECT_MAX] = {
+       [CTA_EXPECT_TIMEOUT-1]          = sizeof(u_int32_t),
+       [CTA_EXPECT_ID-1]               = sizeof(u_int32_t)
+};
+
 static int
 ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, 
                     struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
@@ -1282,6 +1293,9 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
 
        DEBUGP("entered %s\n", __FUNCTION__);
 
+       if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
+               return -EINVAL;
+
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct nfgenmsg *msg = NLMSG_DATA(nlh);
                u32 rlen;
@@ -1312,6 +1326,14 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        if (!exp)
                return -ENOENT;
 
+       if (cda[CTA_EXPECT_ID-1]) {
+               u_int32_t id = *(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
+               if (exp->id != ntohl(id)) {
+                       ip_conntrack_expect_put(exp);
+                       return -ENOENT;
+               }
+       }       
+
        err = -ENOMEM;
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -1344,6 +1366,9 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
        struct ip_conntrack_helper *h;
        int err;
 
+       if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
+               return -EINVAL;
+
        if (cda[CTA_EXPECT_TUPLE-1]) {
                /* delete a single expect by tuple */
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
@@ -1387,7 +1412,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                                ip_conntrack_expect_put(exp);
                        }
                }
-               write_unlock(&ip_conntrack_lock);
+               write_unlock_bh(&ip_conntrack_lock);
        } else {
                /* This basically means we have to flush everything*/
                write_lock_bh(&ip_conntrack_lock);
@@ -1473,6 +1498,9 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
 
        DEBUGP("entered %s\n", __FUNCTION__);   
 
+       if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
+               return -EINVAL;
+
        if (!cda[CTA_EXPECT_TUPLE-1]
            || !cda[CTA_EXPECT_MASK-1]
            || !cda[CTA_EXPECT_MASTER-1])
@@ -1515,29 +1543,22 @@ static struct notifier_block ctnl_notifier_exp = {
 
 static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
        [IPCTNL_MSG_CT_NEW]             = { .call = ctnetlink_new_conntrack,
-                                           .attr_count = CTA_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_MAX, },
        [IPCTNL_MSG_CT_GET]             = { .call = ctnetlink_get_conntrack,
-                                           .attr_count = CTA_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_MAX, },
        [IPCTNL_MSG_CT_DELETE]          = { .call = ctnetlink_del_conntrack,
-                                           .attr_count = CTA_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_MAX, },
        [IPCTNL_MSG_CT_GET_CTRZERO]     = { .call = ctnetlink_get_conntrack,
-                                           .attr_count = CTA_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_MAX, },
 };
 
 static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
        [IPCTNL_MSG_EXP_GET]            = { .call = ctnetlink_get_expect,
-                                           .attr_count = CTA_EXPECT_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_EXPECT_MAX, },
        [IPCTNL_MSG_EXP_NEW]            = { .call = ctnetlink_new_expect,
-                                           .attr_count = CTA_EXPECT_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_EXPECT_MAX, },
        [IPCTNL_MSG_EXP_DELETE]         = { .call = ctnetlink_del_expect,
-                                           .attr_count = CTA_EXPECT_MAX,
-                                           .cap_required = CAP_NET_ADMIN },
+                                           .attr_count = CTA_EXPECT_MAX, },
 };
 
 static struct nfnetlink_subsystem ctnl_subsys = {
@@ -1554,6 +1575,8 @@ static struct nfnetlink_subsystem ctnl_exp_subsys = {
        .cb                             = ctnl_exp_cb,
 };
 
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
+
 static int __init ctnetlink_init(void)
 {
        int ret;