{"id":815504,"url":"http://patchwork.ozlabs.org/api/patches/815504/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170919124954.26852-3-fw@strlen.de/","project":{"id":26,"url":"http://patchwork.ozlabs.org/api/projects/26/?format=json","name":"Netfilter Development","link_name":"netfilter-devel","list_id":"netfilter-devel.vger.kernel.org","list_email":"netfilter-devel@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170919124954.26852-3-fw@strlen.de>","list_archive_url":null,"date":"2017-09-19T12:49:54","name":"[nft,2/2] src: store expression as set key instead of data type","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"22128f71f5e3fb4b0531892344123c50d2595ae0","submitter":{"id":1025,"url":"http://patchwork.ozlabs.org/api/people/1025/?format=json","name":"Florian Westphal","email":"fw@strlen.de"},"delegate":{"id":11902,"url":"http://patchwork.ozlabs.org/api/users/11902/?format=json","username":"strlen","first_name":"Florian","last_name":"Westphal","email":"fw@strlen.de"},"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170919124954.26852-3-fw@strlen.de/mbox/","series":[{"id":3866,"url":"http://patchwork.ozlabs.org/api/series/3866/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=3866","date":"2017-09-19T12:49:52","name":"store expression instead of data type as set key","version":1,"mbox":"http://patchwork.ozlabs.org/series/3866/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/815504/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/815504/checks/","tags":{},"related":[],"headers":{"Return-Path":"<netfilter-devel-owner@vger.kernel.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netfilter-devel-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xxN4S5ljXz9s7m\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 19 Sep 2017 22:49:44 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751898AbdISMto (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tTue, 19 Sep 2017 08:49:44 -0400","from Chamillionaire.breakpoint.cc ([146.0.238.67]:54698 \"EHLO\n\tChamillionaire.breakpoint.cc\" rhost-flags-OK-OK-OK-OK)\n\tby vger.kernel.org with ESMTP id S1751687AbdISMtn (ORCPT\n\t<rfc822;netfilter-devel@vger.kernel.org>);\n\tTue, 19 Sep 2017 08:49:43 -0400","from fw by Chamillionaire.breakpoint.cc with local (Exim 4.84_2)\n\t(envelope-from <fw@breakpoint.cc>)\n\tid 1duHv0-0003Ds-M8; Tue, 19 Sep 2017 14:46:26 +0200"],"From":"Florian Westphal <fw@strlen.de>","To":"<netfilter-devel@vger.kernel.org>","Cc":"Florian Westphal <fw@strlen.de>","Subject":"[PATCH nft 2/2] src: store expression as set key instead of data\n\ttype","Date":"Tue, 19 Sep 2017 14:49:54 +0200","Message-Id":"<20170919124954.26852-3-fw@strlen.de>","X-Mailer":"git-send-email 2.13.5","In-Reply-To":"<20170919124954.26852-1-fw@strlen.de>","References":"<20170919124954.26852-1-fw@strlen.de>","Sender":"netfilter-devel-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netfilter-devel.vger.kernel.org>","X-Mailing-List":"netfilter-devel@vger.kernel.org"},"content":"Doing so retains legth information in case of unqualified data types,\ne.g. we now have 'meta iifname' expression instead of an (unqualified)\nstring type.\n\nThis allows to eventually use iifnames as set keys without adding yet\nanother special data type for them.\n\nSigned-off-by: Florian Westphal <fw@strlen.de>\n---\n include/rule.h            |  6 ++---\n src/evaluate.c            | 69 ++++++++++++++++++++++++++++-------------------\n src/expression.c          |  4 +--\n src/netlink.c             | 27 ++++++++++---------\n src/netlink_delinearize.c | 12 ++++-----\n src/parser_bison.y        | 65 ++++++++++++++++++++++----------------------\n src/rule.c                |  4 +--\n src/segtree.c             |  4 +--\n 8 files changed, 104 insertions(+), 87 deletions(-)","diff":"diff --git a/include/rule.h b/include/rule.h\nindex 631a1bcdf84e..48b3d731b8a1 100644\n--- a/include/rule.h\n+++ b/include/rule.h\n@@ -212,8 +212,7 @@ extern struct rule *rule_lookup(const struct chain *chain, uint64_t handle);\n  * @flags:\tbitmask of set flags\n  * @gc_int:\tgarbage collection interval\n  * @timeout:\tdefault timeout value\n- * @keytype:\tkey data type\n- * @keylen:\tkey length\n+ * @key:\tkey expression (data type, length))\n  * @datatype:\tmapping data type\n  * @datalen:\tmapping data len\n  * @objtype:\tmapping object type\n@@ -230,8 +229,7 @@ struct set {\n \tuint32_t\t\tflags;\n \tuint32_t\t\tgc_int;\n \tuint64_t\t\ttimeout;\n-\tconst struct datatype\t*keytype;\n-\tunsigned int\t\tkeylen;\n+\tstruct expr\t\t*key;\n \tconst struct datatype\t*datatype;\n \tunsigned int\t\tdatalen;\n \tuint32_t\t\tobjtype;\ndiff --git a/src/evaluate.c b/src/evaluate.c\nindex 86159cf55f94..dd7bf5076e7c 100644\n--- a/src/evaluate.c\n+++ b/src/evaluate.c\n@@ -60,6 +60,18 @@ static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,\n \treturn -1;\n }\n \n+static void key_fix_dtype_byteorder(struct expr *key)\n+{\n+\tconst struct datatype *dtype = key->dtype;\n+\n+\tif (dtype->byteorder == key->byteorder)\n+\t\treturn;\n+\n+\tkey->dtype = set_datatype_alloc(dtype, key->byteorder);\n+\tif (dtype->flags & DTYPE_F_ALLOC)\n+\t\tconcat_type_destroy(dtype);\n+}\n+\n static struct expr *implicit_set_declaration(struct eval_ctx *ctx,\n \t\t\t\t\t     const char *name,\n \t\t\t\t\t     struct expr *key,\n@@ -69,11 +81,12 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,\n \tstruct set *set;\n \tstruct handle h;\n \n+\tkey_fix_dtype_byteorder(key);\n+\n \tset = set_alloc(&expr->location);\n \tset->flags\t= NFT_SET_ANONYMOUS | expr->set_flags;\n-\tset->handle.set = xstrdup(name),\n-\tset->keytype \t= set_datatype_alloc(key->dtype, key->byteorder);\n-\tset->keylen\t= key->len;\n+\tset->handle.set = xstrdup(name);\n+\tset->key\t= key;\n \tset->init\t= expr;\n \n \tif (ctx->table != NULL)\n@@ -1023,7 +1036,8 @@ static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)\n \treturn err;\n }\n \n-static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)\n+static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,\n+\t\t\t\tbool evaluate)\n {\n \tconst struct datatype *dtype = ctx->ectx.dtype, *tmp;\n \tuint32_t type = dtype ? dtype->type : 0, ntype = 0;\n@@ -1044,7 +1058,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)\n \t\t\ttmp = concat_subtype_lookup(type, --off);\n \t\texpr_set_context(&ctx->ectx, tmp, tmp->size);\n \n-\t\tif (list_member_evaluate(ctx, &i) < 0)\n+\t\tif (evaluate && list_member_evaluate(ctx, &i) < 0)\n \t\t\treturn -1;\n \t\tflags &= i->flags;\n \n@@ -1204,8 +1218,6 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)\n \t\tmappings = implicit_set_declaration(ctx, \"__map%d\",\n \t\t\t\t\t\t    key,\n \t\t\t\t\t\t    mappings);\n-\t\texpr_free(key);\n-\n \t\tmappings->set->datatype = set_datatype_alloc(ectx.dtype,\n \t\t\t\t\t\t\t     ectx.byteorder);\n \t\tmappings->set->datalen  = ectx.len;\n@@ -1232,11 +1244,11 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)\n \t\t    map->mappings->ops->name);\n \t}\n \n-\tif (!datatype_equal(map->map->dtype, map->mappings->set->keytype))\n+\tif (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))\n \t\treturn expr_binary_error(ctx->msgs, map->mappings, map->map,\n \t\t\t\t\t \"datatype mismatch, map expects %s, \"\n \t\t\t\t\t \"mapping expression has type %s\",\n-\t\t\t\t\t map->mappings->set->keytype->desc,\n+\t\t\t\t\t map->mappings->set->key->dtype->desc,\n \t\t\t\t\t map->map->dtype->desc);\n \n \tmap->dtype = map->mappings->set->datatype;\n@@ -1261,7 +1273,7 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)\n \tif (!(set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)))\n \t\treturn set_error(ctx, set, \"set is not a map\");\n \n-\texpr_set_context(&ctx->ectx, set->keytype, set->keylen);\n+\texpr_set_context(&ctx->ectx, set->key->dtype, set->key->len);\n \tif (expr_evaluate(ctx, &mapping->left) < 0)\n \t\treturn -1;\n \tif (!expr_is_constant(mapping->left))\n@@ -1747,7 +1759,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)\n \tcase EXPR_BINOP:\n \t\treturn expr_evaluate_binop(ctx, expr);\n \tcase EXPR_CONCAT:\n-\t\treturn expr_evaluate_concat(ctx, expr);\n+\t\treturn expr_evaluate_concat(ctx, expr, true);\n \tcase EXPR_LIST:\n \t\treturn expr_evaluate_list(ctx, expr);\n \tcase EXPR_SET:\n@@ -2589,9 +2601,9 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)\n \t\t\t\t  \"Expression does not refer to a set\");\n \n \tif (stmt_evaluate_arg(ctx, stmt,\n-\t\t\t      stmt->set.set->set->keytype,\n-\t\t\t      stmt->set.set->set->keylen,\n-\t\t\t      stmt->set.set->set->keytype->byteorder,\n+\t\t\t      stmt->set.set->set->key->dtype,\n+\t\t\t      stmt->set.set->set->key->len,\n+\t\t\t      stmt->set.set->set->key->byteorder,\n \t\t\t      &stmt->set.key) < 0)\n \t\treturn -1;\n \tif (expr_is_constant(stmt->set.key))\n@@ -2629,8 +2641,6 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)\n \n \t\tmappings = implicit_set_declaration(ctx, \"__objmap%d\",\n \t\t\t\t\t\t    key, mappings);\n-\t\texpr_free(key);\n-\n \t\tmappings->set->datatype = &string_type;\n \t\tmappings->set->datalen  = NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE;\n \t\tmappings->set->objtype  = stmt->objref.type;\n@@ -2657,11 +2667,11 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)\n \t\t    map->mappings->ops->name);\n \t}\n \n-\tif (!datatype_equal(map->map->dtype, map->mappings->set->keytype))\n+\tif (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))\n \t\treturn expr_binary_error(ctx->msgs, map->mappings, map->map,\n \t\t\t\t\t \"datatype mismatch, map expects %s, \"\n \t\t\t\t\t \"mapping expression has type %s\",\n-\t\t\t\t\t map->mappings->set->keytype->desc,\n+\t\t\t\t\t map->mappings->set->key->dtype->desc,\n \t\t\t\t\t map->map->dtype->desc);\n \n \tmap->dtype = map->mappings->set->datatype;\n@@ -2765,7 +2775,7 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)\n \t\t\t\t ctx->cmd->handle.set);\n \n \tctx->set = set;\n-\texpr_set_context(&ctx->ectx, set->keytype, set->keylen);\n+\texpr_set_context(&ctx->ectx, set->key->dtype, set->key->len);\n \tif (expr_evaluate(ctx, expr) < 0)\n \t\treturn -1;\n \tctx->set = NULL;\n@@ -2784,15 +2794,20 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)\n \n \ttype = set->flags & NFT_SET_MAP ? \"map\" : \"set\";\n \n-\tif (set->keytype == NULL)\n-\t\treturn set_error(ctx, set, \"%s definition does not specify \"\n-\t\t\t\t \"key data type\", type);\n+\tif (set->key == NULL)\n+\t\treturn set_error(ctx, set, \"%s definition does not specify key\",\n+\t\t\t\t type);\n \n-\tset->keylen = set->keytype->size;\n-\tif (set->keylen == 0)\n-\t\treturn set_error(ctx, set, \"unqualified key data type \"\n-\t\t\t\t \"specified in %s definition\", type);\n+\tif (set->key->len == 0) {\n+\t\tif (set->key->ops->type == EXPR_CONCAT &&\n+\t\t    expr_evaluate_concat(ctx, &set->key, false) < 0)\n+\t\t\treturn -1;\n \n+\t\tif (set->key->len == 0)\n+\t\t\treturn set_error(ctx, set, \"unqualified key type %s \"\n+\t\t\t\t\t \"specified in %s definition\",\n+\t\t\t\t\t set->key->dtype->name, type);\n+\t}\n \tif (set->flags & NFT_SET_MAP) {\n \t\tif (set->datatype == NULL)\n \t\t\treturn set_error(ctx, set, \"map definition does not \"\n@@ -2809,7 +2824,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)\n \n \tctx->set = set;\n \tif (set->init != NULL) {\n-\t\texpr_set_context(&ctx->ectx, set->keytype, set->keylen);\n+\t\texpr_set_context(&ctx->ectx, set->key->dtype, set->key->len);\n \t\tif (expr_evaluate(ctx, &set->init) < 0)\n \t\t\treturn -1;\n \t}\ndiff --git a/src/expression.c b/src/expression.c\nindex d41ada39cc0f..ff3550c7cd85 100644\n--- a/src/expression.c\n+++ b/src/expression.c\n@@ -832,7 +832,7 @@ struct expr *set_expr_alloc(const struct location *loc, const struct set *set)\n \t\treturn set_expr;\n \n \tset_expr->set_flags = set->flags;\n-\tset_expr->dtype = set->keytype;\n+\tset_expr->dtype = set->key->dtype;\n \n \treturn set_expr;\n }\n@@ -960,7 +960,7 @@ struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)\n {\n \tstruct expr *expr;\n \n-\texpr = expr_alloc(loc, &set_ref_expr_ops, set->keytype, 0, 0);\n+\texpr = expr_alloc(loc, &set_ref_expr_ops, set->key->dtype, 0, 0);\n \texpr->set = set_get(set);\n \texpr->flags |= EXPR_F_CONSTANT;\n \treturn expr;\ndiff --git a/src/netlink.c b/src/netlink.c\nindex 291bbdeeaa68..e414718ba1b9 100644\n--- a/src/netlink.c\n+++ b/src/netlink.c\n@@ -1118,8 +1118,11 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,\n \tset->handle.table  = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE));\n \tset->handle.set    = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME));\n \n-\tset->keytype = set_datatype_alloc(keytype, keybyteorder);\n-\tset->keylen  = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE;\n+\tset->key     = constant_expr_alloc(&netlink_location,\n+\t\t\t\t\t   set_datatype_alloc(keytype, keybyteorder),\n+\t\t\t\t\t   keybyteorder,\n+\t\t\t\t\t   nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE,\n+\t\t\t\t\t   NULL);\n \tset->flags   = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);\n \n \tset->objtype = objtype;\n@@ -1158,9 +1161,9 @@ static int netlink_add_set_compat(struct netlink_ctx *ctx,\n \tnls = alloc_nftnl_set(h);\n \tnftnl_set_set_u32(nls, NFTNL_SET_FLAGS, set->flags);\n \tnftnl_set_set_u32(nls, NFTNL_SET_KEY_TYPE,\n-\t\t\t  dtype_map_to_kernel(set->keytype));\n+\t\t\t  dtype_map_to_kernel(set->key->dtype));\n \tnftnl_set_set_u32(nls, NFTNL_SET_KEY_LEN,\n-\t\t\t  div_round_up(set->keylen, BITS_PER_BYTE));\n+\t\t\t  div_round_up(set->key->len, BITS_PER_BYTE));\n \tif (set->flags & NFT_SET_MAP) {\n \t\tnftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,\n \t\t\t\t  dtype_map_to_kernel(set->datatype));\n@@ -1192,9 +1195,9 @@ static int netlink_add_set_batch(struct netlink_ctx *ctx,\n \tnls = alloc_nftnl_set(h);\n \tnftnl_set_set_u32(nls, NFTNL_SET_FLAGS, set->flags);\n \tnftnl_set_set_u32(nls, NFTNL_SET_KEY_TYPE,\n-\t\t\t  dtype_map_to_kernel(set->keytype));\n+\t\t\t  dtype_map_to_kernel(set->key->dtype));\n \tnftnl_set_set_u32(nls, NFTNL_SET_KEY_LEN,\n-\t\t\t  div_round_up(set->keylen, BITS_PER_BYTE));\n+\t\t\t  div_round_up(set->key->len, BITS_PER_BYTE));\n \tif (set->flags & NFT_SET_MAP) {\n \t\tnftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,\n \t\t\t\t  dtype_map_to_kernel(set->datatype));\n@@ -1226,7 +1229,7 @@ static int netlink_add_set_batch(struct netlink_ctx *ctx,\n \tif (!udbuf)\n \t\tmemory_allocation_error();\n \tif (!nftnl_udata_put_u32(udbuf, UDATA_SET_KEYBYTEORDER,\n-\t\t\t\t set->keytype->byteorder))\n+\t\t\t\t set->key->byteorder))\n \t\tmemory_allocation_error();\n \n \tif (set->flags & NFT_SET_MAP &&\n@@ -1537,10 +1540,10 @@ static int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,\n \t\tflags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS);\n \n \tkey = netlink_alloc_value(&netlink_location, &nld);\n-\tkey->dtype\t= set->keytype;\n-\tkey->byteorder\t= set->keytype->byteorder;\n-\tif (set->keytype->subtypes)\n-\t\tkey = netlink_parse_concat_elem(set->keytype, key);\n+\tkey->dtype\t= set->key->dtype;\n+\tkey->byteorder\t= set->key->byteorder;\n+\tif (set->key->dtype->subtypes)\n+\t\tkey = netlink_parse_concat_elem(set->key->dtype, key);\n \n \tif (!(set->flags & NFT_SET_INTERVAL) &&\n \t    key->byteorder == BYTEORDER_HOST_ENDIAN)\n@@ -2197,7 +2200,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,\n \t\t * used by named sets, so use a dummy set.\n \t\t */\n \t\tdummyset = set_alloc(monh->loc);\n-\t\tdummyset->keytype = set->keytype;\n+\t\tdummyset->key = expr_clone(set->key);\n \t\tdummyset->datatype = set->datatype;\n \t\tdummyset->flags = set->flags;\n \t\tdummyset->init = set_expr_alloc(monh->loc, set);\ndiff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c\nindex 3f42d092d204..42206ebcfff2 100644\n--- a/src/netlink_delinearize.c\n+++ b/src/netlink_delinearize.c\n@@ -307,8 +307,8 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,\n \t\treturn netlink_error(ctx, loc,\n \t\t\t\t     \"Lookup expression has no left hand side\");\n \n-\tif (left->len < set->keylen) {\n-\t\tleft = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen);\n+\tif (left->len < set->key->len) {\n+\t\tleft = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len);\n \t\tif (left == NULL)\n \t\t\treturn;\n \t}\n@@ -1122,8 +1122,8 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,\n \t\treturn netlink_error(ctx, loc,\n \t\t\t\t     \"Dynset statement has no key expression\");\n \n-\tif (expr->len < set->keylen) {\n-\t\texpr = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen);\n+\tif (expr->len < set->key->len) {\n+\t\texpr = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len);\n \t\tif (expr == NULL)\n \t\t\treturn;\n \t}\n@@ -1193,8 +1193,8 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,\n \t\t\treturn netlink_error(ctx, loc,\n \t\t\t\t\t     \"objref expression has no left hand side\");\n \n-\t\tif (left->len < set->keylen) {\n-\t\t\tleft = netlink_parse_concat_expr(ctx, loc, sreg, set->keylen);\n+\t\tif (left->len < set->key->len) {\n+\t\t\tleft = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len);\n \t\t\tif (left == NULL)\n \t\t\t\treturn;\n \t\t}\ndiff --git a/src/parser_bison.y b/src/parser_bison.y\nindex 31a7e8be2bcd..b203599f7be6 100644\n--- a/src/parser_bison.y\n+++ b/src/parser_bison.y\n@@ -469,8 +469,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n \n %type <val>\t\t\ttime_spec quota_used\n \n-%type <val>\t\t\ttype_identifier_list\n-%type <datatype>\t\tdata_type\n+%type <expr>\t\t\tdata_type_expr data_type_atom_expr\n+%destructor { expr_free($$); }  data_type_expr data_type_atom_expr\n \n %type <cmd>\t\t\tline\n %destructor { cmd_free($$); }\tline\n@@ -1387,9 +1387,9 @@ set_block_alloc\t\t:\t/* empty */\n set_block\t\t:\t/* empty */\t{ $$ = $<set>-1; }\n \t\t\t|\tset_block\tcommon_block\n \t\t\t|\tset_block\tstmt_separator\n-\t\t\t|\tset_block\tTYPE\t\tdata_type\tstmt_separator\n+\t\t\t|\tset_block\tTYPE\t\tdata_type_expr\tstmt_separator\n \t\t\t{\n-\t\t\t\t$1->keytype = $3;\n+\t\t\t\t$1->key = $3;\n \t\t\t\t$$ = $1;\n \t\t\t}\n \t\t\t|\tset_block\tFLAGS\t\tset_flag_list\tstmt_separator\n@@ -1441,28 +1441,30 @@ map_block\t\t:\t/* empty */\t{ $$ = $<set>-1; }\n \t\t\t|\tmap_block\tcommon_block\n \t\t\t|\tmap_block\tstmt_separator\n \t\t\t|\tmap_block\tTYPE\n-\t\t\t\t\t\tdata_type\tCOLON\tdata_type\n+\t\t\t\t\t\tdata_type_expr\tCOLON\tdata_type_expr\n \t\t\t\t\t\tstmt_separator\n \t\t\t{\n-\t\t\t\t$1->keytype  = $3;\n-\t\t\t\t$1->datatype = $5;\n+\t\t\t\t$1->key = $3;\n+\t\t\t\t$1->datatype = $5->dtype;\n+\n+\t\t\t\texpr_free($5);\n \t\t\t\t$1->flags |= NFT_SET_MAP;\n \t\t\t\t$$ = $1;\n \t\t\t}\n \t\t\t|\tmap_block\tTYPE\n-\t\t\t\t\t\tdata_type\tCOLON\tCOUNTER\n+\t\t\t\t\t\tdata_type_expr\tCOLON\tCOUNTER\n \t\t\t\t\t\tstmt_separator\n \t\t\t{\n-\t\t\t\t$1->keytype = $3;\n+\t\t\t\t$1->key = $3;\n \t\t\t\t$1->objtype = NFT_OBJECT_COUNTER;\n \t\t\t\t$1->flags  |= NFT_SET_OBJECT;\n \t\t\t\t$$ = $1;\n \t\t\t}\n \t\t\t|\tmap_block\tTYPE\n-\t\t\t\t\t\tdata_type\tCOLON\tQUOTA\n+\t\t\t\t\t\tdata_type_expr\tCOLON\tQUOTA\n \t\t\t\t\t\tstmt_separator\n \t\t\t{\n-\t\t\t\t$1->keytype = $3;\n+\t\t\t\t$1->key = $3;\n \t\t\t\t$1->objtype = NFT_OBJECT_QUOTA;\n \t\t\t\t$1->flags  |= NFT_SET_OBJECT;\n \t\t\t\t$$ = $1;\n@@ -1494,16 +1496,7 @@ set_policy_spec\t\t:\tPERFORMANCE\t{ $$ = NFT_SET_POL_PERFORMANCE; }\n \t\t\t|\tMEMORY\t\t{ $$ = NFT_SET_POL_MEMORY; }\n \t\t\t;\n \n-data_type\t\t:\ttype_identifier_list\n-\t\t\t{\n-\t\t\t\tif ($1 & ~TYPE_MASK)\n-\t\t\t\t\t$$ = concat_type_alloc($1);\n-\t\t\t\telse\n-\t\t\t\t\t$$ = datatype_lookup($1);\n-\t\t\t}\n-\t\t\t;\n-\n-type_identifier_list\t:\ttype_identifier\n+data_type_atom_expr\t:\ttype_identifier\n \t\t\t{\n \t\t\t\tconst struct datatype *dtype = datatype_lookup_byname($1);\n \t\t\t\tif (dtype == NULL) {\n@@ -1512,20 +1505,28 @@ type_identifier_list\t:\ttype_identifier\n \t\t\t\t\txfree($1);\n \t\t\t\t\tYYERROR;\n \t\t\t\t}\n-\t\t\t\txfree($1);\n-\t\t\t\t$$ = dtype->type;\n+\t\t\t\t$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,\n+\t\t\t\t\t\t\t dtype->size, NULL);\n \t\t\t}\n-\t\t\t|\ttype_identifier_list\tDOT\ttype_identifier\n+\t\t\t;\n+\n+data_type_expr\t\t:\tdata_type_atom_expr\n+\t\t\t|\tdata_type_expr\tDOT\tdata_type_atom_expr\n \t\t\t{\n-\t\t\t\tconst struct datatype *dtype = datatype_lookup_byname($3);\n-\t\t\t\tif (dtype == NULL) {\n-\t\t\t\t\terec_queue(error(&@3, \"unknown datatype %s\", $3),\n-\t\t\t\t\t\t   state->msgs);\n-\t\t\t\t\txfree($3);\n-\t\t\t\t\tYYERROR;\n+\t\t\t\tif ($1->ops->type != EXPR_CONCAT) {\n+\t\t\t\t\t$$ = concat_expr_alloc(&@$);\n+\t\t\t\t\tcompound_expr_add($$, $1);\n+\t\t\t\t} else {\n+\t\t\t\t\tstruct location rhs[] = {\n+\t\t\t\t\t\t[1]\t= @2,\n+\t\t\t\t\t\t[2]\t= @3,\n+\t\t\t\t\t};\n+\t\t\t\t\tlocation_update(&$3->location, rhs, 2);\n+\n+\t\t\t\t\t$$ = $1;\n+\t\t\t\t\t$$->location = @$;\n \t\t\t\t}\n-\t\t\t\txfree($3);\n-\t\t\t\t$$ = concat_subtype_add($$, dtype->type);\n+\t\t\t\tcompound_expr_add($$, $3);\n \t\t\t}\n \t\t\t;\n \ndiff --git a/src/rule.c b/src/rule.c\nindex 1bb7b4756171..a417c307dfe8 100644\n--- a/src/rule.c\n+++ b/src/rule.c\n@@ -215,7 +215,7 @@ void set_free(struct set *set)\n \tif (set->init != NULL)\n \t\texpr_free(set->init);\n \thandle_free(&set->handle);\n-\tset_datatype_destroy(set->keytype);\n+\texpr_free(set->key);\n \tset_datatype_destroy(set->datatype);\n \txfree(set);\n }\n@@ -296,7 +296,7 @@ static void set_print_declaration(const struct set *set,\n \n \tprintf(\" %s {%s\", set->handle.set, opts->nl);\n \n-\tprintf(\"%s%stype %s\", opts->tab, opts->tab, set->keytype->name);\n+\tprintf(\"%s%stype %s\", opts->tab, opts->tab, set->key->dtype->name);\n \tif (set->flags & NFT_SET_MAP)\n \t\tprintf(\" : %s\", set->datatype->name);\n \telse if (set->flags & NFT_SET_OBJECT)\ndiff --git a/src/segtree.c b/src/segtree.c\nindex f81e117421a1..f0efd155f0be 100644\n--- a/src/segtree.c\n+++ b/src/segtree.c\n@@ -76,8 +76,8 @@ static void seg_tree_init(struct seg_tree *tree, const struct set *set,\n \n \tfirst = list_entry(init->expressions.next, struct expr, list);\n \ttree->root\t= RB_ROOT;\n-\ttree->keytype\t= set->keytype;\n-\ttree->keylen\t= set->keylen;\n+\ttree->keytype\t= set->key->dtype;\n+\ttree->keylen\t= set->key->len;\n \ttree->datatype\t= set->datatype;\n \ttree->datalen\t= set->datalen;\n \ttree->byteorder\t= first->byteorder;\n","prefixes":["nft","2/2"]}