netfilter: nf_conntrack: add allocation flag to nf_conntrack_alloc
[deliverable/linux.git] / net / netfilter / nf_conntrack_netlink.c
index d4eedc68cc76d19642754dd678cbf57433ecb4c6..dd233393f6952203c2408e5d904e490d784a940f 100644 (file)
@@ -4,7 +4,7 @@
  * (C) 2001 by Jay Schulist <jschlst@samba.org>
  * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org>
  * (C) 2003 by Patrick Mchardy <kaber@trash.net>
- * (C) 2005-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2005-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * Initial connection tracking via netlink development funded and
  * generally made possible by Network Robots, Inc. (www.networkrobots.com)
@@ -59,7 +59,7 @@ ctnetlink_dump_tuples_proto(struct sk_buff *skb,
        nest_parms = nla_nest_start(skb, CTA_TUPLE_PROTO | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
-       NLA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
+       NLA_PUT_U8(skb, CTA_PROTO_NUM, tuple->dst.protonum);
 
        if (likely(l4proto->tuple_to_nlattr))
                ret = l4proto->tuple_to_nlattr(skb, tuple);
@@ -95,7 +95,7 @@ nla_put_failure:
        return -1;
 }
 
-static inline int
+static int
 ctnetlink_dump_tuples(struct sk_buff *skb,
                      const struct nf_conntrack_tuple *tuple)
 {
@@ -120,8 +120,7 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
 static inline int
 ctnetlink_dump_status(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 status = htonl((u_int32_t) ct->status);
-       NLA_PUT(skb, CTA_STATUS, sizeof(status), &status);
+       NLA_PUT_BE32(skb, CTA_STATUS, htonl(ct->status));
        return 0;
 
 nla_put_failure:
@@ -131,15 +130,12 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       long timeout_l = ct->timeout.expires - jiffies;
-       __be32 timeout;
+       long timeout = (ct->timeout.expires - jiffies) / HZ;
 
-       if (timeout_l < 0)
+       if (timeout < 0)
                timeout = 0;
-       else
-               timeout = htonl(timeout_l / HZ);
 
-       NLA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout);
+       NLA_PUT_BE32(skb, CTA_TIMEOUT, htonl(timeout));
        return 0;
 
 nla_put_failure:
@@ -149,10 +145,11 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       struct nf_conntrack_l4proto *l4proto = nf_ct_l4proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
+       struct nf_conntrack_l4proto *l4proto;
        struct nlattr *nest_proto;
        int ret;
 
+       l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct));
        if (!l4proto->to_nlattr) {
                nf_ct_l4proto_put(l4proto);
                return 0;
@@ -193,7 +190,7 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
        nest_helper = nla_nest_start(skb, CTA_HELP | NLA_F_NESTED);
        if (!nest_helper)
                goto nla_put_failure;
-       NLA_PUT(skb, CTA_HELP_NAME, strlen(helper->name), helper->name);
+       NLA_PUT_STRING(skb, CTA_HELP_NAME, helper->name);
 
        if (helper->to_nlattr)
                helper->to_nlattr(skb, ct);
@@ -209,23 +206,21 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_NF_CT_ACCT
-static inline int
+static int
 ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
                        enum ip_conntrack_dir dir)
 {
        enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
        struct nlattr *nest_count;
-       __be32 tmp;
 
        nest_count = nla_nest_start(skb, type | NLA_F_NESTED);
        if (!nest_count)
                goto nla_put_failure;
 
-       tmp = htonl(ct->counters[dir].packets);
-       NLA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp);
-
-       tmp = htonl(ct->counters[dir].bytes);
-       NLA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(u_int32_t), &tmp);
+       NLA_PUT_BE32(skb, CTA_COUNTERS32_PACKETS,
+                    htonl(ct->counters[dir].packets));
+       NLA_PUT_BE32(skb, CTA_COUNTERS32_BYTES,
+                    htonl(ct->counters[dir].bytes));
 
        nla_nest_end(skb, nest_count);
 
@@ -242,9 +237,7 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 mark = htonl(ct->mark);
-
-       NLA_PUT(skb, CTA_MARK, sizeof(u_int32_t), &mark);
+       NLA_PUT_BE32(skb, CTA_MARK, htonl(ct->mark));
        return 0;
 
 nla_put_failure:
