From 0e3871cfd9a1e32a4ac041ce87a8057b11a89924 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 4 May 2021 13:41:38 +0200 Subject: exthdr: Implement SCTP Chunk matching Extend exthdr expression to support scanning through SCTP packet chunks and matching on fixed fields' values. Signed-off-by: Phil Sutter Acked-by: Florian Westphal --- src/sctp_chunk.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 src/sctp_chunk.c (limited to 'src/sctp_chunk.c') diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c new file mode 100644 index 00000000..6e73e72f --- /dev/null +++ b/src/sctp_chunk.c @@ -0,0 +1,261 @@ +/* + * Copyright Red Hat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include +#include + +#include + +#define PHT(__token, __offset, __len) \ + PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \ + __offset, __len) + +static const struct exthdr_desc sctp_chunk_data = { + .name = "data", + .type = SCTP_CHUNK_TYPE_DATA, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_DATA_TSN] = PHT("tsn", 32, 32), + [SCTP_CHUNK_DATA_STREAM] = PHT("stream", 64, 16), + [SCTP_CHUNK_DATA_SSN] = PHT("ssn", 80, 16), + [SCTP_CHUNK_DATA_PPID] = PHT("ppid", 96, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_init = { + .name = "init", + .type = SCTP_CHUNK_TYPE_INIT, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32), + [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16), + [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16), + [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_init_ack = { + .name = "init-ack", + .type = SCTP_CHUNK_TYPE_INIT_ACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32), + [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16), + [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16), + [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_sack = { + .name = "sack", + .type = SCTP_CHUNK_TYPE_SACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_SACK_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32), + [SCTP_CHUNK_SACK_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_SACK_GACK_BLOCKS] = PHT("num-gap-ack-blocks", 96, 16), + [SCTP_CHUNK_SACK_DUP_TSNS] = PHT("num-dup-tsns", 112, 16), + }, +}; + +static const struct exthdr_desc sctp_chunk_shutdown = { + .name = "shutdown", + .type = SCTP_CHUNK_TYPE_SHUTDOWN, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_SHUTDOWN_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_ecne = { + .name = "ecne", + .type = SCTP_CHUNK_TYPE_ECNE, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_cwr = { + .name = "cwr", + .type = SCTP_CHUNK_TYPE_CWR, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_asconf_ack = { + .name = "asconf-ack", + .type = SCTP_CHUNK_TYPE_ASCONF_ACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_forward_tsn = { + .name = "forward-tsn", + .type = SCTP_CHUNK_TYPE_FORWARD_TSN, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_FORWARD_TSN_NCTSN] = PHT("new-cum-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_asconf = { + .name = "asconf", + .type = SCTP_CHUNK_TYPE_ASCONF, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32), + }, +}; + +#define SCTP_CHUNK_DESC_GENERATOR(descname, hname, desctype) \ +static const struct exthdr_desc sctp_chunk_##descname = { \ + .name = #hname, \ + .type = SCTP_CHUNK_TYPE_##desctype, \ + .templates = { \ + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), \ + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), \ + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),\ + }, \ +}; + +SCTP_CHUNK_DESC_GENERATOR(heartbeat, heartbeat, HEARTBEAT) +SCTP_CHUNK_DESC_GENERATOR(heartbeat_ack, heartbeat-ack, HEARTBEAT_ACK) +SCTP_CHUNK_DESC_GENERATOR(abort, abort, ABORT) +SCTP_CHUNK_DESC_GENERATOR(shutdown_ack, shutdown-ack, SHUTDOWN_ACK) +SCTP_CHUNK_DESC_GENERATOR(error, error, ERROR) +SCTP_CHUNK_DESC_GENERATOR(cookie_echo, cookie-echo, COOKIE_ECHO) +SCTP_CHUNK_DESC_GENERATOR(cookie_ack, cookie-ack, COOKIE_ACK) +SCTP_CHUNK_DESC_GENERATOR(shutdown_complete, shutdown-complete, SHUTDOWN_COMPLETE) + +#undef SCTP_CHUNK_DESC_GENERATOR + +static const struct exthdr_desc *sctp_chunk_protocols[] = { + [SCTP_CHUNK_TYPE_DATA] = &sctp_chunk_data, + [SCTP_CHUNK_TYPE_INIT] = &sctp_chunk_init, + [SCTP_CHUNK_TYPE_INIT_ACK] = &sctp_chunk_init_ack, + [SCTP_CHUNK_TYPE_SACK] = &sctp_chunk_sack, + [SCTP_CHUNK_TYPE_HEARTBEAT] = &sctp_chunk_heartbeat, + [SCTP_CHUNK_TYPE_HEARTBEAT_ACK] = &sctp_chunk_heartbeat_ack, + [SCTP_CHUNK_TYPE_ABORT] = &sctp_chunk_abort, + [SCTP_CHUNK_TYPE_SHUTDOWN] = &sctp_chunk_shutdown, + [SCTP_CHUNK_TYPE_SHUTDOWN_ACK] = &sctp_chunk_shutdown_ack, + [SCTP_CHUNK_TYPE_ERROR] = &sctp_chunk_error, + [SCTP_CHUNK_TYPE_COOKIE_ECHO] = &sctp_chunk_cookie_echo, + [SCTP_CHUNK_TYPE_COOKIE_ACK] = &sctp_chunk_cookie_ack, + [SCTP_CHUNK_TYPE_ECNE] = &sctp_chunk_ecne, + [SCTP_CHUNK_TYPE_CWR] = &sctp_chunk_cwr, + [SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE] = &sctp_chunk_shutdown_complete, + [SCTP_CHUNK_TYPE_ASCONF_ACK] = &sctp_chunk_asconf_ack, + [SCTP_CHUNK_TYPE_FORWARD_TSN] = &sctp_chunk_forward_tsn, + [SCTP_CHUNK_TYPE_ASCONF] = &sctp_chunk_asconf, +}; + +const struct exthdr_desc *sctp_chunk_protocol_find(const char *name) +{ + unsigned int i; + + for (i = 0; i < array_size(sctp_chunk_protocols); i++) { + if (sctp_chunk_protocols[i] && + !strcmp(sctp_chunk_protocols[i]->name, name)) + return sctp_chunk_protocols[i]; + } + return NULL; +} + +struct expr *sctp_chunk_expr_alloc(const struct location *loc, + unsigned int type, unsigned int field) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc = NULL; + struct expr *expr; + + if (type < array_size(sctp_chunk_protocols)) + desc = sctp_chunk_protocols[type]; + + if (!desc) + return NULL; + + tmpl = &desc->templates[field]; + if (!tmpl) + return NULL; + + 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_SCTP; + expr->exthdr.raw_type = desc->type; + expr->exthdr.offset = tmpl->offset; + + return expr; +} + +void sctp_chunk_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; + + assert(expr->etype == EXPR_EXTHDR); + + expr->len = len; + expr->exthdr.flags = flags; + expr->exthdr.offset = off; + expr->exthdr.op = NFT_EXTHDR_OP_SCTP; + + if (flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + else + datatype_set(expr, &integer_type); + + if (type >= array_size(sctp_chunk_protocols)) + return; + + expr->exthdr.desc = sctp_chunk_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]; + if (tmpl->offset != off || tmpl->len != len) + continue; + + if ((flags & NFT_EXTHDR_F_PRESENT) == 0) + datatype_set(expr, tmpl->dtype); + + expr->exthdr.tmpl = tmpl; + break; + } +} -- cgit v1.2.3