IPv6: fix race between cleanup and add/delete address
[linux-2.6.git] / net / netfilter / nf_conntrack_netlink.c
index 327c517..2b2af63 100644 (file)
@@ -39,6 +39,7 @@
 #include <net/netfilter/nf_conntrack_l4proto.h>
 #include <net/netfilter/nf_conntrack_tuple.h>
 #include <net/netfilter/nf_conntrack_acct.h>
+#include <net/netfilter/nf_conntrack_zones.h>
 #ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_protocol.h>
@@ -379,6 +380,9 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
                goto nla_put_failure;
        nla_nest_end(skb, nest_parms);
 
+       if (nf_ct_zone(ct))
+               NLA_PUT_BE16(skb, CTA_ZONE, htons(nf_ct_zone(ct)));
+
        if (ctnetlink_dump_status(skb, ct) < 0 ||
            ctnetlink_dump_timeout(skb, ct) < 0 ||
            ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
@@ -517,6 +521,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
                goto nla_put_failure;
        nla_nest_end(skb, nest_parms);
 
+       if (nf_ct_zone(ct))
+               NLA_PUT_BE16(skb, CTA_ZONE, htons(nf_ct_zone(ct)));
+
        if (ctnetlink_dump_id(skb, ct) < 0)
                goto nla_put_failure;
 
@@ -708,6 +715,11 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
        return ret;
 }
 
+static const struct nla_policy tuple_nla_policy[CTA_TUPLE_MAX+1] = {
+       [CTA_TUPLE_IP]          = { .type = NLA_NESTED },
+       [CTA_TUPLE_PROTO]       = { .type = NLA_NESTED },
+};
+
 static int
 ctnetlink_parse_tuple(const struct nlattr * const cda[],
                      struct nf_conntrack_tuple *tuple,
@@ -718,7 +730,7 @@ ctnetlink_parse_tuple(const struct nlattr * const cda[],
 
        memset(tuple, 0, sizeof(*tuple));
 
-       nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], NULL);
+       nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], tuple_nla_policy);
 
        if (!tb[CTA_TUPLE_IP])
                return -EINVAL;
@@ -745,12 +757,31 @@ ctnetlink_parse_tuple(const struct nlattr * const cda[],
        return 0;
 }
 
+static int
+ctnetlink_parse_zone(const struct nlattr *attr, u16 *zone)
+{
+       if (attr)
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+               *zone = ntohs(nla_get_be16(attr));
+#else
+               return -EOPNOTSUPP;
+#endif
+       else
+               *zone = 0;
+
+       return 0;
+}
+
+static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = {
+       [CTA_HELP_NAME]         = { .type = NLA_NUL_STRING },
+};
+
 static inline int
 ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
 {
        struct nlattr *tb[CTA_HELP_MAX+1];
 
-       nla_parse_nested(tb, CTA_HELP_MAX, attr, NULL);
+       nla_parse_nested(tb, CTA_HELP_MAX, attr, help_nla_policy);
 
        if (!tb[CTA_HELP_NAME])
                return -EINVAL;
@@ -761,11 +792,18 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
 }
 
 static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
+       [CTA_TUPLE_ORIG]        = { .type = NLA_NESTED },
+       [CTA_TUPLE_REPLY]       = { .type = NLA_NESTED },
        [CTA_STATUS]            = { .type = NLA_U32 },
+       [CTA_PROTOINFO]         = { .type = NLA_NESTED },
+       [CTA_HELP]              = { .type = NLA_NESTED },
+       [CTA_NAT_SRC]           = { .type = NLA_NESTED },
        [CTA_TIMEOUT]           = { .type = NLA_U32 },
        [CTA_MARK]              = { .type = NLA_U32 },
-       [CTA_USE]               = { .type = NLA_U32 },
        [CTA_ID]                = { .type = NLA_U32 },
+       [CTA_NAT_DST]           = { .type = NLA_NESTED },
+       [CTA_TUPLE_MASTER]      = { .type = NLA_NESTED },
+       [CTA_ZONE]              = { .type = NLA_U16 },
 };
 
 static int
@@ -779,7 +817,12 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        struct nf_conn *ct;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u_int8_t u3 = nfmsg->nfgen_family;
-       int err = 0;
+       u16 zone;
+       int err;
+
+       err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone);
+       if (err < 0)
+               return err;
 
        if (cda[CTA_TUPLE_ORIG])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
@@ -796,7 +839,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       h = nf_conntrack_find_get(net, &tuple);
+       h = nf_conntrack_find_get(net, zone, &tuple);
        if (!h)
                return -ENOENT;
 
@@ -841,12 +884,17 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
        struct sk_buff *skb2 = NULL;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u_int8_t u3 = nfmsg->nfgen_family;
-       int err = 0;
+       u16 zone;
+       int err;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP)
                return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table,
                                          ctnetlink_done);
 
+       err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone);
+       if (err < 0)
+               return err;
+
        if (cda[CTA_TUPLE_ORIG])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3);
        else if (cda[CTA_TUPLE_REPLY])
