From b408b3c339a9b4cf3a0d6a40d2462da92250735b Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Sat, 1 Aug 2015 14:12:15 +0200 Subject: [PATCH] Add support for error causes. This fixes https://github.com/nplab/packetdrill/issues/5. --- gtests/net/packetdrill/lexer.l | 19 + gtests/net/packetdrill/packet.c | 18 +- .../net/packetdrill/packet_to_string_test.c | 36 +- gtests/net/packetdrill/parser.y | 243 +++++- gtests/net/packetdrill/run_packet.c | 110 ++- gtests/net/packetdrill/sctp.h | 6 +- gtests/net/packetdrill/sctp_chunk_to_string.c | 20 +- gtests/net/packetdrill/sctp_iterator.c | 1 + gtests/net/packetdrill/sctp_packet.c | 758 +++++++++++++++++- gtests/net/packetdrill/sctp_packet.h | 111 ++- .../bsd/sctp/sctp_error_causes_active.pkt | 39 + 11 files changed, 1261 insertions(+), 100 deletions(-) create mode 100644 gtests/net/packetdrill/tests/bsd/sctp/sctp_error_causes_active.pkt diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index e7f0c7b2..95cb894c 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -268,6 +268,25 @@ params return PARAMS; IPv4 return IPV4_TYPE; IPv6 return IPV6_TYPE; HOSTNAME return HOSTNAME_TYPE; +CAUSE return CAUSE; +INVALID_STREAM_IDENTIFIER return INVALID_STREAM_IDENTIFIER; +MISSING_MANDATORY_PARAMETER return MISSING_MANDATORY_PARAMETER; +STALE_COOKIE_ERROR return STALE_COOKIE_ERROR; +OUT_OF_RESOURCE return OUT_OF_RESOURCE; +UNRESOLVABLE_ADDRESS return UNRESOLVABLE_ADDRESS; +UNRECOGNIZED_CHUNK_TYPE return UNRECOGNIZED_CHUNK_TYPE; +INVALID_MANDATORY_PARAMETER return INVALID_MANDATORY_PARAMETER; +UNRECOGNIZED_PARAMETERS return UNRECOGNIZED_PARAMETERS; +NO_USER_DATA return NO_USER_DATA; +COOKIE_RECEIVED_WHILE_SHUTDOWN return COOKIE_RECEIVED_WHILE_SHUTDOWN; +RESTART_WITH_NEW_ADDRESSES return RESTART_WITH_NEW_ADDRESSES; +USER_INITIATED_ABORT return USER_INITIATED_ABORT; +PROTOCOL_VIOLATION return PROTOCOL_VIOLATION; +code return CAUSE_CODE; +info return CAUSE_INFO; +staleness return STALENESS; +param return PARAM; +chk return CHK; --[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/packet.c b/gtests/net/packetdrill/packet.c index 97331617..a5edfebe 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -153,6 +153,9 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet, struct sctp_parameter_list_item *old_parameter_item, *new_parameter_item; struct sctp_parameter *new_parameter; struct sctp_parameter_list *new_parameter_list; + struct sctp_cause_list_item *old_cause_item, *new_cause_item; + struct sctp_cause *new_cause; + struct sctp_cause_list *new_cause_list; memcpy(new_base, old_base, bytes_used); @@ -191,11 +194,24 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet, old_parameter_item->flags); sctp_parameter_list_append(new_parameter_list, new_parameter_item); } + new_cause_list = sctp_cause_list_new(); + for (old_cause_item = old_chunk_item->cause_list->first; + old_cause_item != NULL; + old_cause_item = old_cause_item->next) { + new_cause = offset_ptr(old_base, + new_base, + old_cause_item->cause); + new_cause_item = sctp_cause_list_item_new(new_cause, + old_cause_item->length, + old_cause_item->flags); + sctp_cause_list_append(new_cause_list, new_cause_item); + } new_chunk = offset_ptr(old_base, new_base, old_chunk_item->chunk); new_chunk_item = sctp_chunk_list_item_new(new_chunk, old_chunk_item->length, old_chunk_item->flags, - new_parameter_list); + new_parameter_list, + new_cause_list); sctp_chunk_list_append(packet->chunk_list, new_chunk_item); } diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c index ae497533..899ef4b7 100644 --- a/gtests/net/packetdrill/packet_to_string_test.c +++ b/gtests/net/packetdrill/packet_to_string_test.c @@ -256,7 +256,7 @@ static void test_sctp_ipv6_packet_to_string(void) "IPV4_ADDRESS[addr=1.2.3.4], " "IPV6_ADDRESS[addr=::1], " "COOKIE_PRESERVATIVE[incr=65536], " - "HOSTNAME[addr=\"@A\"], " + "HOSTNAME_ADDRESS[addr=\"@A\"], " "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], " "ECN_CAPABLE[], " "PAD[len=16, val=...]]; " @@ -270,11 +270,11 @@ static void test_sctp_ipv6_packet_to_string(void) "HEARTBEAT_ACK[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; " "ABORT[flgs=T]; " "ABORT[flgs=0x00, INVALID_STREAM_IDENTIFIER[sid=255], " - "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "MISSING_MANDATORY_PARAMETER[types=[STATE_COOKIE]], " "STALE_COOKIE_ERROR[staleness=65536], " "OUT_OF_RESOURCES[], " - "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], " - "UNRECOGNIZED_CHUNK[" + "UNRESOLVABLE_ADDRESS[param=HOSTNAME_ADDRESS[addr=\"@A\"]], " + "UNRECOGNIZED_CHUNK[chk=" "CHUNK[type=0xfe, flgs=0x05, value=[0x01]]], " "INVALID_MANDATORY_PARAMETER[], " "UNRECOGNIZED_PARAMETERS[" @@ -284,8 +284,8 @@ static void test_sctp_ipv6_packet_to_string(void) "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], " "IPV4_ADDRESS[addr=2.3.4.5]], " - "USER_INITIATED_ABORT[BYE], " - "PROTOCOL_VIOLATION[@@]]; " + "USER_INITIATED_ABORT[info=\"BYE\"], " + "PROTOCOL_VIOLATION[info=\"@@\"]]; " "SHUTDOWN[flgs=0x00, cum_tsn=16909060]; " "SHUTDOWN_ACK[flgs=0x00]; " "ERROR[flgs=0x00]; " @@ -311,7 +311,7 @@ static void test_sctp_ipv6_packet_to_string(void) "IPV4_ADDRESS[addr=1.2.3.4], " "IPV6_ADDRESS[addr=::1], " "COOKIE_PRESERVATIVE[incr=65536], " - "HOSTNAME[addr=\"@A\"], " + "HOSTNAME_ADDRESS[addr=\"@A\"], " "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], " "ECN_CAPABLE[], " "PAD[len=16, val=...]]; " @@ -325,11 +325,11 @@ static void test_sctp_ipv6_packet_to_string(void) "HEARTBEAT_ACK[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; " "ABORT[flgs=T]; " "ABORT[flgs=0x00, INVALID_STREAM_IDENTIFIER[sid=255], " - "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "MISSING_MANDATORY_PARAMETER[types=[STATE_COOKIE]], " "STALE_COOKIE_ERROR[staleness=65536], " "OUT_OF_RESOURCES[], " - "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], " - "UNRECOGNIZED_CHUNK[" + "UNRESOLVABLE_ADDRESS[param=HOSTNAME_ADDRESS[addr=\"@A\"]], " + "UNRECOGNIZED_CHUNK[chk=" "CHUNK[type=0xfe, flgs=0x05, value=[0x01]]], " "INVALID_MANDATORY_PARAMETER[], " "UNRECOGNIZED_PARAMETERS[" @@ -339,8 +339,8 @@ static void test_sctp_ipv6_packet_to_string(void) "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], " "IPV4_ADDRESS[addr=2.3.4.5]], " - "USER_INITIATED_ABORT[BYE], " - "PROTOCOL_VIOLATION[@@]]; " + "USER_INITIATED_ABORT[info=\"BYE\"], " + "PROTOCOL_VIOLATION[info=\"@@\"]]; " "SHUTDOWN[flgs=0x00, cum_tsn=16909060]; " "SHUTDOWN_ACK[flgs=0x00]; " "ERROR[flgs=0x00]; " @@ -366,7 +366,7 @@ static void test_sctp_ipv6_packet_to_string(void) "IPV4_ADDRESS[addr=1.2.3.4], " "IPV6_ADDRESS[addr=::1], " "COOKIE_PRESERVATIVE[incr=65536], " - "HOSTNAME[addr=\"@A\"], " + "HOSTNAME_ADDRESS[addr=\"@A\"], " "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], " "ECN_CAPABLE[], " "PAD[len=16, val=...]]; " @@ -380,11 +380,11 @@ static void test_sctp_ipv6_packet_to_string(void) "HEARTBEAT_ACK[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; " "ABORT[flgs=T]; " "ABORT[flgs=0x00, INVALID_STREAM_IDENTIFIER[sid=255], " - "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "MISSING_MANDATORY_PARAMETER[types=[STATE_COOKIE]], " "STALE_COOKIE_ERROR[staleness=65536], " "OUT_OF_RESOURCES[], " - "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], " - "UNRECOGNIZED_CHUNK[" + "UNRESOLVABLE_ADDRESS[param=HOSTNAME_ADDRESS[addr=\"@A\"]], " + "UNRECOGNIZED_CHUNK[chk=" "CHUNK[type=0xfe, flgs=0x05, value=[0x01]]], " "INVALID_MANDATORY_PARAMETER[], " "UNRECOGNIZED_PARAMETERS[" @@ -394,8 +394,8 @@ static void test_sctp_ipv6_packet_to_string(void) "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], " "IPV4_ADDRESS[addr=2.3.4.5]], " - "USER_INITIATED_ABORT[BYE], " - "PROTOCOL_VIOLATION[@@]]; " + "USER_INITIATED_ABORT[info=\"BYE\"], " + "PROTOCOL_VIOLATION[info=\"@@\"]]; " "SHUTDOWN[flgs=0x00, cum_tsn=16909060]; " "SHUTDOWN_ACK[flgs=0x00]; " "ERROR[flgs=0x00]; " diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 69a84309..950f92a6 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -472,8 +472,12 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, struct sctp_sack_block_list *sack_block_list; struct sctp_address_type_list_item *address_type_list_item; struct sctp_address_type_list *address_type_list; + struct sctp_parameter_type_list_item *parameter_type_list_item; + struct sctp_parameter_type_list *parameter_type_list; struct sctp_parameter_list_item *parameter_list_item; struct sctp_parameter_list *parameter_list; + struct sctp_cause_list_item *cause_list_item; + struct sctp_cause_list *cause_list; struct syscall_spec *syscall; struct command_spec *command; struct code_spec *code; @@ -516,6 +520,15 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> HOSTNAME_ADDRESS SUPPORTED_ADDRESS_TYPES ECN_CAPABLE %token <reserved> ADDR INCR TYPES PARAMS %token <reserved> IPV4_TYPE IPV6_TYPE HOSTNAME_TYPE +%token <reserved> CAUSE +%token <reserved> CAUSE_CODE CAUSE_INFO +%token <reserved> INVALID_STREAM_IDENTIFIER MISSING_MANDATORY_PARAMETER +%token <reserved> STALE_COOKIE_ERROR OUT_OF_RESOURCE +%token <reserved> UNRESOLVABLE_ADDRESS UNRECOGNIZED_CHUNK_TYPE +%token <reserved> INVALID_MANDATORY_PARAMETER NO_USER_DATA +%token <reserved> COOKIE_RECEIVED_WHILE_SHUTDOWN RESTART_WITH_NEW_ADDRESSES +%token <reserved> USER_INITIATED_ABORT PROTOCOL_VIOLATION +%token <reserved> STALENESS CHK PARAM UNRECOGNIZED_PARAMETERS %token <floating> FLOAT %token <integer> INTEGER HEX_INTEGER %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR @@ -583,17 +596,35 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <parameter_list_item> sctp_supported_address_types_parameter_spec %type <parameter_list_item> sctp_ecn_capable_parameter_spec %type <parameter_list_item> sctp_pad_parameter_spec -%type <integer> opt_chunk_type opt_parameter_type +%type <cause_list> opt_cause_list_spec sctp_cause_list_spec +%type <cause_list_item> sctp_cause_spec +%type <cause_list_item> sctp_generic_cause_spec +%type <cause_list_item> sctp_invalid_stream_identifier_cause_spec +%type <cause_list_item> sctp_missing_mandatory_parameter_cause_spec +%type <cause_list_item> sctp_stale_cookie_error_cause_spec +%type <cause_list_item> sctp_out_of_resources_cause_spec +%type <cause_list_item> sctp_unresolvable_address_cause_spec +%type <cause_list_item> sctp_unrecognized_chunk_type_cause_spec +%type <cause_list_item> sctp_invalid_mandatory_parameter_cause_spec +%type <cause_list_item> sctp_unrecognized_parameters_cause_spec +%type <cause_list_item> sctp_no_user_data_cause_spec +%type <cause_list_item> sctp_cookie_received_while_shutdown_cause_spec +%type <cause_list_item> sctp_restart_with_new_addresses_cause_spec +%type <cause_list_item> sctp_user_initiated_abort_cause_spec +%type <cause_list_item> sctp_protocol_violation_cause_spec +%type <integer> opt_chunk_type opt_parameter_type opt_cause_code %type <integer> opt_flags opt_data_flags opt_abort_flags %type <integer> opt_shutdown_complete_flags opt_len %type <integer> opt_tag opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn %type <integer> opt_cum_tsn opt_ppid -%type <byte_list> opt_val byte_list +%type <byte_list> opt_val opt_info byte_list %type <byte_list_item> byte %type <sack_block_list> opt_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 +%type <parameter_type_list> parameter_types_list +%type <parameter_type_list_item> parameter_type %% /* The grammar follows. */ @@ -852,6 +883,12 @@ opt_val | VAL '=' '[' byte_list ']' { $$ = $4; } ; +opt_info +: CAUSE_INFO '=' ELLIPSIS { $$ = NULL; } +| CAUSE_INFO '=' '[' ELLIPSIS ']' { $$ = NULL; } +| CAUSE_INFO '=' '[' byte_list ']' { $$ = $4; } +; + byte_list : { $$ = sctp_byte_list_new(); } | byte { $$ = sctp_byte_list_new(); @@ -1195,11 +1232,8 @@ sctp_heartbeat_ack_chunk_spec } sctp_abort_chunk_spec -: ABORT '[' opt_abort_flags ']' { - $$ = sctp_abort_chunk_new($3); -} -| ABORT '[' opt_abort_flags ',' ELLIPSIS ']' { - $$ = sctp_abort_chunk_new($3); +: ABORT '[' opt_abort_flags opt_cause_list_spec ']' { + $$ = sctp_abort_chunk_new($3, $4); } sctp_shutdown_chunk_spec @@ -1213,11 +1247,8 @@ sctp_shutdown_ack_chunk_spec } sctp_error_chunk_spec -: ERROR '[' opt_flags ']' { - $$ = sctp_error_chunk_new($3); -} -| ERROR '[' opt_flags ',' ELLIPSIS ']' { - $$ = sctp_error_chunk_new($3); +: ERROR '[' opt_flags opt_cause_list_spec ']' { + $$ = sctp_error_chunk_new($3, $4); } sctp_cookie_echo_chunk_spec @@ -1282,13 +1313,13 @@ opt_parameter_type | TYPE '=' HEX_INTEGER { if (!is_valid_u16($3)) { semantic_error("type value out of range"); - } + } $$ = $3; } | TYPE '=' INTEGER { if (!is_valid_u16($3)) { semantic_error("type value out of range"); - } + } $$ = $3; } ; @@ -1402,8 +1433,8 @@ address_types_list address_type : INTEGER { if (!is_valid_u16($1)) { semantic_error("address type value out of range"); - } - $$ = sctp_address_type_list_item_new($1); } + } + $$ = sctp_address_type_list_item_new($1); } | IPV4_TYPE { $$ = sctp_address_type_list_item_new(SCTP_IPV4_ADDRESS_PARAMETER_TYPE); } | IPV6_TYPE { $$ = sctp_address_type_list_item_new(SCTP_IPV6_ADDRESS_PARAMETER_TYPE); } | HOSTNAME_TYPE { $$ = sctp_address_type_list_item_new(SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE); } @@ -1438,6 +1469,186 @@ sctp_pad_parameter_spec $$ = sctp_pad_parameter_new($5, NULL); } +opt_cause_list_spec +: ',' ELLIPSIS { $$ = NULL; } +| { $$ = sctp_cause_list_new(); } +| ',' sctp_cause_list_spec { $$ = $2; } +; + +sctp_cause_list_spec +: sctp_cause_spec { $$ = sctp_cause_list_new(); + sctp_cause_list_append($$, $1); } +| sctp_cause_list_spec ',' sctp_cause_spec { $$ = $1; + sctp_cause_list_append($1, $3); } +; + +sctp_cause_spec +: sctp_generic_cause_spec { $$ = $1; } +| sctp_invalid_stream_identifier_cause_spec { $$ = $1; } +| sctp_missing_mandatory_parameter_cause_spec { $$ = $1; } +| sctp_stale_cookie_error_cause_spec { $$ = $1; } +| sctp_out_of_resources_cause_spec { $$ = $1; } +| sctp_unresolvable_address_cause_spec { $$ = $1; } +| sctp_unrecognized_chunk_type_cause_spec { $$ = $1; } +| sctp_invalid_mandatory_parameter_cause_spec { $$ = $1; } +| sctp_unrecognized_parameters_cause_spec { $$ = $1; } +| sctp_no_user_data_cause_spec { $$ = $1; } +| sctp_cookie_received_while_shutdown_cause_spec { $$ = $1; } +| sctp_restart_with_new_addresses_cause_spec { $$ = $1; } +| sctp_user_initiated_abort_cause_spec { $$ = $1; } +| sctp_protocol_violation_cause_spec { $$ = $1; } +; + +opt_cause_code +: CAUSE_CODE '=' ELLIPSIS { $$ = -1; } +| CAUSE_CODE '=' HEX_INTEGER { + if (!is_valid_u16($3)) { + semantic_error("cause value out of range"); + } + $$ = $3; +} +| CAUSE_CODE '=' INTEGER { + if (!is_valid_u16($3)) { + semantic_error("cause value out of range"); + } + $$ = $3; +} +; + +sctp_generic_cause_spec +: CAUSE '[' opt_cause_code ',' opt_len ',' opt_info ']' { + if (($5 != -1) && ($5 < sizeof(struct sctp_cause))) { + semantic_error("length value out of range"); + } + if (($5 != -1) && ($7 != NULL) && + ($5 != sizeof(struct sctp_cause) + $7->nr_entries)) { + semantic_error("length value incompatible with val"); + } + if (($5 == -1) && ($7 != NULL)) { + semantic_error("length needs to be specified"); + } + $$ = sctp_generic_cause_new($3, $5, $7); +} + +sctp_invalid_stream_identifier_cause_spec +: INVALID_STREAM_IDENTIFIER '[' SID '=' INTEGER ']' { + if (!is_valid_u16($5)) { + semantic_error("stream identifier out of range"); + } + $$ = sctp_invalid_stream_identifier_cause_new($5); +} +| INVALID_STREAM_IDENTIFIER '[' SID '=' ELLIPSIS ']' { + $$ = sctp_invalid_stream_identifier_cause_new(-1); +} + +parameter_types_list +: { $$ = sctp_parameter_type_list_new(); } +| parameter_type { $$ = sctp_parameter_type_list_new(); + sctp_parameter_type_list_append($$, $1); } +| parameter_types_list ',' parameter_type { $$ = $1; + sctp_parameter_type_list_append($1, $3); } +; + +parameter_type +: INTEGER { if (!is_valid_u16($1)) { + semantic_error("parameter type value out of range"); + } + $$ = sctp_parameter_type_list_item_new($1); } +; + +sctp_missing_mandatory_parameter_cause_spec +: MISSING_MANDATORY_PARAMETER '[' TYPES '=' ELLIPSIS ']' { + $$ = sctp_missing_mandatory_parameter_cause_new(NULL); +} +| MISSING_MANDATORY_PARAMETER '[' TYPES '=' '[' parameter_types_list ']' ']' { + $$ = sctp_missing_mandatory_parameter_cause_new($6); +} + +sctp_stale_cookie_error_cause_spec +: STALE_COOKIE_ERROR '[' STALENESS '=' INTEGER ']' { + if (!is_valid_u32($5)) { + semantic_error("staleness out of range"); + } + $$ = sctp_stale_cookie_error_cause_new($5); +} +| STALE_COOKIE_ERROR '[' STALENESS '=' ELLIPSIS ']' { + $$ = sctp_stale_cookie_error_cause_new(-1); +} + +sctp_out_of_resources_cause_spec +: OUT_OF_RESOURCE '[' ']' { + $$ = sctp_out_of_resources_cause_new(); +} + +sctp_unresolvable_address_cause_spec +: UNRESOLVABLE_ADDRESS '[' PARAM '=' sctp_parameter_spec ']' { + $$ = sctp_unresolvable_address_cause_new($5); +} +| UNRESOLVABLE_ADDRESS '[' PARAM '=' ELLIPSIS ']' { + $$ = sctp_unresolvable_address_cause_new(NULL); +} + +sctp_unrecognized_chunk_type_cause_spec +: UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' sctp_chunk_spec ']' { + $$ = sctp_unrecognized_chunk_type_cause_new($5); +} +| UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' ELLIPSIS ']' { + $$ = sctp_unrecognized_chunk_type_cause_new(NULL); +} + +sctp_invalid_mandatory_parameter_cause_spec +: INVALID_MANDATORY_PARAMETER '[' ']' { + $$ = sctp_invalid_mandatory_parameter_cause_new(); +} + +sctp_unrecognized_parameters_cause_spec +: UNRECOGNIZED_PARAMETERS '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' { + $$ = sctp_unrecognized_parameters_cause_new($6); +} +| UNRECOGNIZED_PARAMETERS '[' PARAMS '=' ELLIPSIS ']' { + $$ = sctp_unrecognized_parameters_cause_new(NULL); +} + +sctp_no_user_data_cause_spec +: NO_USER_DATA '[' TSN '=' INTEGER ']' { + if (!is_valid_u32($5)) { + semantic_error("tsn out of range"); + } + $$ = sctp_no_user_data_cause_new($5); +} +| NO_USER_DATA '[' TSN '=' ELLIPSIS ']' { + $$ = sctp_no_user_data_cause_new(-1); +} + +sctp_cookie_received_while_shutdown_cause_spec +: COOKIE_RECEIVED_WHILE_SHUTDOWN '[' ']' { + $$ = sctp_cookie_received_while_shutdown_cause_new(); +} + +sctp_restart_with_new_addresses_cause_spec +: RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' { + $$ = sctp_restart_with_new_addresses_cause_new($6); +} +| RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' ELLIPSIS ']' { + $$ = sctp_restart_with_new_addresses_cause_new(NULL); +} + +sctp_user_initiated_abort_cause_spec +: USER_INITIATED_ABORT '[' CAUSE_INFO '=' STRING ']' { + $$ = sctp_user_initiated_abort_cause_new($5); +} +| USER_INITIATED_ABORT '[' CAUSE_INFO '=' ELLIPSIS ']' { + $$ = sctp_user_initiated_abort_cause_new(NULL); +} + +sctp_protocol_violation_cause_spec +: PROTOCOL_VIOLATION '[' CAUSE_INFO '=' STRING ']' { + $$ = sctp_protocol_violation_cause_new($5); +} +| PROTOCOL_VIOLATION '[' CAUSE_INFO '=' ELLIPSIS ']' { + $$ = sctp_protocol_violation_cause_new(NULL); +} + tcp_packet_spec : packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options { char *error = NULL; diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 78222d66..5284d7db 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -149,12 +149,12 @@ static struct socket *find_socket_for_live_packet( { struct socket *socket = state->socket_under_test; /* shortcut */ + DEBUGP("find_connect_for_live_packet\n"); if (socket == NULL) return NULL; struct tuple packet_tuple, live_outbound, live_inbound; get_packet_tuple(packet, &packet_tuple); - /* Is packet inbound to the socket under test? */ socket_get_inbound(&socket->live, &live_inbound); if (is_equal_tuple(&packet_tuple, &live_inbound)) { @@ -1083,7 +1083,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length, ntohs(actual_parameter->type), error)) || (flags & FLAG_PARAMETER_LENGTH_NOCHECK ? STATUS_OK : - check_field("sctp_chunk_length", + check_field("sctp_parameter_length", ntohs(script_parameter->length), ntohs(actual_parameter->length), error))) { @@ -1113,6 +1113,71 @@ static int verify_sctp_parameters(u8 *begin, u16 length, return STATUS_OK; } +static int verify_sctp_causes(struct sctp_chunk *chunk, u16 offset, + struct sctp_chunk_list_item *script_chunk_item, + char **error) +{ + struct sctp_causes_iterator iter; + struct sctp_cause *actual_cause; + struct sctp_cause *script_cause; + struct sctp_cause_list_item *script_cause_item; + u32 flags; + + for (actual_cause = sctp_causes_begin(chunk, offset, &iter, error), + script_cause_item = script_chunk_item->cause_list->first; + actual_cause != NULL && script_cause_item != NULL; + actual_cause = sctp_causes_next(&iter, error), + script_cause_item = script_cause_item->next) { + if (*error != NULL) { + DEBUGP("Error during iteration\n"); + return STATUS_ERR; + } + script_cause = script_cause_item->cause; + flags = script_cause_item->flags; + assert(script_cause != NULL); + DEBUGP("script cause: code 0x%04x, length %05d\n", + ntohs(script_cause->code), + ntohs(script_cause->length)); + DEBUGP("actual cause: code 0x%04x, length %05d\n", + ntohs(actual_cause->code), + ntohs(actual_cause->length)); + DEBUGP("flags: %08x\n", flags); + if ((flags & FLAG_CAUSE_CODE_NOCHECK ? STATUS_OK : + check_field("sctp_cause_code", + ntohs(script_cause->code), + ntohs(actual_cause->code), + error)) || + (flags & FLAG_CAUSE_LENGTH_NOCHECK ? STATUS_OK : + check_field("sctp_cause_length", + ntohs(script_cause->length), + ntohs(actual_cause->length), + error))) { + return STATUS_ERR; + } + if ((flags & FLAG_CAUSE_INFORMATION_NOCHECK) == 0) { + assert((flags & FLAG_CAUSE_LENGTH_NOCHECK) == 0); + if (memcmp(script_cause->information, + actual_cause->information, + ntohs(actual_cause->length) - sizeof(struct sctp_cause))) { + asprintf(error, "live packet cause information not as expected"); + return STATUS_ERR; + } + } + } + if (actual_cause != NULL) { + DEBUGP("actual chunk contains more causes than script chunk\n"); + } + if (script_cause_item != NULL) { + DEBUGP("script chunk contains more causes than actual chunk\n"); + } + if ((actual_cause != NULL) || (script_cause_item != NULL)) { + asprintf(error, + "live chunk and expected chunk have not the same number of causes"); + return STATUS_ERR; + } + return STATUS_OK; +} + static int verify_data_chunk(struct sctp_data_chunk *actual_chunk, struct sctp_data_chunk *script_chunk, u32 flags, char **error) @@ -1349,11 +1414,17 @@ static int verify_heartbeat_ack_chunk(struct sctp_heartbeat_ack_chunk *actual_ch } static int verify_abort_chunk(struct sctp_abort_chunk *actual_chunk, - struct sctp_abort_chunk *script_chunk, - u32 flags, char **error) + struct sctp_chunk_list_item *script_chunk_item, + char **error) { - /* FIXME: Validate causes */ - return STATUS_OK; + u32 flags; + + assert(ntohs(actual_chunk->length) >= sizeof(struct sctp_abort_chunk)); + flags = script_chunk_item->flags; + return (flags & FLAG_ABORT_CHUNK_OPT_CAUSES_NOCHECK ? STATUS_OK : + verify_sctp_causes((struct sctp_chunk *)actual_chunk, + sizeof(struct sctp_error_chunk), + script_chunk_item, error)); } static int verify_shutdown_chunk(struct sctp_shutdown_chunk *actual_chunk, @@ -1376,11 +1447,17 @@ static int verify_shutdown_ack_chunk(struct sctp_shutdown_ack_chunk *actual_chun } static int verify_error_chunk(struct sctp_error_chunk *actual_chunk, - struct sctp_error_chunk *script_chunk, - u32 flags, char **error) + struct sctp_chunk_list_item *script_chunk_item, + char **error) { - /* FIXME: Validate causes */ - return STATUS_OK; + u32 flags; + + assert(ntohs(actual_chunk->length) >= sizeof(struct sctp_error_chunk)); + flags = script_chunk_item->flags; + return (flags & FLAG_ERROR_CHUNK_OPT_CAUSES_NOCHECK ? STATUS_OK : + verify_sctp_causes((struct sctp_chunk *)actual_chunk, + sizeof(struct sctp_error_chunk), + script_chunk_item, error)); } static int verify_cookie_echo_chunk(struct sctp_cookie_echo_chunk *actual_chunk, @@ -1540,8 +1617,7 @@ static int verify_sctp( break; case SCTP_ABORT_CHUNK_TYPE: result = verify_abort_chunk((struct sctp_abort_chunk *)actual_chunk, - (struct sctp_abort_chunk *)script_chunk, - flags, error); + script_chunk_item, error); break; case SCTP_SHUTDOWN_CHUNK_TYPE: result = verify_shutdown_chunk((struct sctp_shutdown_chunk *)actual_chunk, @@ -1555,8 +1631,7 @@ static int verify_sctp( break; case SCTP_ERROR_CHUNK_TYPE: result = verify_error_chunk((struct sctp_error_chunk *)actual_chunk, - (struct sctp_error_chunk *)script_chunk, - flags, error); + script_chunk_item, error); break; case SCTP_COOKIE_ECHO_CHUNK_TYPE: result = verify_cookie_echo_chunk((struct sctp_cookie_echo_chunk *)actual_chunk, @@ -2546,6 +2621,7 @@ int abort_association(struct state *state, struct socket *socket) struct sctp_chunk_list *chunk_list; struct tuple live_inbound; int result = STATUS_OK; + s64 flgs; if ((socket->live.local_initiate_tag == 0) && (socket->live.remote_initiate_tag == 0)) { @@ -2553,10 +2629,12 @@ int abort_association(struct state *state, struct socket *socket) } chunk_list = sctp_chunk_list_new(); if (socket->live.local_initiate_tag != 0) { - sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(0)); + flgs = 0; } else { - sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(SCTP_ABORT_CHUNK_T_BIT)); + flgs = SCTP_ABORT_CHUNK_T_BIT; } + /* XXX Provide an error cause */ + sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(flgs, sctp_cause_list_new())); packet = new_sctp_packet(socket->address_family, DIRECTION_INBOUND, ECN_NONE, chunk_list, &error); diff --git a/gtests/net/packetdrill/sctp.h b/gtests/net/packetdrill/sctp.h index b4e36e24..177735b2 100644 --- a/gtests/net/packetdrill/sctp.h +++ b/gtests/net/packetdrill/sctp.h @@ -291,12 +291,12 @@ struct sctp_pad_parameter { } __packed; #define SCTP_INVALID_STREAM_IDENTIFIER_CAUSE_CODE 0x0001 -#define SCTP_MISSING_MADATORY_PARAMETER_CAUSE_CODE 0x0002 +#define SCTP_MISSING_MANDATORY_PARAMETER_CAUSE_CODE 0x0002 #define SCTP_STALE_COOKIE_ERROR_CAUSE_CODE 0x0003 #define SCTP_OUT_OF_RESOURCES_CAUSE_CODE 0x0004 #define SCTP_UNRESOLVABLE_ADDRESS_CAUSE_CODE 0x0005 #define SCTP_UNRECOGNIZED_CHUNK_TYPE_CAUSE_CODE 0x0006 -#define SCTP_INVALID_MADATORY_PARAMETER_CAUSE_CODE 0x0007 +#define SCTP_INVALID_MANDATORY_PARAMETER_CAUSE_CODE 0x0007 #define SCTP_UNRECOGNIZED_PARAMETERS_CAUSE_CODE 0x0008 #define SCTP_NO_USER_DATA_CAUSE_CODE 0x0009 #define SCTP_COOKIE_RECEIVED_WHILE_SHUTDOWN_CAUSE_CODE 0x000a @@ -304,6 +304,8 @@ struct sctp_pad_parameter { #define SCTP_USER_INITIATED_ABORT_CAUSE_CODE 0x000c #define SCTP_PROTOCOL_VIOLATION_CAUSE_CODE 0x000d +#define MAX_SCTP_CAUSE_BYTES 0xffff + struct sctp_cause { __be16 code; __be16 length; diff --git a/gtests/net/packetdrill/sctp_chunk_to_string.c b/gtests/net/packetdrill/sctp_chunk_to_string.c index 993bb94d..59c246a0 100644 --- a/gtests/net/packetdrill/sctp_chunk_to_string.c +++ b/gtests/net/packetdrill/sctp_chunk_to_string.c @@ -151,11 +151,11 @@ static int sctp_hostname_parameter_to_string( length = ntohs(parameter->length); if (length < sizeof(struct sctp_hostname_address_parameter)) { - asprintf(error, "HOSTNAME parameter illegal (length=%u)", + asprintf(error, "HOSTNAME_ADDRESS parameter illegal (length=%u)", length); return STATUS_ERR; } - fprintf(s, "HOSTNAME[addr=\"%.*s\"]", + fprintf(s, "HOSTNAME_ADDRESS[addr=\"%.*s\"]", (int)(length - sizeof(struct sctp_hostname_address_parameter)), (char *)parameter->hostname); return STATUS_OK; @@ -355,7 +355,7 @@ static int sctp_missing_mandatory_parameter_cause_to_string( asprintf(error, "MISSING_MANDATORY_PARAMETER inconsistent"); return STATUS_ERR; } - fputs("MISSING_MANDATORY_PARAMETER[", s); + fputs("MISSING_MANDATORY_PARAMETER[types=[", s); for (i = 0; i < nr_parameters; i++) { if (i > 0) fputs(", ", s); @@ -389,7 +389,7 @@ static int sctp_missing_mandatory_parameter_cause_to_string( break; } } - fputc(']', s); + fputs("]]", s); return STATUS_OK; } @@ -456,7 +456,7 @@ static int sctp_unresolvable_address_cause_to_string( asprintf(error, "UNRESOLVABLE_ADDRESS cause inconsistent"); return STATUS_ERR; } - fputs("UNRESOLVABLE_ADDRESS[", s); + fputs("UNRESOLVABLE_ADDRESS[param=", s); result = sctp_parameter_to_string(s, parameter, error); fputc(']', s); return result; @@ -492,7 +492,7 @@ static int sctp_unrecognized_chunk_type_cause_to_string( asprintf(error, "UNRECOGNIZED_CHUNK cause inconsistent"); return STATUS_ERR; } - fputs("UNRECOGNIZED_CHUNK[", s); + fputs("UNRECOGNIZED_CHUNK[chk=", s); result = sctp_chunk_to_string(s, chunk, error); fputc(']', s); return result; @@ -644,7 +644,7 @@ static int sctp_user_initiated_abort_cause_to_string( length); return STATUS_ERR; } - fprintf(s, "USER_INITIATED_ABORT[%.*s]", + fprintf(s, "USER_INITIATED_ABORT[info=\"%.*s\"]", (int)(length - sizeof(struct sctp_user_initiated_abort_cause)), (char *)cause->information); return STATUS_OK; @@ -663,7 +663,7 @@ static int sctp_protocol_violation_cause_to_string( length); return STATUS_ERR; } - fprintf(s, "PROTOCOL_VIOLATION[%.*s]", + fprintf(s, "PROTOCOL_VIOLATION[info=\"%.*s\"]", (int)(length - sizeof(struct sctp_protocol_violation_cause)), (char *)cause->information); return STATUS_OK; @@ -703,7 +703,7 @@ static int sctp_cause_to_string(FILE *s, struct sctp_cause *cause, char **error) (struct sctp_invalid_stream_identifier_cause *)cause, error); break; - case SCTP_MISSING_MADATORY_PARAMETER_CAUSE_CODE: + case SCTP_MISSING_MANDATORY_PARAMETER_CAUSE_CODE: result = sctp_missing_mandatory_parameter_cause_to_string(s, (struct sctp_missing_mandatory_parameter_cause *)cause, error); @@ -725,7 +725,7 @@ static int sctp_cause_to_string(FILE *s, struct sctp_cause *cause, char **error) (struct sctp_unrecognized_chunk_type_cause *)cause, error); break; - case SCTP_INVALID_MADATORY_PARAMETER_CAUSE_CODE: + case SCTP_INVALID_MANDATORY_PARAMETER_CAUSE_CODE: result = sctp_invalid_mandatory_parameter_cause_to_string(s, (struct sctp_invalid_mandatory_parameter_cause *)cause, error); diff --git a/gtests/net/packetdrill/sctp_iterator.c b/gtests/net/packetdrill/sctp_iterator.c index b4df6d5c..059f2445 100644 --- a/gtests/net/packetdrill/sctp_iterator.c +++ b/gtests/net/packetdrill/sctp_iterator.c @@ -72,6 +72,7 @@ struct sctp_chunk *sctp_chunks_next(struct sctp_chunks_iterator *iter, u16 chunk_length, padding_length; struct sctp_chunk *current_chunk; + if (*error) printf("!!!%s!!!\n", *error); assert(*error == NULL); current_chunk = (struct sctp_chunk *)iter->current_chunk; chunk_length = ntohs(current_chunk->length); diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index f123d542..8f8b3407 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -234,9 +234,73 @@ sctp_address_type_list_item_new(u16 address_type) return item; } +struct sctp_parameter_type_list * +sctp_parameter_type_list_new(void) +{ + struct sctp_parameter_type_list *list; + + list = malloc(sizeof(struct sctp_parameter_type_list)); + assert(list != NULL); + list->first = NULL; + list->last = NULL; + list->nr_entries = 0; + return list; +} + +void +sctp_parameter_type_list_append(struct sctp_parameter_type_list *list, + struct sctp_parameter_type_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_parameter_type_list_free(struct sctp_parameter_type_list *list) +{ + struct sctp_parameter_type_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_parameter_type_list_item * +sctp_parameter_type_list_item_new(u16 parameter_type) +{ + struct sctp_parameter_type_list_item *item; + + item = malloc(sizeof(struct sctp_parameter_type_list_item)); + assert(item != NULL); + item->next = NULL; + item->parameter_type = parameter_type; + return item; +} + struct sctp_chunk_list_item * sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags, - struct sctp_parameter_list *list) + struct sctp_parameter_list *parameter_list, + struct sctp_cause_list *cause_list) { struct sctp_chunk_list_item *item; @@ -244,7 +308,8 @@ sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags, assert(item != NULL); item->next = NULL; item->chunk = chunk; - item->parameter_list = list; + item->parameter_list = parameter_list; + item->cause_list = cause_list; item->length = length; item->flags = flags; return item; @@ -301,7 +366,9 @@ sctp_generic_chunk_new(s64 type, s64 flgs, s64 len, 0, padding_length); return sctp_chunk_list_item_new(chunk, length + padding_length, - flags, sctp_parameter_list_new()); + flags, + sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -363,7 +430,8 @@ sctp_data_chunk_new(s64 flgs, s64 len, s64 tsn, s64 sid, s64 ssn, s64 ppid) length + padding_length - sizeof(struct sctp_data_chunk)); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, length, flags, - sctp_parameter_list_new()); + sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -446,7 +514,7 @@ sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn, } return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + chunk_padding_length, - flags, list); + flags, list, sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -529,7 +597,7 @@ sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn, } return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + chunk_padding_length, - flags, list); + flags, list, sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -607,7 +675,8 @@ sctp_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd, } return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, length, flags, - sctp_parameter_list_new()); + sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -653,7 +722,8 @@ sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info) free(info); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + padding_length, - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -699,17 +769,32 @@ sctp_heartbeat_ack_chunk_new(s64 flgs, struct sctp_parameter_list_item *info) free(info); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + padding_length, - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * -sctp_abort_chunk_new(s64 flgs) +sctp_abort_chunk_new(s64 flgs, struct sctp_cause_list *list) { struct sctp_abort_chunk *chunk; + struct sctp_cause_list_item *item; u32 flags; + u16 offset, chunk_length, chunk_padding_length, cause_padding_length; - flags = FLAG_CHUNK_LENGTH_NOCHECK | FLAG_CHUNK_VALUE_NOCHECK; - chunk = malloc(sizeof(struct sctp_abort_chunk)); + flags = 0; + chunk_length = sizeof(struct sctp_abort_chunk); + if (list != NULL) { + chunk_length += list->length; + } else { + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_ERROR_CHUNK_OPT_CAUSES_NOCHECK; + list = sctp_cause_list_new(); + } + chunk_padding_length = chunk_length % 4; + if (chunk_padding_length > 0) { + chunk_padding_length = 4 - chunk_padding_length; + } + chunk = malloc(chunk_length + chunk_padding_length); assert(chunk != NULL); chunk->type = SCTP_ABORT_CHUNK_TYPE; if (flgs == -1) { @@ -718,10 +803,30 @@ sctp_abort_chunk_new(s64 flgs) } else { chunk->flags = (u8)flgs; } - chunk->length = htons(sizeof(struct sctp_abort_chunk)); + chunk->length = htons(chunk_length); + offset = 0; + for (item = list->first; item != NULL; item = item->next) { + cause_padding_length = item->length % 4; + if (cause_padding_length > 0) { + cause_padding_length = 4 - cause_padding_length; + } + memcpy(chunk->cause + offset, + item->cause, + item->length + cause_padding_length); + free(item->cause); + item->cause = (struct sctp_cause *)(chunk->cause + offset); + if ((item->flags & FLAG_CAUSE_CODE_NOCHECK) || + (item->flags & FLAG_CAUSE_INFORMATION_NOCHECK)) { + flags |= FLAG_CHUNK_VALUE_NOCHECK; + } + if (item->flags & FLAG_CAUSE_LENGTH_NOCHECK) { + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + } + offset += item->length + cause_padding_length; + } return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, - (u32)sizeof(struct sctp_abort_chunk), - flags, sctp_parameter_list_new()); + chunk_length + chunk_padding_length, + flags, sctp_parameter_list_new(), list); } struct sctp_chunk_list_item * @@ -750,7 +855,8 @@ sctp_shutdown_chunk_new(s64 flgs, s64 cum_tsn) return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_shutdown_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -772,17 +878,32 @@ sctp_shutdown_ack_chunk_new(s64 flgs) chunk->length = htons(sizeof(struct sctp_shutdown_ack_chunk)); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_shutdown_ack_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * -sctp_error_chunk_new(s64 flgs) +sctp_error_chunk_new(s64 flgs, struct sctp_cause_list *list) { struct sctp_error_chunk *chunk; + struct sctp_cause_list_item *item; u32 flags; + u16 offset, chunk_length, chunk_padding_length, cause_padding_length; - flags = FLAG_CHUNK_LENGTH_NOCHECK | FLAG_CHUNK_VALUE_NOCHECK; - chunk = malloc(sizeof(struct sctp_error_chunk)); + flags = 0; + chunk_length = sizeof(struct sctp_error_chunk); + if (list != NULL) { + chunk_length += list->length; + } else { + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + flags |= FLAG_ERROR_CHUNK_OPT_CAUSES_NOCHECK; + list = sctp_cause_list_new(); + } + chunk_padding_length = chunk_length % 4; + if (chunk_padding_length > 0) { + chunk_padding_length = 4 - chunk_padding_length; + } + chunk = malloc(chunk_length + chunk_padding_length); assert(chunk != NULL); chunk->type = SCTP_ERROR_CHUNK_TYPE; if (flgs == -1) { @@ -791,10 +912,30 @@ sctp_error_chunk_new(s64 flgs) } else { chunk->flags = (u8)flgs; } - chunk->length = htons(sizeof(struct sctp_error_chunk)); + chunk->length = htons(chunk_length); + offset = 0; + for (item = list->first; item != NULL; item = item->next) { + cause_padding_length = item->length % 4; + if (cause_padding_length > 0) { + cause_padding_length = 4 - cause_padding_length; + } + memcpy(chunk->cause + offset, + item->cause, + item->length + cause_padding_length); + free(item->cause); + item->cause = (struct sctp_cause *)(chunk->cause + offset); + if ((item->flags & FLAG_CAUSE_CODE_NOCHECK) || + (item->flags & FLAG_CAUSE_INFORMATION_NOCHECK)) { + flags |= FLAG_CHUNK_VALUE_NOCHECK; + } + if (item->flags & FLAG_CAUSE_LENGTH_NOCHECK) { + flags |= FLAG_CHUNK_LENGTH_NOCHECK; + } + offset += item->length + cause_padding_length; + } return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, - (u32)sizeof(struct sctp_error_chunk), - flags, sctp_parameter_list_new()); + chunk_length + chunk_padding_length, + flags, sctp_parameter_list_new(), list); } struct sctp_chunk_list_item * @@ -839,7 +980,8 @@ sctp_cookie_echo_chunk_new(s64 flgs, s64 len, u8* cookie) memset(chunk->cookie + cookie_length, 0, padding_length); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + padding_length, - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -861,7 +1003,8 @@ sctp_cookie_ack_chunk_new(s64 flgs) chunk->length = htons(sizeof(struct sctp_cookie_ack_chunk)); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_cookie_ack_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -890,7 +1033,8 @@ sctp_ecne_chunk_new(s64 flgs, s64 lowest_tsn) return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_ecne_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -919,7 +1063,8 @@ sctp_cwr_chunk_new(s64 flgs, s64 lowest_tsn) return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_cwr_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -941,7 +1086,8 @@ sctp_shutdown_complete_chunk_new(s64 flgs) chunk->length = htons(sizeof(struct sctp_shutdown_complete_chunk)); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, (u32)sizeof(struct sctp_shutdown_complete_chunk), - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list_item * @@ -986,7 +1132,8 @@ sctp_pad_chunk_new(s64 flgs, s64 len, u8* padding) memset(chunk->padding_data + padding_length, 0, chunk_padding_length); return sctp_chunk_list_item_new((struct sctp_chunk *)chunk, chunk_length + chunk_padding_length, - flags, sctp_parameter_list_new()); + flags, sctp_parameter_list_new(), + sctp_cause_list_new()); } struct sctp_chunk_list * @@ -1031,6 +1178,8 @@ sctp_chunk_list_free(struct sctp_chunk_list *list) assert(next_item != NULL || current_item == list->last); assert(current_item->parameter_list); sctp_parameter_list_free(current_item->parameter_list); + assert(current_item->cause_list); + sctp_cause_list_free(current_item->cause_list); free(current_item); current_item = next_item; } @@ -1064,7 +1213,7 @@ sctp_generic_parameter_new(s64 type, s64 len, struct sctp_byte_list *bytes) flags |= FLAG_PARAMETER_VALUE_NOCHECK; } if (len == -1) { - parameter_length = (u16)sizeof(struct sctp_chunk); + parameter_length = (u16)sizeof(struct sctp_parameter); flags |= FLAG_PARAMETER_LENGTH_NOCHECK; } else { parameter_length = (u16)len; @@ -1474,6 +1623,528 @@ sctp_parameter_list_free(struct sctp_parameter_list *list) free(list); } +struct sctp_cause_list_item * +sctp_cause_list_item_new(struct sctp_cause *cause, u32 length, u32 flags) +{ + struct sctp_cause_list_item *item; + + item = malloc(sizeof(struct sctp_cause_list_item)); + assert(item != NULL); + item->next = NULL; + item->cause = cause; + item->length = length; + item->flags = flags; + return item; +} + +struct sctp_cause_list_item * +sctp_generic_cause_new(s64 code, s64 len, struct sctp_byte_list *bytes) +{ + struct sctp_cause *cause; + struct sctp_byte_list_item *item; + u32 flags; + u16 cause_length, information_length, padding_length, i; + + flags = 0; + if (bytes == NULL) { + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + if (len == -1) { + cause_length = (u16)sizeof(struct sctp_cause); + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + } else { + cause_length = (u16)len; + } + information_length = cause_length - sizeof(struct sctp_cause); + padding_length = cause_length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(cause_length + padding_length); + assert(cause != NULL); + if (code == -1) { + cause->code = 0; + flags |= FLAG_CAUSE_CODE_NOCHECK; + } else { + cause->code = htons((u16)code); + } + cause->length = htons(cause_length); + if (bytes != NULL) { + for (i = 0, item = bytes->first; + item != NULL; + i++, item = item->next) { + cause->information[i] = item->byte; + } + } else { + memset(cause->information, 0, information_length); + } + /* Clear the padding */ + memset(cause->information + information_length, 0, padding_length); + return sctp_cause_list_item_new(cause, cause_length, flags); +} + +struct sctp_cause_list_item * +sctp_invalid_stream_identifier_cause_new(s64 sid) +{ + struct sctp_invalid_stream_identifier_cause *cause; + u32 flags; + + flags = 0; + cause = malloc(sizeof(struct sctp_invalid_stream_identifier_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_INVALID_STREAM_IDENTIFIER_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_invalid_stream_identifier_cause)); + if (sid == -1) { + cause->sid = htonl(0); + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + assert(is_valid_u16(sid)); + cause->sid = htons((u16)sid); + } + cause->reserved = htons(0); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_invalid_stream_identifier_cause), + flags); +} + +struct sctp_cause_list_item * +sctp_missing_mandatory_parameter_cause_new(struct sctp_parameter_type_list *list) +{ + struct sctp_missing_mandatory_parameter_cause *cause; + + u32 flags; + u16 i, cause_length, padding_length; + struct sctp_parameter_type_list_item *item; + + flags = 0; + cause_length = sizeof(struct sctp_missing_mandatory_parameter_cause); + if (list == NULL) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + assert(list->nr_entries <= + (MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_missing_mandatory_parameter_cause)) / sizeof(u16)); + cause_length += list->nr_entries * sizeof(u16); + } + padding_length = cause_length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + assert(padding_length == 0 || padding_length == 2); + cause = malloc(cause_length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_MISSING_MANDATORY_PARAMETER_CAUSE_CODE); + cause->length = htons(cause_length); + cause->nr_parameters = htonl(list->nr_entries); + if (list != NULL) { + for (i = 0, item = list->first; + (i < list->nr_entries) && (item != NULL); + i++, item = item->next) { + cause->parameter_type[i] = htons(item->parameter_type); + } + assert((i == list->nr_entries) && (item == NULL)); + } + if (padding_length == 2) { + cause->parameter_type[list->nr_entries] = htons(0); + } + return sctp_cause_list_item_new((struct sctp_cause *)cause, + cause_length, flags); +} + +struct sctp_cause_list_item * +sctp_stale_cookie_error_cause_new(s64 staleness) +{ + struct sctp_stale_cookie_error_cause *cause; + u32 flags; + + flags = 0; + cause = malloc(sizeof(struct sctp_stale_cookie_error_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_STALE_COOKIE_ERROR_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_stale_cookie_error_cause)); + if (staleness == -1) { + cause->staleness = htonl(0); + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + assert(is_valid_u32(staleness)); + cause->staleness = htonl((u32)staleness); + } + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_stale_cookie_error_cause), + flags); +} + +struct sctp_cause_list_item * +sctp_out_of_resources_cause_new(void) +{ + struct sctp_out_of_resources_cause *cause; + + cause = malloc(sizeof(struct sctp_out_of_resources_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_OUT_OF_RESOURCES_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_out_of_resources_cause)); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_out_of_resources_cause), + 0); +} + +struct sctp_cause_list_item * +sctp_unresolvable_address_cause_new(struct sctp_parameter_list_item *item) +{ + struct sctp_unresolvable_address_cause *cause; + u32 flags; + u16 length, padding_length; + + assert(item == NULL || + (item->length <= + MAX_SCTP_CAUSE_BYTES - sizeof(struct sctp_unresolvable_address_cause))); + flags = 0; + length = sizeof(struct sctp_unresolvable_address_cause); + if (item != NULL) { + length += item->length; + } + padding_length = length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_UNRESOLVABLE_ADDRESS_CAUSE_CODE); + cause->length = htons(length); + if (item == NULL) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + if ((item->flags & FLAG_PARAMETER_TYPE_NOCHECK) || + (item->flags & FLAG_PARAMETER_VALUE_NOCHECK)) { + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + if (item->flags & FLAG_PARAMETER_LENGTH_NOCHECK) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + } + memcpy(cause->parameter, item->parameter, item->length); + } + memset(cause->parameter + item->length, 0, padding_length); + free(item); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + length, flags); +} + +struct sctp_cause_list_item * +sctp_unrecognized_chunk_type_cause_new(struct sctp_chunk_list_item *item) +{ + struct sctp_unrecognized_chunk_type_cause *cause; + u32 flags; + u16 length, padding_length; + + assert(item == NULL || + (item->length <= + MAX_SCTP_CAUSE_BYTES - sizeof(struct sctp_unrecognized_chunk_type_cause))); + flags = 0; + length = sizeof(struct sctp_unrecognized_chunk_type_cause); + if (item != NULL) { + length += item->length; + } + padding_length = length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_UNRECOGNIZED_CHUNK_TYPE_CAUSE_CODE); + cause->length = htons(length); + if (item == NULL) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + if ((item->flags & FLAG_CHUNK_TYPE_NOCHECK) || + (item->flags & FLAG_CHUNK_FLAGS_NOCHECK) || + (item->flags & FLAG_CHUNK_VALUE_NOCHECK)) { + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + if (item->flags & FLAG_CHUNK_LENGTH_NOCHECK) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + } + memcpy(cause->chunk, item->chunk, item->length); + } + memset(cause->chunk + item->length, 0, padding_length); + free(item); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + length, flags); +} + +struct sctp_cause_list_item * +sctp_invalid_mandatory_parameter_cause_new(void) +{ + struct sctp_invalid_mandatory_parameter_cause *cause; + + cause = malloc(sizeof(struct sctp_invalid_mandatory_parameter_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_INVALID_MANDATORY_PARAMETER_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_invalid_mandatory_parameter_cause)); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_invalid_mandatory_parameter_cause), + 0); +} + +struct sctp_cause_list_item * +sctp_unrecognized_parameters_cause_new(struct sctp_parameter_list *list) +{ + struct sctp_unrecognized_parameters_cause *cause; + struct sctp_parameter_list_item *item; + u32 flags; + u16 cause_length, padding_length, offset; + + assert(list == NULL || + (list->length < + MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_unrecognized_parameters_cause))); + flags = 0; + cause_length = sizeof(struct sctp_unrecognized_parameters_cause); + if (list != NULL) { + cause_length += list->length; + } else { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + padding_length = cause_length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(cause_length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_UNRECOGNIZED_PARAMETERS_CAUSE_CODE); + cause->length = htons(cause_length); + if (list != NULL) { + offset = 0; + for (item = list->first; item != NULL; item = item->next) { + padding_length = item->length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + memcpy(cause->parameters + offset, item->parameter, item->length + padding_length); + if (item->flags & FLAG_PARAMETER_LENGTH_NOCHECK) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + } + if (item->flags & FLAG_PARAMETER_VALUE_NOCHECK) { + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + offset += item->length + padding_length; + } + } + return sctp_cause_list_item_new((struct sctp_cause *)cause, + cause_length, flags); +} + +struct sctp_cause_list_item * +sctp_no_user_data_cause_new(s64 tsn) +{ + struct sctp_no_user_data_cause *cause; + u32 flags; + + flags = 0; + cause = malloc(sizeof(struct sctp_no_user_data_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_NO_USER_DATA_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_no_user_data_cause)); + if (tsn == -1) { + cause->tsn = htonl(0); + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + assert(is_valid_u32(tsn)); + cause->tsn = htonl((u32)tsn); + } + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_no_user_data_cause), + flags); +} + +struct sctp_cause_list_item * +sctp_cookie_received_while_shutdown_cause_new(void) +{ + struct sctp_cookie_received_while_shutdown_cause *cause; + + cause = malloc(sizeof(struct sctp_cookie_received_while_shutdown_cause)); + assert(cause != NULL); + cause->code = htons(SCTP_COOKIE_RECEIVED_WHILE_SHUTDOWN_CAUSE_CODE); + cause->length = htons(sizeof(struct sctp_cookie_received_while_shutdown_cause)); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + sizeof(struct sctp_cookie_received_while_shutdown_cause), + 0); +} + +struct sctp_cause_list_item * +sctp_restart_with_new_addresses_cause_new(struct sctp_parameter_list *list) +{ + struct sctp_restart_with_new_addresses_cause *cause; + struct sctp_parameter_list_item *item; + u32 flags; + u16 cause_length, padding_length, offset; + + assert(list == NULL || + (list->length < + MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_restart_with_new_addresses_cause))); + flags = 0; + cause_length = sizeof(struct sctp_restart_with_new_addresses_cause); + if (list != NULL) { + cause_length += list->length; + } else { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + padding_length = cause_length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(cause_length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_RESTART_WITH_NEW_ADDRESSES_CAUSE_CODE); + cause->length = htons(cause_length); + if (list != NULL) { + offset = 0; + for (item = list->first; item != NULL; item = item->next) { + padding_length = item->length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + memcpy(cause->addresses + offset, item->parameter, item->length + padding_length); + if (item->flags & FLAG_PARAMETER_LENGTH_NOCHECK) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + } + if (item->flags & FLAG_PARAMETER_VALUE_NOCHECK) { + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } + offset += item->length + padding_length; + } + } + return sctp_cause_list_item_new((struct sctp_cause *)cause, + cause_length, flags); +} + +struct sctp_cause_list_item * +sctp_user_initiated_abort_cause_new(char *info) +{ + struct sctp_user_initiated_abort_cause *cause; + u32 flags; + u16 length, info_length, padding_length; + + assert(info == NULL || + (strlen(info) <= + MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_user_initiated_abort_cause))); + flags = 0; + if (info == NULL) { + info_length = 0; + } else { + info_length = strlen(info); + } + length = info_length + sizeof(struct sctp_user_initiated_abort_cause); + padding_length = length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_USER_INITIATED_ABORT_CAUSE_CODE); + cause->length = htons(length); + if (info == NULL) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + memcpy(cause->information, info, info_length); + } + memset(cause->information + info_length, 0, padding_length); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + length, flags); +} + +struct sctp_cause_list_item * +sctp_protocol_violation_cause_new(char *info) +{ + struct sctp_protocol_violation_cause *cause; + u32 flags; + u16 length, info_length, padding_length; + + assert(info == NULL || + (strlen(info) <= + MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_protocol_violation_cause))); + flags = 0; + if (info == NULL) { + info_length = 0; + } else { + info_length = strlen(info); + } + length = info_length + sizeof(struct sctp_protocol_violation_cause); + padding_length = length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + cause = malloc(length + padding_length); + assert(cause != NULL); + cause->code = htons(SCTP_PROTOCOL_VIOLATION_CAUSE_CODE); + cause->length = htons(length); + if (info == NULL) { + flags |= FLAG_CAUSE_LENGTH_NOCHECK; + flags |= FLAG_CAUSE_INFORMATION_NOCHECK; + } else { + memcpy(cause->information, info, info_length); + } + memset(cause->information + info_length, 0, padding_length); + return sctp_cause_list_item_new((struct sctp_cause *)cause, + length, flags); +} + +struct sctp_cause_list * +sctp_cause_list_new(void) +{ + struct sctp_cause_list *list; + + list = malloc(sizeof(struct sctp_cause_list)); + assert(list != NULL); + list->first = NULL; + list->last = NULL; + list->length = 0; + return list; +} + +void +sctp_cause_list_append(struct sctp_cause_list *list, + struct sctp_cause_list_item *item) +{ + u16 padding_length; + + assert(item->next == NULL); + padding_length = list->length % 4; + if (padding_length > 0) { + padding_length = 4 - padding_length; + } + list->length += padding_length; + if (list->last == NULL) { + assert(list->first == NULL); + assert(list->length == 0); + list->first = item; + } else { + assert(list->first != NULL); + list->last->next = item; + } + list->last = item; + list->length += item->length; +} + +void +sctp_cause_list_free(struct sctp_cause_list *list) +{ + struct sctp_cause_list_item *current_item, *next_item; + + assert(list != NULL); + current_item = list->first; + while (current_item != NULL) { + next_item = current_item->next; + assert(next_item != NULL || current_item == list->last); + free(current_item); + current_item = next_item; + } + free(list); +} + struct packet * new_sctp_packet(int address_family, enum direction_t direction, @@ -1485,6 +2156,7 @@ new_sctp_packet(int address_family, struct header *sctp_header; /* the SCTP header info */ struct sctp_chunk_list_item *chunk_item; struct sctp_parameter_list_item *parameter_item; + struct sctp_cause_list_item *cause_item; /* 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) + @@ -1531,6 +2203,20 @@ new_sctp_packet(int address_family, return NULL; } } + for (cause_item = chunk_item->cause_list->first; + cause_item != NULL; + cause_item = cause_item->next) { + if (cause_item->flags & FLAG_CAUSE_LENGTH_NOCHECK) { + asprintf(error, + "cause length must be specified for inbound packets"); + return NULL; + } + if (cause_item->flags & FLAG_CAUSE_INFORMATION_NOCHECK) { + asprintf(error, + "cause information must be specified for inbound packets"); + return NULL; + } + } switch (chunk_item->chunk->type) { case SCTP_DATA_CHUNK_TYPE: if (chunk_item->flags & FLAG_CHUNK_FLAGS_NOCHECK) { @@ -1646,13 +2332,11 @@ new_sctp_packet(int address_family, overbook = true; break; case SCTP_ABORT_CHUNK_TYPE: -#if 0 if (chunk_item->flags & FLAG_CHUNK_LENGTH_NOCHECK) { asprintf(error, "error causes must be specified for inbound packets"); return NULL; } -#endif break; case SCTP_SHUTDOWN_CHUNK_TYPE: if (chunk_item->flags & FLAG_SHUTDOWN_CHUNK_CUM_TSN_NOCHECK) { @@ -1664,13 +2348,11 @@ new_sctp_packet(int address_family, case SCTP_SHUTDOWN_ACK_CHUNK_TYPE: break; case SCTP_ERROR_CHUNK_TYPE: -#if 0 if (chunk_item->flags & FLAG_CHUNK_LENGTH_NOCHECK) { asprintf(error, "error causes must be specified for inbound packets"); return NULL; } -#endif break; case SCTP_COOKIE_ECHO_CHUNK_TYPE: overbook = true; @@ -1758,6 +2440,14 @@ new_sctp_packet(int address_family, ((u8 *)parameter_item->parameter - (u8 *)chunk_item->chunk)); } + for (cause_item = chunk_item->cause_list->first; + cause_item != NULL; + cause_item = cause_item->next) { + cause_item->cause = + (struct sctp_cause *)(sctp_chunk_start + + ((u8 *)cause_item->cause - + (u8 *)chunk_item->chunk)); + } free(chunk_item->chunk); chunk_item->chunk = (struct sctp_chunk *)sctp_chunk_start; sctp_chunk_start += chunk_item->length; diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index 85a3ea62..42071111 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -104,6 +104,30 @@ sctp_address_type_list_free(struct sctp_address_type_list *list); struct sctp_address_type_list_item * sctp_address_type_list_item_new(u16 address_type); +struct sctp_parameter_type_list_item { + struct sctp_parameter_type_list_item *next; + u16 parameter_type; +}; + +struct sctp_parameter_type_list { + struct sctp_parameter_type_list_item *first; + struct sctp_parameter_type_list_item *last; + u16 nr_entries; +}; + +struct sctp_parameter_type_list * +sctp_parameter_type_list_new(void); + +void +sctp_parameter_type_list_append(struct sctp_parameter_type_list *list, + struct sctp_parameter_type_list_item *item); + +void +sctp_parameter_type_list_free(struct sctp_parameter_type_list *list); + +struct sctp_parameter_type_list_item * +sctp_parameter_type_list_item_new(u16 parameter_type); + struct sctp_parameter_list_item { struct sctp_parameter_list_item *next; struct sctp_parameter *parameter; @@ -120,10 +144,27 @@ struct sctp_parameter_list { u32 length; }; +struct sctp_cause_list_item { + struct sctp_cause_list_item *next; + struct sctp_cause *cause; + /* total length in bytes */ + u32 length; + /* metadata */ + u32 flags; +}; + +struct sctp_cause_list { + struct sctp_cause_list_item *first; + struct sctp_cause_list_item *last; + /* length in bytes excluding the padding of the last cause*/ + u32 length; +}; + struct sctp_chunk_list_item { struct sctp_chunk_list_item *next; struct sctp_chunk *chunk; struct sctp_parameter_list *parameter_list; + struct sctp_cause_list *cause_list; /* total length in bytes */ u32 length; /* metadata */ @@ -139,7 +180,8 @@ struct sctp_chunk_list { struct sctp_chunk_list_item * sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags, - struct sctp_parameter_list *list); + struct sctp_parameter_list *parameter_list, + struct sctp_cause_list *cause_list); #define FLAG_CHUNK_TYPE_NOCHECK 0x00000001 #define FLAG_CHUNK_FLAGS_NOCHECK 0x00000002 @@ -196,8 +238,10 @@ sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info); struct sctp_chunk_list_item * sctp_heartbeat_ack_chunk_new(s64 flgs, struct sctp_parameter_list_item *info); +#define FLAG_ABORT_CHUNK_OPT_CAUSES_NOCHECK 0x00000100 + struct sctp_chunk_list_item * -sctp_abort_chunk_new(s64 flgs); +sctp_abort_chunk_new(s64 flgs, struct sctp_cause_list *causes); #define FLAG_SHUTDOWN_CHUNK_CUM_TSN_NOCHECK 0x00000100 @@ -207,8 +251,10 @@ sctp_shutdown_chunk_new(s64 flgs, s64 cum_tsn); struct sctp_chunk_list_item * sctp_shutdown_ack_chunk_new(s64 flgs); +#define FLAG_ERROR_CHUNK_OPT_CAUSES_NOCHECK 0x00000100 + struct sctp_chunk_list_item * -sctp_error_chunk_new(s64 flgs); +sctp_error_chunk_new(s64 flgs, struct sctp_cause_list *causes); struct sctp_chunk_list_item * sctp_cookie_echo_chunk_new(s64 flgs, s64 len, u8* cookie); @@ -293,6 +339,65 @@ sctp_parameter_list_append(struct sctp_parameter_list *list, void sctp_parameter_list_free(struct sctp_parameter_list *list); +struct sctp_cause_list_item * +sctp_cause_list_item_new(struct sctp_cause *cause, + u32 length, u32 flags); + +#define FLAG_CAUSE_CODE_NOCHECK 0x00000001 +#define FLAG_CAUSE_LENGTH_NOCHECK 0x00000002 +#define FLAG_CAUSE_INFORMATION_NOCHECK 0x00000004 + +struct sctp_cause_list_item * +sctp_generic_cause_new(s64 code, s64 len, struct sctp_byte_list *bytes); + +struct sctp_cause_list_item * +sctp_invalid_stream_identifier_cause_new(s64 sid); + +struct sctp_cause_list_item * +sctp_missing_mandatory_parameter_cause_new(struct sctp_parameter_type_list *list); + +struct sctp_cause_list_item * +sctp_stale_cookie_error_cause_new(s64 staleness); + +struct sctp_cause_list_item * +sctp_out_of_resources_cause_new(void); + +struct sctp_cause_list_item * +sctp_unresolvable_address_cause_new(struct sctp_parameter_list_item *item); + +struct sctp_cause_list_item * +sctp_unrecognized_chunk_type_cause_new(struct sctp_chunk_list_item *item); + +struct sctp_cause_list_item * +sctp_invalid_mandatory_parameter_cause_new(void); + +struct sctp_cause_list_item * +sctp_unrecognized_parameters_cause_new(struct sctp_parameter_list *list); + +struct sctp_cause_list_item * +sctp_no_user_data_cause_new(s64 tsn); + +struct sctp_cause_list_item * +sctp_cookie_received_while_shutdown_cause_new(void); + +struct sctp_cause_list_item * +sctp_restart_with_new_addresses_cause_new(struct sctp_parameter_list *list); + +struct sctp_cause_list_item * +sctp_user_initiated_abort_cause_new(char *info); + +struct sctp_cause_list_item * +sctp_protocol_violation_cause_new(char *info); + +struct sctp_cause_list * +sctp_cause_list_new(void); + +void +sctp_cause_list_append(struct sctp_cause_list *list, + struct sctp_cause_list_item *item); + +void +sctp_cause_list_free(struct sctp_cause_list *list); /* Create and initialize a new struct packet containing a SCTP packet. * On success, returns a newly-allocated packet. On failure, returns NULL diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_error_causes_active.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_error_causes_active.pkt new file mode 100644 index 00000000..685453e4 --- /dev/null +++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_error_causes_active.pkt @@ -0,0 +1,39 @@ ++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3 ++0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR) ++0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 +// Check the handshake with an empty(!) cookie ++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress) ++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=1, ...] ++0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=1500, os=1, is=1, tsn=1, STATE_COOKIE[len=4, val=...]] ++0.0 > sctp: COOKIE_ECHO[flgs=0, len=4, val=...] ++0.1 < sctp: COOKIE_ACK[flgs=0] ++0.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 ++0.1 < sctp: ERROR[flgs=0] ++0.1 < sctp: ERROR[flgs=0, CAUSE[code=0x0004, len=4, info=[]]] ++0.1 < sctp: ERROR[flgs=0, CAUSE[code=0x0001, len=8, info=[0x00, 0x01, 0x00, 0x00]], + CAUSE[code=0x0001, len=8, info=[0x00, 0x02, 0x00, 0x00]]] ++0.1 < sctp: ERROR[flgs=0, INVALID_STREAM_IDENTIFIER[sid=65535]] ++0.1 < sctp: ERROR[flgs=0, MISSING_MANDATORY_PARAMETER[types=[123, 234]]] ++0.1 < sctp: ERROR[flgs=0, STALE_COOKIE_ERROR[staleness=12345678]] ++0.1 < sctp: ERROR[flgs=0, OUT_OF_RESOURCE[]] ++0.1 < sctp: ERROR[flgs=0, UNRESOLVABLE_ADDRESS[param=HOSTNAME_ADDRESS[addr="@A"]]] ++0.1 < sctp: ERROR[flgs=0, UNRECOGNIZED_CHUNK_TYPE[chk=CHUNK[type=0xCF, flgs=0, len=7, val=[0x01, 0x02, 0x03]]]] ++0.1 < sctp: ERROR[flgs=0, INVALID_MANDATORY_PARAMETER[]] ++0.1 < sctp: ERROR[flgs=0, UNRECOGNIZED_PARAMETERS[params=[PARAMETER[type=0x1234, len=4, val=[]]]]] ++0.1 < sctp: ERROR[flgs=0, NO_USER_DATA[tsn=12345678]] ++0.1 < sctp: ERROR[flgs=0, COOKIE_RECEIVED_WHILE_SHUTDOWN[]] ++0.1 < sctp: ERROR[flgs=0, RESTART_WITH_NEW_ADDRESSES[params=[IPV4_ADDRESS[addr=1.2.3.4], + IPV4_ADDRESS[addr=2.3.4.5]]]] ++0.1 < sctp: ERROR[flgs=0, USER_INITIATED_ABORT[info="Testing"]] ++0.1 < sctp: ERROR[flgs=0, PROTOCOL_VIOLATION[info="Testing"]] +//+0.1 < sctp: DATA[flgs=IBE, len=16, tsn=1, sid=0, ssn=0, ppid=0] +//+0.0 > sctp: ABORT[flgs=0, NO_USER_DATA[tsn=1]] +//+0.0 > sctp: ABORT[flgs=0, ...] ++1.0 < sctp: DATA[flgs=IBE, len=1016, tsn=1, sid=65535, ssn=0, ppid=0] ++0.0 > sctp: ERROR[flgs=0, CAUSE[code=0x0001, len=8, info=[0xff, 0xff, 0x00, 0x00]]]; + SACK[flgs=0, cum_tsn=1, a_rwnd=..., gaps=[], dups=[]] // cum_tsn? +// Tear down the association ++0.0 close(3) = 0 ++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=1] ++0.1 < sctp: SHUTDOWN_ACK[flgs=0] ++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0] -- GitLab