@@ -258,9 +251,7 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_secmark(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 mark = htonl(ct->secmark);
-
-       NLA_PUT(skb, CTA_SECMARK, sizeof(u_int32_t), &mark);
+       NLA_PUT_BE32(skb, CTA_SECMARK, htonl(ct->secmark));
        return 0;
 
 nla_put_failure:
@@ -294,22 +285,21 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_NF_NAT_NEEDED
-static inline int
+static int
 dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type)
 {
-       __be32 tmp;
        struct nlattr *nest_parms;
 
        nest_parms = nla_nest_start(skb, type | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
 
-       tmp = htonl(natseq->correction_pos);
-       NLA_PUT(skb, CTA_NAT_SEQ_CORRECTION_POS, sizeof(tmp), &tmp);
-       tmp = htonl(natseq->offset_before);
-       NLA_PUT(skb, CTA_NAT_SEQ_OFFSET_BEFORE, sizeof(tmp), &tmp);
-       tmp = htonl(natseq->offset_after);
-       NLA_PUT(skb, CTA_NAT_SEQ_OFFSET_AFTER, sizeof(tmp), &tmp);
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_CORRECTION_POS,
+                    htonl(natseq->correction_pos));
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_OFFSET_BEFORE,
+                    htonl(natseq->offset_before));
+       NLA_PUT_BE32(skb, CTA_NAT_SEQ_OFFSET_AFTER,
+                    htonl(natseq->offset_after));
 
        nla_nest_end(skb, nest_parms);
 
@@ -345,8 +335,7 @@ ctnetlink_dump_nat_seq_adj(struct sk_buff *skb, const struct nf_conn *ct)
 static inline int
 ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 id = htonl((unsigned long)ct);
-       NLA_PUT(skb, CTA_ID, sizeof(u_int32_t), &id);
+       NLA_PUT_BE32(skb, CTA_ID, htonl((unsigned long)ct));
        return 0;
 
 nla_put_failure:
@@ -356,9 +345,7 @@ nla_put_failure:
 static inline int
 ctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct)
 {
-       __be32 use = htonl(atomic_read(&ct->ct_general.use));
-
-       NLA_PUT(skb, CTA_USE, sizeof(u_int32_t), &use);
+       NLA_PUT_BE32(skb, CTA_USE, htonl(atomic_read(&ct->ct_general.use)));
        return 0;
 
 nla_put_failure:
@@ -382,8 +369,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
        nfmsg  = NLMSG_DATA(nlh);
 
        nlh->nlmsg_flags    = (nowait && pid) ? NLM_F_MULTI : 0;
-       nfmsg->nfgen_family =
-               ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
+       nfmsg->nfgen_family = nf_ct_l3num(ct);
        nfmsg->version      = NFNETLINK_V0;
        nfmsg->res_id       = 0;
 
@@ -468,7 +454,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        nfmsg = NLMSG_DATA(nlh);
 
        nlh->nlmsg_flags    = flags;
-       nfmsg->nfgen_family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
+       nfmsg->nfgen_family = nf_ct_l3num(ct);
        nfmsg->version  = NFNETLINK_V0;
        nfmsg->res_id   = 0;
 
@@ -486,14 +472,17 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
                goto nla_put_failure;
        nla_nest_end(skb, nest_parms);
 
+       if (ctnetlink_dump_id(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_status(skb, ct) < 0)
+               goto nla_put_failure;
+
        if (events & IPCT_DESTROY) {
                if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
                    ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)
                        goto nla_put_failure;
        } else {
-               if (ctnetlink_dump_status(skb, ct) < 0)
-                       goto nla_put_failure;
-
                if (ctnetlink_dump_timeout(skb, ct) < 0)
                        goto nla_put_failure;
 
@@ -505,11 +494,6 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
                    && ctnetlink_dump_helpinfo(skb, ct) < 0)
                        goto nla_put_failure;
 
-#ifdef CONFIG_NF_CONNTRACK_MARK
-               if ((events & IPCT_MARK || ct->mark)
-                   && ctnetlink_dump_mark(skb, ct) < 0)
-                       goto nla_put_failure;
-#endif
 #ifdef CONFIG_NF_CONNTRACK_SECMARK
                if ((events & IPCT_SECMARK || ct->secmark)
                    && ctnetlink_dump_secmark(skb, ct) < 0)
@@ -530,6 +514,12 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
                        goto nla_put_failure;
        }
 
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       if ((events & IPCT_MARK || ct->mark)
+           && ctnetlink_dump_mark(skb, ct) < 0)
+               goto nla_put_failure;
+#endif
+
        nlh->nlmsg_len = skb->tail - b;
        nfnetlink_send(skb, 0, group, 0);
        return NOTIFY_DONE;