@@ -857,7 +905,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       h = nf_conntrack_find_get(net, &tuple);
+       h = nf_conntrack_find_get(net, zone, &tuple);
        if (!h)
                return -ENOENT;
 
@@ -1029,9 +1077,8 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
                /* need to zero data of old helper */
                memset(&help->help, 0, sizeof(help->help));
        } else {
-               help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
-               if (help == NULL)
-                       return -ENOMEM;
+               /* we cannot set a helper for an existing conntrack */
+               return -EOPNOTSUPP;
        }
 
        rcu_assign_pointer(help->helper, helper);
@@ -1053,6 +1100,12 @@ ctnetlink_change_timeout(struct nf_conn *ct, const struct nlattr * const cda[])
        return 0;
 }
 
+static const struct nla_policy protoinfo_policy[CTA_PROTOINFO_MAX+1] = {
+       [CTA_PROTOINFO_TCP]     = { .type = NLA_NESTED },
+       [CTA_PROTOINFO_DCCP]    = { .type = NLA_NESTED },
+       [CTA_PROTOINFO_SCTP]    = { .type = NLA_NESTED },
+};
+
 static inline int
 ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[])
 {
@@ -1061,7 +1114,7 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]
        struct nf_conntrack_l4proto *l4proto;
        int err = 0;
 
-       nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, NULL);
+       nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy);
 
        rcu_read_lock();
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
@@ -1073,12 +1126,18 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]
 }
 
 #ifdef CONFIG_NF_NAT_NEEDED
+static const struct nla_policy nat_seq_policy[CTA_NAT_SEQ_MAX+1] = {
+       [CTA_NAT_SEQ_CORRECTION_POS]    = { .type = NLA_U32 },
+       [CTA_NAT_SEQ_OFFSET_BEFORE]     = { .type = NLA_U32 },
+       [CTA_NAT_SEQ_OFFSET_AFTER]      = { .type = NLA_U32 },
+};
+
 static inline int
 change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr)
 {
        struct nlattr *cda[CTA_NAT_SEQ_MAX+1];
 
-       nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, NULL);
+       nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy);
 
        if (!cda[CTA_NAT_SEQ_CORRECTION_POS])
                return -EINVAL;
@@ -1184,7 +1243,7 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
 }
 
 static struct nf_conn *
