diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index 8e931ec28528670813bf29749eb3f205f0d84528..75a922106425fc812e6621d9feebcb3cdd9be200 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -105,11 +105,12 @@ struct packet { s64 time_usecs; /* wall time of receive/send if non-zero */ - u32 flags; /* various meta-flags */ -#define FLAG_WIN_NOCHECK 0x1 /* don't check TCP receive window */ -#define FLAG_OPTIONS_NOCHECK 0x2 /* don't check TCP options */ -#define FLAGS_SCTP_BAD_CRC32C 0x4 /* compute bad CRC32C for SCTP packets */ -#define FLAGS_SCTP_EXPLICIT_TAG 0x8 /* verification tag specified */ + u32 flags; /* various meta-flags */ +#define FLAG_WIN_NOCHECK 0x1 /* don't check TCP receive window */ +#define FLAG_OPTIONS_NOCHECK 0x2 /* don't check TCP options */ +#define FLAGS_SCTP_BAD_CRC32C 0x4 /* compute bad CRC32C for SCTP packets */ +#define FLAGS_SCTP_EXPLICIT_TAG 0x8 /* verification tag specified */ +#define FLAGS_SCTP_GENERIC_PACKET 0x10 /* set if it is a generic packet */ enum ip_ecn_t ecn; /* IPv4/IPv6 ECN treatment for packet */ diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 7799260aaf7490f53a17d1b1746e016c33446474..8f76636f3f3e57ff7a9c0e0c04a85b627174a057 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -637,7 +637,6 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <errno_info> opt_errno %type <chunk_list> sctp_chunk_list_spec %type <chunk_list_item> sctp_chunk_spec -%type <chunk_list_item> sctp_generic_spec %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 @@ -928,6 +927,72 @@ sctp_packet_spec $$ = packet_encapsulate_and_free(outer, inner); } +| packet_prefix opt_ip_info SCTP ':' '[' byte_list ']' { + char *error = NULL; + struct packet *outer = $1, *inner = NULL; + enum direction_t direction = outer->direction; + + inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, + -1, false, $6, &error); + if (inner == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + + $$ = packet_encapsulate_and_free(outer, inner); +} +| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':' '[' byte_list ']' { + char *error = NULL; + struct packet *outer = $1, *inner = NULL; + enum direction_t direction = outer->direction; + + inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, + -1, true, $9, &error); + if (inner == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + + $$ = packet_encapsulate_and_free(outer, inner); +} +| packet_prefix opt_ip_info SCTP '(' TAG '=' INTEGER ')' ':' '[' byte_list ']' { + char *error = NULL; + struct packet *outer = $1, *inner = NULL; + enum direction_t direction = outer->direction; + + if (!is_valid_u32($7)) { + semantic_error("tag value out of range"); + } + inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, + $7, false, $11, &error); + if (inner == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + + $$ = packet_encapsulate_and_free(outer, inner); +} +| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' '[' byte_list ']' { + char *error = NULL; + struct packet *outer = $1, *inner = NULL; + enum direction_t direction = outer->direction; + + if (!is_valid_u32($9)) { + semantic_error("tag value out of range"); + } + inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, + $9, true, $13, &error); + if (inner == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + + $$ = packet_encapsulate_and_free(outer, inner); +} ; sctp_chunk_list_spec @@ -940,7 +1005,6 @@ sctp_chunk_list_spec sctp_chunk_spec : sctp_generic_chunk_spec { $$ = $1; } -| sctp_generic_spec { $$ = $1; } | sctp_data_chunk_spec { $$ = $1; } | sctp_init_chunk_spec { $$ = $1; } | sctp_init_ack_chunk_spec { $$ = $1; } @@ -1449,19 +1513,6 @@ dup } ; -sctp_generic_spec -: '[' byte_list ']' { - if ($2 == NULL) { - semantic_error("byte field can not be empty"); - } - - if ($2->nr_entries < sizeof(struct sctp_chunk)) { - semantic_error("generic sctp chunk must at least contain type, flags and length"); - } - - $$ = sctp_generic_new($2); -} - sctp_generic_chunk_spec : CHUNK '[' opt_chunk_type ',' opt_flags ',' opt_len ',' opt_val ']' { if (($7 != -1) && diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index 8197bb60e90924350d6760a6484bf422a2ab4f1c..0f5c66fd63e569879a90944b1b46a03692f3f611 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -315,59 +315,6 @@ sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags, return item; } -static void print_sctp_byte_list(struct sctp_byte_list *list) { - struct sctp_byte_list_item *item; - - for (item = list->first; item != NULL; item = item->next) { - DEBUGP("0x%.2x,", item->byte); - } -} - -struct sctp_chunk_list_item * -sctp_generic_new(struct sctp_byte_list *bytes) -{ - struct sctp_chunk *chunk; - struct sctp_byte_list_item *item; - u16 length, padding_length, i; - u32 flags = 0; - - print_sctp_byte_list(bytes); - - length = bytes->nr_entries; - - padding_length = length % 4; - if (padding_length > 0) { - padding_length = 4 - padding_length; - } - - chunk = malloc(length + padding_length); - assert(chunk != NULL); - - item = bytes->first; - chunk->type = item->byte; - item = item->next; - chunk->flags = item->byte; - - item = item->next; - chunk->length = (item->byte << 8) | item->next->byte; - chunk->length = htons(chunk->length); - - item = item->next->next; - - for (i = 0; item != NULL; i++, item = item->next) { - chunk->value[i] = item->byte; - } - - memset(chunk->value + (length - sizeof(struct sctp_chunk)), - 0, padding_length); - - return sctp_chunk_list_item_new(chunk, - length + padding_length, - flags, - sctp_parameter_list_new(), - sctp_cause_list_new()); -} - struct sctp_chunk_list_item * sctp_generic_chunk_new(s64 type, s64 flgs, s64 len, struct sctp_byte_list *bytes) @@ -2644,3 +2591,99 @@ new_sctp_packet(int address_family, packet->ip_bytes = ip_bytes; return packet; } + +#ifdef DEBUG_LOGGING +static void print_sctp_byte_list(struct sctp_byte_list *list) { + struct sctp_byte_list_item *item; + + for (item = list->first; item != NULL; item = item->next) { + DEBUGP("0x%.2x,", item->byte); + } +} +#endif + +struct packet * +new_sctp_generic_packet(int address_family, + enum direction_t direction, + enum ip_ecn_t ecn, + s64 tag, + bool bad_crc32c, + struct sctp_byte_list *bytes, + char **error) { + struct packet *packet; /* the newly-allocated result packet */ + struct header *sctp_header; /* the SCTP header info */ + struct sctp_byte_list_item *item = NULL; + /* Calculate lengths in bytes of all sections of the packet */ + const int ip_option_bytes = 0; + const int ip_header_bytes = (ip_header_min_len(address_family) + + ip_option_bytes); + const int sctp_header_bytes = sizeof(struct sctp_common_header); + const int sctp_chunk_bytes = bytes->nr_entries; + const int ip_bytes = + ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + bool overbook = false; + u16 i; + +#ifdef DEBUG_LOGGING + print_sctp_byte_list(bytes); +#endif + + if (direction == DIRECTION_OUTBOUND) { + asprintf(error, + "generic packets can only be specified as inbound."); + return NULL; + } + + /* Sanity-check all the various lengths */ + if (ip_option_bytes & 0x3) { + asprintf(error, "IP options are not padded correctly " + "to ensure IP header is a multiple of 4 bytes: " + "%d excess bytes", ip_option_bytes & 0x3); + return NULL; + } + assert((ip_header_bytes & 0x3) == 0); + + if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) { + asprintf(error, "SCTP packet too large"); + return NULL; + } + + /* Allocate and zero out a packet object of the desired size */ + packet = packet_new(overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); + memset(packet->buffer, 0, overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); + + packet->direction = direction; + packet->flags = FLAGS_SCTP_GENERIC_PACKET; + if (bad_crc32c) { + packet->flags |= FLAGS_SCTP_BAD_CRC32C; + } + if (tag != -1) { + packet->flags |= FLAGS_SCTP_EXPLICIT_TAG; + } + packet->ecn = ecn; + + /* Set IP header fields */ + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_SCTP); + + sctp_header = packet_append_header(packet, HEADER_SCTP, sctp_header_bytes); + sctp_header->total_bytes = sctp_header_bytes + sctp_chunk_bytes; + + /* Find the start of the SCTP common header of the packet */ + packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes); + u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1); + + /* Set SCTP header fields */ + packet->sctp->src_port = htons(0); + packet->sctp->dst_port = htons(0); + packet->sctp->v_tag = htonl((u32)tag); + packet->sctp->crc32c = htonl(0); + + for (i = 0, item = bytes->first; item != NULL; i++, item = item->next) { + sctp_chunk_start[i] = item->byte; + } + + packet->chunk_list = sctp_chunk_list_new(); + packet->ip_bytes = ip_bytes; + return packet; +} \ No newline at end of file diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index 0c7e57c4d7f60bec2760afdc968b138baae56495..a90d0a1581a5f907ceac519566943bd9eb5b876f 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -433,4 +433,13 @@ extern struct packet *new_sctp_packet(int address_family, bool bad_crc32c, struct sctp_chunk_list *chunk_list, char **error); + +struct packet * +new_sctp_generic_packet(int address_family, + enum direction_t direction, + enum ip_ecn_t ecn, + s64 tag, + bool bad_crc32c, + struct sctp_byte_list *bytes, + char **error); #endif /* __SCTP_PACKET_H__ */