]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/netfilter/nf_tables_api.c
Merge tag 'drm-intel-next-fixes-2020-02-13' of git://anongit.freedesktop.org/drm...
[linux.git] / net / netfilter / nf_tables_api.c
index 7e63b481cc86be8d95e92eae750c091036238f87..d1318bdf49ca97bcdc0b4dd9ff209fdb6cb183f2 100644 (file)
@@ -3391,6 +3391,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
        [NFTA_SET_DESC_SIZE]            = { .type = NLA_U32 },
+       [NFTA_SET_DESC_CONCAT]          = { .type = NLA_NESTED },
 };
 
 static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
@@ -3557,6 +3558,33 @@ static __be64 nf_jiffies64_to_msecs(u64 input)
        return cpu_to_be64(jiffies64_to_msecs(input));
 }
 
+static int nf_tables_fill_set_concat(struct sk_buff *skb,
+                                    const struct nft_set *set)
+{
+       struct nlattr *concat, *field;
+       int i;
+
+       concat = nla_nest_start_noflag(skb, NFTA_SET_DESC_CONCAT);
+       if (!concat)
+               return -ENOMEM;
+
+       for (i = 0; i < set->field_count; i++) {
+               field = nla_nest_start_noflag(skb, NFTA_LIST_ELEM);
+               if (!field)
+                       return -ENOMEM;
+
+               if (nla_put_be32(skb, NFTA_SET_FIELD_LEN,
+                                htonl(set->field_len[i])))
+                       return -ENOMEM;
+
+               nla_nest_end(skb, field);
+       }
+
+       nla_nest_end(skb, concat);
+
+       return 0;
+}
+
 static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                              const struct nft_set *set, u16 event, u16 flags)
 {
@@ -3620,11 +3648,17 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                goto nla_put_failure;
 
        desc = nla_nest_start_noflag(skb, NFTA_SET_DESC);
+
        if (desc == NULL)
                goto nla_put_failure;
        if (set->size &&
            nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
                goto nla_put_failure;
+
+       if (set->field_count > 1 &&
+           nf_tables_fill_set_concat(skb, set))
+               goto nla_put_failure;
+
        nla_nest_end(skb, desc);
 
        nlmsg_end(skb, nlh);
@@ -3797,6 +3831,53 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk,
        return err;
 }
 
+static const struct nla_policy nft_concat_policy[NFTA_SET_FIELD_MAX + 1] = {
+       [NFTA_SET_FIELD_LEN]    = { .type = NLA_U32 },
+};
+
+static int nft_set_desc_concat_parse(const struct nlattr *attr,
+                                    struct nft_set_desc *desc)
+{
+       struct nlattr *tb[NFTA_SET_FIELD_MAX + 1];
+       u32 len;
+       int err;
+
+       err = nla_parse_nested_deprecated(tb, NFTA_SET_FIELD_MAX, attr,
+                                         nft_concat_policy, NULL);
+       if (err < 0)
+               return err;
+
+       if (!tb[NFTA_SET_FIELD_LEN])
+               return -EINVAL;
+
+       len = ntohl(nla_get_be32(tb[NFTA_SET_FIELD_LEN]));
+
+       if (len * BITS_PER_BYTE / 32 > NFT_REG32_COUNT)
+               return -E2BIG;
+
+       desc->field_len[desc->field_count++] = len;
+
+       return 0;
+}
+
+static int nft_set_desc_concat(struct nft_set_desc *desc,
+                              const struct nlattr *nla)
+{
+       struct nlattr *attr;
+       int rem, err;
+
+       nla_for_each_nested(attr, nla, rem) {
+               if (nla_type(attr) != NFTA_LIST_ELEM)
+                       return -EINVAL;
+
+               err = nft_set_desc_concat_parse(attr, desc);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
                                    const struct nlattr *nla)
 {
@@ -3810,8 +3891,10 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
 
        if (da[NFTA_SET_DESC_SIZE] != NULL)
                desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
+       if (da[NFTA_SET_DESC_CONCAT])
+               err = nft_set_desc_concat(desc, da[NFTA_SET_DESC_CONCAT]);
 
-       return 0;
+       return err;
 }
 
 static int nf_tables_newset(struct net *net, struct sock *nlsk,
@@ -3834,6 +3917,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        unsigned char *udata;
        u16 udlen;
        int err;
+       int i;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
@@ -4012,6 +4096,10 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
        set->gc_int = gc_int;
        set->handle = nf_tables_alloc_handle(table);
 
+       set->field_count = desc.field_count;
+       for (i = 0; i < desc.field_count; i++)
+               set->field_len[i] = desc.field_len[i];
+
        err = ops->init(set, &desc, nla);
        if (err < 0)
                goto err3;
@@ -4215,6 +4303,9 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
                .len    = sizeof(struct nft_userdata),
                .align  = __alignof__(struct nft_userdata),
        },
+       [NFT_SET_EXT_KEY_END]           = {
+               .align  = __alignof__(u32),
+       },
 };
 EXPORT_SYMBOL_GPL(nft_set_ext_types);
 
@@ -4233,6 +4324,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_EXPR]            = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_OBJREF]          = { .type = NLA_STRING,
                                            .len = NFT_OBJ_MAXNAMELEN - 1 },
