]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: allow to map key to nfqueue number
authorFlorian Westphal <fw@strlen.de>
Fri, 25 Oct 2024 07:47:25 +0000 (09:47 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 11 Nov 2024 10:40:08 +0000 (11:40 +0100)
Allow to specify a numeric queue id as part of a map.
The parser side is easy, but the reverse direction (listing) is not.

'queue' is a statement, it doesn't have an expression.

Add a generic 'queue_type' datatype as a shim to the real basetype with
constant expressions, this is used only for udata build/parse, it stores
the "key" (the parser token, here "queue") as udata in kernel and can
then restore the original key.

Add a dumpfile to validate parser & output.

JSON support is missing because JSON allow typeof only since quite
recently.

Joint work with Pablo.

Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1455
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/datatype.h
src/datatype.c
src/expression.c
src/json. [new file with mode: 0644]
src/netlink.c
src/parser_bison.y
tests/shell/testcases/nft-f/dumps/nfqueue.nft [new file with mode: 0644]
tests/shell/testcases/nft-f/nfqueue [new file with mode: 0755]

index 75d6d6b8c62842a0e153b9f44e88adbd236a6996..8b950f9165a583bb3521dfdd79f278f6587519d0 100644 (file)
@@ -140,7 +140,8 @@ struct parse_ctx;
 struct datatype {
        uint32_t                        type;
        enum byteorder                  byteorder:8;
-       uint32_t                        alloc:1;
+       uint32_t                        alloc:1,
+                                       is_typeof:1;
        unsigned int                    size;
        unsigned int                    subtypes;
        const char                      *name;
@@ -271,6 +272,7 @@ extern const struct datatype boolean_type;
 extern const struct datatype priority_type;
 extern const struct datatype policy_type;
 extern const struct datatype cgroupv2_type;
+extern const struct datatype queue_type;
 
 /* private datatypes for reject statement. */
 extern const struct datatype reject_icmp_code_type;
index ea73eaf9a691d6e305f558bc82b8643d3490f28d..0c13bbd4270e7a3e9810cbc29c23d310b9f05cf2 100644 (file)
@@ -505,6 +505,24 @@ const struct datatype xinteger_type = {
        .parse          = integer_type_parse,
 };
 
+static void queue_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+       nft_gmp_print(octx, "queue");
+}
+
+/* Dummy queue_type for set declaration with typeof, see
+ * constant_expr_build_udata and constant_expr_parse_udata,
+ * basetype is used for elements.
+*/
+const struct datatype queue_type = {
+       .type           = TYPE_INTEGER,
+       .is_typeof      = 1,
+       .name           = "queue",
+       .desc           = "queue",
+       .basetype       = &integer_type,
+       .print          = queue_type_print,
+};
+
 static void string_type_print(const struct expr *expr, struct output_ctx *octx)
 {
        unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
index c0cb7f22eb73dcb5c4dea2d727964fbc02ad9343..62786f483eeda35b484dbab4e2a42921a6e66176 100644 (file)
@@ -371,6 +371,84 @@ struct expr *variable_expr_alloc(const struct location *loc,
        return expr;
 }
 
+#define NFTNL_UDATA_CONSTANT_TYPE 0
+#define NFTNL_UDATA_CONSTANT_MAX NFTNL_UDATA_CONSTANT_TYPE
+
+#define CONSTANT_EXPR_NFQUEUE_ID 0
+
+static int constant_expr_build_udata(struct nftnl_udata_buf *udbuf,
+                                    const struct expr *expr)
+{
+       uint32_t type;
+
+       if (expr->dtype == &queue_type)
+               type = CONSTANT_EXPR_NFQUEUE_ID;
+       else
+               return -1;
+
+       if (!nftnl_udata_put_u32(udbuf, NFTNL_UDATA_CONSTANT_TYPE, type))
+               return -1;
+
+       return 0;
+}
+
+static int constant_parse_udata(const struct nftnl_udata *attr, void *data)
+{
+       const struct nftnl_udata **ud = data;
+       uint8_t type = nftnl_udata_type(attr);
+       uint8_t len = nftnl_udata_len(attr);
+       uint32_t value;
+
+       switch (type) {
+       case NFTNL_UDATA_CONSTANT_TYPE:
+               if (len != sizeof(uint32_t))
+                       return -1;
+
+               value = nftnl_udata_get_u32(attr);
+               switch (value) {
+               case CONSTANT_EXPR_NFQUEUE_ID:
+                       break;
+               default:
+                       return -1;
+               }
+               break;
+       default:
+               return 0;
+       }
+
+       ud[type] = attr;
+
+       return 0;
+}
+
+static struct expr *constant_expr_parse_udata(const struct nftnl_udata *attr)
+{
+       const struct nftnl_udata *ud[NFTNL_UDATA_CONSTANT_MAX + 1] = {};
+       const struct datatype *dtype = NULL;
+       uint32_t type;
+       int err;
+
+       err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
+                               constant_parse_udata, ud);
+       if (err < 0)
+               return NULL;
+
+       if (!ud[NFTNL_UDATA_CONSTANT_TYPE])
+               return NULL;
+
+       type = nftnl_udata_get_u32(ud[NFTNL_UDATA_CONSTANT_TYPE]);
+       switch (type) {
+       case CONSTANT_EXPR_NFQUEUE_ID:
+               dtype = &queue_type;
+               break;
+       default:
+               break;
+       }
+
+       return constant_expr_alloc(&internal_location, dtype, BYTEORDER_HOST_ENDIAN,
+                                  16, NULL);
+}
+
 static void constant_expr_print(const struct expr *expr,
                                 struct output_ctx *octx)
 {
@@ -401,6 +479,8 @@ static const struct expr_ops constant_expr_ops = {
        .cmp            = constant_expr_cmp,
        .clone          = constant_expr_clone,
        .destroy        = constant_expr_destroy,
+       .build_udata    = constant_expr_build_udata,
+       .parse_udata    = constant_expr_parse_udata,
 };
 
 struct expr *constant_expr_alloc(const struct location *loc,
diff --git a/src/json. b/src/json.
new file mode 100644 (file)
index 0000000..e69de29
index 25ee3419772b305137d971fe22d59b03916efbbe..36140fb63d6fd397458fcf22887a0ec78a4acab9 100644 (file)
@@ -1466,7 +1466,11 @@ key_end:
                data = netlink_alloc_data(&netlink_location, &nld,
                                          set->data->dtype->type == TYPE_VERDICT ?
                                          NFT_REG_VERDICT : NFT_REG_1);
-               datatype_set(data, set->data->dtype);
+
+               if (set->data->dtype->is_typeof)
+                       datatype_set(data, set->data->dtype->basetype);
+               else
+                       datatype_set(data, set->data->dtype);
                data->byteorder = set->data->byteorder;
 
                if (set->data->dtype->subtypes) {
index 602fc60e6de3d537a5d3a5e903682a29918e32a3..6e6f3cf8335d543fdc5be48327e4b7a2b05cac62 100644 (file)
@@ -2173,6 +2173,10 @@ typeof_data_expr :       INTERVAL        typeof_expr
                        {
                                $$ = $1;
                        }
+                       |       QUEUE
+                       {
+                               $$ = constant_expr_alloc(&@$, &queue_type, BYTEORDER_HOST_ENDIAN, 16, NULL);
+                       }
                        ;
 
 typeof_expr            :       primary_expr
diff --git a/tests/shell/testcases/nft-f/dumps/nfqueue.nft b/tests/shell/testcases/nft-f/dumps/nfqueue.nft
new file mode 100644 (file)
index 0000000..7fe3ca6
--- /dev/null
@@ -0,0 +1,11 @@
+table inet t {
+       map get_queue_id {
+               typeof ip saddr . ip daddr . tcp dport : queue
+               elements = { 127.0.0.1 . 127.0.0.1 . 22 : 1,
+                            127.0.0.1 . 127.0.0.2 . 22 : 2 }
+       }
+
+       chain test {
+               queue flags bypass to ip saddr . ip daddr . tcp dport map @get_queue_id
+       }
+}
diff --git a/tests/shell/testcases/nft-f/nfqueue b/tests/shell/testcases/nft-f/nfqueue
new file mode 100755 (executable)
index 0000000..07820b7
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"