From ad566e27398e81ed803c4225179bb8df4718a2e9 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 12 Jul 2021 17:44:08 +0200 Subject: [PATCH] json: tcp: add raw tcp option match support Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1979334 Upstream Status: nftables commit cb21869649208 commit cb21869649208118ed61354e2674858e4ff6c23c Author: Florian Westphal Date: Tue Nov 3 12:04:20 2020 +0100 json: tcp: add raw tcp option match support To similar change as in previous one, this time for the jason (de)serialization. Re-uses the raw payload match syntax, i.e. base,offset,length. Signed-off-by: Florian Westphal --- src/json.c | 22 ++++++++-------- src/parser_json.c | 52 ++++++++++++++++++++++++++------------ tests/py/any/tcpopt.t.json | 34 +++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/json.c b/src/json.c index 1906e7d..b77c6d2 100644 --- a/src/json.c +++ b/src/json.c @@ -656,30 +656,32 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx) json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx) { const char *desc = expr->exthdr.desc ? - expr->exthdr.desc->name : - "unknown-exthdr"; + expr->exthdr.desc->name : NULL; const char *field = expr->exthdr.tmpl->token; json_t *root; bool is_exists = expr->exthdr.flags & NFT_EXTHDR_F_PRESENT; if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) { + static const char *offstrs[] = { "", "1", "2", "3" }; unsigned int offset = expr->exthdr.offset / 64; + const char *offstr = ""; - if (offset) { - const char *offstrs[] = { "0", "1", "2", "3" }; - const char *offstr = ""; - + if (desc) { if (offset < 4) offstr = offstrs[offset]; root = json_pack("{s:s+}", "name", desc, offstr); + + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); } else { - root = json_pack("{s:s}", "name", desc); + root = json_pack("{s:i, s:i, s:i}", + "base", expr->exthdr.raw_type, + "offset", expr->exthdr.offset, + "len", expr->len); + is_exists = false; } - if (!is_exists) - json_object_set_new(root, "field", json_string(field)); - return json_pack("{s:o}", "tcp option", root); } if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) { diff --git a/src/parser_json.c b/src/parser_json.c index ab2375f..fbf7db5 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -500,6 +500,8 @@ static int json_parse_tcp_option_field(int type, const char *name, int *val) return 1; desc = tcpopt_protocols[type]; + if (!desc) + return 1; for (i = 0; i < array_size(desc->templates); i++) { if (desc->templates[i].token && @@ -599,30 +601,48 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx, static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx, const char *type, json_t *root) { + int fieldval, kind, offset, len; const char *desc, *field; - int descval, fieldval; struct expr *expr; - if (json_unpack_err(ctx, root, "{s:s}", "name", &desc)) - return NULL; - - if (json_parse_tcp_option_type(desc, &descval)) { - json_error(ctx, "Unknown tcp option name '%s'.", desc); - return NULL; - } + if (!json_unpack(root, "{s:i, s:i, s:i}", + "base", &kind, "offset", &offset, "len", &len)) { + uint32_t flag = 0; - if (json_unpack(root, "{s:s}", "field", &field)) { - expr = tcpopt_expr_alloc(int_loc, descval, + expr = tcpopt_expr_alloc(int_loc, kind, TCPOPT_COMMON_KIND); - expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + if (kind < 0 || kind > 255) + return NULL; + + if (offset == TCPOPT_COMMON_KIND && len == 8) + flag = NFT_EXTHDR_F_PRESENT; + + tcpopt_init_raw(expr, kind, offset, len, flag); return expr; + } else if (!json_unpack(root, "{s:s}", "name", &desc)) { + if (json_parse_tcp_option_type(desc, &kind)) { + json_error(ctx, "Unknown tcp option name '%s'.", desc); + return NULL; + } + + if (json_unpack(root, "{s:s}", "field", &field)) { + expr = tcpopt_expr_alloc(int_loc, kind, + TCPOPT_COMMON_KIND); + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + return expr; + } + + if (json_parse_tcp_option_field(kind, field, &fieldval)) { + json_error(ctx, "Unknown tcp option field '%s'.", field); + return NULL; + } + + return tcpopt_expr_alloc(int_loc, kind, fieldval); } - if (json_parse_tcp_option_field(descval, field, &fieldval)) { - json_error(ctx, "Unknown tcp option field '%s'.", field); - return NULL; - } - return tcpopt_expr_alloc(int_loc, descval, fieldval); + + json_error(ctx, "Invalid tcp option expression properties."); + return NULL; } static int json_parse_ip_option_type(const char *name, int *val) diff --git a/tests/py/any/tcpopt.t.json b/tests/py/any/tcpopt.t.json index b15e36e..139e97d 100644 --- a/tests/py/any/tcpopt.t.json +++ b/tests/py/any/tcpopt.t.json @@ -414,6 +414,40 @@ } ] +# tcp option 255 missing +[ + { + "match": { + "left": { + "tcp option": { + "base": 255, + "len": 8, + "offset": 0 + } + }, + "op": "==", + "right": false + } + } +] + +# tcp option @255,8,8 255 +[ + { + "match": { + "left": { + "tcp option": { + "base": 255, + "len": 8, + "offset": 8 + } + }, + "op": "==", + "right": 255 + } + } +] + # tcp option window exists [ { -- 2.31.1