nftables/SOURCES/0052-tcpopt-split-tcpopt_hdr_fields-into-per-option-enum.patch
2021-10-08 14:03:18 +00:00

539 lines
16 KiB
Diff

From 9697436145bf374093dc61e3ad857f7122de08ee Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Mon, 12 Jul 2021 17:44:08 +0200
Subject: [PATCH] tcpopt: split tcpopt_hdr_fields into per-option enum
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1979334
Upstream Status: nftables commit 2e1f821d713aa
commit 2e1f821d713aa44717b38901ee80cac8e2aa0335
Author: Florian Westphal <fw@strlen.de>
Date: Mon Nov 2 15:22:40 2020 +0100
tcpopt: split tcpopt_hdr_fields into per-option enum
Currently we're limited to ten template fields in exthdr_desc struct.
Using a single enum for all tpc option fields thus won't work
indefinitely (TCPOPTHDR_FIELD_TSECR is 9) when new option templates get
added.
Fortunately we can just use one enum per tcp option to avoid this.
As a side effect this also allows to simplify the sack offset
calculations. Rather than computing that on-the-fly, just add extra
fields to the SACK template.
expr->exthdr.offset now holds the 'raw' value, filled in from the option
template. This would ease implementation of 'raw option matching'
using offset and length to load from the option.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/tcpopt.h | 46 +++++++++++----
src/evaluate.c | 16 ++---
src/exthdr.c | 1 +
src/ipopt.c | 2 +-
src/netlink_delinearize.c | 2 +-
src/netlink_linearize.c | 4 +-
src/parser_bison.y | 18 +++---
src/parser_json.c | 36 ++++++++++--
src/tcpopt.c | 119 ++++++++++++++++----------------------
9 files changed, 139 insertions(+), 105 deletions(-)
diff --git a/include/tcpopt.h b/include/tcpopt.h
index 7f3fbb8..667c8a7 100644
--- a/include/tcpopt.h
+++ b/include/tcpopt.h
@@ -33,16 +33,42 @@ enum tcpopt_kind {
TCPOPT_KIND_SACK3 = 258,
};
-enum tcpopt_hdr_fields {
- TCPOPTHDR_FIELD_INVALID,
- TCPOPTHDR_FIELD_KIND,
- TCPOPTHDR_FIELD_LENGTH,
- TCPOPTHDR_FIELD_SIZE,
- TCPOPTHDR_FIELD_COUNT,
- TCPOPTHDR_FIELD_LEFT,
- TCPOPTHDR_FIELD_RIGHT,
- TCPOPTHDR_FIELD_TSVAL,
- TCPOPTHDR_FIELD_TSECR,
+/* Internal identifiers */
+enum tcpopt_common {
+ TCPOPT_COMMON_KIND,
+ TCPOPT_COMMON_LENGTH,
+};
+
+enum tcpopt_maxseg {
+ TCPOPT_MAXSEG_KIND,
+ TCPOPT_MAXSEG_LENGTH,
+ TCPOPT_MAXSEG_SIZE,
+};
+
+enum tcpopt_timestamp {
+ TCPOPT_TS_KIND,
+ TCPOPT_TS_LENGTH,
+ TCPOPT_TS_TSVAL,
+ TCPOPT_TS_TSECR,
+};
+
+enum tcpopt_windowscale {
+ TCPOPT_WINDOW_KIND,
+ TCPOPT_WINDOW_LENGTH,
+ TCPOPT_WINDOW_COUNT,
+};
+
+enum tcpopt_hdr_field_sack {
+ TCPOPT_SACK_KIND,
+ TCPOPT_SACK_LENGTH,
+ TCPOPT_SACK_LEFT,
+ TCPOPT_SACK_RIGHT,
+ TCPOPT_SACK_LEFT1,
+ TCPOPT_SACK_RIGHT1,
+ TCPOPT_SACK_LEFT2,
+ TCPOPT_SACK_RIGHT2,
+ TCPOPT_SACK_LEFT3,
+ TCPOPT_SACK_RIGHT3,
};
extern const struct exthdr_desc *tcpopt_protocols[__TCPOPT_KIND_MAX];
diff --git a/src/evaluate.c b/src/evaluate.c
index 0181750..99a66c2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -474,7 +474,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
&extra_len);
break;
case EXPR_EXTHDR:
- shift = expr_offset_shift(expr, expr->exthdr.tmpl->offset,
+ shift = expr_offset_shift(expr, expr->exthdr.offset,
&extra_len);
break;
default:
@@ -526,18 +526,16 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
- if (expr->exthdr.tmpl->offset % BITS_PER_BYTE != 0 ||
+ if (expr->exthdr.offset % BITS_PER_BYTE != 0 ||
expr->len % BITS_PER_BYTE != 0)
expr_evaluate_bits(ctx, exprp);
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT: {
static const unsigned int max_tcpoptlen = (15 * 4 - 20) * BITS_PER_BYTE;
- unsigned int totlen = 0;
+ unsigned int totlen;
- totlen += expr->exthdr.tmpl->offset;
- totlen += expr->exthdr.tmpl->len;
- totlen += expr->exthdr.offset;
+ totlen = expr->exthdr.tmpl->len + expr->exthdr.offset;
if (totlen > max_tcpoptlen)
return expr_error(ctx->msgs, expr,
@@ -547,11 +545,9 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
}
case NFT_EXTHDR_OP_IPV4: {
static const unsigned int max_ipoptlen = 40 * BITS_PER_BYTE;
- unsigned int totlen = 0;
+ unsigned int totlen;
- totlen += expr->exthdr.tmpl->offset;
- totlen += expr->exthdr.tmpl->len;
- totlen += expr->exthdr.offset;
+ totlen = expr->exthdr.offset + expr->exthdr.tmpl->len;
if (totlen > max_ipoptlen)
return expr_error(ctx->msgs, expr,
diff --git a/src/exthdr.c b/src/exthdr.c
index e1ec6f3..c28213f 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -99,6 +99,7 @@ struct expr *exthdr_expr_alloc(const struct location *loc,
BYTEORDER_BIG_ENDIAN, tmpl->len);
expr->exthdr.desc = desc;
expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
return expr;
}
diff --git a/src/ipopt.c b/src/ipopt.c
index b3d0279..7ecb8b9 100644
--- a/src/ipopt.c
+++ b/src/ipopt.c
@@ -102,7 +102,7 @@ struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
expr->exthdr.desc = desc;
expr->exthdr.tmpl = tmpl;
expr->exthdr.op = NFT_EXTHDR_OP_IPV4;
- expr->exthdr.offset = calc_offset(desc, tmpl, ptr);
+ expr->exthdr.offset = tmpl->offset + calc_offset(desc, tmpl, ptr);
return expr;
}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 157a473..790336a 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -727,8 +727,8 @@ static void netlink_parse_numgen(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- enum nft_registers dreg;
uint32_t type, until, offset;
+ enum nft_registers dreg;
struct expr *expr;
type = nftnl_expr_get_u32(nle, NFTNL_EXPR_NG_TYPE);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 25be634..9d1a064 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -168,7 +168,7 @@ static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
{
- unsigned int offset = expr->exthdr.tmpl->offset + expr->exthdr.offset;
+ unsigned int offset = expr->exthdr.offset;
struct nftnl_expr *nle;
nle = alloc_nft_expr("exthdr");
@@ -896,7 +896,7 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx,
expr = stmt->exthdr.expr;
- offset = expr->exthdr.tmpl->offset + expr->exthdr.offset;
+ offset = expr->exthdr.offset;
nle = alloc_nft_expr("exthdr");
netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 8f77766..114b289 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4715,7 +4715,7 @@ tcp_hdr_expr : TCP tcp_hdr_field
}
| TCP OPTION tcp_hdr_option_type
{
- $$ = tcpopt_expr_alloc(&@$, $3, TCPOPTHDR_FIELD_KIND);
+ $$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
;
@@ -4746,14 +4746,14 @@ tcp_hdr_option_type : EOL { $$ = TCPOPT_KIND_EOL; }
| TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
;
-tcp_hdr_option_field : KIND { $$ = TCPOPTHDR_FIELD_KIND; }
- | LENGTH { $$ = TCPOPTHDR_FIELD_LENGTH; }
- | SIZE { $$ = TCPOPTHDR_FIELD_SIZE; }
- | COUNT { $$ = TCPOPTHDR_FIELD_COUNT; }
- | LEFT { $$ = TCPOPTHDR_FIELD_LEFT; }
- | RIGHT { $$ = TCPOPTHDR_FIELD_RIGHT; }
- | TSVAL { $$ = TCPOPTHDR_FIELD_TSVAL; }
- | TSECR { $$ = TCPOPTHDR_FIELD_TSECR; }
+tcp_hdr_option_field : KIND { $$ = TCPOPT_COMMON_KIND; }
+ | LENGTH { $$ = TCPOPT_COMMON_LENGTH; }
+ | SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
+ | COUNT { $$ = TCPOPT_WINDOW_COUNT; }
+ | LEFT { $$ = TCPOPT_SACK_LEFT; }
+ | RIGHT { $$ = TCPOPT_SACK_RIGHT; }
+ | TSVAL { $$ = TCPOPT_TS_TSVAL; }
+ | TSECR { $$ = TCPOPT_TS_TSECR; }
;
dccp_hdr_expr : DCCP dccp_hdr_field
diff --git a/src/parser_json.c b/src/parser_json.c
index 44b58a0..ab2375f 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -466,8 +466,10 @@ static int json_parse_tcp_option_type(const char *name, int *val)
}
/* special case for sack0 - sack3 */
if (sscanf(name, "sack%u", &i) == 1 && i < 4) {
- if (val)
- *val = TCPOPT_KIND_SACK + i;
+ if (val && i == 0)
+ *val = TCPOPT_KIND_SACK;
+ else if (val && i > 0)
+ *val = TCPOPT_KIND_SACK1 + i - 1;
return 0;
}
return 1;
@@ -475,12 +477,38 @@ static int json_parse_tcp_option_type(const char *name, int *val)
static int json_parse_tcp_option_field(int type, const char *name, int *val)
{
+ const struct exthdr_desc *desc;
+ unsigned int block = 0;
unsigned int i;
- const struct exthdr_desc *desc = tcpopt_protocols[type];
+
+ switch (type) {
+ case TCPOPT_KIND_SACK1:
+ type = TCPOPT_KIND_SACK;
+ block = 1;
+ break;
+ case TCPOPT_KIND_SACK2:
+ type = TCPOPT_KIND_SACK;
+ block = 2;
+ break;
+ case TCPOPT_KIND_SACK3:
+ type = TCPOPT_KIND_SACK;
+ block = 3;
+ break;
+ }
+
+ if (type < 0 || type >= (int)array_size(tcpopt_protocols))
+ return 1;
+
+ desc = tcpopt_protocols[type];
for (i = 0; i < array_size(desc->templates); i++) {
if (desc->templates[i].token &&
!strcmp(desc->templates[i].token, name)) {
+ if (block) {
+ block--;
+ continue;
+ }
+
if (val)
*val = i;
return 0;
@@ -585,7 +613,7 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
if (json_unpack(root, "{s:s}", "field", &field)) {
expr = tcpopt_expr_alloc(int_loc, descval,
- TCPOPTHDR_FIELD_KIND);
+ TCPOPT_COMMON_KIND);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
diff --git a/src/tcpopt.c b/src/tcpopt.c
index 17cb580..d1dd13b 100644
--- a/src/tcpopt.c
+++ b/src/tcpopt.c
@@ -22,7 +22,7 @@ static const struct exthdr_desc tcpopt_eol = {
.name = "eol",
.type = TCPOPT_KIND_EOL,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
},
};
@@ -30,7 +30,7 @@ static const struct exthdr_desc tcpopt_nop = {
.name = "nop",
.type = TCPOPT_KIND_NOP,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
},
};
@@ -38,9 +38,9 @@ static const struct exthdr_desc tcptopt_maxseg = {
.name = "maxseg",
.type = TCPOPT_KIND_MAXSEG,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_SIZE] = PHT("size", 16, 16),
+ [TCPOPT_MAXSEG_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_MAXSEG_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_MAXSEG_SIZE] = PHT("size", 16, 16),
},
};
@@ -48,9 +48,9 @@ static const struct exthdr_desc tcpopt_window = {
.name = "window",
.type = TCPOPT_KIND_WINDOW,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_COUNT] = PHT("count", 16, 8),
+ [TCPOPT_WINDOW_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_WINDOW_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_WINDOW_COUNT] = PHT("count", 16, 8),
},
};
@@ -58,8 +58,8 @@ static const struct exthdr_desc tcpopt_sack_permitted = {
.name = "sack-perm",
.type = TCPOPT_KIND_SACK_PERMITTED,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
},
};
@@ -67,10 +67,16 @@ static const struct exthdr_desc tcpopt_sack = {
.name = "sack",
.type = TCPOPT_KIND_SACK,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_LEFT] = PHT("left", 16, 32),
- [TCPOPTHDR_FIELD_RIGHT] = PHT("right", 48, 32),
+ [TCPOPT_SACK_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_SACK_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_SACK_LEFT] = PHT("left", 16, 32),
+ [TCPOPT_SACK_RIGHT] = PHT("right", 48, 32),
+ [TCPOPT_SACK_LEFT1] = PHT("left", 80, 32),
+ [TCPOPT_SACK_RIGHT1] = PHT("right", 112, 32),
+ [TCPOPT_SACK_LEFT2] = PHT("left", 144, 32),
+ [TCPOPT_SACK_RIGHT2] = PHT("right", 176, 32),
+ [TCPOPT_SACK_LEFT3] = PHT("left", 208, 32),
+ [TCPOPT_SACK_RIGHT3] = PHT("right", 240, 32),
},
};
@@ -78,12 +84,13 @@ static const struct exthdr_desc tcpopt_timestamp = {
.name = "timestamp",
.type = TCPOPT_KIND_TIMESTAMP,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_TSVAL] = PHT("tsval", 16, 32),
- [TCPOPTHDR_FIELD_TSECR] = PHT("tsecr", 48, 32),
+ [TCPOPT_TS_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_TS_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_TS_TSVAL] = PHT("tsval", 16, 32),
+ [TCPOPT_TS_TSECR] = PHT("tsecr", 48, 32),
},
};
+
#undef PHT
const struct exthdr_desc *tcpopt_protocols[] = {
@@ -96,65 +103,43 @@ const struct exthdr_desc *tcpopt_protocols[] = {
[TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp,
};
-static unsigned int calc_offset(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int num)
-{
- if (!desc || tmpl == &tcpopt_unknown_template)
- return 0;
-
- switch (desc->type) {
- case TCPOPT_SACK:
- /* Make sure, offset calculations only apply to left and right
- * fields
- */
- return (tmpl->offset < 16) ? 0 : num * 64;
- default:
- return 0;
- }
-}
-
-
-static unsigned int calc_offset_reverse(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int offset)
-{
- if (!desc || tmpl == &tcpopt_unknown_template)
- return offset;
-
- switch (desc->type) {
- case TCPOPT_SACK:
- /* We can safely ignore the first left/right field */
- return offset < 80 ? offset : (offset % 64);
- default:
- return offset;
- }
-}
-
struct expr *tcpopt_expr_alloc(const struct location *loc,
unsigned int kind,
unsigned int field)
{
const struct proto_hdr_template *tmpl;
- const struct exthdr_desc *desc;
- uint8_t optnum = 0;
+ const struct exthdr_desc *desc = NULL;
struct expr *expr;
switch (kind) {
case TCPOPT_KIND_SACK1:
kind = TCPOPT_KIND_SACK;
- optnum = 1;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT1;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT1;
break;
case TCPOPT_KIND_SACK2:
kind = TCPOPT_KIND_SACK;
- optnum = 2;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT2;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT2;
break;
case TCPOPT_KIND_SACK3:
kind = TCPOPT_KIND_SACK;
- optnum = 3;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT3;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT3;
+ break;
}
- desc = tcpopt_protocols[kind];
+ if (kind < array_size(tcpopt_protocols))
+ desc = tcpopt_protocols[kind];
+
+ if (!desc)
+ return NULL;
tmpl = &desc->templates[field];
if (!tmpl)
return NULL;
@@ -164,34 +149,32 @@ 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.offset = calc_offset(desc, tmpl, optnum);
+ expr->exthdr.offset = tmpl->offset;
return expr;
}
-void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
unsigned int len, uint32_t flags)
{
const struct proto_hdr_template *tmpl;
- unsigned int i, off;
+ unsigned int i;
assert(expr->etype == EXPR_EXTHDR);
expr->len = len;
expr->exthdr.flags = flags;
- expr->exthdr.offset = offset;
+ expr->exthdr.offset = off;
+
+ if (type >= array_size(tcpopt_protocols))
+ return;
- assert(type < array_size(tcpopt_protocols));
expr->exthdr.desc = tcpopt_protocols[type];
expr->exthdr.flags = flags;
assert(expr->exthdr.desc != NULL);
for (i = 0; i < array_size(expr->exthdr.desc->templates); ++i) {
tmpl = &expr->exthdr.desc->templates[i];
- /* We have to reverse calculate the offset for the sack options
- * at this point
- */
- off = calc_offset_reverse(expr->exthdr.desc, tmpl, offset);
if (tmpl->offset != off || tmpl->len != len)
continue;
--
2.31.1