From d66b043a46f4b8e48ab96503613d4ea7483899d4 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 7 Nov 2024 18:38:46 +0100 Subject: [PATCH] json: Support typeof in set and map types JIRA: https://issues.redhat.com/browse/RHEL-65346 Upstream Status: nftables commit bb6312484af93a83a9ec8716f3887a43566a775a commit bb6312484af93a83a9ec8716f3887a43566a775a Author: Phil Sutter Date: Sat Sep 28 00:55:34 2024 +0200 json: Support typeof in set and map types Implement this as a special "type" property value which is an object with sole property "typeof". The latter's value is the JSON representation of the expression in set->key, so for concatenated typeofs it is a concat expression. All this is a bit clumsy right now but it works and it should be possible to tear it down a bit for more user-friendliness in a compatible way by either replacing the concat expression by the array it contains or even the whole "typeof" object - the parser would just assume any object (or objects in an array) in the "type" property value are expressions to extract a type from. Signed-off-by: Phil Sutter Signed-off-by: Phil Sutter --- doc/libnftables-json.adoc | 7 ++- src/json.c | 13 ++++- src/parser_json.c | 9 +++ tests/monitor/testcases/map-expr.t | 2 +- tests/monitor/testcases/set-concat-interval.t | 2 +- .../maps/dumps/0012map_concat_0.json-nft | 21 +++++-- .../maps/dumps/0017_map_variable_0.json-nft | 18 +++++- .../maps/dumps/named_limits.json-nft | 55 ++++++++++++++++--- .../dumps/typeof_maps_add_delete.json-nft | 9 ++- .../maps/dumps/typeof_maps_update_0.json-nft | 9 ++- .../maps/dumps/vmap_timeout.json-nft | 22 ++++++-- .../packetpath/dumps/set_lookups.json-nft | 42 +++++++++++--- .../sets/dumps/0048set_counters_0.json-nft | 9 ++- .../testcases/sets/dumps/inner_0.json-nft | 34 ++++++++++-- .../set_element_timeout_updates.json-nft | 9 ++- 15 files changed, 220 insertions(+), 41 deletions(-) diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc index 2f29ac0..244eb41 100644 --- a/doc/libnftables-json.adoc +++ b/doc/libnftables-json.adoc @@ -341,7 +341,7 @@ ____ "auto-merge":* 'BOOLEAN' *}}* -'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* +'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* | *{ "typeof":* 'EXPRESSION' *}* 'SET_TYPE_LIST' := 'STRING' [*,* 'SET_TYPE_LIST' ] 'SET_POLICY' := *"performance"* | *"memory"* 'SET_FLAG_LIST' := 'SET_FLAG' [*,* 'SET_FLAG_LIST' ] @@ -381,8 +381,9 @@ that they translate a unique key to a value. Automatic merging of adjacent/overlapping set elements in interval sets. ==== TYPE -The set type might be a string, such as *"ipv4_addr"* or an array -consisting of strings (for concatenated types). +The set type might be a string, such as *"ipv4_addr"*, an array +consisting of strings (for concatenated types) or a *typeof* object containing +an expression to extract the type from. ==== ELEM A single set element might be given as string, integer or boolean value for diff --git a/src/json.c b/src/json.c index b1531ff..1f609bf 100644 --- a/src/json.c +++ b/src/json.c @@ -96,6 +96,17 @@ static json_t *set_dtype_json(const struct expr *key) return root; } +static json_t *set_key_dtype_json(const struct set *set, + struct output_ctx *octx) +{ + bool use_typeof = set->key_typeof_valid; + + if (!use_typeof) + return set_dtype_json(set->key); + + return json_pack("{s:o}", "typeof", expr_print_json(set->key, octx)); +} + static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) { char buf[1024]; @@ -158,7 +169,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) "family", family2str(set->handle.family), "name", set->handle.set.name, "table", set->handle.table.name, - "type", set_dtype_json(set->key), + "type", set_key_dtype_json(set, octx), "handle", set->handle.handle.id); if (set->comment) diff --git a/src/parser_json.c b/src/parser_json.c index 68c0600..02cfcd6 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1731,7 +1731,16 @@ static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root) compound_expr_add(expr, i); } return expr; + } else if (json_is_object(root)) { + const char *key; + json_t *val; + + if (!json_unpack_stmt(ctx, root, &key, &val) && + !strcmp(key, "typeof")) { + return json_parse_expr(ctx, val); + } } + json_error(ctx, "Invalid set datatype."); return NULL; } diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t index 8729c0b..d11ad0e 100644 --- a/tests/monitor/testcases/map-expr.t +++ b/tests/monitor/testcases/map-expr.t @@ -3,4 +3,4 @@ I add table ip t I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; } O - J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} -J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": ["day", "hour"], "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} +J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} diff --git a/tests/monitor/testcases/set-concat-interval.t b/tests/monitor/testcases/set-concat-interval.t index 75f3828..3542b82 100644 --- a/tests/monitor/testcases/set-concat-interval.t +++ b/tests/monitor/testcases/set-concat-interval.t @@ -10,6 +10,6 @@ I add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; elem O add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; } O add element ip t s { 20-80 . 0x14 : accept } O add element ip t s { 1-10 . 0xa : drop } -J {"add": {"map": {"family": "ip", "name": "s", "table": "t", "type": ["integer", "integer"], "handle": 0, "map": "verdict", "flags": ["interval"]}}} +J {"add": {"map": {"family": "ip", "name": "s", "table": "t", "type": {"typeof": {"concat": [{"payload": {"protocol": "udp", "field": "length"}}, {"payload": {"base": "ih", "offset": 32, "len": 32}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"]}}} J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [20, 80]}, 20]}, {"accept": null}]]}}}} J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [1, 10]}, 10]}, {"drop": null}]]}}}} diff --git a/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft index 0005223..88bf498 100644 --- a/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft +++ b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft @@ -31,10 +31,23 @@ "family": "ip", "name": "w", "table": "x", - "type": [ - "ipv4_addr", - "mark" - ], + "type": { + "typeof": { + "concat": [ + { + "payload": { + "protocol": "ip", + "field": "saddr" + } + }, + { + "meta": { + "key": "mark" + } + } + ] + } + }, "handle": 0, "map": "verdict", "flags": [ diff --git a/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft index 725498c..8eacf61 100644 --- a/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft +++ b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft @@ -19,7 +19,14 @@ "family": "ip", "name": "y", "table": "x", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "map": "mark", "elem": [ @@ -39,7 +46,14 @@ "family": "ip", "name": "z", "table": "x", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "map": "mark", "elem": [ diff --git a/tests/shell/testcases/maps/dumps/named_limits.json-nft b/tests/shell/testcases/maps/dumps/named_limits.json-nft index 7fa1298..3c6845a 100644 --- a/tests/shell/testcases/maps/dumps/named_limits.json-nft +++ b/tests/shell/testcases/maps/dumps/named_limits.json-nft @@ -75,7 +75,14 @@ "family": "inet", "name": "tarpit4", "table": "filter", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "size": 10000, "flags": [ @@ -90,7 +97,14 @@ "family": "inet", "name": "tarpit6", "table": "filter", - "type": "ipv6_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip6", + "field": "saddr" + } + } + }, "handle": 0, "size": 10000, "flags": [ @@ -105,11 +119,29 @@ "family": "inet", "name": "addr4limit", "table": "filter", - "type": [ - "inet_proto", - "ipv4_addr", - "inet_service" - ], + "type": { + "typeof": { + "concat": [ + { + "meta": { + "key": "l4proto" + } + }, + { + "payload": { + "protocol": "ip", + "field": "saddr" + } + }, + { + "payload": { + "protocol": "tcp", + "field": "sport" + } + } + ] + } + }, "handle": 0, "map": "limit", "flags": [ @@ -244,7 +276,14 @@ "family": "inet", "name": "saddr6limit", "table": "filter", - "type": "ipv6_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip6", + "field": "saddr" + } + } + }, "handle": 0, "map": "limit", "flags": [ diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft index b3204a2..effe02d 100644 --- a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft +++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft @@ -39,7 +39,14 @@ "family": "ip", "name": "dynmark", "table": "dynset", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "daddr" + } + } + }, "handle": 0, "map": "mark", "size": 64, diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft index 1d50477..7315146 100644 --- a/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft +++ b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft @@ -50,7 +50,14 @@ "family": "ip", "name": "sticky-set-svc-153CN2XYVUHRQ7UB", "table": "kube-nfproxy-v4", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "daddr" + } + } + }, "handle": 0, "map": "mark", "size": 65535, diff --git a/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft index 1c3aa59..71e9a9e 100644 --- a/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft +++ b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft @@ -87,10 +87,24 @@ "family": "inet", "name": "portaddrmap", "table": "filter", - "type": [ - "ipv4_addr", - "inet_service" - ], + "type": { + "typeof": { + "concat": [ + { + "payload": { + "protocol": "ip", + "field": "daddr" + } + }, + { + "payload": { + "protocol": "th", + "field": "dport" + } + } + ] + } + }, "handle": 0, "map": "verdict", "flags": [ diff --git a/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft index 24363f9..bcf6914 100644 --- a/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft +++ b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft @@ -60,10 +60,23 @@ "family": "ip", "name": "s2", "table": "t", - "type": [ - "ipv4_addr", - "iface_index" - ], + "type": { + "typeof": { + "concat": [ + { + "payload": { + "protocol": "ip", + "field": "saddr" + } + }, + { + "meta": { + "key": "iif" + } + } + ] + } + }, "handle": 0, "elem": [ { @@ -113,10 +126,23 @@ "family": "ip", "name": "nomatch", "table": "t", - "type": [ - "ipv4_addr", - "iface_index" - ], + "type": { + "typeof": { + "concat": [ + { + "payload": { + "protocol": "ip", + "field": "saddr" + } + }, + { + "meta": { + "key": "iif" + } + } + ] + } + }, "handle": 0, "elem": [ { diff --git a/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft index 62a6a17..4be4112 100644 --- a/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft +++ b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft @@ -31,7 +31,14 @@ "family": "ip", "name": "y", "table": "x", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "elem": [ { diff --git a/tests/shell/testcases/sets/dumps/inner_0.json-nft b/tests/shell/testcases/sets/dumps/inner_0.json-nft index 8d84e1c..e5dc198 100644 --- a/tests/shell/testcases/sets/dumps/inner_0.json-nft +++ b/tests/shell/testcases/sets/dumps/inner_0.json-nft @@ -27,10 +27,26 @@ "family": "netdev", "name": "x", "table": "x", - "type": [ - "ipv4_addr", - "ipv4_addr" - ], + "type": { + "typeof": { + "concat": [ + { + "payload": { + "tunnel": "vxlan", + "protocol": "ip", + "field": "saddr" + } + }, + { + "payload": { + "tunnel": "vxlan", + "protocol": "ip", + "field": "daddr" + } + } + ] + } + }, "handle": 0, "elem": [ { @@ -47,7 +63,15 @@ "family": "netdev", "name": "y", "table": "x", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "tunnel": "vxlan", + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "size": 65535, "flags": [ diff --git a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft index aa90829..d92d8d7 100644 --- a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft +++ b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft @@ -31,7 +31,14 @@ "family": "ip", "name": "s", "table": "t", - "type": "ipv4_addr", + "type": { + "typeof": { + "payload": { + "protocol": "ip", + "field": "saddr" + } + } + }, "handle": 0, "flags": [ "timeout"