From 71b2d88655a05c9112fe2cf7d644122f0174ad0a Mon Sep 17 00:00:00 2001 From: Aomx <aomx@riseup.net> Date: Thu, 14 Jul 2016 11:24:17 +0200 Subject: [PATCH] first commit regarding support of nr-sack chunks --- gtests/net/packetdrill/lexer.l | 2 + gtests/net/packetdrill/parser.y | 21 ++- gtests/net/packetdrill/run_packet.c | 131 +++++++++++++++++- gtests/net/packetdrill/sctp.h | 26 ++++ gtests/net/packetdrill/sctp_chunk_to_string.c | 53 +++++++ gtests/net/packetdrill/sctp_packet.c | 125 +++++++++++++++++ gtests/net/packetdrill/sctp_packet.h | 12 ++ 7 files changed, 364 insertions(+), 6 deletions(-) diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index da93842b..62768487 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -541,6 +541,7 @@ DATA return DATA; INIT return INIT; INIT_ACK return INIT_ACK; SACK return SACK; +NR_SACK return NR_SACK; HEARTBEAT return HEARTBEAT; HEARTBEAT_ACK return HEARTBEAT_ACK; ABORT return ABORT; @@ -570,6 +571,7 @@ ppid return PPID; fsn return FSN; cum_tsn return CUM_TSN; gaps return GAPS; +nr_gaps return NR_GAPS; dups return DUPS; adaptation_code_point return ADAPTATION_CODE_POINT; OUTGOING_SSN_RESET return OUTGOING_SSN_RESET; diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index dc41710d..d89c874e 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -499,7 +499,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> MSG_NAME MSG_IOV MSG_FLAGS MSG_CONTROL CMSG_LEN CMSG_LEVEL CMSG_TYPE CMSG_DATA %token <reserved> SF_HDTR_HEADERS SF_HDTR_TRAILERS %token <reserved> FD EVENTS REVENTS ONOFF LINGER -%token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO +%token <reserved> ACK ECR EOL MSS NOP SACK NR_SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO %token <reserved> FAST_OPEN %token <reserved> IOV_BASE IOV_LEN %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN @@ -521,7 +521,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECNE CWR %token <reserved> SHUTDOWN_COMPLETE I_DATA PAD RECONFIG %token <reserved> TYPE FLAGS LEN -%token <reserved> TAG A_RWND OS IS TSN SID SSN MID PPID FSN CUM_TSN GAPS DUPS +%token <reserved> TAG A_RWND OS IS TSN SID SSN MID PPID FSN CUM_TSN GAPS NR_GAPS DUPS %token <reserved> PARAMETER HEARTBEAT_INFORMATION IPV4_ADDRESS IPV6_ADDRESS %token <reserved> STATE_COOKIE UNRECOGNIZED_PARAMETER COOKIE_PRESERVATIVE %token <reserved> HOSTNAME_ADDRESS SUPPORTED_ADDRESS_TYPES ECN_CAPABLE @@ -664,7 +664,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <chunk_list_item> sctp_generic_chunk_spec %type <chunk_list_item> sctp_data_chunk_spec %type <chunk_list_item> sctp_init_chunk_spec sctp_init_ack_chunk_spec -%type <chunk_list_item> sctp_sack_chunk_spec +%type <chunk_list_item> sctp_sack_chunk_spec sctp_nr_sack_chunk_spec %type <chunk_list_item> sctp_heartbeat_chunk_spec sctp_heartbeat_ack_chunk_spec %type <chunk_list_item> sctp_abort_chunk_spec %type <chunk_list_item> sctp_shutdown_chunk_spec @@ -720,7 +720,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <byte_list_item> byte %type <u16_list> u16_list %type <u16_item> u16_item -%type <sack_block_list> opt_gaps gap_list opt_dups dup_list +%type <sack_block_list> opt_gaps opt_nr_gaps gap_list opt_dups dup_list %type <sack_block_list_item> gap dup %type <address_type_list> address_types_list %type <address_type_list_item> address_type @@ -1039,6 +1039,7 @@ sctp_chunk_spec | sctp_init_chunk_spec { $$ = $1; } | sctp_init_ack_chunk_spec { $$ = $1; } | sctp_sack_chunk_spec { $$ = $1; } +| sctp_nr_sack_chunk_spec { $$ = $1; } | sctp_heartbeat_chunk_spec { $$ = $1; } | sctp_heartbeat_ack_chunk_spec { $$ = $1; } | sctp_abort_chunk_spec { $$ = $1; } @@ -1521,6 +1522,12 @@ opt_gaps | GAPS '=' '[' gap_list ']' { $$ = $4; } ; +opt_nr_gaps +: NR_GAPS '=' ELLIPSIS { $$ = NULL; } +| NR_GAPS '=' '[' ELLIPSIS ']' { $$ = NULL; } +| NR_GAPS '=' '[' gap_list ']' { $$ = $4; } +; + gap_list : { $$ = sctp_sack_block_list_new(); } | gap { $$ = sctp_sack_block_list_new(); @@ -1604,6 +1611,12 @@ sctp_sack_chunk_spec $$ = sctp_sack_chunk_new($3, $5, $7, $9, $11); } +sctp_nr_sack_chunk_spec +: NR_SACK '[' opt_flags ',' opt_cum_tsn ',' opt_a_rwnd ',' opt_gaps ',' opt_nr_gaps ',' opt_dups']' { + $$ = sctp_nr_sack_chunk_new($3, $5, $7, $9, $11, $13); +} + + sctp_heartbeat_chunk_spec : HEARTBEAT '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' { $$ = sctp_heartbeat_chunk_new($3, $5); diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 56fd664b..df55aa96 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -646,6 +646,7 @@ static int map_inbound_sctp_packet( struct sctp_init_chunk *init; struct sctp_init_ack_chunk *init_ack; struct sctp_sack_chunk *sack; + struct sctp_nr_sack_chunk *nr_sack; struct sctp_abort_chunk *abort; struct sctp_shutdown_chunk *shutdown; struct sctp_ecne_chunk *ecne; @@ -655,7 +656,7 @@ static int map_inbound_sctp_packet( struct sctp_reconfig_chunk *reconfig; u32 local_diff, remote_diff; u32 v_tag; - u16 nr_gap_blocks, nr_dup_tsns, i; + u16 nr_gap_blocks, number_of_nr_gap_blocks, nr_dup_tsns, i; bool reflect_v_tag; bool contains_init_chunk; @@ -710,6 +711,22 @@ static int map_inbound_sctp_packet( } } break; + case SCTP_NR_SACK_CHUNK_TYPE: + nr_sack = (struct sctp_nr_sack_chunk *)chunk; + DEBUGP("Old SACK cum TSN %d\n", ntohl(nr_sack->cum_tsn)); + nr_sack->cum_tsn = htonl(ntohl(nr_sack->cum_tsn) + local_diff); + DEBUGP("New SACK cum TSN %d\n", ntohl(nr_sack->cum_tsn)); + nr_gap_blocks = ntohs(nr_sack->nr_gap_blocks); + number_of_nr_gap_blocks = ntohs(nr_sack->nr_of_nr_gap_blocks); + nr_dup_tsns = ntohs(nr_sack->nr_dup_tsns); + + if (ntohs(nr_sack->length) == sizeof(struct sctp_nr_sack_chunk) + sizeof(union sctp_nr_sack_block) * (nr_dup_tsns+nr_gap_blocks)) { + for (i = 0; i < nr_dup_tsns; i++) { + u16 offset = nr_gap_blocks + number_of_nr_gap_blocks; + nr_sack->block[i + offset].tsn = htonl(ntohl(nr_sack->block[i + offset].tsn) + local_diff); + } + } + break; case SCTP_ABORT_CHUNK_TYPE: abort = (struct sctp_abort_chunk *)chunk; if (abort->flags & SCTP_ABORT_CHUNK_T_BIT) { @@ -943,13 +960,14 @@ static int map_outbound_live_sctp_packet( struct sctp_init_chunk *init; struct sctp_init_ack_chunk *init_ack; struct sctp_sack_chunk *sack; + struct sctp_nr_sack_chunk *nr_sack; struct sctp_shutdown_chunk *shutdown; struct sctp_ecne_chunk *ecne; struct sctp_cwr_chunk *cwr; struct sctp_i_data_chunk *i_data; struct sctp_reconfig_chunk *reconfig; u32 local_diff, remote_diff; - u16 nr_gap_blocks, nr_dup_tsns, i; + u16 nr_gap_blocks, nr_dup_tsns, number_of_nr_gap_blocks, i; /* FIXME: transform v-tag in the common header*/ DEBUGP("map_outbound_live_sctp_packet\n"); @@ -992,6 +1010,17 @@ static int map_outbound_live_sctp_packet( sack->block[i + nr_gap_blocks].tsn = htonl(ntohl(sack->block[i + nr_gap_blocks].tsn) + remote_diff); } break; + case SCTP_NR_SACK_CHUNK_TYPE: + nr_sack = (struct sctp_nr_sack_chunk *)chunk; + nr_sack->cum_tsn = htonl(ntohl(nr_sack->cum_tsn) + remote_diff); + nr_gap_blocks = ntohs(nr_sack->nr_gap_blocks); + number_of_nr_gap_blocks = ntohs(nr_sack->nr_of_nr_gap_blocks); + nr_dup_tsns = ntohs(nr_sack->nr_dup_tsns); + for (i = 0; i < nr_dup_tsns; i++) { + u16 offset = nr_gap_blocks + number_of_nr_gap_blocks; + nr_sack->block[i + offset].tsn = htonl(ntohl(nr_sack->block[i + offset].tsn) + remote_diff); + } + break; case SCTP_SHUTDOWN_CHUNK_TYPE: shutdown = (struct sctp_shutdown_chunk *)chunk; shutdown->cum_tsn = htonl(ntohl(shutdown->cum_tsn) + remote_diff); @@ -1812,6 +1841,99 @@ static int verify_sack_chunk(struct sctp_sack_chunk *actual_chunk, return STATUS_OK; } +static int verify_nr_sack_chunk(struct sctp_nr_sack_chunk *actual_chunk, + struct sctp_nr_sack_chunk *script_chunk, + u32 flags, char **error) +{ + u16 actual_nr_gap_blocks, actual_nr_of_nr_gap_blocks, actual_nr_dup_tsns; + u16 script_nr_gap_blocks, script_nr_of_nr_gap_blocks, script_nr_dup_tsns; + u16 i, actual_base, script_base; + + actual_nr_gap_blocks = ntohs(actual_chunk->nr_gap_blocks); + actual_nr_of_nr_gap_blocks = ntohs(actual_chunk->nr_of_nr_gap_blocks); + actual_nr_dup_tsns = ntohs(actual_chunk->nr_dup_tsns); + script_nr_gap_blocks = ntohs(script_chunk->nr_gap_blocks); + script_nr_of_nr_gap_blocks = ntohs(script_chunk->nr_of_nr_gap_blocks); + script_nr_dup_tsns = ntohs(script_chunk->nr_dup_tsns); + + script_base = 0; + + if ((flags & FLAG_NR_SACK_CHUNK_CUM_TSN_NOCHECK ? STATUS_OK : + check_field("sctp_nr_sack_chunk_cum_tsn", + ntohl(script_chunk->cum_tsn), + ntohl(actual_chunk->cum_tsn), + error)) || + (flags & FLAG_NR_SACK_CHUNK_A_RWND_NOCHECK ? STATUS_OK : + check_field("sctp_nr_sack_chunk_a_rwnd", + ntohl(script_chunk->a_rwnd), + ntohl(actual_chunk->a_rwnd), + error)) || + (flags & FLAG_NR_SACK_CHUNK_GAP_BLOCKS_NOCHECK? STATUS_OK : + check_field("sctp_nr_sack_chunk_nr_gap_blocks", + script_nr_gap_blocks, + actual_nr_gap_blocks, + error)) || + (flags & FLAG_NR_SACK_CHUNK_NR_GAP_BLOCKS_NOCHECK? STATUS_OK : + check_field("sctp_nr_sack_chunk_nr_of_nr_gap_blocks", + script_nr_of_nr_gap_blocks, + actual_nr_of_nr_gap_blocks, + error)) || + (flags & FLAG_NR_SACK_CHUNK_DUP_TSNS_NOCHECK? STATUS_OK : + check_field("sctp_nr_sack_chunk_nr_dup_tsns", + script_nr_dup_tsns, + actual_nr_dup_tsns, + error))) { + return STATUS_ERR; + } + + if ((flags & FLAG_NR_SACK_CHUNK_GAP_BLOCKS_NOCHECK) == 0) { + for (i = 0; i < script_nr_gap_blocks; i++) { + if (check_field("sctp_sack_chunk_gap_block_start", + ntohs(script_chunk->block[i].gap.start), + ntohs(actual_chunk->block[i].gap.start), + error) || + check_field("sctp_sack_chunk_gap_block_end", + ntohs(script_chunk->block[i].gap.end), + ntohs(actual_chunk->block[i].gap.end), + error)) { + return STATUS_ERR; + } + } + script_base += actual_nr_gap_blocks; + } + + if ((flags & FLAG_NR_SACK_CHUNK_NR_GAP_BLOCKS_NOCHECK) == 0) { + actual_base = actual_nr_gap_blocks; + for (i = 0; i < script_nr_of_nr_gap_blocks; i++) { + if (check_field("sctp_sack_chunk_nr_gap_block_start", + ntohs(script_chunk->block[script_base + i].gap.start), + ntohs(actual_chunk->block[actual_base + i].gap.start), + error) || + check_field("sctp_sack_chunk_nr_gap_block_end", + ntohs(script_chunk->block[script_base + i].gap.end), + ntohs(actual_chunk->block[actual_base + i].gap.end), + error)) { + return STATUS_ERR; + } + } + script_base += script_nr_of_nr_gap_blocks; + } + + + if ((flags & FLAG_NR_SACK_CHUNK_DUP_TSNS_NOCHECK) == 0) { + actual_base = actual_nr_gap_blocks + actual_nr_of_nr_gap_blocks; + for (i = 0; i < script_nr_dup_tsns; i++) { + if (check_field("sctp_sack_chunk_dup_tsn", + ntohl(script_chunk->block[script_base + i].tsn), + ntohl(actual_chunk->block[actual_base + i].tsn), + error)) { + return STATUS_ERR; + } + } + } + return STATUS_OK; +} + static int verify_heartbeat_chunk(struct sctp_heartbeat_chunk *actual_chunk, struct sctp_heartbeat_chunk *script_chunk, u32 flags, char **error) @@ -2111,6 +2233,11 @@ static int verify_sctp( (struct sctp_sack_chunk *)script_chunk, flags, error); break; + case SCTP_NR_SACK_CHUNK_TYPE: + result = verify_nr_sack_chunk((struct sctp_nr_sack_chunk *)actual_chunk, + (struct sctp_nr_sack_chunk *)script_chunk, + flags, error); + break; case SCTP_HEARTBEAT_CHUNK_TYPE: result = verify_heartbeat_chunk((struct sctp_heartbeat_chunk *)actual_chunk, (struct sctp_heartbeat_chunk *)script_chunk, diff --git a/gtests/net/packetdrill/sctp.h b/gtests/net/packetdrill/sctp.h index 0b95445b..54d87e8b 100644 --- a/gtests/net/packetdrill/sctp.h +++ b/gtests/net/packetdrill/sctp.h @@ -41,6 +41,7 @@ struct sctp_common_header { #define SCTP_INIT_CHUNK_TYPE 0x01 #define SCTP_INIT_ACK_CHUNK_TYPE 0x02 #define SCTP_SACK_CHUNK_TYPE 0x03 +#define SCTP_NR_SACK_CHUNK_TYPE 0x10 #define SCTP_HEARTBEAT_CHUNK_TYPE 0x04 #define SCTP_HEARTBEAT_ACK_CHUNK_TYPE 0x05 #define SCTP_ABORT_CHUNK_TYPE 0x06 @@ -126,6 +127,31 @@ struct sctp_sack_chunk { union sctp_sack_block block[]; } __packed; +union sctp_nr_sack_block { + struct { + __be16 start; + __be16 end; + } gap; + struct { + __be16 start; + __be16 end; + } nr_gap; + u32 tsn; +} __packed; + +struct sctp_nr_sack_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 cum_tsn; + __be32 a_rwnd; + __be16 nr_gap_blocks; + __be16 nr_of_nr_gap_blocks; + __be16 nr_dup_tsns; + __be16 reserved; + union sctp_nr_sack_block block[]; +} __packed; + struct sctp_heartbeat_chunk { __u8 type; __u8 flags; diff --git a/gtests/net/packetdrill/sctp_chunk_to_string.c b/gtests/net/packetdrill/sctp_chunk_to_string.c index 5ef8e002..049d9af3 100644 --- a/gtests/net/packetdrill/sctp_chunk_to_string.c +++ b/gtests/net/packetdrill/sctp_chunk_to_string.c @@ -251,6 +251,9 @@ static int sctp_supported_extensions_parameter_to_string( case SCTP_SACK_CHUNK_TYPE: fputs("SACK", s); break; + case SCTP_NR_SACK_CHUNK_TYPE: + fputs("NR-SACK", s); + break; case SCTP_HEARTBEAT_CHUNK_TYPE: fputs("HEARTBEAT", s); break; @@ -1252,6 +1255,52 @@ static int sctp_sack_chunk_to_string(FILE *s, return STATUS_OK; } +static int sctp_nr_sack_chunk_to_string(FILE *s, + struct sctp_nr_sack_chunk *chunk, + char **error) +{ + u16 length; + u16 nr_gaps, nr_of_nr_gaps, nr_dups; + u16 i; + + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_nr_sack_chunk)) { + asprintf(error, "NR-SACK chunk too short (length=%u)", length); + return STATUS_ERR; + } + nr_gaps = ntohs(chunk->nr_gap_blocks); + nr_of_nr_gaps = ntohs(chunk->nr_of_nr_gap_blocks); + nr_dups = ntohs(chunk->nr_dup_tsns); + if (length != sizeof(struct sctp_nr_sack_chunk) + + (nr_gaps + nr_of_nr_gaps + nr_dups) * sizeof(u32)) { + asprintf(error, "NR-SACK chunk length inconsistent"); + return STATUS_ERR; + } + fputs("NR-SACK[", s); + fprintf(s, "flgs=0x%02x, ", chunk->flags); + fprintf(s, "cum_tsn=%u, ", ntohl(chunk->cum_tsn)); + fprintf(s, "a_rwnd=%u, ", ntohl(chunk->a_rwnd)); + fputs("gaps=[", s); + for (i = 0; i < nr_gaps; i++) + fprintf(s, "%s%u:%u", + i > 0 ? ", " : "", + ntohs(chunk->block[i].gap.start), + ntohs(chunk->block[i].gap.end)); + fputs("], nr-gaps=[", s); + for (i = 0; i < nr_of_nr_gaps; i++) + fprintf(s, "%s%u:%u", + i > 0 ? ", " : "", + ntohs(chunk->block[i + nr_gaps].gap.start), + ntohs(chunk->block[i + nr_gaps].gap.end)); + fputs("], dups=[", s); + for (i = 0; i < nr_dups; i++) + fprintf(s, "%s%u", + i > 0 ? ", " : "", + ntohl(chunk->block[i + nr_gaps + nr_of_nr_gaps].tsn)); + fputs("]]", s); + return STATUS_OK; +} + static int sctp_heartbeat_chunk_to_string(FILE *s, struct sctp_heartbeat_chunk *chunk, char **error) @@ -1673,6 +1722,10 @@ int sctp_chunk_to_string(FILE *s, struct sctp_chunk *chunk, char **error) result = sctp_sack_chunk_to_string(s, (struct sctp_sack_chunk *)chunk, error); break; + case SCTP_NR_SACK_CHUNK_TYPE: + result = sctp_nr_sack_chunk_to_string(s, + (struct sctp_nr_sack_chunk *)chunk, error); + break; case SCTP_HEARTBEAT_CHUNK_TYPE: result = sctp_heartbeat_chunk_to_string(s, (struct sctp_heartbeat_chunk *)chunk, error); diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index 725d59ba..2795253c 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -742,6 +742,104 @@ sctp_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd, sctp_cause_list_new()); } +struct sctp_chunk_list_item * +sctp_nr_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd, + struct sctp_sack_block_list *gaps, + struct sctp_sack_block_list *nr_gaps_list, + struct sctp_sack_block_list *dups) { + struct sctp_nr_sack_chunk *chunk; + struct sctp_sack_block_list_item *item; + u32 flags; + u32 length; + u16 i, nr_gaps, nr_dups, number_of_nr_gaps; + + flags = 0; + length = sizeof(struct sctp_nr_sack_chunk); + if (gaps == NULL) { + nr_gaps = 0; + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_NR_SACK_CHUNK_GAP_BLOCKS_NOCHECK; + } else { + nr_gaps = gaps->nr_entries; + length += nr_gaps * sizeof(union sctp_nr_sack_block); + } + if (nr_gaps_list == NULL) { + number_of_nr_gaps = 0; + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_NR_SACK_CHUNK_NR_GAP_BLOCKS_NOCHECK; + } + else { + number_of_nr_gaps = nr_gaps_list->nr_entries; + length += number_of_nr_gaps * sizeof(union sctp_nr_sack_block); + } + if (dups == NULL) { + nr_dups = 0; + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_NR_SACK_CHUNK_DUP_TSNS_NOCHECK; + } else { + nr_dups = dups->nr_entries; + length += nr_dups * sizeof(union sctp_nr_sack_block); + } + assert(is_valid_u16(length)); + assert(length % 4 == 0); + chunk = malloc(length); + assert(chunk != NULL); + chunk->type = SCTP_NR_SACK_CHUNK_TYPE; + if (flgs == -1) { + chunk->flags = 0; + flags |= FLAG_CHUNK_FLAGS_NOCHECK; + } else { + chunk->flags = (u8)flgs; + } + chunk->length = htons(length); + if (cum_tsn == -1) { + chunk->cum_tsn = htonl(0); + flags |= FLAG_NR_SACK_CHUNK_CUM_TSN_NOCHECK; + } else { + chunk->cum_tsn = htonl((u32)cum_tsn); + } + if (a_rwnd == -1) { + chunk->a_rwnd = htonl(0); + flags |= FLAG_NR_SACK_CHUNK_A_RWND_NOCHECK; + } else { + chunk->a_rwnd = htonl((u32)a_rwnd); + } + chunk->nr_gap_blocks = htons(nr_gaps); + chunk->nr_dup_tsns = htons(nr_dups); + chunk->nr_of_nr_gap_blocks = htons(number_of_nr_gaps); + + if (gaps != NULL) { + for (i = 0, item = gaps->first; + (i < nr_gaps) && (item != NULL); + i++, item = item->next) { + chunk->block[i].gap.start = htons(item->block.gap.start); + chunk->block[i].gap.end = htons(item->block.gap.end); + } + assert((i == nr_gaps) && (item == NULL)); + } + if (nr_gaps_list != NULL) { + for (i = 0, item = nr_gaps_list->first; + (i < number_of_nr_gaps) && (item != NULL); + i++, item = item->next) { + chunk->block[i + nr_gaps].gap.start = htons(item->block.gap.start); + chunk->block[i + nr_gaps].gap.end = htons(item->block.gap.end); + } + assert((i == number_of_nr_gaps) && (item == NULL)); + } + if (dups != NULL) { + for (i = 0, item = dups->first; + (i < nr_dups) && (item != NULL); + i++, item = item->next) { + chunk->block[i + nr_gaps + number_of_nr_gaps].tsn= htonl(item->block.tsn); + } + assert((i == nr_dups) && (item == NULL)); + } + return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, + length, flags, + sctp_parameter_list_new(), + sctp_cause_list_new()); +} + struct sctp_chunk_list_item * sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info) { @@ -2908,6 +3006,33 @@ new_sctp_packet(int address_family, return NULL; } break; + case SCTP_NR_SACK_CHUNK_TYPE: + if (chunk_item->flags & FLAG_NR_SACK_CHUNK_CUM_TSN_NOCHECK) { + asprintf(error, + "CUM_TSN must be specified for inbound packets"); + return NULL; + } + if (chunk_item->flags & FLAG_NR_SACK_CHUNK_A_RWND_NOCHECK) { + asprintf(error, + "A_RWND must be specified for inbound packets"); + return NULL; + } + if (chunk_item->flags & FLAG_NR_SACK_CHUNK_GAP_BLOCKS_NOCHECK) { + asprintf(error, + "GAP_BLOCKS must be specified for inbound packets"); + return NULL; + } + if (chunk_item->flags & FLAG_NR_SACK_CHUNK_NR_GAP_BLOCKS_NOCHECK) { + asprintf(error, + "NR_GAP_BLOCKS must be specified for inbound packets"); + return NULL; + } + if (chunk_item->flags & FLAG_NR_SACK_CHUNK_DUP_TSNS_NOCHECK) { + asprintf(error, + "DUP_TSNS must be specified for inbound packets"); + return NULL; + } + break; case SCTP_HEARTBEAT_CHUNK_TYPE: break; case SCTP_HEARTBEAT_ACK_CHUNK_TYPE: diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index 01922281..9a845e0d 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -256,6 +256,18 @@ sctp_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd, struct sctp_sack_block_list *gaps, struct sctp_sack_block_list *dups); +#define FLAG_NR_SACK_CHUNK_CUM_TSN_NOCHECK 0x00000100 +#define FLAG_NR_SACK_CHUNK_A_RWND_NOCHECK 0x00000200 +#define FLAG_NR_SACK_CHUNK_GAP_BLOCKS_NOCHECK 0x00000400 +#define FLAG_NR_SACK_CHUNK_NR_GAP_BLOCKS_NOCHECK 0x00000800 +#define FLAG_NR_SACK_CHUNK_DUP_TSNS_NOCHECK 0x00001000 + +struct sctp_chunk_list_item * +sctp_nr_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd, + struct sctp_sack_block_list *gaps, + struct sctp_sack_block_list *nr_gaps, + struct sctp_sack_block_list *dups); + struct sctp_chunk_list_item * sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info); -- GitLab