]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
json: add set statement list support
authorFernando Fernandez Mancera <ffmancera@riseup.net>
Thu, 1 Sep 2022 10:31:43 +0000 (12:31 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 1 Sep 2022 11:11:19 +0000 (13:11 +0200)
When listing a set with statements with JSON support, the statements were
ignored.

Output example:

{
  "set": {
    "op": "add",
    "elem": {
      "payload": {
        "protocol": "ip",
        "field": "saddr"
      }
    },
    "stmt": [
      {
        "limit": {
          "rate": 10,
          "burst": 5,
          "per": "second"
        }
      },
      {
        "counter": {
          "packets": 0,
          "bytes": 0
        }
      }
    ],
    "set": "@my_ssh_meter"
  }
}

Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1495
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/json.c
src/parser_json.c
tests/shell/testcases/json/0001set_statements_0 [new file with mode: 0755]
tests/shell/testcases/json/dumps/0001set_statements_0.nft [new file with mode: 0644]

index a525fd1bde20b7f95d5384b6f97a29577cd5a5ba..55959eea94f9f45963cdd78328ef46669c46d828 100644 (file)
@@ -1439,11 +1439,28 @@ json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
                         "bytes", stmt->counter.bytes);
 }
 
+static json_t *set_stmt_list_json(const struct list_head *stmt_list,
+                                  struct output_ctx *octx)
+{
+       json_t *root, *tmp;
+       struct stmt *i;
+
+       root = json_array();
+
+       list_for_each_entry(i, stmt_list, list) {
+               tmp = stmt_print_json(i, octx);
+               json_array_append_new(root, tmp);
+       }
+
+       return root;
+}
+
 json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
 {
-       return json_pack("{s:{s:s, s:o, s:s+}}", "set",
+       return json_pack("{s:{s:s, s:o, s:o, s:s+}}", "set",
                         "op", set_stmt_op_names[stmt->set.op],
                         "elem", expr_print_json(stmt->set.key, octx),
+                        "stmt", set_stmt_list_json(&stmt->set.stmt_list, octx),
                         "set", "@", stmt->set.set->set->handle.set.name);
 }
 
index 2437b1bae178eaae882fb80bb5d944b9fe16b030..fc72c25ff69132a994aa66c14c6b430904507ab1 100644 (file)
@@ -2230,13 +2230,36 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
        return stmt;
 }
 
+static void json_parse_set_stmt_list(struct json_ctx *ctx,
+                                     struct list_head *stmt_list,
+                                     json_t *stmt_json)
+{
+       struct list_head *head;
+       struct stmt *tmp;
+       json_t *value;
+       size_t index;
+
+       if (!stmt_json)
+               return;
+
+       if (!json_is_array(stmt_json))
+               json_error(ctx, "Unexpected object type in stmt");
+
+       head = stmt_list;
+       json_array_foreach(stmt_json, index, value) {
+               tmp = json_parse_stmt(ctx, value);
+               list_add(&tmp->list, head);
+               head = &tmp->list;
+       }
+}
+
 static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
                                          const char *key, json_t *value)
 {
        const char *opstr, *set;
        struct expr *expr, *expr2;
+       json_t *elem, *stmt_json;
        struct stmt *stmt;
-       json_t *elem;
        int op;
 
        if (json_unpack_err(ctx, value, "{s:s, s:o, s:s}",
@@ -2271,6 +2294,10 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
        stmt->set.op = op;
        stmt->set.key = expr;
        stmt->set.set = expr2;
+
+       if (!json_unpack(value, "{s:o}", "stmt", &stmt_json))
+               json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json);
+
        return stmt;
 }
 
diff --git a/tests/shell/testcases/json/0001set_statements_0 b/tests/shell/testcases/json/0001set_statements_0
new file mode 100755 (executable)
index 0000000..1c72d35
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"version": "1.0.5", "release_name": "Lester Gooch #4", "json_schema_version": 1}}, {"table": {"family": "ip", "name": "testt", "handle": 3}}, {"set": {"family": "ip", "name": "ssh_meter", "table": "testt", "type": "ipv4_addr", "handle": 2, "size": 65535}}, {"chain": {"family": "ip", "table": "testt", "name": "testc", "handle": 1, "type": "filter", "hook": "input", "prio": 0, "policy": "accept"}}, {"rule": {"family": "ip", "table": "testt", "chain": "testc", "handle": 3, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 22}}, {"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": "new"}}, {"set": {"op": "add", "elem": {"payload": {"protocol": "ip", "field": "saddr"}}, "stmt": [{"limit": {"rate": 10, "burst": 5, "per": "second"}}], "set": "@ssh_meter"}}, {"accept": null}]}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/dumps/0001set_statements_0.nft b/tests/shell/testcases/json/dumps/0001set_statements_0.nft
new file mode 100644 (file)
index 0000000..ee4a867
--- /dev/null
@@ -0,0 +1,12 @@
+table ip testt {
+       set ssh_meter {
+               type ipv4_addr
+               size 65535
+               flags dynamic
+       }
+
+       chain testc {
+               type filter hook input priority filter; policy accept;
+               tcp dport 22 ct state new add @ssh_meter { ip saddr limit rate 10/second } accept
+       }
+}