]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
tcpopt: allow to check for presence of any tcp option
authorFlorian Westphal <fw@strlen.de>
Wed, 21 Oct 2020 21:54:17 +0000 (23:54 +0200)
committerFlorian Westphal <fw@strlen.de>
Mon, 9 Nov 2020 11:19:31 +0000 (12:19 +0100)
nft currently doesn't allow to check for presence of arbitrary tcp options.
Only known options where nft provides a template can be tested for.

This allows to test for presence of raw protocol values as well.

Example:

tcp option 42 exists

Signed-off-by: Florian Westphal <fw@strlen.de>
include/expression.h
src/exthdr.c
src/ipopt.c
src/netlink_linearize.c
src/parser_bison.y
src/tcpopt.c
tests/py/any/tcpopt.t
tests/py/any/tcpopt.t.payload

index b039882cf1d18f94d2b8679e5b75eca64de9351a..894a68d2e822e4125fcf6b58fcda25e8d7a771ca 100644 (file)
@@ -311,7 +311,8 @@ struct expr {
                        /* EXPR_EXTHDR */
                        const struct exthdr_desc        *desc;
                        const struct proto_hdr_template *tmpl;
-                       unsigned int                    offset;
+                       uint16_t                        offset;
+                       uint8_t                         raw_type;
                        enum nft_exthdr_op              op;
                        unsigned int                    flags;
                } exthdr;
index e1b7f14d41944327ab22c97505c9075b3d204e14..8995ad1775a0c9417c3cc3c8aca27999eb662741 100644 (file)
@@ -52,6 +52,13 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
                 */
                unsigned int offset = expr->exthdr.offset / 64;
 
+               if (expr->exthdr.desc == NULL &&
+                   expr->exthdr.offset == 0 &&
+                   expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) {
+                       nft_print(octx, "tcp option %d", expr->exthdr.raw_type);
+                       return;
+               }
+
                nft_print(octx, "tcp option %s", expr->exthdr.desc->name);
                if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
                        return;
@@ -79,6 +86,7 @@ static bool exthdr_expr_cmp(const struct expr *e1, const struct expr *e2)
        return e1->exthdr.desc == e2->exthdr.desc &&
               e1->exthdr.tmpl == e2->exthdr.tmpl &&
               e1->exthdr.op == e2->exthdr.op &&
+              e1->exthdr.raw_type == e2->exthdr.raw_type &&
               e1->exthdr.flags == e2->exthdr.flags;
 }
 
@@ -89,6 +97,7 @@ static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
        new->exthdr.offset = expr->exthdr.offset;
        new->exthdr.op = expr->exthdr.op;
        new->exthdr.flags = expr->exthdr.flags;
+       new->exthdr.raw_type = expr->exthdr.raw_type;
 }
 
 #define NFTNL_UDATA_EXTHDR_DESC 0
@@ -192,6 +201,7 @@ struct expr *exthdr_expr_alloc(const struct location *loc,
        expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
                          BYTEORDER_BIG_ENDIAN, tmpl->len);
        expr->exthdr.desc = desc;
+       expr->exthdr.raw_type = desc ? desc->type : 0;
        expr->exthdr.tmpl = tmpl;
        expr->exthdr.offset = tmpl->offset;
        return expr;
@@ -270,6 +280,8 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
        unsigned int i;
 
        assert(expr->etype == EXPR_EXTHDR);
+       expr->exthdr.raw_type = type;
+
        if (op == NFT_EXTHDR_OP_TCPOPT)
                return tcpopt_init_raw(expr, type, offset, len, flags);
        if (op == NFT_EXTHDR_OP_IPV4)
index 7ecb8b9c8e32a2417d5f9539bb58f48c6bda80fc..5f9f908c0b34ad2fe33834e413e18261f776075b 100644 (file)
@@ -103,6 +103,7 @@ struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
        expr->exthdr.tmpl   = tmpl;
        expr->exthdr.op     = NFT_EXTHDR_OP_IPV4;
        expr->exthdr.offset = tmpl->offset + calc_offset(desc, tmpl, ptr);