@@ -548,8 +538,6 @@ static int ctnetlink_done(struct netlink_callback *cb)
        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)
 {
@@ -559,19 +547,19 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
 
-       read_lock_bh(&nf_conntrack_lock);
+       rcu_read_lock();
        last = (struct nf_conn *)cb->args[1];
        for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
 restart:
-               hlist_for_each_entry(h, n, &nf_conntrack_hash[cb->args[0]],
-                                    hnode) {
+               hlist_for_each_entry_rcu(h, n, &nf_conntrack_hash[cb->args[0]],
+                                        hnode) {
                        if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
                        /* Dump entries of a given L3 protocol number.
                         * If it is not specified, ie. l3proto == 0,
                         * then dump everything. */
-                       if (l3proto && L3PROTO(ct) != l3proto)
+                       if (l3proto && nf_ct_l3num(ct) != l3proto)
                                continue;
                        if (cb->args[1]) {
                                if (ct != last)
@@ -582,7 +570,8 @@ restart:
                                                cb->nlh->nlmsg_seq,
                                                IPCTNL_MSG_CT_NEW,
                                                1, ct) < 0) {
-                               nf_conntrack_get(&ct->ct_general);
+                               if (!atomic_inc_not_zero(&ct->ct_general.use))
+                                       continue;
                                cb->args[1] = (unsigned long)ct;
                                goto out;
                        }
@@ -598,7 +587,7 @@ restart:
                }
        }
 out:
-       read_unlock_bh(&nf_conntrack_lock);
+       rcu_read_unlock();
        if (last)
                nf_ct_put(last);
 
@@ -646,7 +635,7 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
 
        if (!tb[CTA_PROTO_NUM])
                return -EINVAL;
-       tuple->dst.protonum = *(u_int8_t *)nla_data(tb[CTA_PROTO_NUM]);
+       tuple->dst.protonum = nla_get_u8(tb[CTA_PROTO_NUM]);
 
        l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
 
@@ -662,7 +651,7 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
        return ret;
 }
 
-static inline int
+static int
 ctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple,
                      enum ctattr_tuple type, u_int8_t l3num)
 {
@@ -709,27 +698,18 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr,
                                     struct nf_nat_range *range)
 {
        struct nlattr *tb[CTA_PROTONAT_MAX+1];
-       struct nf_nat_protocol *npt;
+       const struct nf_nat_protocol *npt;
        int err;
 
        err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy);
        if (err < 0)
                return err;
 
-       npt = nf_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
-
-       if (!npt->nlattr_to_range) {
-               nf_nat_proto_put(npt);
-               return 0;
-       }
-
-       /* nlattr_to_range returns 1 if it parsed, 0 if not, neg. on error */
-       if (npt->nlattr_to_range(tb, range) > 0)
-               range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
-
+       npt = nf_nat_proto_find_get(nf_ct_protonum(ct));
+       if (npt->nlattr_to_range)
+               err = npt->nlattr_to_range(tb, range);
        nf_nat_proto_put(npt);
-
-       return 0;
+       return err;
 }
 
 static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
@@ -751,12 +731,12 @@ nfnetlink_parse_nat(struct nlattr *nat,
                return err;
 
        if (tb[CTA_NAT_MINIP])
-               range->min_ip = *(__be32 *)nla_data(tb[CTA_NAT_MINIP]);
+               range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]);
 
        if (!tb[CTA_NAT_MAXIP])
                range->max_ip = range->min_ip;
        else
-               range->max_ip = *(__be32 *)nla_data(tb[CTA_NAT_MAXIP]);
+               range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]);
 
        if (range->min_ip)
                range->flags |= IP_NAT_RANGE_MAP_IPS;
@@ -826,15 +806,14 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        ct = nf_ct_tuplehash_to_ctrack(h);
 
        if (cda[CTA_ID]) {
-               u_int32_t id = ntohl(*(__be32 *)nla_data(cda[CTA_ID]));
+               u_int32_t id = ntohl(nla_get_be32(cda[CTA_ID]));
                if (id != (u32)(unsigned long)ct) {
                        nf_ct_put(ct);
                        return -ENOENT;
                }
        }
