summaryrefslogtreecommitdiffstats
path: root/src/tcpopt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tcpopt.c')
-rw-r--r--src/tcpopt.c301
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;