summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhil Sutter <phil@nwl.cc>2021-05-04 13:41:38 +0200
committerPhil Sutter <phil@nwl.cc>2021-05-19 11:52:05 +0200
commit0e3871cfd9a1e32a4ac041ce87a8057b11a89924 (patch)
tree926b57779c199024a9d8e1ceb8fd031ff28f88b7 /src
parentfd81d3ec3ae8b8d1d54a708d63b2dab2c8508c90 (diff)
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 <phil@nwl.cc> Acked-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/evaluate.c1
-rw-r--r--src/exthdr.c8
-rw-r--r--src/json.c2
-rw-r--r--src/parser_bison.y148
-rw-r--r--src/parser_json.c49
-rw-r--r--src/scanner.l38
-rw-r--r--src/sctp_chunk.c261
8 files changed, 506 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2f6d434b..01c12c81 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,7 @@ libnftables_la_SOURCES = \
tcpopt.c \
socket.c \
print.c \
+ sctp_chunk.c \
libnftables.c \
libnftables.map
diff --git a/src/evaluate.c b/src/evaluate.c
index a3a1d1c0..006b04af 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -568,6 +568,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
+ case NFT_EXTHDR_OP_SCTP:
return __expr_evaluate_exthdr(ctx, exprp);
case NFT_EXTHDR_OP_IPV4:
dependency = &proto_ip;
diff --git a/src/exthdr.c b/src/exthdr.c
index b0243ada..22a08b0c 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -22,6 +22,7 @@
#include <headers.h>
#include <expression.h>
#include <statement.h>
+#include <sctp_chunk.h>
static const struct exthdr_desc *exthdr_definitions[PROTO_DESC_MAX + 1] = {
[EXTHDR_DESC_HBH] = &exthdr_hbh,
@@ -75,6 +76,11 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
+ } else if (expr->exthdr.op == NFT_EXTHDR_OP_SCTP) {
+ nft_print(octx, "sctp chunk %s", expr->exthdr.desc->name);
+ if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
+ return;
+ nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
nft_print(octx, "exthdr %s", expr->exthdr.desc->name);
@@ -291,6 +297,8 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
return tcpopt_init_raw(expr, type, offset, len, flags);
if (op == NFT_EXTHDR_OP_IPV4)
return ipopt_init_raw(expr, type, offset, len, flags, true);
+ if (op == NFT_EXTHDR_OP_SCTP)
+ return sctp_chunk_init_raw(expr, type, offset, len, flags);
expr->len = len;
expr->exthdr.flags = flags;
diff --git a/src/json.c b/src/json.c
index b611c3e0..a3d1008f 100644
--- a/src/json.c
+++ b/src/json.c
@@ -719,6 +719,8 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_IPV4:
return json_pack("{s:o}", "ip option", root);
+ case NFT_EXTHDR_OP_SCTP:
+ return json_pack("{s:o}", "sctp chunk", root);
default:
return json_pack("{s:o}", "exthdr", root);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index cc440c69..957fb528 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -38,6 +38,7 @@
#include <utils.h>
#include <parser.h>
#include <erec.h>
+#include <sctp_chunk.h>
#include "parser_bison.h"
@@ -417,6 +418,40 @@ int nft_lex(void *, void *, void *);
%token DCCP "dccp"
%token SCTP "sctp"
+%token CHUNK "chunk"
+%token DATA "data"
+%token INIT "init"
+%token INIT_ACK "init-ack"
+%token HEARTBEAT "heartbeat"
+%token HEARTBEAT_ACK "heartbeat-ack"
+%token ABORT "abort"
+%token SHUTDOWN "shutdown"
+%token SHUTDOWN_ACK "shutdown-ack"
+%token ERROR "error"
+%token COOKIE_ECHO "cookie-echo"
+%token COOKIE_ACK "cookie-ack"
+%token ECNE "ecne"
+%token CWR "cwr"
+%token SHUTDOWN_COMPLETE "shutdown-complete"
+%token ASCONF_ACK "asconf-ack"
+%token FORWARD_TSN "forward-tsn"
+%token ASCONF "asconf"
+%token TSN "tsn"
+%token STREAM "stream"
+%token SSN "ssn"
+%token PPID "ppid"
+%token INIT_TAG "init-tag"
+%token A_RWND "a-rwnd"
+%token NUM_OSTREAMS "num-outbound-streams"
+%token NUM_ISTREAMS "num-inbound-streams"
+%token INIT_TSN "initial-tsn"
+%token CUM_TSN_ACK "cum-tsn-ack"
+%token NUM_GACK_BLOCKS "num-gap-ack-blocks"
+%token NUM_DUP_TSNS "num-dup-tsns"
+%token LOWEST_TSN "lowest-tsn"
+%token SEQNO "seqno"
+%token NEW_CUM_TSN "new-cum-tsn"
+
%token VTAG "vtag"
%token RT "rt"
@@ -768,9 +803,12 @@ int nft_lex(void *, void *, void *);
%type <expr> udp_hdr_expr udplite_hdr_expr
%destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr
%type <val> udp_hdr_field udplite_hdr_field
-%type <expr> dccp_hdr_expr sctp_hdr_expr
-%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr
+%type <expr> dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
+%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
%type <val> dccp_hdr_field sctp_hdr_field
+%type <val> sctp_chunk_type sctp_chunk_common_field
+%type <val> sctp_chunk_data_field sctp_chunk_init_field
+%type <val> sctp_chunk_sack_field
%type <expr> th_hdr_expr
%destructor { expr_free($$); } th_hdr_expr
%type <val> th_hdr_field
@@ -881,6 +919,7 @@ close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); };
close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); };
close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); };
close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); };
+close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); };
close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); };
close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); }
@@ -5396,10 +5435,115 @@ dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
| TYPE { $$ = DCCPHDR_TYPE; }
;
+sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
+ | INIT { $$ = SCTP_CHUNK_TYPE_INIT; }
+ | INIT_ACK { $$ = SCTP_CHUNK_TYPE_INIT_ACK; }
+ | SACK { $$ = SCTP_CHUNK_TYPE_SACK; }
+ | HEARTBEAT { $$ = SCTP_CHUNK_TYPE_HEARTBEAT; }
+ | HEARTBEAT_ACK { $$ = SCTP_CHUNK_TYPE_HEARTBEAT_ACK; }
+ | ABORT { $$ = SCTP_CHUNK_TYPE_ABORT; }
+ | SHUTDOWN { $$ = SCTP_CHUNK_TYPE_SHUTDOWN; }
+ | SHUTDOWN_ACK { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_ACK; }
+ | ERROR { $$ = SCTP_CHUNK_TYPE_ERROR; }
+ | COOKIE_ECHO { $$ = SCTP_CHUNK_TYPE_COOKIE_ECHO; }
+ | COOKIE_ACK { $$ = SCTP_CHUNK_TYPE_COOKIE_ACK; }
+ | ECNE { $$ = SCTP_CHUNK_TYPE_ECNE; }
+ | CWR { $$ = SCTP_CHUNK_TYPE_CWR; }
+ | SHUTDOWN_COMPLETE { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE; }
+ | ASCONF_ACK { $$ = SCTP_CHUNK_TYPE_ASCONF_ACK; }
+ | FORWARD_TSN { $$ = SCTP_CHUNK_TYPE_FORWARD_TSN; }
+ | ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; }
+ ;
+
+sctp_chunk_common_field : TYPE { $$ = SCTP_CHUNK_COMMON_TYPE; }
+ | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
+ | LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
+ ;
+
+sctp_chunk_data_field : TSN { $$ = SCTP_CHUNK_DATA_TSN; }
+ | STREAM { $$ = SCTP_CHUNK_DATA_STREAM; }
+ | SSN { $$ = SCTP_CHUNK_DATA_SSN; }
+ | PPID { $$ = SCTP_CHUNK_DATA_PPID; }
+ ;
+
+sctp_chunk_init_field : INIT_TAG { $$ = SCTP_CHUNK_INIT_TAG; }
+ | A_RWND { $$ = SCTP_CHUNK_INIT_RWND; }
+ | NUM_OSTREAMS { $$ = SCTP_CHUNK_INIT_OSTREAMS; }
+ | NUM_ISTREAMS { $$ = SCTP_CHUNK_INIT_ISTREAMS; }
+ | INIT_TSN { $$ = SCTP_CHUNK_INIT_TSN; }
+ ;
+
+sctp_chunk_sack_field : CUM_TSN_ACK { $$ = SCTP_CHUNK_SACK_CTSN_ACK; }
+ | A_RWND { $$ = SCTP_CHUNK_SACK_RWND; }
+ | NUM_GACK_BLOCKS { $$ = SCTP_CHUNK_SACK_GACK_BLOCKS; }
+ | NUM_DUP_TSNS { $$ = SCTP_CHUNK_SACK_DUP_TSNS; }
+ ;
+
+sctp_chunk_alloc : sctp_chunk_type
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, SCTP_CHUNK_COMMON_TYPE);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ | sctp_chunk_type sctp_chunk_common_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, $2);
+ }
+ | DATA sctp_chunk_data_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_DATA, $2);
+ }
+ | INIT sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT, $2);
+ }
+ | INIT_ACK sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT_ACK, $2);
+ }
+ | SACK sctp_chunk_sack_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SACK, $2);
+ }
+ | SHUTDOWN CUM_TSN_ACK
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SHUTDOWN,
+ SCTP_CHUNK_SHUTDOWN_CTSN_ACK);
+ }
+ | ECNE LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ECNE,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | CWR LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_CWR,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | ASCONF_ACK SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF_ACK,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ | FORWARD_TSN NEW_CUM_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_FORWARD_TSN,
+ SCTP_CHUNK_FORWARD_TSN_NCTSN);
+ }
+ | ASCONF SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ ;
+
sctp_hdr_expr : SCTP sctp_hdr_field close_scope_sctp
{
$$ = payload_expr_alloc(&@$, &proto_sctp, $2);
}
+ | SCTP CHUNK sctp_chunk_alloc close_scope_sctp_chunk close_scope_sctp
+ {
+ $$ = $3;
+ }
;
sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
diff --git a/src/parser_json.c b/src/parser_json.c
index 17bb10c3..9b79497c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -11,6 +11,7 @@
#include <netlink.h>
#include <parser.h>
#include <rule.h>
+#include <sctp_chunk.h>
#include <socket.h>
#include <netdb.h>
@@ -707,6 +708,53 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
return ipopt_expr_alloc(int_loc, descval, fieldval, 0);
}
+static int json_parse_sctp_chunk_field(const struct exthdr_desc *desc,
+ const char *name, int *val)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_size(desc->templates); i++) {
+ if (desc->templates[i].token &&
+ !strcmp(desc->templates[i].token, name)) {
+ if (val)
+ *val = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ const struct exthdr_desc *desc;
+ const char *name, *field;
+ struct expr *expr;
+ int fieldval;
+
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &name))
+ return NULL;
+
+ desc = sctp_chunk_protocol_find(name);
+ if (!desc) {
+ json_error(ctx, "Unknown sctp chunk name '%s'.", name);
+ return NULL;
+ }
+
+ if (json_unpack(root, "{s:s}", "field", &field)) {
+ expr = sctp_chunk_expr_alloc(int_loc, desc->type,
+ SCTP_CHUNK_COMMON_TYPE);
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+
+ return expr;
+ }
+ if (json_parse_sctp_chunk_field(desc, field, &fieldval)) {
+ json_error(ctx, "Unknown sctp chunk field '%s'.", field);
+ return NULL;
+ }
+ return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval);
+}
+
static const struct exthdr_desc *exthdr_lookup_byname(const char *name)
{
const struct exthdr_desc *exthdr_tbl[] = {
@@ -1412,6 +1460,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
+ { "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
{ "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
diff --git a/src/scanner.l b/src/scanner.l
index d568847d..5c493e39 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -213,6 +213,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_EXPR_NUMGEN
%s SCANSTATE_EXPR_QUEUE
%s SCANSTATE_EXPR_RT
+%s SCANSTATE_EXPR_SCTP_CHUNK
%s SCANSTATE_EXPR_SOCKET
%s SCANSTATE_STMT_LOG
@@ -530,9 +531,46 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
<SCANSTATE_SCTP>{
+ "chunk" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; }
"vtag" { return VTAG; }
}
+<SCANSTATE_EXPR_SCTP_CHUNK>{
+ "data" { return DATA; }
+ "init" { return INIT; }
+ "init-ack" { return INIT_ACK; }
+ "heartbeat" { return HEARTBEAT; }
+ "heartbeat-ack" { return HEARTBEAT_ACK; }
+ "abort" { return ABORT; }
+ "shutdown" { return SHUTDOWN; }
+ "shutdown-ack" { return SHUTDOWN_ACK; }
+ "error" { return ERROR; }
+ "cookie-echo" { return COOKIE_ECHO; }
+ "cookie-ack" { return COOKIE_ACK; }
+ "ecne" { return ECNE; }
+ "cwr" { return CWR; }
+ "shutdown-complete" { return SHUTDOWN_COMPLETE; }
+ "asconf-ack" { return ASCONF_ACK; }
+ "forward-tsn" { return FORWARD_TSN; }
+ "asconf" { return ASCONF; }
+
+ "tsn" { return TSN; }
+ "stream" { return STREAM; }
+ "ssn" { return SSN; }
+ "ppid" { return PPID; }
+ "init-tag" { return INIT_TAG; }
+ "a-rwnd" { return A_RWND; }
+ "num-outbound-streams" { return NUM_OSTREAMS; }
+ "num-inbound-streams" { return NUM_ISTREAMS; }
+ "initial-tsn" { return INIT_TSN; }
+ "cum-tsn-ack" { return CUM_TSN_ACK; }
+ "num-gap-ack-blocks" { return NUM_GACK_BLOCKS; }
+ "num-dup-tsns" { return NUM_DUP_TSNS; }
+ "lowest-tsn" { return LOWEST_TSN; }
+ "seqno" { return SEQNO; }
+ "new-cum-tsn" { return NEW_CUM_TSN; }
+}
+
"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
"rt0" { return RT0; }
"rt2" { return RT2; }
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 <exthdr.h>
+#include <sctp_chunk.h>
+
+#include <string.h>
+
+#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;
+ }
+}