From 2c356609eac47f8870efeb6e6edbe889a8f3dfbf Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Sat, 2 May 2015 22:38:58 +0200 Subject: [PATCH] Add support for gaps and dups in SACK using the syntax SACK[tsn=2 gaps=[4:4, 6:6] dups=[8]] This fixes https://github.com/nplab/packetdrill/issues/4 --- gtests/net/packetdrill/lexer.l | 2 + gtests/net/packetdrill/parser.y | 53 ++++++++- gtests/net/packetdrill/sctp_packet.c | 159 +++++++++++++++++++++++---- gtests/net/packetdrill/sctp_packet.h | 31 +++++- 4 files changed, 221 insertions(+), 24 deletions(-) diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index d1d3c111..5d177ad1 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -235,6 +235,8 @@ tsn return TSN; sid return SID; ssn return SSN; ppid return PPID; +gaps return GAPS; +dups return DUPS; --[a-zA-Z0-9_]+ yylval.string = option(yytext); return OPTION; [-]?[0-9]*[.][0-9]+ yylval.floating = atof(yytext); return FLOAT; [-]?[0-9]+ yylval.integer = atoll(yytext); return INTEGER; diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index b6b22e1e..7590b0cc 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -465,6 +465,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, struct packet *packet; struct sctp_chunk_list_item *chunk_list_item; struct sctp_chunk_list *chunk_list; + struct sctp_sack_block_list_item *sack_block_list_item; + struct sctp_sack_block_list *sack_block_list; struct syscall_spec *syscall; struct command_spec *command; struct code_spec *code; @@ -497,7 +499,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> DATA INIT INIT_ACK HEARTBEAT HEARTBEAT_ACK ABORT %token <reserved> SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECNE CWR %token <reserved> SHUTDOWN_COMPLETE -%token <reserved> TAG A_RWND OS IS TSN SID SSN PPID +%token <reserved> TAG A_RWND OS IS TSN SID SSN PPID GAPS DUPS %token <floating> FLOAT %token <integer> INTEGER HEX_INTEGER %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR @@ -550,6 +552,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <chunk_list_item> sctp_ecne_chunk_spec sctp_cwr_chunk_spec %type <chunk_list_item> sctp_shutdown_complete_chunk_spec %type <integer> opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn opt_ppid +%type <sack_block_list> opt_gaps gap_list opt_dups dup_list +%type <sack_block_list_item> gap dup %% /* The grammar follows. */ @@ -821,6 +825,49 @@ opt_ppid $$ = $3; } +opt_gaps +: { $$ = NULL; } +| GAPS '=' '[' gap_list ']' { $$ = $4; } +; + +gap_list +: { $$ = sctp_sack_block_list_new(); } +| gap { $$ = sctp_sack_block_list_new(); + sctp_sack_block_list_append($$, $1); } +| gap_list ',' gap { $$ = $1; + sctp_sack_block_list_append($1, $3); } +; + +gap +: INTEGER ':' INTEGER { if (!is_valid_u32($1)) { + semantic_error("start value out of range"); + } + if (!is_valid_u32($3)) { + semantic_error("end value out of range"); + } + $$ = sctp_sack_block_list_item_gap_new($1, $3); } +; + +opt_dups +: { $$ = NULL; } +| DUPS '=' '[' dup_list ']' { $$ = $4; } +; + +dup_list +: { $$ = sctp_sack_block_list_new(); } +| dup { $$ = sctp_sack_block_list_new(); + sctp_sack_block_list_append($$, $1); } +| dup_list ',' dup { $$ = $1; + sctp_sack_block_list_append($1, $3); } +; + +dup +: INTEGER { if (!is_valid_u32($1)) { + semantic_error("tsn value out of range"); + } + $$ = sctp_sack_block_list_item_dup_new($1); } +; + sctp_data_chunk_spec : DATA '[' opt_tsn opt_sid opt_ssn opt_ppid ']' { $$ = sctp_data_chunk_new($3, $4, $5, $6); @@ -849,8 +896,8 @@ sctp_init_ack_chunk_spec } sctp_sack_chunk_spec -: SACK '[' opt_tsn opt_a_rwnd']' { - $$ = sctp_sack_chunk_new($3, $4); +: SACK '[' opt_tsn opt_a_rwnd opt_gaps opt_dups']' { + $$ = sctp_sack_chunk_new($3, $4, $5, $6); } sctp_heartbeat_chunk_spec diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index 9d1777e2..6f03f444 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -34,7 +34,83 @@ * - Add support for parameters (fix hard coded state cookie in INIT-ACK) * - Add support for error causes */ - + +struct sctp_sack_block_list * +sctp_sack_block_list_new(void) +{ + struct sctp_sack_block_list *list; + + list = malloc(sizeof(struct sctp_sack_block_list)); + assert(list != NULL); + list->first = NULL; + list->last = NULL; + list->nr_entries = 0; + return list; +} + +void +sctp_sack_block_list_append(struct sctp_sack_block_list *list, + struct sctp_sack_block_list_item *item) +{ + assert(item->next == NULL); + if (list->last == NULL) { + assert(list->first == NULL); + assert(list->nr_entries == 0); + list->first = item; + } else { + assert(list->first != NULL); + list->last->next = item; + } + list->last = item; + list->nr_entries++; +} + +void +sctp_sctp_sack_block_list_free(struct sctp_sack_block_list *list) +{ + struct sctp_sack_block_list_item *current_item, *next_item; + + if (list == NULL) { + return; + } + current_item = list->first; + while (current_item != NULL) { + assert(list->nr_entries > 0); + next_item = current_item->next; + assert(next_item != NULL || current_item == list->last); + free(current_item); + current_item = next_item; + list->nr_entries--; + } + assert(list->nr_entries == 0); + free(list); +} + +struct sctp_sack_block_list_item * +sctp_sack_block_list_item_gap_new(u16 start, u16 end) +{ + struct sctp_sack_block_list_item *item; + + item = malloc(sizeof(struct sctp_sack_block_list_item)); + assert(item != NULL); + item->next = NULL; + item->block.gap.start = start; + item->block.gap.end = end; + return item; +} + +struct sctp_sack_block_list_item * +sctp_sack_block_list_item_dup_new(u32 tsn) +{ + struct sctp_sack_block_list_item *item; + + item = malloc(sizeof(struct sctp_sack_block_list_item)); + assert(item != NULL); + item->next = NULL; + item->block.tsn = tsn; + return item; +} + struct sctp_chunk_list_item * sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags) { @@ -182,18 +258,41 @@ sctp_init_ack_chunk_new(s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn) } struct sctp_chunk_list_item * -sctp_sack_chunk_new(s64 cum_tsn, s64 a_rwnd) +sctp_sack_chunk_new(s64 cum_tsn, s64 a_rwnd, + struct sctp_sack_block_list *gaps, + struct sctp_sack_block_list *dups) { - struct sctp_chunk_list_item *item; struct sctp_sack_chunk *chunk; + struct sctp_sack_block_list_item *item; u32 flags; + u32 length; + u16 i, nr_gaps, nr_dups; flags = 0; - chunk = malloc(sizeof(struct sctp_sack_chunk)); + length = sizeof(struct sctp_sack_chunk); + if (gaps == NULL) { + nr_gaps = 0; + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_SACK_CHUNK_GAP_BLOCKS_NOCHECK; + } else { + nr_gaps = gaps->nr_entries; + length += nr_gaps * sizeof(union sctp_sack_block); + } + if (dups == NULL) { + nr_dups = 0; + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_SACK_CHUNK_DUP_TSNS_NOCHECK; + } else { + nr_dups = dups->nr_entries; + length += nr_dups * sizeof(union sctp_sack_block); + } + assert(is_valid_u16(length)); + assert(length % 4 == 0); + chunk = malloc(length); assert(chunk != NULL); chunk->type = SCTP_SACK_CHUNK_TYPE; chunk->flags = 0; - chunk->length = htons(sizeof(struct sctp_sack_chunk)); + chunk->length = htons(length); if (cum_tsn == -1) { chunk->cum_tsn = htonl(0); flags |= FLAG_SACK_CHUNK_CUM_TSN_NOCHECK; @@ -206,12 +305,28 @@ sctp_sack_chunk_new(s64 cum_tsn, s64 a_rwnd) } else { chunk->a_rwnd = htonl((u32)a_rwnd); } - chunk->nr_gap_blocks = htons(0); - chunk->nr_dup_tsns = htons(0); - item = sctp_chunk_list_item_new((struct sctp_chunk *)chunk, - (u32)sizeof(struct sctp_sack_chunk), - flags); - return item; + chunk->nr_gap_blocks = htons(nr_gaps); + chunk->nr_dup_tsns = htons(nr_dups); + + 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 (dups != NULL) { + for (i = 0, item = dups->first; + (i < nr_dups) && (item != NULL); + i++, item = item->next) { + chunk->block[i + 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); } struct sctp_chunk_list_item * @@ -428,7 +543,8 @@ sctp_shutdown_complete_chunk_new(u8 flags) return item; } -struct sctp_chunk_list *sctp_chunk_list_new(void) +struct sctp_chunk_list * +sctp_chunk_list_new(void) { struct sctp_chunk_list *list; @@ -440,8 +556,9 @@ struct sctp_chunk_list *sctp_chunk_list_new(void) return list; } -void sctp_chunk_list_append(struct sctp_chunk_list *list, - struct sctp_chunk_list_item *item) +void +sctp_chunk_list_append(struct sctp_chunk_list *list, + struct sctp_chunk_list_item *item) { assert(item->next == NULL); if (list->last == NULL) { @@ -456,7 +573,8 @@ void sctp_chunk_list_append(struct sctp_chunk_list *list, list->length += item->length; } -void sctp_chunk_list_free(struct sctp_chunk_list *list) +void +sctp_chunk_list_free(struct sctp_chunk_list *list) { struct sctp_chunk_list_item *current_item, *next_item; @@ -472,11 +590,12 @@ void sctp_chunk_list_free(struct sctp_chunk_list *list) free(list); } -struct packet *new_sctp_packet(int address_family, - enum direction_t direction, - enum ip_ecn_t ecn, - struct sctp_chunk_list *list, - char **error) +struct packet * +new_sctp_packet(int address_family, + enum direction_t direction, + enum ip_ecn_t ecn, + struct sctp_chunk_list *list, + char **error) { struct packet *packet; /* the newly-allocated result packet */ struct header *sctp_header; /* the SCTP header info */ diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index b2e90e85..dfe4eba0 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -29,6 +29,33 @@ #include "packet.h" #include "sctp.h" +struct sctp_sack_block_list_item { + struct sctp_sack_block_list_item *next; + union sctp_sack_block block; +}; + +struct sctp_sack_block_list { + struct sctp_sack_block_list_item *first; + struct sctp_sack_block_list_item *last; + u16 nr_entries; +}; + +struct sctp_sack_block_list * +sctp_sack_block_list_new(void); + +void +sctp_sack_block_list_append(struct sctp_sack_block_list *list, + struct sctp_sack_block_list_item *item); + +void +sctp_sctp_sack_block_list_free(struct sctp_sack_block_list *list); + +struct sctp_sack_block_list_item * +sctp_sack_block_list_item_gap_new(u16 start, u16 end); + +struct sctp_sack_block_list_item * +sctp_sack_block_list_item_dup_new(u32 tsn); + struct sctp_chunk_list_item { struct sctp_chunk_list_item *next; struct sctp_chunk *chunk; @@ -81,7 +108,9 @@ sctp_init_ack_chunk_new(s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn); #define FLAG_SACK_CHUNK_DUP_TSNS_NOCHECK 0x00000800 struct sctp_chunk_list_item * -sctp_sack_chunk_new(s64 cum_tsn, s64 a_rwnd); +sctp_sack_chunk_new(s64 cum_tsn, s64 a_rwnd, + struct sctp_sack_block_list *gaps, + struct sctp_sack_block_list *dups); struct sctp_chunk_list_item * sctp_heartbeat_chunk_new(u8 flags); -- GitLab