]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
json: Support typeof in set and map types
authorPhil Sutter <phil@nwl.cc>
Fri, 27 Sep 2024 22:55:34 +0000 (00:55 +0200)
committerPhil Sutter <phil@nwl.cc>
Wed, 6 Nov 2024 10:00:21 +0000 (11:00 +0100)
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 <phil@nwl.cc>
15 files changed:
doc/libnftables-json.adoc
src/json.c
src/parser_json.c
tests/monitor/testcases/map-expr.t
tests/monitor/testcases/set-concat-interval.t
tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
tests/shell/testcases/maps/dumps/named_limits.json-nft
tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
tests/shell/testcases/sets/dumps/inner_0.json-nft
tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft

index 2f29ac0436280719e50016ee92a6b78605894831..244eb412fbdc7a4eef0d8c4ca3c822dc9c95f1e8 100644 (file)
@@ -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
index b1531ff3f4c9e7b42a4aceaf51308a60342aef85..1f609bf2b03e9b6d4df61762dbdfa0cc36f6c1c4 100644 (file)
@@ -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)
index 68c0600df0857e8e521ec79687876175dff27b17..02cfcd69adbc626ec6bebdc52dc77e5185ec3491 100644 (file)
@@ -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;
 }
index 8729c0b44ee2c94abe6273269035c3e76a600bac..d11ad0ebc0d5761ce809f65f2cc24cce8d7a967f 100644 (file)
@@ -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}]}}}
index 75f38280bf82ef4a11e55666309853d73add09a4..3542b8225ebd1a9659b75140f32589db96cc037b 100644 (file)
@@ -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}]]}}}}
index 000522365df9fb31ae49f83c48776297b2788f10..88bf4984dbde72b85c8c506fcb34a1a91c740178 100644 (file)
         "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": [
index 725498cdcbef8e5fb5e063cab36ad14644f3f880..8eacf612d12fb532aad02fa179734fe1355e0b21 100644 (file)
         "family": "ip",
         "name": "y",
         "table": "x",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "map": "mark",
         "elem": [
         "family": "ip",
         "name": "z",
         "table": "x",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "map": "mark",
         "elem": [
index 7fa1298103832be3e208329a8d76db7e5ff6d750..3c6845ac43b421cfe270a2684d420444f4a1abac 100644 (file)
         "family": "inet",
         "name": "tarpit4",
         "table": "filter",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "size": 10000,
         "flags": [
         "family": "inet",
         "name": "tarpit6",
         "table": "filter",
-        "type": "ipv6_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip6",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "size": 10000,
         "flags": [
         "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": [
         "family": "inet",
         "name": "saddr6limit",
         "table": "filter",
-        "type": "ipv6_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip6",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "map": "limit",
         "flags": [
index b3204a283d0ad6687008f44f5605d92b3f6edf31..effe02dcf8364e3956874329dc312ecb763cf709 100644 (file)
         "family": "ip",
         "name": "dynmark",
         "table": "dynset",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "daddr"
+            }
+          }
+        },
         "handle": 0,
         "map": "mark",
         "size": 64,
index 1d50477d783dfb0f9f34a5ae8e900fa6c0904612..731514663b1aa625a051505188b8b9e905095cfd 100644 (file)
         "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,
index 1c3aa590f846e2fd609020a7678aa8a01daf6e1e..71e9a9ee9f21bec64ec506e7a910d8b20ebaad2c 100644 (file)
         "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": [
index 24363f9071b222d103d479de8841d0972323a917..bcf6914e95cb90b73e9d6d1c8db8e7659218b9d8 100644 (file)
         "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": [
           {
         "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": [
           {
index 62a6a177b7776f1106a73abc18662e6fdc56f5d1..4be4112bf79357bfc9ebc42ff7043368256a90a4 100644 (file)
         "family": "ip",
         "name": "y",
         "table": "x",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "elem": [
           {
index 8d84e1ccecb9f8041b26be8666b8ebf054047ec7..e5dc198f436be28f371eb80956f927d00ad4763b 100644 (file)
         "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": [
           {
         "family": "netdev",
         "name": "y",
         "table": "x",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "tunnel": "vxlan",
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "size": 65535,
         "flags": [
index aa908297e49ea3ab800b7e01cd4387da38442599..d92d8d7a549408796d50a2c48eb82844cb9c7e54 100644 (file)
         "family": "ip",
         "name": "s",
         "table": "t",
-        "type": "ipv4_addr",
+        "type": {
+          "typeof": {
+            "payload": {
+              "protocol": "ip",
+              "field": "saddr"
+            }
+          }
+        },
         "handle": 0,
         "flags": [
           "timeout"