+       expr->exthdr.raw_type = desc->type;
 
        return expr;
 }
index a37e4b940973ba00c8431f739f3d14e6f4b239b7..05af8bb1b4854a6d2c4025141ba3f5fdf9ec7580 100644 (file)
@@ -206,7 +206,7 @@ static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
        nle = alloc_nft_expr("exthdr");
        netlink_put_register(nle, NFTNL_EXPR_EXTHDR_DREG, dreg);
        nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
-                         expr->exthdr.desc->type);
+                         expr->exthdr.raw_type);
        nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE);
        nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
                           div_round_up(expr->len, BITS_PER_BYTE));
index 7dedbd2564b238a95ade36a039bcc1b89c79343c..9a2b1b6f0301e56a5d221ad06ccf50ecb21d9c49 100644 (file)
@@ -5224,6 +5224,13 @@ tcp_hdr_option_type      :       EOL             { $$ = TCPOPT_KIND_EOL; }
                        |       SACK3           { $$ = TCPOPT_KIND_SACK3; }
                        |       ECHO            { $$ = TCPOPT_KIND_ECHO; }
                        |       TIMESTAMP       { $$ = TCPOPT_KIND_TIMESTAMP; }
+                       |       NUM             {
+                               if ($1 > 255) {
+                                       erec_queue(error(&@1, "value too large"), state->msgs);
+                                       YYERROR;
+                               }
+                               $$ = $1;
+                       }
                        ;
 
 tcp_hdr_option_field   :       KIND            { $$ = TCPOPT_COMMON_KIND; }
index d1dd13b868e8e54d6db1d8f8f4d450bff92e9c05..1cf97a563bc27030b051a2087083cf44b9238d6f 100644 (file)
@@ -103,6 +103,19 @@ const struct exthdr_desc *tcpopt_protocols[] = {
        [TCPOPT_KIND_TIMESTAMP]         = &tcpopt_timestamp,
 };
 
+/**
+ * tcpopt_expr_alloc - allocate tcp option extension expression
+ *
+ * @loc: location from parser
+ * @kind: raw tcp option value to find in packet
+ * @field: highlevel field to find in the option if @kind is present in packet
+ *
+ * Allocate a new tcp option expression.
+ * @kind is the raw option value to find in the packet.
+ * Exception: SACK may use extra OOB data that is mangled here.
+ *
+ * @field is the optional field to extract from the @type option.
+ */
 struct expr *tcpopt_expr_alloc(const struct location *loc,
                               unsigned int kind,
                               unsigned int field)
@@ -138,8 +151,22 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
        if (kind < array_size(tcpopt_protocols))
                desc = tcpopt_protocols[kind];
 
-       if (!desc)
-               return NULL;
+       if (!desc) {
+               if (field != TCPOPT_COMMON_KIND || kind > 255)
+                       return NULL;
+
+               expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type,
+                                 BYTEORDER_BIG_ENDIAN, 8);
+
+               desc = tcpopt_protocols[TCPOPT_NOP];
+               tmpl = &desc->templates[field];
+               expr->exthdr.desc   = desc;
+               expr->exthdr.tmpl   = tmpl;
+               expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+               expr->exthdr.raw_type = kind;
+               return expr;
+       }
+
        tmpl = &desc->templates[field];
        if (!tmpl)
                return NULL;
@@ -149,6 +176,7 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
        expr->exthdr.desc   = desc;
        expr->exthdr.tmpl   = tmpl;
        expr->exthdr.op     = NFT_EXTHDR_OP_TCPOPT;
+       expr->exthdr.raw_type = desc->type;
        expr->exthdr.offset = tmpl->offset;
 
        return expr;
@@ -165,6 +193,10 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
        expr->len = len;
        expr->exthdr.flags = flags;
        expr->exthdr.offset = off;
+       expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
+       if (flags & NFT_EXTHDR_F_PRESENT)
+               datatype_set(expr, &boolean_type);
 
        if (type >= array_size(tcpopt_protocols))
                return;