-       if (del_timer(&ct->timeout))
-               ct->timeout.function((unsigned long)ct);
 
+       nf_ct_kill(ct);
        nf_ct_put(ct);
 
        return 0;
@@ -902,29 +881,28 @@ out:
        return err;
 }
 
-static inline int
+static int
 ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[])
 {
        unsigned long d;
-       unsigned int status = ntohl(*(__be32 *)nla_data(cda[CTA_STATUS]));
+       unsigned int status = ntohl(nla_get_be32(cda[CTA_STATUS]));
        d = ct->status ^ status;
 
        if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING))
                /* unchangeable */
-               return -EINVAL;
+               return -EBUSY;
 
        if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY))
                /* SEEN_REPLY bit can only be set */
-               return -EINVAL;
-
+               return -EBUSY;
 
        if (d & IPS_ASSURED && !(status & IPS_ASSURED))
                /* ASSURED bit can only be set */
-               return -EINVAL;
+               return -EBUSY;
 
        if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
 #ifndef CONFIG_NF_NAT_NEEDED
-               return -EINVAL;
+               return -EOPNOTSUPP;
 #else
                struct nf_nat_range range;
 
@@ -932,19 +910,17 @@ ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[])
                        if (nfnetlink_parse_nat(cda[CTA_NAT_DST], ct,
                                                &range) < 0)
                                return -EINVAL;
-                       if (nf_nat_initialized(ct,
-                                              HOOK2MANIP(NF_INET_PRE_ROUTING)))
+                       if (nf_nat_initialized(ct, IP_NAT_MANIP_DST))
                                return -EEXIST;
-                       nf_nat_setup_info(ct, &range, NF_INET_PRE_ROUTING);
+                       nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
                }
                if (cda[CTA_NAT_SRC]) {
                        if (nfnetlink_parse_nat(cda[CTA_NAT_SRC], ct,
                                                &range) < 0)
                                return -EINVAL;
-                       if (nf_nat_initialized(ct,
-                                              HOOK2MANIP(NF_INET_POST_ROUTING)))
+                       if (nf_nat_initialized(ct, IP_NAT_MANIP_SRC))
                                return -EEXIST;
-                       nf_nat_setup_info(ct, &range, NF_INET_POST_ROUTING);
+                       nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
                }
 #endif
        }
@@ -967,7 +943,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
 
        /* don't change helper of sibling connections */
        if (ct->master)
-               return -EINVAL;
+               return -EBUSY;
 
        err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
        if (err < 0)
@@ -985,7 +961,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
 
        helper = __nf_conntrack_helper_find_byname(helpname);
        if (helper == NULL)
-               return -EINVAL;
+               return -EOPNOTSUPP;
 
        if (help) {
                if (help->helper == helper)
@@ -1008,7 +984,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
 static inline int
 ctnetlink_change_timeout(struct nf_conn *ct, struct nlattr *cda[])
 {
-       u_int32_t timeout = ntohl(*(__be32 *)nla_data(cda[CTA_TIMEOUT]));
+       u_int32_t timeout = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        if (!del_timer(&ct->timeout))
                return -ETIME;
@@ -1024,14 +1000,11 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[])
 {
        struct nlattr *tb[CTA_PROTOINFO_MAX+1], *attr = cda[CTA_PROTOINFO];
        struct nf_conntrack_l4proto *l4proto;
-       u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
-       u_int16_t l3num = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
        int err = 0;
 
        nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, NULL);
 
-       l4proto = nf_ct_l4proto_find_get(l3num, npt);
-
+       l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct));
        if (l4proto->from_nlattr)
                err = l4proto->from_nlattr(tb, ct);
        nf_ct_l4proto_put(l4proto);
@@ -1051,19 +1024,19 @@ change_nat_seq_adj(struct nf_nat_seq *natseq, struct nlattr *attr)
                return -EINVAL;
 
        natseq->correction_pos =
-               ntohl(*(__be32 *)nla_data(cda[CTA_NAT_SEQ_CORRECTION_POS]));
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_CORRECTION_POS]));
 
        if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE])
                return -EINVAL;
 
        natseq->offset_before =
-               ntohl(*(__be32 *)nla_data(cda[CTA_NAT_SEQ_OFFSET_BEFORE]));
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_BEFORE]));
 
        if (!cda[CTA_NAT_SEQ_OFFSET_AFTER])
                return -EINVAL;
 
        natseq->offset_after =
-               ntohl(*(__be32 *)nla_data(cda[CTA_NAT_SEQ_OFFSET_AFTER]));
+               ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_AFTER]));
 
        return 0;
 }
@@ -1130,7 +1103,7 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
 
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
-               ct->mark = ntohl(*(__be32 *)nla_data(cda[CTA_MARK]));
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
 #endif
 
 #ifdef CONFIG_NF_NAT_NEEDED
@@ -1155,13 +1128,13 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
        struct nf_conn_help *help;
        struct nf_conntrack_helper *helper;
 
-       ct = nf_conntrack_alloc(otuple, rtuple);
+       ct = nf_conntrack_alloc(otuple, rtuple, GFP_KERNEL);
        if (ct == NULL || IS_ERR(ct))
                return -ENOMEM;
 
        if (!cda[CTA_TIMEOUT])
                goto err;
-       ct->timeout.expires = ntohl(*(__be32 *)nla_data(cda[CTA_TIMEOUT]));
+       ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
        ct->status |= IPS_CONFIRMED;
@@ -1180,14 +1153,15 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
-               ct->mark = ntohl(*(__be32 *)nla_data(cda[CTA_MARK]));
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
 #endif
 
-       helper = nf_ct_helper_find_get(rtuple);
+       rcu_read_lock();
+       helper = __nf_ct_helper_find(rtuple);
        if (helper) {
                help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
                if (help == NULL) {
-                       nf_ct_helper_put(helper);
+                       rcu_read_unlock();
                        err = -ENOMEM;
                        goto err;
                }
@@ -1203,9 +1177,7 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
 
        add_timer(&ct->timeout);
        nf_conntrack_hash_insert(ct);
-
-       if (helper)
-               nf_ct_helper_put(helper);
+       rcu_read_unlock();
 
        return 0;
 
@@ -1236,11 +1208,11 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        return err;
        }
 
-       write_lock_bh(&nf_conntrack_lock);
+       spin_lock_bh(&nf_conntrack_lock);
        if (cda[CTA_TUPLE_ORIG])
-               h = __nf_conntrack_find(&otuple, NULL);
+               h = __nf_conntrack_find(&otuple);
        else if (cda[CTA_TUPLE_REPLY])
-               h = __nf_conntrack_find(&rtuple, NULL);
+               h = __nf_conntrack_find(&rtuple);
 
        if (h == NULL) {
                struct nf_conntrack_tuple master;
@@ -1253,9 +1225,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                                                    CTA_TUPLE_MASTER,
                                                    u3);
                        if (err < 0)
-                               return err;
+                               goto out_unlock;
 
-                       master_h = __nf_conntrack_find(&master, NULL);
+                       master_h = __nf_conntrack_find(&master);
                        if (master_h == NULL) {
                                err = -ENOENT;
                                goto out_unlock;
@@ -1264,7 +1236,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        atomic_inc(&master_ct->ct_general.use);
                }
 
-               write_unlock_bh(&nf_conntrack_lock);
+               spin_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_CREATE)
                        err = ctnetlink_create_conntrack(cda,
@@ -1284,12 +1256,12 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
                /* we only allow nat config for new conntracks */
                if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
-                       err = -EINVAL;
+                       err = -EOPNOTSUPP;
                        goto out_unlock;
                }
                /* can't link an existing conntrack to a master */
                if (cda[CTA_TUPLE_MASTER]) {
-                       err = -EINVAL;
+                       err = -EOPNOTSUPP;
                        goto out_unlock;
                }
                err = ctnetlink_change_conntrack(nf_ct_tuplehash_to_ctrack(h),
@@ -1297,7 +1269,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        }
 
 out_unlock:
-       write_unlock_bh(&nf_conntrack_lock);
+       spin_unlock_bh(&nf_conntrack_lock);
        return err;
 }
 
@@ -1365,13 +1337,15 @@ nla_put_failure:
        return -1;
 }
 
-static inline int
+static int
 ctnetlink_exp_dump_expect(struct sk_buff *skb,
                          const struct nf_conntrack_expect *exp)
 {
        struct nf_conn *master = exp->master;
-       __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ);
-       __be32 id = htonl((unsigned long)exp);
+       long timeout = (exp->timeout.expires - jiffies) / HZ;
+
+       if (timeout < 0)
+               timeout = 0;
 
        if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
                goto nla_put_failure;