-ctnetlink_create_conntrack(struct net *net,
+ctnetlink_create_conntrack(struct net *net, u16 zone,
                           const struct nlattr * const cda[],
                           struct nf_conntrack_tuple *otuple,
                           struct nf_conntrack_tuple *rtuple,
@@ -1194,7 +1253,7 @@ ctnetlink_create_conntrack(struct net *net,
        int err = -EINVAL;
        struct nf_conntrack_helper *helper;
 
-       ct = nf_conntrack_alloc(net, otuple, rtuple, GFP_ATOMIC);
+       ct = nf_conntrack_alloc(net, zone, otuple, rtuple, GFP_ATOMIC);
        if (IS_ERR(ct))
                return ERR_PTR(-ENOMEM);
 
@@ -1203,7 +1262,6 @@ ctnetlink_create_conntrack(struct net *net,
        ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
 
        ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
-       ct->status |= IPS_CONFIRMED;
 
        rcu_read_lock();
        if (cda[CTA_HELP]) {
@@ -1254,14 +1312,19 @@ ctnetlink_create_conntrack(struct net *net,
                        goto err2;
        }
 
-       if (cda[CTA_STATUS]) {
-               err = ctnetlink_change_status(ct, cda);
+       if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
+               err = ctnetlink_change_nat(ct, cda);
                if (err < 0)
                        goto err2;
        }
 
-       if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
-               err = ctnetlink_change_nat(ct, cda);
+       nf_ct_acct_ext_add(ct, GFP_ATOMIC);
+       nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
+       /* we must add conntrack extensions before confirmation. */
+       ct->status |= IPS_CONFIRMED;
+
+       if (cda[CTA_STATUS]) {
+               err = ctnetlink_change_status(ct, cda);
                if (err < 0)
                        goto err2;
        }
@@ -1280,9 +1343,6 @@ ctnetlink_create_conntrack(struct net *net,
                        goto err2;
        }
 
-       nf_ct_acct_ext_add(ct, GFP_ATOMIC);
-       nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
-
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
                ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
@@ -1298,7 +1358,7 @@ ctnetlink_create_conntrack(struct net *net,
                if (err < 0)
                        goto err2;
 
-               master_h = nf_conntrack_find_get(net, &master);
+               master_h = nf_conntrack_find_get(net, zone, &master);
                if (master_h == NULL) {
                        err = -ENOENT;
                        goto err2;
@@ -1331,7 +1391,12 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        struct nf_conntrack_tuple_hash *h = NULL;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u_int8_t u3 = nfmsg->nfgen_family;
-       int err = 0;
+       u16 zone;
+       int err;
+
+       err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone);
+       if (err < 0)
+               return err;
 
        if (cda[CTA_TUPLE_ORIG]) {
                err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG, u3);
@@ -1347,9 +1412,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
 
        spin_lock_bh(&nf_conntrack_lock);
        if (cda[CTA_TUPLE_ORIG])
-               h = __nf_conntrack_find(net, &otuple);
+               h = __nf_conntrack_find(net, zone, &otuple);
        else if (cda[CTA_TUPLE_REPLY])
-               h = __nf_conntrack_find(net, &rtuple);
+               h = __nf_conntrack_find(net, zone, &rtuple);
 
        if (h == NULL) {
                err = -ENOENT;
@@ -1357,7 +1422,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        struct nf_conn *ct;
                        enum ip_conntrack_events events;
 
-                       ct = ctnetlink_create_conntrack(net, cda, &otuple,
+                       ct = ctnetlink_create_conntrack(net, zone, cda, &otuple,
                                                        &rtuple, u3);
                        if (IS_ERR(ct)) {
                                err = PTR_ERR(ct);
@@ -1485,6 +1550,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
                          const struct nf_conntrack_expect *exp)
 {
        struct nf_conn *master = exp->master;
+       struct nf_conntrack_helper *helper;
        long timeout = (exp->timeout.expires - jiffies) / HZ;
 
        if (timeout < 0)
@@ -1501,6 +1567,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 
        NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
        NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
+       helper = rcu_dereference(nfct_help(master)->helper);
+       if (helper)
+               NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
 
        return 0;
 
@@ -1648,8 +1717,12 @@ out:
 }
 
 static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
+       [CTA_EXPECT_MASTER]     = { .type = NLA_NESTED },
+       [CTA_EXPECT_TUPLE]      = { .type = NLA_NESTED },
+       [CTA_EXPECT_MASK]       = { .type = NLA_NESTED },
        [CTA_EXPECT_TIMEOUT]    = { .type = NLA_U32 },
        [CTA_EXPECT_ID]         = { .type = NLA_U32 },
+       [CTA_EXPECT_HELP_NAME]  = { .type = NLA_NUL_STRING },
 };
 
 static int
@@ -1663,7 +1736,8 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        struct sk_buff *skb2;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u_int8_t u3 = nfmsg->nfgen_family;
-       int err = 0;
+       u16 zone;
+       int err;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                return netlink_dump_start(ctnl, skb, nlh,
@@ -1671,6 +1745,10 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
                                          ctnetlink_exp_done);
        }
 
+       err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
+       if (err < 0)
+               return err;
+
        if (cda[CTA_EXPECT_MASTER])
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER, u3);
        else
@@ -1679,7 +1757,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        if (err < 0)
                return err;
 
-       exp = nf_ct_expect_find_get(net, &tuple);
+       exp = nf_ct_expect_find_get(net, zone, &tuple);
        if (!exp)
                return -ENOENT;
 
@@ -1726,16 +1804,21 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
        struct hlist_node *n, *next;
        u_int8_t u3 = nfmsg->nfgen_family;
        unsigned int i;
+       u16 zone;
        int err;
 
        if (cda[CTA_EXPECT_TUPLE]) {
                /* delete a single expect by tuple */
+               err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
+               if (err < 0)
+                       return err;
+
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
                if (err < 0)
                        return err;
 
                /* bump usage count to 2 */
-               exp = nf_ct_expect_find_get(net, &tuple);
+               exp = nf_ct_expect_find_get(net, zone, &tuple);
                if (!exp)
                        return -ENOENT;
 
@@ -1797,7 +1880,8 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
 }
 
 static int
-ctnetlink_create_expect(struct net *net, const struct nlattr * const cda[],
+ctnetlink_create_expect(struct net *net, u16 zone,
+                       const struct nlattr * const cda[],
                        u_int8_t u3,
                        u32 pid, int report)
 {
@@ -1820,7 +1904,7 @@ ctnetlink_create_expect(struct net *net, const struct nlattr * const cda[],
                return err;
 
        /* Look for master conntrack of this expectation */
-       h = nf_conntrack_find_get(net, &master_tuple);
+       h = nf_conntrack_find_get(net, zone, &master_tuple);
        if (!h)
                return -ENOENT;
        ct = nf_ct_tuplehash_to_ctrack(h);
@@ -1865,25 +1949,30 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
        struct nf_conntrack_expect *exp;
        struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        u_int8_t u3 = nfmsg->nfgen_family;
-       int err = 0;
+       u16 zone;
+       int err;
 
        if (!cda[CTA_EXPECT_TUPLE]
            || !cda[CTA_EXPECT_MASK]
            || !cda[CTA_EXPECT_MASTER])
                return -EINVAL;
 
+       err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
+       if (err < 0)
+               return err;
+
        err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE, u3);
        if (err < 0)
                return err;
 
        spin_lock_bh(&nf_conntrack_lock);
-       exp = __nf_ct_expect_find(net, &tuple);
+       exp = __nf_ct_expect_find(net, zone, &tuple);
 
        if (!exp) {
                spin_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_CREATE) {
-                       err = ctnetlink_create_expect(net, cda,
+                       err = ctnetlink_create_expect(net, zone, cda,
                                                      u3,
                                                      NETLINK_CB(skb).pid,
                                                      nlmsg_report(nlh));