netfilter: nfnetlink: extended ACK reporting
[linux-2.6-block.git] / net / netfilter / nfnetlink_cthelper.c
index d45558178da5b62a8ad7c896e096c1862c512091..41628b3936731b77885717c27cf4dfa8e62b3c0f 100644 (file)
@@ -77,7 +77,8 @@ nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
        int err;
        struct nlattr *tb[NFCTH_TUPLE_MAX+1];
 
-       err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
+       err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr,
+                              nfnl_cthelper_tuple_pol, NULL);
        if (err < 0)
                return err;
 
@@ -104,7 +105,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
        if (help->helper->data_len == 0)
                return -EINVAL;
 
-       memcpy(help->data, nla_data(attr), help->helper->data_len);
+       nla_memcpy(help->data, nla_data(attr), sizeof(help->data));
        return 0;
 }
 
@@ -137,7 +138,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
        int err;
        struct nlattr *tb[NFCTH_POLICY_MAX+1];
 
-       err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
+       err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr,
+                              nfnl_cthelper_expect_pol, NULL);
        if (err < 0)
                return err;
 
@@ -150,6 +152,9 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
                nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
        expect_policy->max_expected =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
+       if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
+               return -EINVAL;
+
        expect_policy->timeout =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
 
@@ -171,7 +176,7 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
        unsigned int class_max;
 
        ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
-                              nfnl_cthelper_expect_policy_set);
+                              nfnl_cthelper_expect_policy_set, NULL);
        if (ret < 0)
                return ret;
 
@@ -213,6 +218,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
 {
        struct nf_conntrack_helper *helper;
        struct nfnl_cthelper *nfcth;
+       unsigned int size;
        int ret;
 
        if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
@@ -228,7 +234,12 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
                goto err1;
 
        strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
-       helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
+       size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
+       if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
+               ret = -ENOMEM;
+               goto err2;
+       }
+
        helper->flags |= NF_CT_HELPER_F_USERSPACE;
        memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
 
@@ -276,7 +287,7 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
        int err;
 
        err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr,
-                              nfnl_cthelper_expect_pol);
+                              nfnl_cthelper_expect_pol, NULL);
        if (err < 0)
                return err;
 
@@ -290,6 +301,9 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
 
        new_policy->max_expected =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
+       if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
+               return -EINVAL;
+
        new_policy->timeout =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
 
@@ -336,7 +350,7 @@ static int nfnl_cthelper_update_policy(struct nf_conntrack_helper *helper,
        int err;
 
        err = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
-                              nfnl_cthelper_expect_policy_set);
+                              nfnl_cthelper_expect_policy_set, NULL);
        if (err < 0)
                return err;
 
@@ -384,7 +398,8 @@ nfnl_cthelper_update(const struct nlattr * const tb[],
 
 static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const tb[])
+                            const struct nlattr * const tb[],
+                            struct netlink_ext_ack *extack)
 {
        const char *helper_name;
        struct nf_conntrack_helper *cur, *helper = NULL;
@@ -501,7 +516,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
        unsigned int flags = portid ? NLM_F_MULTI : 0;
        int status;
 
-       event |= NFNL_SUBSYS_CTHELPER << 8;
+       event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event);
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
        if (nlh == NULL)
                goto nlmsg_failure;
@@ -585,7 +600,8 @@ out:
 
 static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const tb[])
+                            const struct nlattr * const tb[],
+                            struct netlink_ext_ack *extack)
 {
        int ret = -ENOENT;
        struct nf_conntrack_helper *cur;
@@ -652,7 +668,8 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
 
 static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
                             struct sk_buff *skb, const struct nlmsghdr *nlh,
-                            const struct nlattr * const tb[])
+                            const struct nlattr * const tb[],
+                            struct netlink_ext_ack *extack)
 {
        char *helper_name = NULL;
        struct nf_conntrack_helper *cur;
@@ -672,6 +689,7 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
                tuple_set = true;
        }
 
+       ret = -ENOENT;
        list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) {
                cur = &nlcth->helper;
                j++;
@@ -685,16 +703,20 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
                     tuple.dst.protonum != cur->tuple.dst.protonum))
                        continue;
 
-               found = true;
-               nf_conntrack_helper_unregister(cur);
-               kfree(cur->expect_policy);
+               if (refcount_dec_if_one(&cur->refcnt)) {
+                       found = true;
+                       nf_conntrack_helper_unregister(cur);
+                       kfree(cur->expect_policy);
 
-               list_del(&nlcth->list);
-               kfree(nlcth);
+                       list_del(&nlcth->list);
+                       kfree(nlcth);
+               } else {
+                       ret = -EBUSY;
+               }
        }
 
        /* Make sure we return success if we flush and there is no helpers */
-       return (found || j == 0) ? 0 : -ENOENT;
+       return (found || j == 0) ? 0 : ret;
 }
 
 static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = {