From 1271cf018fb47a2c2591cdff076aaf53b21c7e1c Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Sun, 11 Oct 2015 09:36:05 +0200
Subject: [PATCH] Add support for injecting SCTP packets with bad CRC32Cs.

While there, remove trailing whitespaces.
This fixes https://github.com/nplab/packetdrill/issues/33.
---
 gtests/net/packetdrill/lexer.l            |  1 +
 gtests/net/packetdrill/packet.h           |  1 +
 gtests/net/packetdrill/packet_checksum.c  | 12 ++++--
 gtests/net/packetdrill/packet_to_string.c |  6 ++-
 gtests/net/packetdrill/parser.y           | 48 +++++++++++++++--------
 gtests/net/packetdrill/run_packet.c       |  2 +-
 gtests/net/packetdrill/sctp_packet.c      | 10 +++++
 gtests/net/packetdrill/sctp_packet.h      |  1 +
 8 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 4980fdd8..5179907b 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -318,6 +318,7 @@ info				return CAUSE_INFO;
 staleness			return STALENESS;
 param				return PARAM;
 chk				return CHK;
+bad_crc32c			return BAD_CRC32C;
 --[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.h b/gtests/net/packetdrill/packet.h
index 6348cc4e..832f6125 100644
--- a/gtests/net/packetdrill/packet.h
+++ b/gtests/net/packetdrill/packet.h
@@ -108,6 +108,7 @@ struct packet {
 	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 */
 
 	enum ip_ecn_t ecn;	/* IPv4/IPv6 ECN treatment for packet */
 
diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c
index 2c5a142f..a81a3d05 100644
--- a/gtests/net/packetdrill/packet_checksum.c
+++ b/gtests/net/packetdrill/packet_checksum.c
@@ -47,8 +47,11 @@ static void checksum_ipv4_packet(struct packet *packet)
 	/* Fill in IPv4-based layer 4 checksum. */
 	if (packet->sctp != NULL) {
 		struct sctp_common_header *sctp = packet->sctp;
-		sctp->crc32c = 0;
+		sctp->crc32c = htonl(0);
 		sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
+			sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1);
+		}
 	} else if (packet->tcp != NULL) {
 		struct tcp *tcp = packet->tcp;
 		tcp->check = 0;
@@ -98,8 +101,11 @@ static void checksum_ipv6_packet(struct packet *packet)
 	/* Fill in IPv6-based layer 4 checksum. */
 	if (packet->sctp != NULL) {
 		struct sctp_common_header *sctp = packet->sctp;
-		sctp->crc32c = 0;
+		sctp->crc32c = htonl(0);
 		sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
+			sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1);
+		}
 	} else if (packet->tcp != NULL) {
 		struct tcp *tcp = packet->tcp;
 		tcp->check = 0;
@@ -134,7 +140,7 @@ static void checksum_ipv6_packet(struct packet *packet)
 					    &ipv6->dst_ip,
 					    IPPROTO_ICMPV6, icmpv6, l4_bytes);
 	} else {
-		assert(!"not TCP or UDP or UDPLite or ICMP");
+		assert(!"not SCTP or TCP or UDP or UDPLite or ICMP");
 	}
 }
 
diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c
index 08573d39..9b1dd53e 100644
--- a/gtests/net/packetdrill/packet_to_string.c
+++ b/gtests/net/packetdrill/packet_to_string.c
@@ -134,7 +134,11 @@ static int sctp_packet_to_string(FILE *s, struct packet *packet,
 		fputc(' ', s);
 	}
 
-	fputs("sctp:", s);
+	fputs("sctp", s);
+	if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
+		fputs("(bad_crc32c)", s);
+	}
+	fputc(':', s);
 
 	index = 0;
 	for (chunk = sctp_chunks_begin(packet, &iter, error);
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 255215c6..90aad785 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -535,9 +535,10 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> STALENESS CHK PARAM UNRECOGNIZED_PARAMETERS
 %token <reserved> SPP_ADDRESS SPP_HBINTERVAL SPP_PATHMAXRXT SPP_PATHMTU
 %token <reserved> SPP_FLAGS SPP_IPV6_FLOWLABEL_ SPP_DSCP_
-%token <reserved> SASOC_ASOCMAXRXT SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND 
+%token <reserved> SASOC_ASOCMAXRXT SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
 %token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE SE_TYPE SE_ON
 %token <reserved> SND_SID SND_FLAGS SND_PPID SND_CONTEXT SSB_ADAPTATION_IND
+%token <reserved> BAD_CRC32C
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -577,15 +578,15 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> linger l_onoff l_linger
 %type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata
 %type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary
-%type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts 
-%type <expression> sinit_max_init_timeo sctp_assoc_value sctp_stream_value 
+%type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts
+%type <expression> sinit_max_init_timeo sctp_assoc_value sctp_stream_value
 %type <expression> sctp_sackinfo sack_delay sack_freq
 %type <expression> sctp_rtoinfo srto_initial srto_max srto_min sctp_paddrinfo
 %type <expression> sctp_paddrparams spp_address spp_hbinterval spp_pathmtu spp_pathmaxrxt
 %type <expression> spp_flags spp_ipv6_flowlabel spp_dscp
 %type <expression> spinfo_address spinfo_state spinfo_cwnd spinfo_srtt spinfo_rto spinfo_mtu
-%type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd 
-%type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams 
+%type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd
+%type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams
 %type <expression> sctp_sndinfo snd_sid snd_flags snd_ppid snd_context
 %type <expression> sctp_event se_type se_on sctp_setadaptation
 %type <errno_info> opt_errno
@@ -819,8 +820,23 @@ sctp_packet_spec
 	struct packet *outer = $1, *inner = NULL;
 	enum direction_t direction = outer->direction;
 
-	inner = new_sctp_packet(in_config->wire_protocol, direction, $2, $5,
-				&error);
+	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
+	                        false, $5, &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 ')' ':' sctp_chunk_list_spec {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
+	                        true, $8, &error);
 	if (inner == NULL) {
 		assert(error != NULL);
 		semantic_error(error);
@@ -2372,7 +2388,7 @@ expression
 }
 | sctp_assocparams  {
 	$$ = $1;
-} 
+}
 | sctp_event        {
 	$$ = $1;
 }