@@ -1382,8 +1356,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
                                 CTA_EXPECT_MASTER) < 0)
                goto nla_put_failure;
 
-       NLA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(timeout), &timeout);
-       NLA_PUT(skb, CTA_EXPECT_ID, sizeof(u_int32_t), &id);
+       NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
+       NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
 
        return 0;
 
@@ -1486,7 +1460,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        struct hlist_node *n;
        u_int8_t l3proto = nfmsg->nfgen_family;
 
-       read_lock_bh(&nf_conntrack_lock);
+       rcu_read_lock();
        last = (struct nf_conntrack_expect *)cb->args[1];
        for (; cb->args[0] < nf_ct_expect_hsize; cb->args[0]++) {
 restart:
@@ -1503,7 +1477,8 @@ restart:
                                                    cb->nlh->nlmsg_seq,
                                                    IPCTNL_MSG_EXP_NEW,
                                                    1, exp) < 0) {
-                               atomic_inc(&exp->use);
+                               if (!atomic_inc_not_zero(&exp->use))
+                                       continue;
                                cb->args[1] = (unsigned long)exp;
                                goto out;
                        }
@@ -1514,7 +1489,7 @@ restart:
                }
        }
 out:
-       read_unlock_bh(&nf_conntrack_lock);
+       rcu_read_unlock();
        if (last)
                nf_ct_expect_put(last);
 
@@ -1556,7 +1531,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
                return -ENOENT;
 
        if (cda[CTA_EXPECT_ID]) {
-               __be32 id = *(__be32 *)nla_data(cda[CTA_EXPECT_ID]);
+               __be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
                if (ntohl(id) != (u32)(unsigned long)exp) {
                        nf_ct_expect_put(exp);
                        return -ENOENT;
@@ -1610,7 +1585,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                        return -ENOENT;
 
                if (cda[CTA_EXPECT_ID]) {
-                       __be32 id = *(__be32 *)nla_data(cda[CTA_EXPECT_ID]);
+                       __be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
                        if (ntohl(id) != (u32)(unsigned long)exp) {
                                nf_ct_expect_put(exp);
                                return -ENOENT;
@@ -1627,11 +1602,11 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                struct nf_conn_help *m_help;
 
                /* delete all expectations for this helper */
-               write_lock_bh(&nf_conntrack_lock);
+               spin_lock_bh(&nf_conntrack_lock);
                h = __nf_conntrack_helper_find_byname(name);
                if (!h) {
-                       write_unlock_bh(&nf_conntrack_lock);
-                       return -EINVAL;
+                       spin_unlock_bh(&nf_conntrack_lock);
+                       return -EOPNOTSUPP;
                }
                for (i = 0; i < nf_ct_expect_hsize; i++) {
                        hlist_for_each_entry_safe(exp, n, next,
@@ -1645,10 +1620,10 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                                }
                        }
                }
-               write_unlock_bh(&nf_conntrack_lock);
+               spin_unlock_bh(&nf_conntrack_lock);
        } else {
                /* This basically means we have to flush everything*/
-               write_lock_bh(&nf_conntrack_lock);
+               spin_lock_bh(&nf_conntrack_lock);
                for (i = 0; i < nf_ct_expect_hsize; i++) {
                        hlist_for_each_entry_safe(exp, n, next,
                                                  &nf_ct_expect_hash[i],
@@ -1659,7 +1634,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
                                }
                        }
                }
-               write_unlock_bh(&nf_conntrack_lock);
+               spin_unlock_bh(&nf_conntrack_lock);
        }
 
        return 0;
@@ -1745,11 +1720,11 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       write_lock_bh(&nf_conntrack_lock);
+       spin_lock_bh(&nf_conntrack_lock);
        exp = __nf_ct_expect_find(&tuple);
 
        if (!exp) {
-               write_unlock_bh(&nf_conntrack_lock);
+               spin_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_CREATE)
                        err = ctnetlink_create_expect(cda, u3);
@@ -1759,7 +1734,7 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
        err = -EEXIST;
        if (!(nlh->nlmsg_flags & NLM_F_EXCL))
                err = ctnetlink_change_expect(exp, cda);
-       write_unlock_bh(&nf_conntrack_lock);
+       spin_unlock_bh(&nf_conntrack_lock);
 
        return err;
 }
This page took 0.074337 seconds and 5 git commands to generate.