This new datatype is a string subtype. It will allow us to build named maps/sets using meta keys like 'iifname', 'oifname', 'ibriport' or 'obriport'. Example: table inet t { set s { type ifname elements = { "eth0", "eth1" } } chain c { iifname @s accept oifname @s accept } } Signed-off-by: Arturo Borrero Gonzalez <arturo@xxxxxxxxxxxxx> --- doc/nft.xml | 8 ++--- include/datatype.h | 2 + include/meta.h | 1 + src/datatype.c | 1 + src/evaluate.c | 19 +++++++++-- src/meta.c | 17 +++++++--- src/netlink_delinearize.c | 27 +++++++++++---- src/netlink_linearize.c | 14 +++++--- .../shell/testcases/maps/0007named_ifname_dtype_0 | 35 ++++++++++++++++++++ .../shell/testcases/sets/0029named_ifname_dtype_0 | 35 ++++++++++++++++++++ 10 files changed, 134 insertions(+), 25 deletions(-) create mode 100755 tests/shell/testcases/maps/0007named_ifname_dtype_0 create mode 100755 tests/shell/testcases/sets/0029named_ifname_dtype_0 diff --git a/doc/nft.xml b/doc/nft.xml index 9d21e9a..6748265 100644 --- a/doc/nft.xml +++ b/doc/nft.xml @@ -2572,7 +2572,7 @@ filter output icmpv6 type { echo-request, echo-reply } <row> <entry>iifname</entry> <entry>Input interface name</entry> - <entry>string</entry> + <entry>ifname</entry> </row> <row> <entry>iiftype</entry> @@ -2587,7 +2587,7 @@ filter output icmpv6 type { echo-request, echo-reply } <row> <entry>oifname</entry> <entry>Output interface name</entry> - <entry>string</entry> + <entry>ifname</entry> </row> <row> <entry>oiftype</entry> @@ -2612,12 +2612,12 @@ filter output icmpv6 type { echo-request, echo-reply } <row> <entry>ibriport</entry> <entry>Input bridge interface name</entry> - <entry>string</entry> + <entry>ifname</entry> </row> <row> <entry>obriport</entry> <entry>Output bridge interface name</entry> - <entry>string</entry> + <entry>ifname</entry> </row> <row> <entry>pkttype</entry> diff --git a/include/datatype.h b/include/datatype.h index cc4cb07..dd94e80 100644 --- a/include/datatype.h +++ b/include/datatype.h @@ -41,6 +41,7 @@ * @TYPE_ICMPX_CODE: icmpx code (integer subtype) * @TYPE_DEVGROUP: devgroup code (integer subtype) * @TYPE_DSCP: Differentiated Services Code Point (integer subtype) + * @TYPE_IFNAME: interface name (string subtype) */ enum datatypes { TYPE_INVALID, @@ -84,6 +85,7 @@ enum datatypes { TYPE_FIB_ADDR, TYPE_BOOLEAN, TYPE_CT_EVENTBIT, + TYPE_IFNAME, __TYPE_MAX }; #define TYPE_MAX (__TYPE_MAX - 1) diff --git a/include/meta.h b/include/meta.h index 47b16c4..6086a71 100644 --- a/include/meta.h +++ b/include/meta.h @@ -38,5 +38,6 @@ extern const struct datatype gid_type; extern const struct datatype uid_type; extern const struct datatype devgroup_type; extern const struct datatype pkttype_type; +extern const struct datatype ifname_type; #endif /* NFTABLES_META_H */ diff --git a/src/datatype.c b/src/datatype.c index 93726ca..324ac80 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -68,6 +68,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = { [TYPE_ECN] = &ecn_type, [TYPE_FIB_ADDR] = &fib_addr_type, [TYPE_BOOLEAN] = &boolean_type, + [TYPE_IFNAME] = &ifname_type, }; const struct datatype *datatype_lookup(enum datatypes type) diff --git a/src/evaluate.c b/src/evaluate.c index e5ad104..80e6370 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -224,6 +224,7 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp) struct expr *value, *prefix; int data_len = ctx->ectx.len > 0 ? ctx->ectx.len : len + 1; char data[data_len]; + const struct datatype *dtype; if (ctx->ectx.len > 0) { if (expr->len > ctx->ectx.len) @@ -240,12 +241,21 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp) return expr_error(ctx->msgs, expr, "Empty string is not allowed"); + switch (ctx->ectx.dtype->type) { + case TYPE_IFNAME: + dtype = &ifname_type; + break; + default: + dtype = &string_type; + break; + } + datalen = strlen(data) - 1; if (data[datalen] != '*') { /* We need to reallocate the constant expression with the right * expression length to avoid problems on big endian. */ - value = constant_expr_alloc(&expr->location, &string_type, + value = constant_expr_alloc(&expr->location, dtype, BYTEORDER_HOST_ENDIAN, expr->len, data); expr_free(expr); @@ -260,20 +270,20 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp) memset(unescaped_str, 0, sizeof(unescaped_str)); xstrunescape(data, unescaped_str); - value = constant_expr_alloc(&expr->location, &string_type, + value = constant_expr_alloc(&expr->location, dtype, BYTEORDER_HOST_ENDIAN, expr->len, unescaped_str); expr_free(expr); *exprp = value; return 0; } - value = constant_expr_alloc(&expr->location, &string_type, + value = constant_expr_alloc(&expr->location, dtype, BYTEORDER_HOST_ENDIAN, datalen * BITS_PER_BYTE, data); prefix = prefix_expr_alloc(&expr->location, value, datalen * BITS_PER_BYTE); - prefix->dtype = &string_type; + prefix->dtype = dtype; prefix->flags |= EXPR_F_CONSTANT; prefix->byteorder = BYTEORDER_HOST_ENDIAN; @@ -1769,6 +1779,7 @@ static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp) meta->meta.key == NFT_META_NFPROTO) return expr_error(ctx->msgs, meta, "meta nfproto is only useful in the inet family"); + return expr_evaluate_primary(ctx, exprp); } diff --git a/src/meta.c b/src/meta.c index 8c2eca2..11de2da 100644 --- a/src/meta.c +++ b/src/meta.c @@ -369,6 +369,15 @@ const struct datatype devgroup_type = { .flags = DTYPE_F_PREFIX, }; +const struct datatype ifname_type = { + .type = TYPE_IFNAME, + .name = "ifname", + .desc = "network interface name", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = IFNAMSIZ * BITS_PER_BYTE, + .basetype = &string_type, +}; + static const struct meta_template meta_templates[] = { [NFT_META_LEN] = META_TEMPLATE("length", &integer_type, 4 * 8, BYTEORDER_HOST_ENDIAN), @@ -384,14 +393,14 @@ static const struct meta_template meta_templates[] = { 4 * 8, BYTEORDER_HOST_ENDIAN), [NFT_META_IIF] = META_TEMPLATE("iif", &ifindex_type, 4 * 8, BYTEORDER_HOST_ENDIAN), - [NFT_META_IIFNAME] = META_TEMPLATE("iifname", &string_type, + [NFT_META_IIFNAME] = META_TEMPLATE("iifname", &ifname_type, IFNAMSIZ * BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN), [NFT_META_IIFTYPE] = META_TEMPLATE("iiftype", &arphrd_type, 2 * 8, BYTEORDER_HOST_ENDIAN), [NFT_META_OIF] = META_TEMPLATE("oif", &ifindex_type, 4 * 8, BYTEORDER_HOST_ENDIAN), - [NFT_META_OIFNAME] = META_TEMPLATE("oifname", &string_type, + [NFT_META_OIFNAME] = META_TEMPLATE("oifname", &ifname_type, IFNAMSIZ * BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN), [NFT_META_OIFTYPE] = META_TEMPLATE("oiftype", &arphrd_type, @@ -404,10 +413,10 @@ static const struct meta_template meta_templates[] = { 1 , BYTEORDER_HOST_ENDIAN), [NFT_META_RTCLASSID] = META_TEMPLATE("rtclassid", &realm_type, 4 * 8, BYTEORDER_HOST_ENDIAN), - [NFT_META_BRI_IIFNAME] = META_TEMPLATE("ibriport", &string_type, + [NFT_META_BRI_IIFNAME] = META_TEMPLATE("ibriport", &ifname_type, IFNAMSIZ * BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN), - [NFT_META_BRI_OIFNAME] = META_TEMPLATE("obriport", &string_type, + [NFT_META_BRI_OIFNAME] = META_TEMPLATE("obriport", &ifname_type, IFNAMSIZ * BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN), [NFT_META_PKTTYPE] = META_TEMPLATE("pkttype", &pkttype_type, diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 622425e..8f95934 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -270,10 +270,15 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx, nld.value = nftnl_expr_get(nle, NFTNL_EXPR_CMP_DATA, &nld.len); right = netlink_alloc_value(loc, &nld); - if (left->len > right->len && - left->dtype != &string_type) { - return netlink_error(ctx, loc, - "Relational expression size mismatch"); + if (left->len > right->len) { + switch (left->dtype->type) { + case TYPE_STRING: + case TYPE_IFNAME: + break; + default: + return netlink_error(ctx, loc, "Relational expression " + "size mismatch"); + } } else if (left->len > 0 && left->len < right->len) { left = netlink_parse_concat_expr(ctx, loc, sreg, right->len); if (left == NULL) @@ -1728,7 +1733,7 @@ static struct expr *string_wildcard_expr_alloc(struct location *loc, data[pos] = '*'; data[pos + 1] = '\0'; - return constant_expr_alloc(loc, &string_type, BYTEORDER_HOST_ENDIAN, + return constant_expr_alloc(loc, expr->dtype, BYTEORDER_HOST_ENDIAN, expr->len + BITS_PER_BYTE, data); } @@ -1744,7 +1749,7 @@ static void escaped_string_wildcard_expr_alloc(struct expr **exprp, data[pos - 1] = '\\'; data[pos] = '*'; - tmp = constant_expr_alloc(&expr->location, &string_type, + tmp = constant_expr_alloc(&expr->location, expr->dtype, BYTEORDER_HOST_ENDIAN, expr->len + BITS_PER_BYTE, data); expr_free(expr); @@ -1789,7 +1794,7 @@ static struct expr *expr_postprocess_string(struct expr *expr) { struct expr *mask; - assert(expr->dtype->type == TYPE_STRING); + assert(expr_basetype(expr)->type == TYPE_STRING); if (__expr_postprocess_string(&expr)) return expr; @@ -1893,8 +1898,14 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) if (expr->byteorder == BYTEORDER_HOST_ENDIAN) mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); - if (expr->dtype->type == TYPE_STRING) + switch (expr->dtype->type) { + case TYPE_STRING: + case TYPE_IFNAME: *exprp = expr_postprocess_string(expr); + break; + default: + break; + } if (expr->dtype->basetype != NULL && expr->dtype->basetype->type == TYPE_BITMASK) diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 99a4dde..6ec06be 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -364,15 +364,19 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx, return netlink_gen_lookup(ctx, expr, dreg); case EXPR_PREFIX: sreg = get_register(ctx, expr->left); - if (expr->left->dtype->type != TYPE_STRING) { - len = div_round_up(expr->right->len, BITS_PER_BYTE); - netlink_gen_expr(ctx, expr->left, sreg); - right = netlink_gen_prefix(ctx, expr, sreg); - } else { + switch (expr->left->dtype->type) { + case TYPE_STRING: + case TYPE_IFNAME: len = div_round_up(expr->right->prefix_len, BITS_PER_BYTE); right = expr->right->prefix; expr->left->len = expr->right->prefix_len; netlink_gen_expr(ctx, expr->left, sreg); + break; + default: + len = div_round_up(expr->right->len, BITS_PER_BYTE); + netlink_gen_expr(ctx, expr->left, sreg); + right = netlink_gen_prefix(ctx, expr, sreg); + break; } break; default: diff --git a/tests/shell/testcases/maps/0007named_ifname_dtype_0 b/tests/shell/testcases/maps/0007named_ifname_dtype_0 new file mode 100755 index 0000000..dcbcf2f --- /dev/null +++ b/tests/shell/testcases/maps/0007named_ifname_dtype_0 @@ -0,0 +1,35 @@ +#!/bin/bash + +# support for ifname in named maps + +tmpfile=$(mktemp) +if [ ! -w $tmpfile ] ; then + echo "Failed to create tmp file" >&2 + exit 0 +fi + +trap "rm -rf $tmpfile" EXIT # cleanup if aborted + +EXPECTED="table inet t { + map m1 { + type ifname : ipv4_addr + elements = { \"eth0\" : 1.1.1.1 } + } + + chain c { + ip daddr set iifname map @m1 + ip daddr set oifname map @m1 + } +}" + +set -e +echo "$EXPECTED" > $tmpfile +$NFT -f $tmpfile + +GET="$($NFT list ruleset)" +if [ "$EXPECTED" != "$GET" ] ; then + DIFF="$(which diff)" + [ -x $DIFF ] && $DIFF -u <(echo "$EXPECTED") <(echo "$GET") + exit 1 +fi + diff --git a/tests/shell/testcases/sets/0029named_ifname_dtype_0 b/tests/shell/testcases/sets/0029named_ifname_dtype_0 new file mode 100755 index 0000000..8b7ab98 --- /dev/null +++ b/tests/shell/testcases/sets/0029named_ifname_dtype_0 @@ -0,0 +1,35 @@ +#!/bin/bash + +# support for ifname in named sets + +tmpfile=$(mktemp) +if [ ! -w $tmpfile ] ; then + echo "Failed to create tmp file" >&2 + exit 0 +fi + +trap "rm -rf $tmpfile" EXIT # cleanup if aborted + +EXPECTED="table inet t { + set s { + type ifname + elements = { \"eth0\" } + } + + chain c { + iifname @s accept + oifname @s accept + } +}" + +set -e +echo "$EXPECTED" > $tmpfile +$NFT -f $tmpfile + +GET="$($NFT list ruleset)" +if [ "$EXPECTED" != "$GET" ] ; then + DIFF="$(which diff)" + [ -x $DIFF ] && $DIFF -u <(echo "$EXPECTED") <(echo "$GET") + exit 1 +fi + -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html