@@ -2614,7 +2630,7 @@ sinit_max_init_timeo
 ;
 
 sctp_initmsg
-: '{' sinit_num_ostreams ',' sinit_max_instreams ',' sinit_max_attempts ',' sinit_max_init_timeo '}' 
+: '{' sinit_num_ostreams ',' sinit_max_instreams ',' sinit_max_attempts ',' sinit_max_init_timeo '}'
 {
 	$$ = new_expression(EXPR_SCTP_INITMSG);
 	$$->value.sctp_paddrinfo = calloc(1, sizeof(struct sctp_initmsg_expr));
@@ -2674,7 +2690,7 @@ sctp_sackinfo
 	$$ = new_expression(EXPR_SCTP_SACKINFO);
 	$$->value.sctp_sack_info = calloc(1, sizeof(struct sctp_sack_info_expr));
 	$$->value.sctp_sack_info->sack_delay = $2;
-	$$->value.sctp_sack_info->sack_freq = $4;	
+	$$->value.sctp_sack_info->sack_freq = $4;
 #else
 	$$ = NULL;
 #endif
@@ -2984,9 +3000,9 @@ se_type
 	}
 	$$ = new_integer_expression($3, "%hu");
 }
-| SE_TYPE '=' WORD { 
+| SE_TYPE '=' WORD {
 	$$ = new_expression(EXPR_WORD);
-	$$->value.string = $3; 
+	$$->value.string = $3;
 }
 ;
 
@@ -3013,7 +3029,7 @@ sctp_event
 }
 ;
 
-snd_sid 
+snd_sid
 : SND_SID '=' INTEGER {
 	if (!is_valid_u16($3)) {
 		semantic_error("snd_sid out of range");
@@ -3023,7 +3039,7 @@ snd_sid
 | SND_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
-snd_flags 
+snd_flags
 : SND_FLAGS '=' INTEGER {
 	if (!is_valid_u16($3)) {
 		semantic_error("snd_flags out of range");
@@ -3032,12 +3048,12 @@ snd_flags
 }
 | SND_FLAGS '=' WORD {
 	$$ = new_expression(EXPR_WORD);
-	$$->value.string = $3; 
+	$$->value.string = $3;
 }
 | SND_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
-snd_ppid 
+snd_ppid
 : SND_PPID '=' INTEGER {
 	if (!is_valid_u32($3)) {
 		semantic_error("snd_ppid out of range");
diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c
index 697feeea..45d44a1c 100644
--- a/gtests/net/packetdrill/run_packet.c
+++ b/gtests/net/packetdrill/run_packet.c
@@ -2695,7 +2695,7 @@ int abort_association(struct state *state, struct socket *socket)
 	chunk_list = sctp_chunk_list_new();
 	sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(flgs, cause_list));
 	packet = new_sctp_packet(socket->address_family,
-				 DIRECTION_INBOUND, ECN_NONE,
+				 DIRECTION_INBOUND, ECN_NONE, false,
 				 chunk_list, &error);
 	if (packet == NULL)
 		die("%s", error);
diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c
index 23112204..2658b47d 100644
--- a/gtests/net/packetdrill/sctp_packet.c
+++ b/gtests/net/packetdrill/sctp_packet.c
@@ -2242,6 +2242,7 @@ struct packet *
 new_sctp_packet(int address_family,
                 enum direction_t direction,
                 enum ip_ecn_t ecn,
+                bool bad_crc32c,
                 struct sctp_chunk_list *list,
                 char **error)
 {
@@ -2494,6 +2495,12 @@ new_sctp_packet(int address_family,
 				break;
 			}
 		}
+	} else {
+		if (bad_crc32c) {
+			asprintf(error,
+				 "bad CRC32C can only be requested for outbound packets");
+			return NULL;
+		}
 	}
 
 	/* Allocate and zero out a packet object of the desired size */
@@ -2502,6 +2509,9 @@ new_sctp_packet(int address_family,
 
 	packet->direction = direction;
 	packet->flags = 0;
+	if (bad_crc32c) {
+		packet->flags |= FLAGS_SCTP_BAD_CRC32C;
+	}
 	packet->ecn = ecn;
 
 	/* Set IP header fields */
diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h
index ba972b39..ca0256b1 100644
--- a/gtests/net/packetdrill/sctp_packet.h
+++ b/gtests/net/packetdrill/sctp_packet.h
@@ -423,6 +423,7 @@ sctp_cause_list_free(struct sctp_cause_list *list);
 extern struct packet *new_sctp_packet(int address_family,
 				      enum direction_t direction,
 				      enum ip_ecn_t ecn,
+				      bool bad_crc32c,
 				      struct sctp_chunk_list *chunk_list,
 				      char **error);
 #endif /* __SCTP_PACKET_H__ */
-- 
GitLab