On 2020-11-05, at 19:11:46 +0000, Jeremy Sowden wrote: > On 2020-11-05, at 15:11:42 +0100, Florian Westphal wrote: > > 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@xxxxxxxxx> > > --- > > include/expression.h | 3 +- > > src/exthdr.c | 12 ++++++++ > > src/ipopt.c | 1 + > > src/netlink_linearize.c | 2 +- > > src/parser_bison.y | 7 +++++ > > src/tcpopt.c | 42 +++++++++++++++++++++++---- > > tests/py/any/tcpopt.t | 2 ++ > > tests/py/any/tcpopt.t.payload | 53 +++-------------------------------- > > 8 files changed, 65 insertions(+), 57 deletions(-) > > > > diff --git a/include/expression.h b/include/expression.h > > index b039882cf1d1..894a68d2e822 100644 > > --- a/include/expression.h > > +++ b/include/expression.h > > @@ -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; > > diff --git a/src/exthdr.c b/src/exthdr.c > > index e1b7f14d4194..8995ad1775a0 100644 > > --- a/src/exthdr.c > > +++ b/src/exthdr.c > > @@ -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) > > diff --git a/src/ipopt.c b/src/ipopt.c > > index 7ecb8b9c8e32..5f9f908c0b34 100644 > > --- a/src/ipopt.c > > +++ b/src/ipopt.c > > @@ -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; > > } > > diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c > > index a37e4b940973..05af8bb1b485 100644 > > --- a/src/netlink_linearize.c > > +++ b/src/netlink_linearize.c > > @@ -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)); > > diff --git a/src/parser_bison.y b/src/parser_bison.y > > index e8df98b8949a..393f66862810 100644 > > --- a/src/parser_bison.y > > +++ b/src/parser_bison.y > > @@ -5225,6 +5225,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; } > > diff --git a/src/tcpopt.c b/src/tcpopt.c > > index d1dd13b868e8..1cf97a563bc2 100644 > > --- a/src/tcpopt.c > > +++ b/src/tcpopt.c > > @@ -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; > > } > > } > > diff --git a/tests/py/any/tcpopt.t b/tests/py/any/tcpopt.t > > index 1d42de8746cd..7b17014b3003 100644 > > --- a/tests/py/any/tcpopt.t > > +++ b/tests/py/any/tcpopt.t > > @@ -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 > > diff --git a/tests/py/any/tcpopt.t.payload b/tests/py/any/tcpopt.t.payload > > index 9c480c8bd06b..34f8e26c4409 100644 > > --- a/tests/py/any/tcpopt.t.payload > > +++ b/tests/py/any/tcpopt.t.payload > > @@ -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 ] > > - > > You are removing unrelated duplicate payloads. Is it worth doing that > separately? This seems to dedup' the payloads correctly: perl -nle ' our @payload; our $rule; if (/^# (.+)/) { if ($rule ne $1) { print for @payload; $rule = $1 } @payload = () } push @payload, $_; END { print for @payload } ' tests/py/any/tcpopt.t.payload > tests/py/any/tcpopt.t.payload.tmp mv tests/py/any/tcpopt.t.payload.tmp tests/py/any/tcpopt.t.payload One could use perl's -i switch, but the printing of the final payload will be to stdout and not in-place. > > # 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 ] > > -- > > 2.26.2 > > > >
Attachment:
signature.asc
Description: PGP signature