+       [NFTA_SET_ELEM_KEY_END]         = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -4282,6 +4374,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                          NFT_DATA_VALUE, set->klen) < 0)
                goto nla_put_failure;
 
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
+           nft_data_dump(skb, NFTA_SET_ELEM_KEY_END, nft_set_ext_key_end(ext),
+                         NFT_DATA_VALUE, set->klen) < 0)
+               goto nla_put_failure;
+
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
            nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext),
                          set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
@@ -4524,11 +4621,28 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
        return 0;
 }
 
+static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
+                                struct nft_data *key, struct nlattr *attr)
+{
+       struct nft_data_desc desc;
+       int err;
+
+       err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr);
+       if (err < 0)
+               return err;
+
+       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
+               nft_data_release(key, desc.type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
-       struct nft_data_desc desc;
        struct nft_set_elem elem;
        struct sk_buff *skb;
        uint32_t flags = 0;
@@ -4547,15 +4661,16 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                return err;
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
                return err;
 
-       err = -EINVAL;
-       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
-               nft_data_release(&elem.key.val, desc.type);
-               return err;
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       return err;
        }
 
        priv = set->ops->get(ctx->net, set, &elem, flags);
@@ -4683,8 +4798,8 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
 
 void *nft_set_elem_init(const struct nft_set *set,
                        const struct nft_set_ext_tmpl *tmpl,
-                       const u32 *key, const u32 *data,
-                       u64 timeout, u64 expiration, gfp_t gfp)
+                       const u32 *key, const u32 *key_end,
+                       const u32 *data, u64 timeout, u64 expiration, gfp_t gfp)
 {
        struct nft_set_ext *ext;
        void *elem;
@@ -4697,6 +4812,8 @@ void *nft_set_elem_init(const struct nft_set *set,
        nft_set_ext_init(ext, tmpl);
 
        memcpy(nft_set_ext_key(ext), key, set->klen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
+               memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                memcpy(nft_set_ext_data(ext), data, set->dlen);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
@@ -4756,13 +4873,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        u8 genmask = nft_genmask_next(ctx->net);
-       struct nft_data_desc d1, d2;
        struct nft_set_ext_tmpl tmpl;
        struct nft_set_ext *ext, *ext2;
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        struct nft_object *obj = NULL;
        struct nft_userdata *udata;
+       struct nft_data_desc desc;
        struct nft_data data;
        enum nft_registers dreg;
        struct nft_trans *trans;
@@ -4828,15 +4945,22 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return err;
        }
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &d1,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
-               goto err1;
-       err = -EINVAL;
-       if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
-               goto err2;
+               return err;
+
+       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       goto err_parse_key;
+
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+       }
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, d1.len);
        if (timeout > 0) {
                nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
                if (timeout != set->timeout)
@@ -4846,27 +4970,27 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
                if (!(set->flags & NFT_SET_OBJECT)) {
                        err = -EINVAL;
-                       goto err2;
+                       goto err_parse_key_end;
                }
                obj = nft_obj_lookup(ctx->net, ctx->table,
                                     nla[NFTA_SET_ELEM_OBJREF],
                                     set->objtype, genmask);
                if (IS_ERR(obj)) {
                        err = PTR_ERR(obj);
-                       goto err2;
+                       goto err_parse_key_end;
                }
                nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
        }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
