From 5a8d6197929e30520bb3839c9165d89930888daf Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 13 Jul 2021 13:54:42 +0200 Subject: [PATCH] exthdr: Implement SCTP Chunk matching Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1979334 Upstream Status: nftables commit 0e3871cfd9a1e Conflicts: * Context changes due to missing other scopes. * Context change due to missing commit 6e6ef00028f1c ("exthdr: remove tcp dependency for tcp option matching"). commit 0e3871cfd9a1e32a4ac041ce87a8057b11a89924 Author: Phil Sutter Date: Tue May 4 13:41:38 2021 +0200 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 --- doc/libnftables-json.adoc | 13 + doc/payload-expression.txt | 53 +++ include/linux/netfilter/nf_tables.h | 2 + include/parser.h | 1 + include/sctp_chunk.h | 87 +++++ src/Makefile.am | 1 + src/evaluate.c | 2 + src/exthdr.c | 8 + src/json.c | 2 + src/parser_bison.y | 148 ++++++++- src/parser_json.c | 49 +++ src/scanner.l | 38 +++ src/sctp_chunk.c | 261 +++++++++++++++ tests/py/inet/sctp.t | 37 +++ tests/py/inet/sctp.t.json | 478 ++++++++++++++++++++++++++++ tests/py/inet/sctp.t.payload | 155 +++++++++ 16 files changed, 1333 insertions(+), 2 deletions(-) create mode 100644 include/sctp_chunk.h create mode 100644 src/sctp_chunk.c diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc index 858abbf..fba4cb0 100644 --- a/doc/libnftables-json.adoc +++ b/doc/libnftables-json.adoc @@ -1200,6 +1200,19 @@ Create a reference to a field (*field*) of a TCP option header (*name*). If the *field* property is not given, the expression is to be used as a TCP option existence check in a *match* statement with a boolean on the right hand side. +=== SCTP CHUNK +[verse] +*{ "sctp chunk": { + "name":* 'STRING'*, + "field":* 'STRING' +*}}* + +Create a reference to a field (*field*) of an SCTP chunk (*name*). + +If the *field* property is not given, the expression is to be used as an SCTP +chunk existence check in a *match* statement with a boolean on the right hand +side. + === META [verse] ____ diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt index b6d2a28..bd03ca2 100644 --- a/doc/payload-expression.txt +++ b/doc/payload-expression.txt @@ -369,7 +369,33 @@ integer (16 bit) SCTP HEADER EXPRESSION ~~~~~~~~~~~~~~~~~~~~~~~ [verse] +____ *sctp* {*sport* | *dport* | *vtag* | *checksum*} +*sctp chunk* 'CHUNK' [ 'FIELD' ] + +'CHUNK' := *data* | *init* | *init-ack* | *sack* | *heartbeat* | + *heartbeat-ack* | *abort* | *shutdown* | *shutdown-ack* | *error* | + *cookie-echo* | *cookie-ack* | *ecne* | *cwr* | *shutdown-complete* + | *asconf-ack* | *forward-tsn* | *asconf* + +'FIELD' := 'COMMON_FIELD' | 'DATA_FIELD' | 'INIT_FIELD' | 'INIT_ACK_FIELD' | + 'SACK_FIELD' | 'SHUTDOWN_FIELD' | 'ECNE_FIELD' | 'CWR_FIELD' | + 'ASCONF_ACK_FIELD' | 'FORWARD_TSN_FIELD' | 'ASCONF_FIELD' + +'COMMON_FIELD' := *type* | *flags* | *length* +'DATA_FIELD' := *tsn* | *stream* | *ssn* | *ppid* +'INIT_FIELD' := *init-tag* | *a-rwnd* | *num-outbound-streams* | + *num-inbound-streams* | *initial-tsn* +'INIT_ACK_FIELD' := 'INIT_FIELD' +'SACK_FIELD' := *cum-tsn-ack* | *a-rwnd* | *num-gap-ack-blocks* | + *num-dup-tsns* +'SHUTDOWN_FIELD' := *cum-tsn-ack* +'ECNE_FIELD' := *lowest-tsn* +'CWR_FIELD' := *lowest-tsn* +'ASCONF_ACK_FIELD' := *seqno* +'FORWARD_TSN_FIELD' := *new-cum-tsn* +'ASCONF_FIELD' := *seqno* +____ .SCTP header expression [options="header"] @@ -387,8 +413,35 @@ integer (32 bit) |checksum| Checksum| integer (32 bit) +|chunk| +Search chunk in packet| +without 'FIELD', boolean indicating existence |================ +.SCTP chunk fields +[options="header"] +|================== +|Name| Width in bits | Chunk | Notes +|type| 8 | all | not useful, defined by chunk type +|flags| 8 | all | semantics defined on per-chunk basis +|length| 16 | all | length of this chunk in bytes excluding padding +|tsn| 32 | data | transmission sequence number +|stream| 16 | data | stream identifier +|ssn| 16 | data | stream sequence number +|ppid| 32 | data | payload protocol identifier +|init-tag| 32 | init, init-ack | initiate tag +|a-rwnd| 32 | init, init-ack, sack | advertised receiver window credit +|num-outbound-streams| 16 | init, init-ack | number of outbound streams +|num-inbound-streams| 16 | init, init-ack | number of inbound streams +|initial-tsn| 32 | init, init-ack | initial transmit sequence number +|cum-tsn-ack| 32 | sack, shutdown | cumulative transmission sequence number acknowledged +|num-gap-ack-blocks| 16 | sack | number of Gap Ack Blocks included +|num-dup-tsns| 16 | sack | number of duplicate transmission sequence numbers received +|lowest-tsn| 32 | ecne, cwr | lowest transmission sequence number +|seqno| 32 | asconf-ack, asconf | sequence number +|new-cum-tsn| 32 | forward-tsn | new cumulative transmission sequence number +|================== + DCCP HEADER EXPRESSION ~~~~~~~~~~~~~~~~~~~~~~ [verse] diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 1328b8e..960a5b4 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -755,11 +755,13 @@ enum nft_exthdr_flags { * @NFT_EXTHDR_OP_IPV6: match against ipv6 extension headers * @NFT_EXTHDR_OP_TCP: match against tcp options * @NFT_EXTHDR_OP_IPV4: match against ipv4 options + * @NFT_EXTHDR_OP_SCTP: match against sctp chunks */ enum nft_exthdr_op { NFT_EXTHDR_OP_IPV6, NFT_EXTHDR_OP_TCPOPT, NFT_EXTHDR_OP_IPV4, + NFT_EXTHDR_OP_SCTP, __NFT_EXTHDR_OP_MAX }; #define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1) diff --git a/include/parser.h b/include/parser.h index 2e6ef4d..99bed3c 100644 --- a/include/parser.h +++ b/include/parser.h @@ -33,6 +33,7 @@ enum startcond_type { PARSER_SC_SCTP, PARSER_SC_EXPR_HASH, PARSER_SC_EXPR_NUMGEN, + PARSER_SC_EXPR_SCTP_CHUNK, }; struct mnl_socket; diff --git a/include/sctp_chunk.h b/include/sctp_chunk.h new file mode 100644 index 0000000..3819200 --- /dev/null +++ b/include/sctp_chunk.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef NFTABLES_SCTP_CHUNK_H +#define NFTABLES_SCTP_CHUNK_H + +/* SCTP chunk types used on wire */ +enum sctp_hdr_chunk_types { + SCTP_CHUNK_TYPE_DATA = 0, + SCTP_CHUNK_TYPE_INIT = 1, + SCTP_CHUNK_TYPE_INIT_ACK = 2, + SCTP_CHUNK_TYPE_SACK = 3, + SCTP_CHUNK_TYPE_HEARTBEAT = 4, + SCTP_CHUNK_TYPE_HEARTBEAT_ACK = 5, + SCTP_CHUNK_TYPE_ABORT = 6, + SCTP_CHUNK_TYPE_SHUTDOWN = 7, + SCTP_CHUNK_TYPE_SHUTDOWN_ACK = 8, + SCTP_CHUNK_TYPE_ERROR = 9, + SCTP_CHUNK_TYPE_COOKIE_ECHO = 10, + SCTP_CHUNK_TYPE_COOKIE_ACK = 11, + SCTP_CHUNK_TYPE_ECNE = 12, + SCTP_CHUNK_TYPE_CWR = 13, + SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE = 14, + SCTP_CHUNK_TYPE_ASCONF_ACK = 128, + SCTP_CHUNK_TYPE_FORWARD_TSN = 192, + SCTP_CHUNK_TYPE_ASCONF = 193, +}; + +enum sctp_hdr_chunk_common_fields { + SCTP_CHUNK_COMMON_TYPE, + SCTP_CHUNK_COMMON_FLAGS, + SCTP_CHUNK_COMMON_LENGTH, + __SCTP_CHUNK_COMMON_MAX, +}; + +#define SCTP_CHUNK_START_INDEX __SCTP_CHUNK_COMMON_MAX + +enum sctp_hdr_chunk_data_fields { + SCTP_CHUNK_DATA_TSN = SCTP_CHUNK_START_INDEX, + SCTP_CHUNK_DATA_STREAM, + SCTP_CHUNK_DATA_SSN, + SCTP_CHUNK_DATA_PPID, +}; + +enum sctp_hdr_chunk_init_fields { + SCTP_CHUNK_INIT_TAG = SCTP_CHUNK_START_INDEX, + SCTP_CHUNK_INIT_RWND, + SCTP_CHUNK_INIT_OSTREAMS, + SCTP_CHUNK_INIT_ISTREAMS, + SCTP_CHUNK_INIT_TSN, +}; + +enum sctp_hdr_chunk_sack_fields { + SCTP_CHUNK_SACK_CTSN_ACK = SCTP_CHUNK_START_INDEX, + SCTP_CHUNK_SACK_RWND, + SCTP_CHUNK_SACK_GACK_BLOCKS, + SCTP_CHUNK_SACK_DUP_TSNS, +}; + +enum sctp_hdr_chunk_shutdown_fields { + SCTP_CHUNK_SHUTDOWN_CTSN_ACK = SCTP_CHUNK_START_INDEX, +}; + +enum sctp_hdr_chunk_ecne_cwr_fields { + SCTP_CHUNK_ECNE_CWR_MIN_TSN = SCTP_CHUNK_START_INDEX, +}; + +enum sctp_hdr_chunk_asconf_fields { + SCTP_CHUNK_ASCONF_SEQNO = SCTP_CHUNK_START_INDEX, +}; + +enum sctp_hdr_chunk_fwd_tsn_fields { + SCTP_CHUNK_FORWARD_TSN_NCTSN = SCTP_CHUNK_START_INDEX, +}; + +struct expr *sctp_chunk_expr_alloc(const struct location *loc, + unsigned int type, unsigned int field); +void sctp_chunk_init_raw(struct expr *expr, uint8_t type, unsigned int off, + unsigned int len, uint32_t flags); +const struct exthdr_desc *sctp_chunk_protocol_find(const char *name); + +#endif /* NFTABLES_SCTP_CHUNK_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 740c21f..366820b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,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 99a66c2..00ec20b 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -579,6 +579,8 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp) dependency = &proto_tcp; pb = PROTO_BASE_TRANSPORT_HDR; break; + case NFT_EXTHDR_OP_SCTP: + return __expr_evaluate_exthdr(ctx, exprp); case NFT_EXTHDR_OP_IPV4: dependency = &proto_ip; break; diff --git a/src/exthdr.c b/src/exthdr.c index 5c75720..f5689e7 100644 --- a/src/exthdr.c +++ b/src/exthdr.c @@ -22,6 +22,7 @@ #include #include #include +#include static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx) { @@ -55,6 +56,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); @@ -197,6 +203,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 a6d0716..dfc9031 100644 --- a/src/json.c +++ b/src/json.c @@ -692,6 +692,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 beb5995..5ab5744 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -38,6 +38,7 @@ #include #include #include +#include #include "parser_bison.h" @@ -402,6 +403,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" @@ -746,9 +781,12 @@ int nft_lex(void *, void *, void *); %type udp_hdr_expr udplite_hdr_expr %destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr %type udp_hdr_field udplite_hdr_field -%type dccp_hdr_expr sctp_hdr_expr -%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr +%type dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc +%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc %type dccp_hdr_field sctp_hdr_field +%type sctp_chunk_type sctp_chunk_common_field +%type sctp_chunk_data_field sctp_chunk_init_field +%type sctp_chunk_sack_field %type th_hdr_expr %destructor { expr_free($$); } th_hdr_expr %type th_hdr_field @@ -843,6 +881,7 @@ opt_newline : NEWLINE close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); }; close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); }; 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); }; common_block : INCLUDE QUOTED_STRING stmt_separator { @@ -4783,10 +4822,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 fbf7db5..a069a89 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -705,6 +706,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[] = { @@ -1410,6 +1458,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 b79ae55..fe1b8ad 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -199,6 +199,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) %s SCANSTATE_SCTP %s SCANSTATE_EXPR_HASH %s SCANSTATE_EXPR_NUMGEN +%s SCANSTATE_EXPR_SCTP_CHUNK %% @@ -492,9 +493,46 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; } { + "chunk" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; } "vtag" { return VTAG; } } +{ + "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" { 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 0000000..6e73e72 --- /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; + } +} diff --git a/tests/py/inet/sctp.t b/tests/py/inet/sctp.t index 5188b57..3d1c2fd 100644 --- a/tests/py/inet/sctp.t +++ b/tests/py/inet/sctp.t @@ -41,3 +41,40 @@ sctp vtag {33, 55, 67, 88};ok sctp vtag != {33, 55, 67, 88};ok sctp vtag { 33-55};ok sctp vtag != { 33-55};ok + +# assert all chunk types are recognized +sctp chunk data exists;ok +sctp chunk init exists;ok +sctp chunk init-ack exists;ok +sctp chunk sack exists;ok +sctp chunk heartbeat exists;ok +sctp chunk heartbeat-ack exists;ok +sctp chunk abort exists;ok +sctp chunk shutdown exists;ok +sctp chunk shutdown-ack exists;ok +sctp chunk error exists;ok +sctp chunk cookie-echo exists;ok +sctp chunk cookie-ack exists;ok +sctp chunk ecne exists;ok +sctp chunk cwr exists;ok +sctp chunk shutdown-complete exists;ok +sctp chunk asconf-ack exists;ok +sctp chunk forward-tsn exists;ok +sctp chunk asconf exists;ok + +# test common header fields in random chunk types +sctp chunk data type 0;ok +sctp chunk init flags 23;ok +sctp chunk init-ack length 42;ok + +# test one custom field in every applicable chunk type +sctp chunk data stream 1337;ok +sctp chunk init initial-tsn 5;ok +sctp chunk init-ack num-outbound-streams 3;ok +sctp chunk sack a-rwnd 1;ok +sctp chunk shutdown cum-tsn-ack 65535;ok +sctp chunk ecne lowest-tsn 5;ok +sctp chunk cwr lowest-tsn 8;ok +sctp chunk asconf-ack seqno 12345;ok +sctp chunk forward-tsn new-cum-tsn 31337;ok +sctp chunk asconf seqno 12345;ok diff --git a/tests/py/inet/sctp.t.json b/tests/py/inet/sctp.t.json index 2684b03..8135686 100644 --- a/tests/py/inet/sctp.t.json +++ b/tests/py/inet/sctp.t.json @@ -608,3 +608,481 @@ } ] +# sctp chunk data exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "data" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk init exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "init" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk init-ack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "init-ack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk sack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "sack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk heartbeat exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "heartbeat" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk heartbeat-ack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "heartbeat-ack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk abort exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "abort" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk shutdown exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "shutdown" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk shutdown-ack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "shutdown-ack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk error exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "error" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk cookie-echo exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "cookie-echo" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk cookie-ack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "cookie-ack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk ecne exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "ecne" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk cwr exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "cwr" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk shutdown-complete exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "shutdown-complete" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk asconf-ack exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "asconf-ack" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk forward-tsn exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "forward-tsn" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk asconf exists +[ + { + "match": { + "left": { + "sctp chunk": { + "name": "asconf" + } + }, + "op": "==", + "right": true + } + } +] + +# sctp chunk data type 0 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "type", + "name": "data" + } + }, + "op": "==", + "right": 0 + } + } +] + +# sctp chunk init flags 23 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "flags", + "name": "init" + } + }, + "op": "==", + "right": 23 + } + } +] + +# sctp chunk init-ack length 42 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "length", + "name": "init-ack" + } + }, + "op": "==", + "right": 42 + } + } +] + +# sctp chunk data stream 1337 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "stream", + "name": "data" + } + }, + "op": "==", + "right": 1337 + } + } +] + +# sctp chunk init initial-tsn 5 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "initial-tsn", + "name": "init" + } + }, + "op": "==", + "right": 5 + } + } +] + +# sctp chunk init-ack num-outbound-streams 3 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "num-outbound-streams", + "name": "init-ack" + } + }, + "op": "==", + "right": 3 + } + } +] + +# sctp chunk sack a-rwnd 1 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "a-rwnd", + "name": "sack" + } + }, + "op": "==", + "right": 1 + } + } +] + +# sctp chunk shutdown cum-tsn-ack 65535 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "cum-tsn-ack", + "name": "shutdown" + } + }, + "op": "==", + "right": 65535 + } + } +] + +# sctp chunk ecne lowest-tsn 5 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "lowest-tsn", + "name": "ecne" + } + }, + "op": "==", + "right": 5 + } + } +] + +# sctp chunk cwr lowest-tsn 8 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "lowest-tsn", + "name": "cwr" + } + }, + "op": "==", + "right": 8 + } + } +] + +# sctp chunk asconf-ack seqno 12345 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "seqno", + "name": "asconf-ack" + } + }, + "op": "==", + "right": 12345 + } + } +] + +# sctp chunk forward-tsn new-cum-tsn 31337 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "new-cum-tsn", + "name": "forward-tsn" + } + }, + "op": "==", + "right": 31337 + } + } +] + +# sctp chunk asconf seqno 12345 +[ + { + "match": { + "left": { + "sctp chunk": { + "field": "seqno", + "name": "asconf" + } + }, + "op": "==", + "right": 12345 + } + } +] + diff --git a/tests/py/inet/sctp.t.payload b/tests/py/inet/sctp.t.payload index ecfcc72..9c4854c 100644 --- a/tests/py/inet/sctp.t.payload +++ b/tests/py/inet/sctp.t.payload @@ -274,3 +274,158 @@ inet test-inet input [ payload load 4b @ transport header + 4 => reg 1 ] [ lookup reg 1 set __set%d 0x1 ] +# sctp chunk data exists +ip + [ exthdr load 1b @ 0 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk init exists +ip + [ exthdr load 1b @ 1 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk init-ack exists +ip + [ exthdr load 1b @ 2 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk sack exists +ip + [ exthdr load 1b @ 3 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk heartbeat exists +ip + [ exthdr load 1b @ 4 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk heartbeat-ack exists +ip + [ exthdr load 1b @ 5 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk abort exists +ip + [ exthdr load 1b @ 6 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk shutdown exists +ip + [ exthdr load 1b @ 7 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk shutdown-ack exists +ip + [ exthdr load 1b @ 8 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk error exists +ip + [ exthdr load 1b @ 9 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk cookie-echo exists +ip + [ exthdr load 1b @ 10 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk cookie-ack exists +ip + [ exthdr load 1b @ 11 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk ecne exists +ip + [ exthdr load 1b @ 12 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk cwr exists +ip + [ exthdr load 1b @ 13 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk shutdown-complete exists +ip + [ exthdr load 1b @ 14 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk asconf-ack exists +ip + [ exthdr load 1b @ 128 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk forward-tsn exists +ip + [ exthdr load 1b @ 192 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk asconf exists +ip + [ exthdr load 1b @ 193 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# sctp chunk data type 0 +ip + [ exthdr load 1b @ 0 + 0 => reg 1 ] + [ cmp eq reg 1 0x00000000 ] + +# sctp chunk init flags 23 +ip + [ exthdr load 1b @ 1 + 1 => reg 1 ] + [ cmp eq reg 1 0x00000017 ] + +# sctp chunk init-ack length 42 +ip + [ exthdr load 2b @ 2 + 2 => reg 1 ] + [ cmp eq reg 1 0x00002a00 ] + +# sctp chunk data stream 1337 +ip + [ exthdr load 2b @ 0 + 8 => reg 1 ] + [ cmp eq reg 1 0x00003905 ] + +# sctp chunk init initial-tsn 5 +ip + [ exthdr load 4b @ 1 + 16 => reg 1 ] + [ cmp eq reg 1 0x05000000 ] + +# sctp chunk init-ack num-outbound-streams 3 +ip + [ exthdr load 2b @ 2 + 12 => reg 1 ] + [ cmp eq reg 1 0x00000300 ] + +# sctp chunk sack a-rwnd 1 +ip + [ exthdr load 4b @ 3 + 8 => reg 1 ] + [ cmp eq reg 1 0x01000000 ] + +# sctp chunk shutdown cum-tsn-ack 65535 +ip + [ exthdr load 4b @ 7 + 4 => reg 1 ] + [ cmp eq reg 1 0xffff0000 ] + +# sctp chunk ecne lowest-tsn 5 +ip + [ exthdr load 4b @ 12 + 4 => reg 1 ] + [ cmp eq reg 1 0x05000000 ] + +# sctp chunk cwr lowest-tsn 8 +ip + [ exthdr load 4b @ 13 + 4 => reg 1 ] + [ cmp eq reg 1 0x08000000 ] + +# sctp chunk asconf-ack seqno 12345 +ip + [ exthdr load 4b @ 128 + 4 => reg 1 ] + [ cmp eq reg 1 0x39300000 ] + +# sctp chunk forward-tsn new-cum-tsn 31337 +ip + [ exthdr load 4b @ 192 + 4 => reg 1 ] + [ cmp eq reg 1 0x697a0000 ] + +# sctp chunk asconf seqno 12345 +ip + [ exthdr load 4b @ 193 + 4 => reg 1 ] + [ cmp eq reg 1 0x39300000 ] + -- 2.31.1