diff options
Diffstat (limited to 'src/tcpopt.c')
-rw-r--r-- | src/tcpopt.c | 301 |
1 files changed, 174 insertions, 127 deletions
diff --git a/src/tcpopt.c b/src/tcpopt.c index ec305d94..f977e417 100644 --- a/src/tcpopt.c +++ b/src/tcpopt.c @@ -1,8 +1,7 @@ +#include <nft.h> + #include <stddef.h> -#include <stdlib.h> #include <stdio.h> -#include <stdint.h> -#include <string.h> #include <netinet/in.h> #include <netinet/ip6.h> #include <netinet/tcp.h> @@ -20,221 +19,269 @@ static const struct proto_hdr_template tcpopt_unknown_template = __offset, __len) static const struct exthdr_desc tcpopt_eol = { .name = "eol", - .type = TCPOPT_EOL, + .type = TCPOPT_KIND_EOL, .templates = { - [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), }, }; static const struct exthdr_desc tcpopt_nop = { - .name = "noop", - .type = TCPOPT_NOP, + .name = "nop", + .type = TCPOPT_KIND_NOP, .templates = { - [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), }, }; static const struct exthdr_desc tcptopt_maxseg = { .name = "maxseg", - .type = TCPOPT_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), }, }; static const struct exthdr_desc tcpopt_window = { .name = "window", - .type = TCPOPT_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), }, }; static const struct exthdr_desc tcpopt_sack_permitted = { - .name = "sack-permitted", - .type = 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), }, }; static const struct exthdr_desc tcpopt_sack = { .name = "sack", - .type = TCPOPT_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), }, }; static const struct exthdr_desc tcpopt_timestamp = { .name = "timestamp", - .type = TCPOPT_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 -#define TCPOPT_OBSOLETE ((struct exthdr_desc *)NULL) -#define TCPOPT_ECHO 6 -#define TCPOPT_ECHO_REPLY 7 -static const struct exthdr_desc *tcpopt_protocols[] = { - [TCPOPT_EOL] = &tcpopt_eol, - [TCPOPT_NOP] = &tcpopt_nop, - [TCPOPT_MAXSEG] = &tcptopt_maxseg, - [TCPOPT_WINDOW] = &tcpopt_window, - [TCPOPT_SACK_PERMITTED] = &tcpopt_sack_permitted, - [TCPOPT_SACK] = &tcpopt_sack, - [TCPOPT_ECHO] = TCPOPT_OBSOLETE, - [TCPOPT_ECHO_REPLY] = TCPOPT_OBSOLETE, - [TCPOPT_TIMESTAMP] = &tcpopt_timestamp, +static const struct exthdr_desc tcpopt_fastopen = { + .name = "fastopen", + .type = TCPOPT_KIND_FASTOPEN, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, }; -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 const struct exthdr_desc tcpopt_md5sig = { + .name = "md5sig", + .type = TCPOPT_KIND_MD5SIG, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; -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; - } -} +static const struct exthdr_desc tcpopt_mptcp = { + .name = "mptcp", + .type = TCPOPT_KIND_MPTCP, + .templates = { + [TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8), + [TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8), + [TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4), + }, +}; -const struct exthdr_desc *tcpopthdr_protocols[__TCPOPTHDR_MAX] = { - [TCPOPTHDR_EOL] = &tcpopt_eol, - [TCPOPTHDR_NOOP] = &tcpopt_nop, - [TCPOPTHDR_MAXSEG] = &tcptopt_maxseg, - [TCPOPTHDR_WINDOW] = &tcpopt_window, - [TCPOPTHDR_SACK_PERMITTED] = &tcpopt_sack_permitted, - [TCPOPTHDR_SACK0] = &tcpopt_sack, - [TCPOPTHDR_SACK1] = &tcpopt_sack, - [TCPOPTHDR_SACK2] = &tcpopt_sack, - [TCPOPTHDR_SACK3] = &tcpopt_sack, - [TCPOPTHDR_ECHO] = TCPOPT_OBSOLETE, - [TCPOPTHDR_ECHO_REPLY] = TCPOPT_OBSOLETE, - [TCPOPTHDR_TIMESTAMP] = &tcpopt_timestamp, +static const struct exthdr_desc tcpopt_fallback = { + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, }; +#undef PHT -static uint8_t tcpopt_optnum[] = { - [TCPOPTHDR_SACK0] = 0, - [TCPOPTHDR_SACK1] = 1, - [TCPOPTHDR_SACK2] = 2, - [TCPOPTHDR_SACK3] = 3, +const struct exthdr_desc *tcpopt_protocols[] = { + [TCPOPT_KIND_EOL] = &tcpopt_eol, + [TCPOPT_KIND_NOP] = &tcpopt_nop, + [TCPOPT_KIND_MAXSEG] = &tcptopt_maxseg, + [TCPOPT_KIND_WINDOW] = &tcpopt_window, + [TCPOPT_KIND_SACK_PERMITTED] = &tcpopt_sack_permitted, + [TCPOPT_KIND_SACK] = &tcpopt_sack, + [TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp, + [TCPOPT_KIND_MD5SIG] = &tcpopt_md5sig, + [TCPOPT_KIND_MPTCP] = &tcpopt_mptcp, + [TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen, }; -static uint8_t tcpopt_find_optnum(uint8_t optnum) +static void tcpopt_assign_tmpl(struct expr *expr, + const struct proto_hdr_template *tmpl, + const struct exthdr_desc *desc) { - if (optnum > TCPOPTHDR_SACK3) - return 0; + expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; - return tcpopt_optnum[optnum]; + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.offset = tmpl->offset; } -struct expr *tcpopt_expr_alloc(const struct location *loc, uint8_t type, - uint8_t field) +/** + * 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) { const struct proto_hdr_template *tmpl; - const struct exthdr_desc *desc; + const struct exthdr_desc *desc = NULL; struct expr *expr; - uint8_t optnum; - desc = tcpopthdr_protocols[type]; + switch (kind) { + case TCPOPT_KIND_SACK1: + kind = TCPOPT_KIND_SACK; + 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; + 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; + if (field == TCPOPT_SACK_LEFT) + field = TCPOPT_SACK_LEFT3; + else if (field == TCPOPT_SACK_RIGHT) + field = TCPOPT_SACK_RIGHT3; + break; + } + + if (kind < array_size(tcpopt_protocols)) + desc = tcpopt_protocols[kind]; + + if (!desc) { + if (kind > 255) + return NULL; + + desc = &tcpopt_fallback; + + switch (field) { + case TCPOPT_COMMON_KIND: + case TCPOPT_COMMON_LENGTH: + tmpl = &desc->templates[field]; + break; + default: + tmpl = &tcpopt_unknown_template; + break; + } + + expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type, + BYTEORDER_BIG_ENDIAN, 8); + + expr->exthdr.raw_type = kind; + tcpopt_assign_tmpl(expr, tmpl, desc); + return expr; + } + tmpl = &desc->templates[field]; - if (!tmpl) + if (!tmpl || !tmpl->dtype) return NULL; - optnum = tcpopt_find_optnum(type); - expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, BYTEORDER_BIG_ENDIAN, tmpl->len); - 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.raw_type = desc->type; + tcpopt_assign_tmpl(expr, tmpl, desc); 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; + expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; + expr->exthdr.tmpl = &tcpopt_unknown_template; + + if (flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + else + datatype_set(expr, &integer_type); + + if (type >= array_size(tcpopt_protocols) || + !tcpopt_protocols[type]) + return; - assert(type < array_size(tcpopt_protocols)); expr->exthdr.desc = tcpopt_protocols[type]; expr->exthdr.flags = flags; - assert(expr->exthdr.desc != TCPOPT_OBSOLETE); + 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; - 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; } } -bool tcpopt_find_template(struct expr *expr, const struct expr *mask, - unsigned int *shift) +bool tcpopt_find_template(struct expr *expr, unsigned int offset, unsigned int len) { if (expr->exthdr.tmpl != &tcpopt_unknown_template) return false; - tcpopt_init_raw(expr, expr->exthdr.desc->type, expr->exthdr.offset, - expr->len, 0); + tcpopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0); if (expr->exthdr.tmpl == &tcpopt_unknown_template) return false; |