@@ -178,12 +210,10 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
                if (tmpl->offset != off || tmpl->len != len)
                        continue;
 
-               if (flags & NFT_EXTHDR_F_PRESENT)
-                       datatype_set(expr, &boolean_type);
-               else
+               if ((flags & NFT_EXTHDR_F_PRESENT) == 0)
                        datatype_set(expr, tmpl->dtype);
+
                expr->exthdr.tmpl = tmpl;
-               expr->exthdr.op   = NFT_EXTHDR_OP_TCPOPT;
                break;
        }
 }
index 1d42de8746cd984e951c8c3fdea6291166dabb66..7b17014b300315e3fb03ab8937163d677d13d556 100644 (file)
@@ -30,6 +30,7 @@ tcp option timestamp kind 1;ok
 tcp option timestamp length 1;ok
 tcp option timestamp tsval 1;ok
 tcp option timestamp tsecr 1;ok
+tcp option 255 missing;ok
 
 tcp option foobar;fail
 tcp option foo bar;fail
@@ -38,6 +39,7 @@ tcp option eol left 1;fail
 tcp option eol left 1;fail
 tcp option sack window;fail
 tcp option sack window 1;fail
+tcp option 256 exists;fail
 
 tcp option window exists;ok
 tcp option window missing;ok
index 9c480c8bd06b105dd762dafdbd665a312c31ed03..34f8e26c440937b2470922afd6080b89f6b49072 100644 (file)
@@ -509,20 +509,6 @@ inet
   [ exthdr load tcpopt 4b @ 8 + 2 => reg 1 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# tcp option timestamp tsecr 1
-ip 
-  [ meta load l4proto => reg 1 ]
-  [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 4b @ 8 + 6 => reg 1 ]
-  [ cmp eq reg 1 0x01000000 ]
-
-# tcp option timestamp tsecr 1
-ip6 
-  [ meta load l4proto => reg 1 ]
-  [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 4b @ 8 + 6 => reg 1 ]
-  [ cmp eq reg 1 0x01000000 ]
-
 # tcp option timestamp tsecr 1
 inet 
   [ meta load l4proto => reg 1 ]
@@ -530,19 +516,12 @@ inet
   [ exthdr load tcpopt 4b @ 8 + 6 => reg 1 ]
   [ cmp eq reg 1 0x01000000 ]
 
-# tcp option window exists
-ip 
-  [ meta load l4proto => reg 1 ]
-  [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
-  [ cmp eq reg 1 0x00000001 ]
-
-# tcp option window exists
-ip6 
+# tcp option 255 missing
+inet
   [ meta load l4proto => reg 1 ]
   [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
-  [ cmp eq reg 1 0x00000001 ]
+  [ exthdr load tcpopt 1b @ 255 + 0 present => reg 1 ]
+  [ cmp eq reg 1 0x00000000 ]
 
 # tcp option window exists
 inet 
@@ -551,20 +530,6 @@ inet
   [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
   [ cmp eq reg 1 0x00000001 ]
 
-# tcp option window missing
-ip 
-  [ meta load l4proto => reg 1 ]
-  [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
-  [ cmp eq reg 1 0x00000000 ]
-
-# tcp option window missing
-ip6 
-  [ meta load l4proto => reg 1 ]
-  [ cmp eq reg 1 0x00000006 ]
-  [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
-  [ cmp eq reg 1 0x00000000 ]
-
 # tcp option window missing
 inet 
   [ meta load l4proto => reg 1 ]
@@ -572,16 +537,6 @@ inet
   [ exthdr load tcpopt 1b @ 3 + 0 present => reg 1 ]
   [ cmp eq reg 1 0x00000000 ]
 
-# tcp option maxseg size set 1360
-ip 
-  [ immediate reg 1 0x00005005 ]
-  [ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ]
-
-# tcp option maxseg size set 1360
-ip6 
-  [ immediate reg 1 0x00005005 ]
-  [ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ]
-
 # tcp option maxseg size set 1360
 inet 
   [ immediate reg 1 0x00005005 ]