-               err = nft_data_init(ctx, &data, sizeof(data), &d2,
+               err = nft_data_init(ctx, &data, sizeof(data), &desc,
                                    nla[NFTA_SET_ELEM_DATA]);
                if (err < 0)
-                       goto err2;
+                       goto err_parse_key_end;
 
                err = -EINVAL;
-               if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
-                       goto err3;
+               if (set->dtype != NFT_DATA_VERDICT && desc.len != set->dlen)
+                       goto err_parse_data;
 
                dreg = nft_type_to_reg(set->dtype);
                list_for_each_entry(binding, &set->bindings, list) {
@@ -4882,18 +5006,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 
                        err = nft_validate_register_store(&bind_ctx, dreg,
                                                          &data,
-                                                         d2.type, d2.len);
+                                                         desc.type, desc.len);
                        if (err < 0)
-                               goto err3;
+                               goto err_parse_data;
 
-                       if (d2.type == NFT_DATA_VERDICT &&
+                       if (desc.type == NFT_DATA_VERDICT &&
                            (data.verdict.code == NFT_GOTO ||
                             data.verdict.code == NFT_JUMP))
                                nft_validate_state_update(ctx->net,
                                                          NFT_VALIDATE_NEED);
                }
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len);
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, desc.len);
        }
 
        /* The full maximum length of userdata can exceed the maximum
@@ -4909,10 +5033,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        }
 
        err = -ENOMEM;
-       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, data.data,
+       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
+                                     elem.key_end.val.data, data.data,
                                      timeout, expiration, GFP_KERNEL);
        if (elem.priv == NULL)
-               goto err3;
+               goto err_parse_data;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
@@ -4929,7 +5054,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
        if (trans == NULL)
-               goto err4;
+               goto err_trans;
 
        ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
        err = set->ops->insert(ctx->net, set, &elem, &ext2);
@@ -4940,7 +5065,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
                            nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
                                err = -EBUSY;
-                               goto err5;
+                               goto err_element_clash;
                        }
                        if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
                             nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
@@ -4953,33 +5078,35 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        else if (!(nlmsg_flags & NLM_F_EXCL))
                                err = 0;
                }
-               goto err5;
+               goto err_element_clash;
        }
 
        if (set->size &&
            !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
                err = -ENFILE;
-               goto err6;
+               goto err_set_full;
        }
 
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
-err6:
+err_set_full:
        set->ops->remove(ctx->net, set, &elem);
-err5:
+err_element_clash:
        kfree(trans);
-err4:
+err_trans:
        if (obj)
                obj->use--;
        kfree(elem.priv);
-err3:
+err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
-               nft_data_release(&data, d2.type);
-err2:
-       nft_data_release(&elem.key.val, d1.type);
-err1:
+               nft_data_release(&data, desc.type);
+err_parse_key_end:
+       nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
+err_parse_key:
+       nft_data_release(&elem.key.val, NFT_DATA_VALUE);
+
        return err;
 }
 
@@ -5074,7 +5201,6 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_set_ext_tmpl tmpl;
-       struct nft_data_desc desc;
        struct nft_set_elem elem;
        struct nft_set_ext *ext;
        struct nft_trans *trans;
@@ -5085,11 +5211,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
                                          nft_set_elem_policy, NULL);
        if (err < 0)
-               goto err1;
+               return err;
 
-       err = -EINVAL;
        if (nla[NFTA_SET_ELEM_KEY] == NULL)
-               goto err1;
+               return -EINVAL;
 
        nft_set_ext_prepare(&tmpl);
 
@@ -5099,37 +5224,41 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (flags != 0)
                nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
 
-       err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc,
-                           nla[NFTA_SET_ELEM_KEY]);
+       err = nft_setelem_parse_key(ctx, set, &elem.key.val,
+                                   nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
-               goto err1;
+               return err;
 
-       err = -EINVAL;
-       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
-               goto err2;
+       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
 
-       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, desc.len);
+       if (nla[NFTA_SET_ELEM_KEY_END]) {
+               err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
+                                           nla[NFTA_SET_ELEM_KEY_END]);
+               if (err < 0)
+                       return err;
+
+               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+       }
 
        err = -ENOMEM;
-       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, NULL, 0,
-                                     0, GFP_KERNEL);
+       elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
+                                     elem.key_end.val.data, NULL, 0, 0,
+                                     GFP_KERNEL);
        if (elem.priv == NULL)
-               goto err2;
+               goto fail_elem;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
                *nft_set_ext_flags(ext) = flags;
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
-       if (trans == NULL) {
-               err = -ENOMEM;
-               goto err3;
-       }
+       if (trans == NULL)
+               goto fail_trans;
 
        priv = set->ops->deactivate(ctx->net, set, &elem);
        if (priv == NULL) {
                err = -ENOENT;
-               goto err4;
+               goto fail_ops;
        }
        kfree(elem.priv);
        elem.priv = priv;
@@ -5140,13 +5269,12 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
-err4:
+fail_ops:
        kfree(trans);
-err3:
+fail_trans:
        kfree(elem.priv);
-err2:
-       nft_data_release(&elem.key.val, desc.type);
-err1:
+fail_elem:
+       nft_data_release(&elem.key.val, NFT_DATA_VALUE);
        return err;
 }