diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 36fa98fd3991fba1dc7426388cbfc21483e1a65a..d5fb23b8e82c1c0caccc758a0a02d92f95d8053b 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -237,8 +237,25 @@ tsn			return TSN;
 sid			return SID;
 ssn			return SSN;
 ppid			return PPID;
+cum_tsn			return CUM_TSN;
 gaps			return GAPS;
 dups			return DUPS;
+HEARTBEAT_INFORMATION	return HEARTBEAT_INFORMATION;
+IPV4_ADDRESS		return IPV4_ADDRESS;
+IPV6_ADDRESS		return IPV6_ADDRESS;
+STATE_COOKIE		return STATE_COOKIE;
+UNRECOGNIZED_PARAMETER	return UNRECOGNIZED_PARAMETER;
+COOKIE_PRESERVATIVE	return COOKIE_PRESERVATIVE;
+HOSTNAME_ADDRESS	return HOSTNAME_ADDRESS;
+SUPPORTED_ADDRESS_TYPES	return SUPPORTED_ADDRESS_TYPES;
+ECN_CAPABLE		return ECN_CAPABLE;
+addr			return ADDR;
+incr			return INCR;
+types			return TYPES;
+params			return PARAMS;
+IPv4			return IPV4_TYPE;
+IPv6			return IPV6_TYPE;
+HOSTNAME		return HOSTNAME_TYPE;
 --[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 19b9e6a7b048239443f4c6178ee15ceaa5c9bca0..97331617438eba5a62bb0702633a713ac186f470 100644
--- a/gtests/net/packetdrill/packet.c
+++ b/gtests/net/packetdrill/packet.c
@@ -148,8 +148,11 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet,
 	struct packet *packet = packet_new(max(bytes_headroom + bytes_used, old_packet->buffer_bytes));
 	u8 *old_base = old_packet->buffer;
 	u8 *new_base = packet->buffer + bytes_headroom;
-	struct sctp_chunk_list_item *old_item, *new_item;
+	struct sctp_chunk_list_item *old_chunk_item, *new_chunk_item;
 	struct sctp_chunk *new_chunk;
+	struct sctp_parameter_list_item *old_parameter_item, *new_parameter_item;
+	struct sctp_parameter *new_parameter;
+	struct sctp_parameter_list *new_parameter_list;
 
 	memcpy(new_base, old_base, bytes_used);
 
@@ -172,14 +175,28 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet,
 	packet->icmpv4	= offset_ptr(old_base, new_base, old_packet->icmpv4);
 	packet->icmpv6	= offset_ptr(old_base, new_base, old_packet->icmpv6);
 
-	for (old_item = old_packet->chunk_list->first;
-	     old_item != NULL;
-	     old_item = old_item->next) {
-		new_chunk = offset_ptr(old_base, new_base, old_item->chunk);
-		new_item = sctp_chunk_list_item_new(new_chunk,
-		                                    old_item->length,
-		                                    old_item->flags);
-		sctp_chunk_list_append(packet->chunk_list, new_item);
+	/* Go through the SCTP specific lists */
+	for (old_chunk_item = old_packet->chunk_list->first;
+	     old_chunk_item != NULL;
+	     old_chunk_item = old_chunk_item->next) {
+		new_parameter_list = sctp_parameter_list_new();
+		for (old_parameter_item = old_chunk_item->parameter_list->first;
+		     old_parameter_item != NULL;
+		     old_parameter_item = old_parameter_item->next) {
+			new_parameter = offset_ptr(old_base,
+			                           new_base,
+			                           old_parameter_item->parameter);
+			new_parameter_item = sctp_parameter_list_item_new(new_parameter,
+			                                                  old_parameter_item->length,
+			                                                  old_parameter_item->flags);
+			sctp_parameter_list_append(new_parameter_list, new_parameter_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);
+		sctp_chunk_list_append(packet->chunk_list, new_chunk_item);
 	}
 
 	packet->tcp_ts_val	= offset_ptr(old_base, new_base,
diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c
index 0e0f0788a7a13b56d0ccc5426086fa9af0e828d1..b81befabba85096a9d04ec6b8525183529549db5 100644
--- a/gtests/net/packetdrill/packet_to_string_test.c
+++ b/gtests/net/packetdrill/packet_to_string_test.c
@@ -64,7 +64,7 @@ static void test_sctp_ipv4_packet_to_string(void)
 	assert(error == NULL);
 	printf("dump = '%s'\n", dump);
 	expected =
-		"sctp: ABORT[flags=T]";
+		"sctp: ABORT[flgs=T]";
 	assert(strcmp(dump, expected) == 0);
 	free(dump);
 
@@ -75,7 +75,7 @@ static void test_sctp_ipv4_packet_to_string(void)
 	printf("dump = '%s'\n", dump);
 	expected =
 		"2.2.2.2:1234 > 1.1.1.1:8080 "
-		"sctp: ABORT[flags=T]";
+		"sctp: ABORT[flgs=T]";
 	assert(strcmp(dump, expected) == 0);
 	free(dump);
 
@@ -86,7 +86,7 @@ static void test_sctp_ipv4_packet_to_string(void)
 	printf("dump = '%s'\n", dump);
 	expected =
 		"2.2.2.2:1234 > 1.1.1.1:8080 "
-		"sctp: ABORT[flags=T]"
+		"sctp: ABORT[flgs=T]"
 		"\n"
 		"0x0000: 45 00 00 24 00 00 00 00 ff 84 b5 50 02 02 02 02 " "\n"
 		"0x0010: 01 01 01 01 04 d2 1f 90 01 02 03 04 3d 99 bf e3 " "\n"
@@ -102,7 +102,7 @@ static void test_sctp_ipv6_packet_to_string(void)
 	/* An IPv6/SCTP packet. */
 	u8 data[] = {
 		/* IPv6 Base Header: */
-		0x60, 0x00, 0x00, 0x00, 0x01, 0x78, 0x84, 0xff,
+		0x60, 0x00, 0x00, 0x00, 0x01, 0x80, 0x84, 0xff,
 		0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22,
 		0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -110,7 +110,7 @@ static void test_sctp_ipv6_packet_to_string(void)
 		/* SCTP Common Header: */
 		0x04, 0xd2, 0x1f, 0x90,
 		0x01, 0x02, 0x03, 0x04,
-		0x1b, 0x68, 0x75, 0x8e,
+		0x22, 0x3e, 0x3f, 0x1c,
 		/* SCTP DATA Chunk */
 		0x00, 0x0f, 0x00, 0x13,
 		0x01, 0x02, 0x03, 0x04,
@@ -158,10 +158,12 @@ static void test_sctp_ipv6_packet_to_string(void)
 		0x10, 0x00, 0x10, 0x14,
 		0x01, 0x02, 0x03, 0x04,
 		/* SCTP HEARTBEAT Chunk */
-		0x04, 0x00, 0x00, 0x06,
+		0x04, 0x00, 0x00, 0x0a,
+		0x00, 0x01, 0x00, 0x06,
 		0x01, 0x02, 0x00, 0x00,
 		/* SCTP HEARTBEAT-ACK Chunk */
-		0x05, 0x00, 0x00, 0x06,
+		0x05, 0x00, 0x00, 0x0a,
+		0x00, 0x01, 0x00, 0x06,
 		0x01, 0x02, 0x00, 0x00,
 		/* SCTP ABORT Chunk: */
 		0x06, 0x01, 0x00, 0x04,
@@ -240,29 +242,28 @@ static void test_sctp_ipv6_packet_to_string(void)
 	printf("dump = '%s'\n", dump);
 	expected =
 		"sctp: "
-		"DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, "
-		     "payload_len=3]; "
-		"INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-		     "IPV4_ADDRESS[1.2.3.4], "
-		     "IPV6_ADDRESS[::1], "
-		     "COOKIE_PRESERVATIVE[65536], "
-		     "HOSTNAME[@A], "
-		     "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], "
+		"DATA[flgs=IUBE, len=19, tsn=16909060, sid=255, ssn=256, ppid=0]; "
+		"INIT[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+		     "IPV4_ADDRESS[addr=1.2.3.4], "
+		     "IPV6_ADDRESS[addr=::1], "
+		     "COOKIE_PRESERVATIVE[incr=65536], "
+		     "HOSTNAME[addr=\"@A\"], "
+		     "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], "
 		     "ECN_CAPABLE[]]; "
-		"INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-			 "STATE_COOKIE[cookie_len=3], "
-			 "UNRECOGNIZED_PARAMETER["
-			   "PARAMETER[type=0x8001, value=[]]]]; "
-		"SACK[cum_tsn=16909060, a_rwnd=65536, "
-		     "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; "
-		"HEARTBEAT[info_len=2]; "
-		"HEARTBEAT_ACK[info_len=2]; "
-		"ABORT[flags=T]; "
-		"ABORT[INVALID_STREAM_IDENTIFIER[sid=255], "
+		"INIT_ACK[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+			 "STATE_COOKIE[len=7, val=...], "
+			 "UNRECOGNIZED_PARAMETER[params=["
+			   "PARAMETER[type=0x8001, value=[]]]]]; "
+		"SACK[flgs=0x00, cum_tsn=16909060, a_rwnd=65536, "
+		     "gaps=[1:3, 5:15, 4096:4116], dups=[16909060]]; "
+		"HEARTBEAT[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; "
+		"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], "
 		      "STALE_COOKIE_ERROR[staleness=65536], "
 		      "OUT_OF_RESOURCES[], "
-		      "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], "
+		      "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], "
 		      "UNRECOGNIZED_CHUNK["
 			"CHUNK[type=0xfe, flags=0x05, value=[0x01]]], "
 		      "INVALID_MANDATORY_PARAMETER[], "
@@ -271,18 +272,18 @@ static void test_sctp_ipv6_packet_to_string(void)
 			"PARAMETER[type=0x800b, value=[0x01]]], "
 		      "NO_USER_DATA[tsn=16909060], "
 		      "COOKIE_RECEIVED_WHILE_SHUTDOWN[], "
-		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], "
-						 "IPV4_ADDRESS[2.3.4.5]], "
+		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], "
+						 "IPV4_ADDRESS[addr=2.3.4.5]], "
 		      "USER_INITIATED_ABORT[BYE], "
 		      "PROTOCOL_VIOLATION[@@]]; "
-		"SHUTDOWN[tsn=16909060]; "
-		"SHUTDOWN_ACK[]; "
-		"ERROR[]; "
-		"COOKIE_ECHO[cookie_len=1]; "
-		"COOKIE_ACK[]; "
-		"ECNE[tsn=16909060]; "
-		"CWR[tsn=16909060]; "
-		"SHUTDOWN_COMPLETE[flags=T]";
+		"SHUTDOWN[flgs=0x00, cum_tsn=16909060]; "
+		"SHUTDOWN_ACK[flgs=0x00]; "
+		"ERROR[flgs=0x00]; "
+		"COOKIE_ECHO[flgs=0x00, len=5]; "
+		"COOKIE_ACK[flgs=0x00]; "
+		"ECNE[flgs=0x00, tsn=16909060]; "
+		"CWR[flgs=0x00, tsn=16909060]; "
+		"SHUTDOWN_COMPLETE[flgs=T]";
 	assert(strcmp(dump, expected) == 0);
 	free(dump);
 
@@ -294,29 +295,28 @@ static void test_sctp_ipv6_packet_to_string(void)
 	expected =
 		"2::2222:1234 > 1::1111:8080 "
 		"sctp: "
-		"DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, "
-		     "payload_len=3]; "
-		"INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-		     "IPV4_ADDRESS[1.2.3.4], "
-		     "IPV6_ADDRESS[::1], "
-		     "COOKIE_PRESERVATIVE[65536], "
-		     "HOSTNAME[@A], "
-		     "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], "
+		"DATA[flgs=IUBE, len=19, tsn=16909060, sid=255, ssn=256, ppid=0]; "
+		"INIT[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+		     "IPV4_ADDRESS[addr=1.2.3.4], "
+		     "IPV6_ADDRESS[addr=::1], "
+		     "COOKIE_PRESERVATIVE[incr=65536], "
+		     "HOSTNAME[addr=\"@A\"], "
+		     "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], "
 		     "ECN_CAPABLE[]]; "
-		"INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-			 "STATE_COOKIE[cookie_len=3], "
-			 "UNRECOGNIZED_PARAMETER["
-			   "PARAMETER[type=0x8001, value=[]]]]; "
-		"SACK[cum_tsn=16909060, a_rwnd=65536, "
-		     "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; "
-		"HEARTBEAT[info_len=2]; "
-		"HEARTBEAT_ACK[info_len=2]; "
-		"ABORT[flags=T]; "
-		"ABORT[INVALID_STREAM_IDENTIFIER[sid=255], "
+		"INIT_ACK[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+			 "STATE_COOKIE[len=7, val=...], "
+			 "UNRECOGNIZED_PARAMETER[params=["
+			   "PARAMETER[type=0x8001, value=[]]]]]; "
+		"SACK[flgs=0x00, cum_tsn=16909060, a_rwnd=65536, "
+		     "gaps=[1:3, 5:15, 4096:4116], dups=[16909060]]; "
+		"HEARTBEAT[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; "
+		"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], "
 		      "STALE_COOKIE_ERROR[staleness=65536], "
 		      "OUT_OF_RESOURCES[], "
-		      "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], "
+		      "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], "
 		      "UNRECOGNIZED_CHUNK["
 			"CHUNK[type=0xfe, flags=0x05, value=[0x01]]], "
 		      "INVALID_MANDATORY_PARAMETER[], "
@@ -325,18 +325,18 @@ static void test_sctp_ipv6_packet_to_string(void)
 			"PARAMETER[type=0x800b, value=[0x01]]], "
 		      "NO_USER_DATA[tsn=16909060], "
 		      "COOKIE_RECEIVED_WHILE_SHUTDOWN[], "
-		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], "
-						 "IPV4_ADDRESS[2.3.4.5]], "
+		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], "
+						 "IPV4_ADDRESS[addr=2.3.4.5]], "
 		      "USER_INITIATED_ABORT[BYE], "
 		      "PROTOCOL_VIOLATION[@@]]; "
-		"SHUTDOWN[tsn=16909060]; "
-		"SHUTDOWN_ACK[]; "
-		"ERROR[]; "
-		"COOKIE_ECHO[cookie_len=1]; "
-		"COOKIE_ACK[]; "
-		"ECNE[tsn=16909060]; "
-		"CWR[tsn=16909060]; "
-		"SHUTDOWN_COMPLETE[flags=T]";
+		"SHUTDOWN[flgs=0x00, cum_tsn=16909060]; "
+		"SHUTDOWN_ACK[flgs=0x00]; "
+		"ERROR[flgs=0x00]; "
+		"COOKIE_ECHO[flgs=0x00, len=5]; "
+		"COOKIE_ACK[flgs=0x00]; "
+		"ECNE[flgs=0x00, tsn=16909060]; "
+		"CWR[flgs=0x00, tsn=16909060]; "
+		"SHUTDOWN_COMPLETE[flgs=T]";
 	assert(strcmp(dump, expected) == 0);
 	free(dump);
 
@@ -348,29 +348,28 @@ static void test_sctp_ipv6_packet_to_string(void)
 	expected =
 		"2::2222:1234 > 1::1111:8080 "
 		"sctp: "
-		"DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, "
-		     "payload_len=3]; "
-		"INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-		     "IPV4_ADDRESS[1.2.3.4], "
-		     "IPV6_ADDRESS[::1], "
-		     "COOKIE_PRESERVATIVE[65536], "
-		     "HOSTNAME[@A], "
-		     "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], "
+		"DATA[flgs=IUBE, len=19, tsn=16909060, sid=255, ssn=256, ppid=0]; "
+		"INIT[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+		     "IPV4_ADDRESS[addr=1.2.3.4], "
+		     "IPV6_ADDRESS[addr=::1], "
+		     "COOKIE_PRESERVATIVE[incr=65536], "
+		     "HOSTNAME[addr=\"@A\"], "
+		     "SUPPORTED_ADDRESS_TYPES[types=[IPv4, IPv6, HOSTNAME]], "
 		     "ECN_CAPABLE[]]; "
-		"INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
-			 "STATE_COOKIE[cookie_len=3], "
-			 "UNRECOGNIZED_PARAMETER["
-			   "PARAMETER[type=0x8001, value=[]]]]; "
-		"SACK[cum_tsn=16909060, a_rwnd=65536, "
-		     "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; "
-		"HEARTBEAT[info_len=2]; "
-		"HEARTBEAT_ACK[info_len=2]; "
-		"ABORT[flags=T]; "
-		"ABORT[INVALID_STREAM_IDENTIFIER[sid=255], "
+		"INIT_ACK[flgs=0x00, tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, "
+			 "STATE_COOKIE[len=7, val=...], "
+			 "UNRECOGNIZED_PARAMETER[params=["
+			   "PARAMETER[type=0x8001, value=[]]]]]; "
+		"SACK[flgs=0x00, cum_tsn=16909060, a_rwnd=65536, "
+		     "gaps=[1:3, 5:15, 4096:4116], dups=[16909060]]; "
+		"HEARTBEAT[flgs=0x00, HEARTBEAT_INFORMATION[len=6, val=...]]; "
+		"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], "
 		      "STALE_COOKIE_ERROR[staleness=65536], "
 		      "OUT_OF_RESOURCES[], "
-		      "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], "
+		      "UNRESOLVABLE_ADDRESS[HOSTNAME[addr=\"@A\"]], "
 		      "UNRECOGNIZED_CHUNK["
 			"CHUNK[type=0xfe, flags=0x05, value=[0x01]]], "
 		      "INVALID_MANDATORY_PARAMETER[], "
@@ -379,23 +378,23 @@ static void test_sctp_ipv6_packet_to_string(void)
 			"PARAMETER[type=0x800b, value=[0x01]]], "
 		      "NO_USER_DATA[tsn=16909060], "
 		      "COOKIE_RECEIVED_WHILE_SHUTDOWN[], "
-		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], "
-						 "IPV4_ADDRESS[2.3.4.5]], "
+		      "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[addr=1.2.3.4], "
+						 "IPV4_ADDRESS[addr=2.3.4.5]], "
 		      "USER_INITIATED_ABORT[BYE], "
 		      "PROTOCOL_VIOLATION[@@]]; "
-		"SHUTDOWN[tsn=16909060]; "
-		"SHUTDOWN_ACK[]; "
-		"ERROR[]; "
-		"COOKIE_ECHO[cookie_len=1]; "
-		"COOKIE_ACK[]; "
-		"ECNE[tsn=16909060]; "
-		"CWR[tsn=16909060]; "
-		"SHUTDOWN_COMPLETE[flags=T]"
+		"SHUTDOWN[flgs=0x00, cum_tsn=16909060]; "
+		"SHUTDOWN_ACK[flgs=0x00]; "
+		"ERROR[flgs=0x00]; "
+		"COOKIE_ECHO[flgs=0x00, len=5]; "
+		"COOKIE_ACK[flgs=0x00]; "
+		"ECNE[flgs=0x00, tsn=16909060]; "
+		"CWR[flgs=0x00, tsn=16909060]; "
+		"SHUTDOWN_COMPLETE[flgs=T]"
 		"\n"
-		"0x0000: 60 00 00 00 01 78 84 ff 00 02 00 00 00 00 00 00 " "\n"
+		"0x0000: 60 00 00 00 01 80 84 ff 00 02 00 00 00 00 00 00 " "\n"
 		"0x0010: 00 00 00 00 00 00 22 22 00 01 00 00 00 00 00 00 " "\n"
 		"0x0020: 00 00 00 00 00 00 11 11 04 d2 1f 90 01 02 03 04 " "\n"
-		"0x0030: 1b 68 75 8e 00 0f 00 13 01 02 03 04 00 ff 01 00 " "\n"
+		"0x0030: 22 3e 3f 1c 00 0f 00 13 01 02 03 04 00 ff 01 00 " "\n"
 		"0x0040: 00 00 00 00 00 01 02 00 01 00 00 50 00 00 00 01 " "\n"
 		"0x0050: 00 01 00 00 00 0f 00 0f 01 02 03 04 00 05 00 08 " "\n"
 		"0x0060: 01 02 03 04 00 06 00 14 00 00 00 00 00 00 00 00 " "\n"
@@ -405,19 +404,20 @@ static void test_sctp_ipv6_packet_to_string(void)
 		"0x00a0: 00 01 00 00 00 0f 00 0f 01 02 03 04 00 07 00 07 " "\n"
 		"0x00b0: 01 02 03 00 00 08 00 08 80 01 00 04 03 00 00 20 " "\n"
 		"0x00c0: 01 02 03 04 00 01 00 00 00 03 00 01 00 01 00 03 " "\n"
-		"0x00d0: 00 05 00 0f 10 00 10 14 01 02 03 04 04 00 00 06 " "\n"
-		"0x00e0: 01 02 00 00 05 00 00 06 01 02 00 00 06 01 00 04 " "\n"
-		"0x00f0: 06 00 00 80 00 01 00 08 00 ff 00 00 00 02 00 0a " "\n"
-		"0x0100: 00 00 00 01 00 07 00 00 00 03 00 08 00 01 00 00 " "\n"
-		"0x0110: 00 04 00 04 00 05 00 0c 00 0b 00 06 40 41 00 00 " "\n"
-		"0x0120: 00 06 00 0c fe 05 00 05 01 00 00 00 00 07 00 04 " "\n"
-		"0x0130: 00 08 00 10 80 0a 00 04 80 0b 00 05 01 00 00 00 " "\n"
-		"0x0140: 00 09 00 08 01 02 03 04 00 0a 00 04 00 0b 00 14 " "\n"
-		"0x0150: 00 05 00 08 01 02 03 04 00 05 00 08 02 03 04 05 " "\n"
-		"0x0160: 00 0c 00 07 42 59 45 00 00 0d 00 06 40 40 00 00 " "\n"
-		"0x0170: 07 00 00 08 01 02 03 04 08 00 00 04 09 00 00 04 " "\n"
-		"0x0180: 0a 00 00 05 45 00 00 00 0b 00 00 04 0c 00 00 08 " "\n"
-		"0x0190: 01 02 03 04 0d 00 00 08 01 02 03 04 0e 01 00 04 " "\n";
+		"0x00d0: 00 05 00 0f 10 00 10 14 01 02 03 04 04 00 00 0a " "\n"
+		"0x00e0: 00 01 00 06 01 02 00 00 05 00 00 0a 00 01 00 06 " "\n"
+		"0x00f0: 01 02 00 00 06 01 00 04 06 00 00 80 00 01 00 08 " "\n"
+		"0x0100: 00 ff 00 00 00 02 00 0a 00 00 00 01 00 07 00 00 " "\n"
+		"0x0110: 00 03 00 08 00 01 00 00 00 04 00 04 00 05 00 0c " "\n"
+		"0x0120: 00 0b 00 06 40 41 00 00 00 06 00 0c fe 05 00 05 " "\n"
+		"0x0130: 01 00 00 00 00 07 00 04 00 08 00 10 80 0a 00 04 " "\n"
+		"0x0140: 80 0b 00 05 01 00 00 00 00 09 00 08 01 02 03 04 " "\n"
+		"0x0150: 00 0a 00 04 00 0b 00 14 00 05 00 08 01 02 03 04 " "\n"
+		"0x0160: 00 05 00 08 02 03 04 05 00 0c 00 07 42 59 45 00 " "\n"
+		"0x0170: 00 0d 00 06 40 40 00 00 07 00 00 08 01 02 03 04 " "\n"
+		"0x0180: 08 00 00 04 09 00 00 04 0a 00 00 05 45 00 00 00 " "\n"
+		"0x0190: 0b 00 00 04 0c 00 00 08 01 02 03 04 0d 00 00 08 " "\n"
+		"0x01a0: 01 02 03 04 0e 01 00 04 " "\n";
 	assert(strcmp(dump, expected) == 0);
 	free(dump);
 	packet_free(packet);
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index ec0443458324c848d317c5427193d515f8f2fd11..2e1d93f153c5e12f8232e3dddb28041d43930ecf 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -468,6 +468,10 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	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 sctp_address_type_list_item *address_type_list_item;
+	struct sctp_address_type_list *address_type_list;
+	struct sctp_parameter_list_item *parameter_list_item;
+	struct sctp_parameter_list *parameter_list;
 	struct syscall_spec *syscall;
 	struct command_spec *command;
 	struct code_spec *code;
@@ -500,7 +504,12 @@ 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> FLAGS LEN TAG A_RWND OS IS TSN SID SSN PPID GAPS DUPS
+%token <reserved> FLAGS LEN TAG A_RWND OS IS TSN SID SSN PPID CUM_TSN GAPS DUPS
+%token <reserved> HEARTBEAT_INFORMATION IPV4_ADDRESS IPV6_ADDRESS STATE_COOKIE
+%token <reserved> UNRECOGNIZED_PARAMETER COOKIE_PRESERVATIVE HOSTNAME_ADDRESS
+%token <reserved> SUPPORTED_ADDRESS_TYPES ECN_CAPABLE
+%token <reserved> ADDR INCR TYPES PARAMS
+%token <reserved> IPV4_TYPE IPV6_TYPE HOSTNAME_TYPE
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -552,11 +561,26 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <chunk_list_item> sctp_cookie_echo_chunk_spec sctp_cookie_ack_chunk_spec
 %type <chunk_list_item> sctp_ecne_chunk_spec sctp_cwr_chunk_spec
 %type <chunk_list_item> sctp_shutdown_complete_chunk_spec
+%type <parameter_list> opt_parameter_list_spec sctp_parameter_list_spec
+%type <parameter_list_item> sctp_parameter_spec
+%type <parameter_list_item> sctp_heartbeat_information_parameter_spec
+%type <parameter_list_item> sctp_ipv4_address_parameter_spec
+%type <parameter_list_item> sctp_ipv6_address_parameter_spec
+%type <parameter_list_item> sctp_state_cookie_parameter_spec
+%type <parameter_list_item> sctp_unrecognized_parameter_parameter_spec
+%type <parameter_list_item> sctp_cookie_preservative_parameter_spec
+%type <parameter_list_item> sctp_hostname_address_parameter_spec
+%type <parameter_list_item> sctp_supported_address_types_parameter_spec
+%type <parameter_list_item> sctp_ecn_capable_parameter_spec
+
 %type <integer> opt_flags opt_data_flags opt_abort_flags
-%type <integer> opt_shutdown_complete_flags opt_data_chunk_len
-%type <integer> opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn opt_ppid
+%type <integer> opt_shutdown_complete_flags opt_chunk_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 <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
 
 %%  /* The grammar follows. */
 
@@ -765,33 +789,46 @@ sctp_chunk_spec
 | sctp_shutdown_complete_chunk_spec { $$ = $1; }
 ;
 
-opt_data_chunk_len
-:                  { $$ = -1; }
-| LEN '=' INTEGER  {if (!is_valid_u16($3) ||
-                        $3 < sizeof(struct sctp_data_chunk)) {
-                      semantic_error("length value out of range");
-                    }
-                    $$ = $3;
+opt_chunk_len
+: LEN '=' ELLIPSIS { $$ = -1; }
+| LEN '=' INTEGER  {
+	if (!is_valid_u16($3)) {
+		semantic_error("length value out of range");
+        }
+        $$ = $3;
 }
+;
 
 opt_flags
-:                        { $$ = -1; }
-| FLAGS '=' HEX_INTEGER  {if (!is_valid_u8($3)) {
-                            semantic_error("flags value out of range");
-                          }
-                          $$ = $3;
+: FLAGS '=' ELLIPSIS    { $$ = -1; }
+| FLAGS '=' HEX_INTEGER {
+	if (!is_valid_u8($3)) {
+		semantic_error("flags value out of range");
+        }
+	$$ = $3;
 }
+| FLAGS '=' INTEGER     {
+	if (!is_valid_u8($3)) {
+		semantic_error("flags value out of range");
+        }
+	$$ = $3;
+}
+;
 
 opt_data_flags
-:                       {
-	$$ = -1;
-}
+: FLAGS '=' ELLIPSIS    { $$ = -1; }
 | FLAGS '=' HEX_INTEGER {
 	if (!is_valid_u8($3)) {
 		semantic_error("flags value out of range");
 	}
 	$$ = $3;
 }
+| FLAGS '=' INTEGER     {
+	if (!is_valid_u8($3)) {
+		semantic_error("flags value out of range");
+        }
+	$$ = $3;
+}
 | FLAGS '=' WORD        {
 	u64 flags;
 	char *c;
@@ -801,28 +838,28 @@ opt_data_flags
 		switch (*c) {
 		case 'I':
 			if (flags & SCTP_DATA_CHUNK_I_BIT) {
-				semantic_error("Specifying the I-bit multiple times");
+				semantic_error("I-bit specified multiple times");
 			} else {
 				flags |= SCTP_DATA_CHUNK_I_BIT;
 			}
 			break;
 		case 'U':
 			if (flags & SCTP_DATA_CHUNK_U_BIT) {
-				semantic_error("Specifying the U-bit multiple times");
+				semantic_error("U-bit specified multiple times");
 			} else {
 				flags |= SCTP_DATA_CHUNK_U_BIT;
 			}
 			break;
 		case 'B':
 			if (flags & SCTP_DATA_CHUNK_B_BIT) {
-				semantic_error("Specifying the B-bit multiple times");
+				semantic_error("B-bit specified multiple times");
 			} else {
 				flags |= SCTP_DATA_CHUNK_B_BIT;
 			}
 			break;
 		case 'E':
 			if (flags & SCTP_DATA_CHUNK_E_BIT) {
-				semantic_error("Specifying the E-bit multiple times");
+				semantic_error("E-bit specified multiple times");
 			} else {
 				flags |= SCTP_DATA_CHUNK_E_BIT;
 			}
@@ -834,17 +871,22 @@ opt_data_flags
 	}
 	$$ = flags;
 }
+;
 
 opt_abort_flags
-:                       {
-	$$ = -1;
-}
+: FLAGS '=' ELLIPSIS    { $$ = -1; }
 | FLAGS '=' HEX_INTEGER {
 	if (!is_valid_u8($3)) {
 		semantic_error("flags value out of range");
 	}
 	$$ = $3;
 }
+| FLAGS '=' INTEGER     {
+	if (!is_valid_u8($3)) {
+		semantic_error("flags value out of range");
+        }
+	$$ = $3;
+}
 | FLAGS '=' WORD        {
 	u64 flags;
 	char *c;
@@ -854,7 +896,7 @@ opt_abort_flags
 		switch (*c) {
 		case 'T':
 			if (flags & SCTP_ABORT_CHUNK_T_BIT) {
-				semantic_error("Specifying the T-bit multiple times");
+				semantic_error("T-bit specified multiple times");
 			} else {
 				flags |= SCTP_ABORT_CHUNK_T_BIT;
 			}
@@ -866,17 +908,22 @@ opt_abort_flags
 	}
 	$$ = flags;
 }
+;
 
 opt_shutdown_complete_flags
-:                       {
-	$$ = -1;
-}
+: FLAGS '=' ELLIPSIS    { $$ = -1; }
 | FLAGS '=' HEX_INTEGER {
 	if (!is_valid_u8($3)) {
 		semantic_error("flags value out of range");
 	}
 	$$ = $3;
 }
+| FLAGS '=' INTEGER     {
+	if (!is_valid_u8($3)) {
+		semantic_error("flags value out of range");
+        }
+	$$ = $3;
+}
 | FLAGS '=' WORD        {
 	u64 flags;
 	char *c;
@@ -886,7 +933,7 @@ opt_shutdown_complete_flags
 		switch (*c) {
 		case 'T':
 			if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) {
-				semantic_error("Specifying the T-bit multiple times");
+				semantic_error("T-bit specified multiple times");
 			} else {
 				flags |= SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT;
 			}
@@ -898,72 +945,100 @@ opt_shutdown_complete_flags
 	}
 	$$ = flags;
 }
+;
+
+opt_tag
+: TAG '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("tag value out of range");
+	}
+	$$ = $3;
+}
+;
 
 opt_a_rwnd
-:		{ $$ = -1; }
-| A_RWND '=' INTEGER	{
+: A_RWND '=' ELLIPSIS   { $$ = -1; }
+| A_RWND '=' INTEGER    {
 	if (!is_valid_u32($3)) {
 		semantic_error("a_rwnd value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_os
-:		{ $$ = -1; }
-| OS '=' INTEGER	{
+: OS '=' ELLIPSIS { $$ = -1; }
+| OS '=' INTEGER  {
 	if (!is_valid_u16($3)) {
 		semantic_error("os value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_is
-:		{ $$ = -1; }
-| IS '=' INTEGER	{
+: IS '=' ELLIPSIS { $$ = -1; }
+| IS '=' INTEGER  {
 	if (!is_valid_u16($3)) {
 		semantic_error("is value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_tsn
-:		{ $$ = -1; }
-| TSN '=' INTEGER	{
+: TSN '=' ELLIPSIS { $$ = -1; }
+| TSN '=' INTEGER  {
 	if (!is_valid_u32($3)) {
 		semantic_error("tsn value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_sid
-:		{ $$ = -1; }
-| SID '=' INTEGER	{
+: SID '=' ELLIPSIS { $$ = -1; }
+| SID '=' INTEGER  {
 	if (!is_valid_u16($3)) {
 		semantic_error("sid value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_ssn
-:		{ $$ = -1; }
-| SSN '=' INTEGER	{
+: SSN '=' ELLIPSIS { $$ = -1; }
+| SSN '=' INTEGER  {
 	if (!is_valid_u16($3)) {
 		semantic_error("ssn value out of range");
 	}
 	$$ = $3;
 }
+;
 
 opt_ppid
-:		{ $$ = -1; }
-| PPID '=' INTEGER	{
+: PPID '=' ELLIPSIS { $$ = -1; }
+| PPID '=' INTEGER  {
 	if (!is_valid_u32($3)) {
 		semantic_error("ppid value out of range");
 	}
 	$$ = $3;
 }
+;
+
+opt_cum_tsn
+: CUM_TSN '=' ELLIPSIS { $$ = -1; }
+| CUM_TSN '=' INTEGER  {
+	if (!is_valid_u32($3)) {
+		semantic_error("cum_tsn value out of range");
+	}
+	$$ = $3;
+}
+;
 
 opt_gaps
-:                           { $$ = NULL; }
+: GAPS '=' ELLIPSIS         { $$ = NULL; }
+| GAPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
 | GAPS '=' '[' gap_list ']' { $$ = $4; }
 ;
 
@@ -976,17 +1051,20 @@ gap_list
 ;
 
 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); }
+: INTEGER ':' INTEGER {
+	if (!is_valid_u16($1)) {
+		semantic_error("start value out of range");
+	}
+	if (!is_valid_u16($3)) {
+		semantic_error("end value out of range");
+	}
+	$$ = sctp_sack_block_list_item_gap_new($1, $3);
+}
 ;
 
 opt_dups
-:                           { $$ = NULL; }
+: DUPS '=' ELLIPSIS         { $$ = NULL; }
+| DUPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
 | DUPS '=' '[' dup_list ']' { $$ = $4; }
 ;
 
@@ -999,52 +1077,57 @@ dup_list
 ;
 
 dup
-: INTEGER   { if (!is_valid_u32($1)) {
-                semantic_error("tsn value out of range");
-	      }
-	      $$ = sctp_sack_block_list_item_dup_new($1); }
+: 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_data_flags opt_data_chunk_len opt_tsn opt_sid opt_ssn opt_ppid ']' {
-	$$ = sctp_data_chunk_new($3, $4, $5, $6, $7, $8);
+: DATA '[' opt_data_flags ',' opt_chunk_len ',' opt_tsn ',' opt_sid ',' opt_ssn ',' opt_ppid ']' {
+	if (($5 != -1) && ($5 < sizeof(struct sctp_data_chunk))) {
+		semantic_error("length value out of range");
+	}
+	$$ = sctp_data_chunk_new($3, $5, $7, $9, $11, $13);
 }
 
 sctp_init_chunk_spec
-: INIT '[' opt_flags TAG '=' INTEGER opt_a_rwnd opt_os opt_is TSN '=' INTEGER ']' {
-	if (!is_valid_u32($6)) {
-		semantic_error("tag value out of range");
+: INIT '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
+	if ($5 == -1) {
+		semantic_error("tag value must be specified");
 	}
-	if (!is_valid_u32($12)) {
-		semantic_error("tsn value out of range");
+	if ($13 == -1) {
+		semantic_error("tsn value must be specified");
 	}
-	$$ = sctp_init_chunk_new($3, $6, $7, $8, $9, $12);
+	$$ = sctp_init_chunk_new($3, $5, $7, $9, $11, $13, $14);
 }
 
 sctp_init_ack_chunk_spec
-: INIT_ACK '[' opt_flags TAG '=' INTEGER opt_a_rwnd opt_os opt_is TSN '=' INTEGER ']' {
-	if (!is_valid_u32($6)) {
-		semantic_error("tag value out of range");
+: INIT_ACK '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
+	if ($5 == -1) {
+		semantic_error("tag value must be specified");
 	}
-	if (!is_valid_u32($12)) {
-		semantic_error("tsn value out of range");
+	if ($13 == -1) {
+		semantic_error("tsn value must be specified");
 	}
-	$$ = sctp_init_ack_chunk_new($3, $6, $7, $8, $9, $12);
+	$$ = sctp_init_ack_chunk_new($3, $5, $7, $9, $11, $13, $14);
 }
 
 sctp_sack_chunk_spec
-: SACK '[' opt_flags opt_tsn opt_a_rwnd opt_gaps opt_dups']' {
-	$$ = sctp_sack_chunk_new($3, $4, $5, $6, $7);
+: SACK '[' opt_flags ',' opt_cum_tsn ',' opt_a_rwnd ',' opt_gaps ',' opt_dups']' {
+	$$ = sctp_sack_chunk_new($3, $5, $7, $9, $11);
 }
 
 sctp_heartbeat_chunk_spec
-: HEARTBEAT '[' opt_flags ']' {
-	$$ = sctp_heartbeat_chunk_new($3);
+: HEARTBEAT '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
+	$$ = sctp_heartbeat_chunk_new($3, $5);
 }
 
 sctp_heartbeat_ack_chunk_spec
-: HEARTBEAT_ACK '[' opt_flags ']' {
-	$$ = sctp_heartbeat_ack_chunk_new($3);
+: HEARTBEAT_ACK '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
+	$$ = sctp_heartbeat_ack_chunk_new($3, $5);
 }
 
 sctp_abort_chunk_spec
@@ -1053,8 +1136,8 @@ sctp_abort_chunk_spec
 }
 
 sctp_shutdown_chunk_spec
-: SHUTDOWN '[' opt_flags opt_tsn ']' {
-	$$ = sctp_shutdown_chunk_new($3, $4);
+: SHUTDOWN '[' opt_flags ',' opt_cum_tsn ']' {
+	$$ = sctp_shutdown_chunk_new($3, $5);
 }
 
 sctp_shutdown_ack_chunk_spec
@@ -1068,8 +1151,8 @@ sctp_error_chunk_spec
 }
 
 sctp_cookie_echo_chunk_spec
-: COOKIE_ECHO '[' opt_flags ']' {
-	$$ = sctp_cookie_echo_chunk_new($3);
+: COOKIE_ECHO '[' opt_flags ',' opt_chunk_len ',' VAL '=' ELLIPSIS ']' {
+	$$ = sctp_cookie_echo_chunk_new($3, $5, NULL);
 }
 
 sctp_cookie_ack_chunk_spec
@@ -1092,6 +1175,146 @@ sctp_shutdown_complete_chunk_spec
 	$$ = sctp_shutdown_complete_chunk_new($3);
 }
 
+opt_parameter_list_spec
+: ',' ELLIPSIS                 { $$ = NULL; }
+|                              { $$ = sctp_parameter_list_new(); }
+| ',' sctp_parameter_list_spec { $$ = $2; }
+;
+
+sctp_parameter_list_spec
+: sctp_parameter_spec                              { $$ = sctp_parameter_list_new();
+                                                     sctp_parameter_list_append($$, $1); }
+| sctp_parameter_list_spec ',' sctp_parameter_spec { $$ = $1;
+                                                     sctp_parameter_list_append($1, $3); }
+;
+
+sctp_parameter_spec
+: sctp_heartbeat_information_parameter_spec   { $$ = $1; }
+| sctp_ipv4_address_parameter_spec            { $$ = $1; }
+| sctp_ipv6_address_parameter_spec            { $$ = $1; }
+| sctp_state_cookie_parameter_spec            { $$ = $1; }
+| sctp_unrecognized_parameter_parameter_spec  { $$ = $1; }
+| sctp_cookie_preservative_parameter_spec     { $$ = $1; }
+| sctp_hostname_address_parameter_spec        { $$ = $1; }
+| sctp_supported_address_types_parameter_spec { $$ = $1; }
+| sctp_ecn_capable_parameter_spec             { $$ = $1; }
+;
+
+sctp_heartbeat_information_parameter_spec
+: HEARTBEAT_INFORMATION '[' ELLIPSIS ']' {
+	$$ = sctp_heartbeat_information_parameter_new(-1, NULL);
+}
+| HEARTBEAT_INFORMATION '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
+	$$ = sctp_heartbeat_information_parameter_new(-1, NULL);
+}
+| HEARTBEAT_INFORMATION '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
+	if (($5 < sizeof(struct sctp_heartbeat_information_parameter)) ||
+	    !is_valid_u16($5)) {
+		semantic_error("len value out of range");
+	}
+	$$ = sctp_heartbeat_information_parameter_new($5, NULL);
+}
+
+sctp_ipv4_address_parameter_spec
+: IPV4_ADDRESS '[' ADDR '=' IPV4_ADDR ']' {
+	struct in_addr addr;
+
+	if (inet_pton(AF_INET, $5, &addr) != 1) {
+		semantic_error("Invalid address");
+	}
+	$$ = sctp_ipv4_address_parameter_new(&addr);
+}
+| IPV4_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
+	$$ = sctp_ipv4_address_parameter_new(NULL);
+}
+
+sctp_ipv6_address_parameter_spec
+: IPV6_ADDRESS '[' ADDR '=' IPV6_ADDR ']' {
+	struct in6_addr addr;
+
+	if (inet_pton(AF_INET6, $5, &addr) != 1) {
+		semantic_error("Invalid address");
+	}
+	$$ = sctp_ipv6_address_parameter_new(&addr);
+}
+| IPV6_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
+	$$ = sctp_ipv6_address_parameter_new(NULL);
+}
+
+sctp_state_cookie_parameter_spec
+: STATE_COOKIE '[' ELLIPSIS ']' {
+	$$ = sctp_state_cookie_parameter_new(-1, NULL);
+}
+| STATE_COOKIE '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
+	$$ = sctp_state_cookie_parameter_new(-1, NULL);
+}
+| STATE_COOKIE '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
+	if (($5 < sizeof(struct sctp_state_cookie_parameter)) ||
+	    !is_valid_u32($5)) {
+		semantic_error("len value out of range");
+	}
+	$$ = sctp_state_cookie_parameter_new($5, NULL);
+}
+
+sctp_unrecognized_parameter_parameter_spec
+: UNRECOGNIZED_PARAMETER '[' PARAMS '=' ELLIPSIS ']' {
+	$$ = sctp_unrecognized_parameters_parameter_new(NULL);
+}
+| UNRECOGNIZED_PARAMETER '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
+	$$ = sctp_unrecognized_parameters_parameter_new($6);
+}
+
+sctp_cookie_preservative_parameter_spec
+: COOKIE_PRESERVATIVE '[' INCR '=' INTEGER ']' {
+	if (!is_valid_u32($5)) {
+		semantic_error("increment value out of range");
+	}
+	$$ = sctp_cookie_preservative_parameter_new($5);
+}
+| COOKIE_PRESERVATIVE '[' INCR '=' ELLIPSIS ']' {
+	$$ = sctp_cookie_preservative_parameter_new(-1);
+}
+
+sctp_hostname_address_parameter_spec
+: HOSTNAME_ADDRESS '[' ADDR '=' STRING ']' {
+	$$ = sctp_hostname_address_parameter_new($5);
+}
+| HOSTNAME_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
+	$$ = sctp_hostname_address_parameter_new(NULL);
+}
+
+address_types_list
+:                                     { $$ = sctp_address_type_list_new(); }
+| address_type                        { $$ = sctp_address_type_list_new();
+                                        sctp_address_type_list_append($$, $1); }
+| address_types_list ',' address_type { $$ = $1;
+                                        sctp_address_type_list_append($1, $3); }
+;
+
+address_type
+: INTEGER       { if (!is_valid_u16($1)) {
+                  semantic_error("address type value out of range");
+	          }
+	          $$ = 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); }
+;
+
+sctp_supported_address_types_parameter_spec
+: SUPPORTED_ADDRESS_TYPES '[' TYPES '=' ELLIPSIS ']' {
+	$$ = sctp_supported_address_types_parameter_new(NULL);
+}
+| SUPPORTED_ADDRESS_TYPES '[' TYPES '=' '[' address_types_list ']' ']' {
+	$$ = sctp_supported_address_types_parameter_new($6);
+}
+
+
+sctp_ecn_capable_parameter_spec
+: ECN_CAPABLE '[' ']' {
+	$$ = sctp_ecn_capable_parameter_new();
+}
+
 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 331903c5b94cf54b84d51962c422c7d2122821d0..3eea270325520200845bdf87786fc212fdf7c1be 100644
--- a/gtests/net/packetdrill/run_packet.c
+++ b/gtests/net/packetdrill/run_packet.c
@@ -1047,6 +1047,70 @@ static int verify_ipv6(
 	return STATUS_OK;
 }
 
+static int verify_sctp_parameters(u8 *begin, u16 length,
+                                  struct sctp_chunk_list_item *script_chunk_item,
+                                  char **error)
+{
+	struct sctp_parameters_iterator iter;
+	struct sctp_parameter *actual_parameter;
+	struct sctp_parameter *script_parameter;
+	struct sctp_parameter_list_item *script_parameter_item;
+	u32 flags;
+
+	for (actual_parameter = sctp_parameters_begin(begin, length, &iter, error),
+	     script_parameter_item = script_chunk_item->parameter_list->first;
+	     actual_parameter != NULL && script_parameter_item != NULL;
+	     actual_parameter = sctp_parameters_next(&iter, error),
+	     script_parameter_item = script_parameter_item->next) {
+		if (*error != NULL) {
+			DEBUGP("Error during iteration\n");
+			return STATUS_ERR;
+		}
+		script_parameter = script_parameter_item->parameter;
+		flags = script_parameter_item->flags;
+		assert(script_parameter != NULL);
+		DEBUGP("script parameter: type %04d, length %04d\n",
+		       ntohs(script_parameter->type),
+		       ntohs(script_parameter->length));
+		DEBUGP("actual parameter: type %04d, length %04d\n",
+		       ntohs(actual_parameter->type),
+		       ntohs(actual_parameter->length));
+		if ((flags & FLAG_PARAMETER_TYPE_NOCHECK ? STATUS_OK :
+		        check_field("sctp_parameter_type",
+		                    ntohs(script_parameter->type),
+		                    ntohs(actual_parameter->type),
+		                    error)) ||
+		    (flags & FLAG_PARAMETER_LENGTH_NOCHECK ? STATUS_OK :
+		        check_field("sctp_chunk_length",
+		                    ntohs(script_parameter->length),
+		                    ntohs(actual_parameter->length),
+		                    error))) {
+			return STATUS_ERR;
+		}
+		if ((flags & FLAG_PARAMETER_VALUE_NOCHECK) == 0) {
+			assert((flags & FLAG_PARAMETER_LENGTH_NOCHECK) == 0);
+			if (memcmp(script_parameter->value,
+			           actual_parameter->value,
+			           ntohs(actual_parameter->length) - sizeof(struct sctp_parameter))) {
+				asprintf(error, "live packet parameter value not as expected");
+				return STATUS_ERR;
+			}
+		}
+	}
+	if (actual_parameter != NULL) {
+		DEBUGP("actual chunk contains more parameters than script chunk\n");
+	}
+	if (script_parameter_item != NULL) {
+		DEBUGP("script chunk contains more parameters than actual chunk\n");
+	}
+	if ((actual_parameter != NULL) || (script_parameter_item != NULL)) {
+		asprintf(error,
+		         "live chunk and expected chunk have not the same number of parameters");
+		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)
@@ -1076,9 +1140,18 @@ static int verify_data_chunk(struct sctp_data_chunk *actual_chunk,
 }
 
 static int verify_init_chunk(struct sctp_init_chunk *actual_chunk,
-                             struct sctp_init_chunk *script_chunk,
-                             u32 flags, char **error)
+                             struct sctp_chunk_list_item *script_chunk_item,
+                             char **error)
 {
+	struct sctp_init_chunk *script_chunk;
+	u32 flags;
+
+	script_chunk = (struct sctp_init_chunk *)script_chunk_item->chunk;
+	flags = script_chunk_item->flags;
+	u16 parameters_length;
+
+	assert(ntohs(actual_chunk->length) >= sizeof(struct sctp_init_chunk));
+	parameters_length = ntohs(actual_chunk->length) - sizeof(struct sctp_init_chunk);
 	if (check_field("sctp_init_chunk_tag",
 		        ntohl(script_chunk->initiate_tag),
 		        ntohl(actual_chunk->initiate_tag),
@@ -1101,10 +1174,14 @@ static int verify_init_chunk(struct sctp_init_chunk *actual_chunk,
 	    check_field("sctp_init_chunk_tsn",
 		        ntohl(script_chunk->initial_tsn),
 		        ntohl(actual_chunk->initial_tsn),
-		        error)) {
+		        error) ||
+	    (flags & FLAG_INIT_CHUNK_OPT_PARAM_NOCHECK? STATUS_OK :
+	        verify_sctp_parameters(actual_chunk->parameter,
+	                               parameters_length,
+	                               script_chunk_item,
+	                               error))) {
 		return STATUS_ERR;
 	}
-	/* FIXME: Validate parameters */
 	return STATUS_OK;
 }
 
@@ -1415,8 +1492,7 @@ static int verify_sctp(
 			break;
 		case SCTP_INIT_CHUNK_TYPE:
 			result = verify_init_chunk((struct sctp_init_chunk *)actual_chunk,
-			                           (struct sctp_init_chunk *)script_chunk,
-			                           flags, error);
+			                           script_chunk_item, error);
 			break;
 		case SCTP_INIT_ACK_CHUNK_TYPE:
 			result = verify_init_ack_chunk((struct sctp_init_ack_chunk *)actual_chunk,
diff --git a/gtests/net/packetdrill/sctp_chunk_to_string.c b/gtests/net/packetdrill/sctp_chunk_to_string.c
index 842978bf0f0ffd3dfb9e4db256a4cb21019ad891..c365e7c37c231026a369a7b1e866785bd890d286 100644
--- a/gtests/net/packetdrill/sctp_chunk_to_string.c
+++ b/gtests/net/packetdrill/sctp_chunk_to_string.c
@@ -27,6 +27,23 @@
 
 static int sctp_parameter_to_string(FILE *, struct sctp_parameter *, char **);
 
+static int sctp_heartbeat_information_parameter_to_string(
+	FILE *s,
+	struct sctp_heartbeat_information_parameter *parameter,
+	char **error)
+{
+	u16 length;
+
+	length = ntohs(parameter->length);
+	if (length < sizeof(struct sctp_heartbeat_information_parameter)) {
+		asprintf(error, "HEARTBEAT_INFORMATION parameter illegal (length=%u)",
+			 length);
+		return STATUS_ERR;
+	}
+	fprintf(s, "HEARTBEAT_INFORMATION[len=%u, val=...]", length);
+	return STATUS_OK;
+}
+
 static int sctp_ipv4_address_parameter_to_string(
 	FILE *s,
 	struct sctp_ipv4_address_parameter *parameter,
@@ -42,7 +59,7 @@ static int sctp_ipv4_address_parameter_to_string(
 		return STATUS_ERR;
 	}
 	inet_ntop(AF_INET, &parameter->addr, buffer, INET_ADDRSTRLEN);
-	fprintf(s, "IPV4_ADDRESS[%s]", buffer);
+	fprintf(s, "IPV4_ADDRESS[addr=%s]", buffer);
 	return STATUS_OK;
 }
 
@@ -61,7 +78,7 @@ static int sctp_ipv6_address_parameter_to_string(
 		return STATUS_ERR;
 	}
 	inet_ntop(AF_INET6, &parameter->addr, buffer, INET6_ADDRSTRLEN);
-	fprintf(s, "IPV6_ADDRESS[%s]", buffer);
+	fprintf(s, "IPV6_ADDRESS[addr=%s]", buffer);
 	return STATUS_OK;
 }
 
@@ -70,7 +87,7 @@ static int sctp_state_cookie_parameter_to_string(
 	struct sctp_state_cookie_parameter *parameter,
 	char **error)
 {
-	u16 length, cookie_length;
+	u16 length;
 
 	length = ntohs(parameter->length);
 	if (length < sizeof(struct sctp_state_cookie_parameter)) {
@@ -78,8 +95,7 @@ static int sctp_state_cookie_parameter_to_string(
 			 length);
 		return STATUS_ERR;
 	}
-	cookie_length = length - sizeof(struct sctp_state_cookie_parameter);
-	fprintf(s, "STATE_COOKIE[cookie_len=%d]", cookie_length);
+	fprintf(s, "STATE_COOKIE[len=%d, val=...]", length);
 	return STATUS_OK;
 }
 
@@ -99,10 +115,10 @@ static int sctp_unrecognized_parameter_parameter_to_string(
 			 length);
 		return STATUS_ERR;
 	}
-	fputs("UNRECOGNIZED_PARAMETER[", s);
+	fputs("UNRECOGNIZED_PARAMETER[params=[", s);
 	result = sctp_parameter_to_string(s,
 		(struct sctp_parameter *)parameter->value, error);
-	fputc(']', s);
+	fputs("]]", s);
 	return result;
 }
 
@@ -120,7 +136,7 @@ static int sctp_cookie_preservative_parameter_to_string(
 			 length);
 		return STATUS_ERR;
 	}
-	fputs("COOKIE_PRESERVATIVE[", s);
+	fputs("COOKIE_PRESERVATIVE[incr=", s);
 	fprintf(s, "%u", ntohl(parameter->increment));
 	fputc(']', s);
 	return STATUS_OK;
@@ -139,7 +155,7 @@ static int sctp_hostname_parameter_to_string(
 			 length);
 		return STATUS_ERR;
 	}
-	fprintf(s, "HOSTNAME[%.*s]",
+	fprintf(s, "HOSTNAME[addr=\"%.*s\"]",
 		(int)(length - sizeof(struct sctp_hostname_address_parameter)),
 		(char *)parameter->hostname);
 	return STATUS_OK;
@@ -163,16 +179,16 @@ static int sctp_supported_address_types_parameter_to_string(
 	nr_address_types =
 		(length - sizeof(struct sctp_supported_address_types_parameter))
 		/ sizeof(u16);
-	fputs("SUPPORTED_ADDRESS_TYPES[", s);
+	fputs("SUPPORTED_ADDRESS_TYPES[types=[", s);
 	for (i = 0; i < nr_address_types; i++) {
 		if (i > 0)
 			fputs(", ", s);
 		switch (ntohs(parameter->address_type[i])) {
 		case SCTP_IPV4_ADDRESS_PARAMETER_TYPE:
-			fputs("IPV4", s);
+			fputs("IPv4", s);
 			break;
 		case SCTP_IPV6_ADDRESS_PARAMETER_TYPE:
-			fputs("IPV6", s);
+			fputs("IPv6", s);
 			break;
 		case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE:
 			fputs("HOSTNAME", s);
@@ -182,7 +198,7 @@ static int sctp_supported_address_types_parameter_to_string(
 			break;
 		}
 	}
-	fputs("]", s);
+	fputs("]]", s);
 	return STATUS_OK;
 }
 
@@ -235,6 +251,10 @@ static int sctp_parameter_to_string(FILE *s,
 	int result;
 
 	switch (ntohs(parameter->type)) {
+	case SCTP_HEARTBEAT_INFORMATION_PARAMETER_TYPE:
+		result = sctp_heartbeat_information_parameter_to_string(s,
+			(struct sctp_heartbeat_information_parameter *)parameter, error);
+		break;
 	case SCTP_IPV4_ADDRESS_PARAMETER_TYPE:
 		result = sctp_ipv4_address_parameter_to_string(s,
 			(struct sctp_ipv4_address_parameter *)parameter, error);
@@ -740,31 +760,28 @@ static int sctp_data_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("DATA[", s);
-	if (flags != 0x00) {
-		fputs("flags=", s);
-		if (flags & ~(SCTP_DATA_CHUNK_I_BIT |
-			      SCTP_DATA_CHUNK_U_BIT |
-			      SCTP_DATA_CHUNK_B_BIT |
-			      SCTP_DATA_CHUNK_E_BIT))
-			fprintf(s, "0x%02x", chunk->flags);
-		else {
-			if (flags & SCTP_DATA_CHUNK_I_BIT)
-				fputc('I', s);
-			if (flags & SCTP_DATA_CHUNK_U_BIT)
-				fputc('U', s);
-			if (flags & SCTP_DATA_CHUNK_B_BIT)
-				fputc('B', s);
-			if (flags & SCTP_DATA_CHUNK_E_BIT)
-				fputc('E', s);
-		}
-		fputs(", ", s);
+	fputs("flgs=", s);
+	if (flags & ~(SCTP_DATA_CHUNK_I_BIT |
+		      SCTP_DATA_CHUNK_U_BIT |
+		      SCTP_DATA_CHUNK_B_BIT |
+		      SCTP_DATA_CHUNK_E_BIT))
+		fprintf(s, "0x%02x", chunk->flags);
+	else {
+		if (flags & SCTP_DATA_CHUNK_I_BIT)
+			fputc('I', s);
+		if (flags & SCTP_DATA_CHUNK_U_BIT)
+			fputc('U', s);
+		if (flags & SCTP_DATA_CHUNK_B_BIT)
+			fputc('B', s);
+		if (flags & SCTP_DATA_CHUNK_E_BIT)
+			fputc('E', s);
 	}
+	fputs(", ", s);
+	fprintf(s, "len=%u, ", length);
 	fprintf(s, "tsn=%u, ", ntohl(chunk->tsn));
 	fprintf(s, "sid=%d, ", ntohs(chunk->sid));
 	fprintf(s, "ssn=%u, ", ntohs(chunk->ssn));
-	fprintf(s, "ppid=%u, ", ntohl(chunk->ppid));
-	fprintf(s, "payload_len=%u]",
-		length - (u16)sizeof(struct sctp_data_chunk));
+	fprintf(s, "ppid=%u]", ntohl(chunk->ppid));
 	return STATUS_OK;
 }
 
@@ -787,8 +804,7 @@ static int sctp_init_chunk_to_string(FILE *s,
 	}
 	parameters_length = length - sizeof(struct sctp_init_chunk);
 	fputs("INIT[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
+	fprintf(s, "flgs=0x%02x, ", chunk->flags);
 	fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag));
 	fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd));
 	fprintf(s, "os=%u, ", ntohs(chunk->os));
@@ -831,8 +847,7 @@ static int sctp_init_ack_chunk_to_string(FILE *s,
 	}
 	parameters_length = length - sizeof(struct sctp_init_ack_chunk);
 	fputs("INIT_ACK[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
+	fprintf(s, "flgs=0x%02x, ", chunk->flags);
 	fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag));
 	fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd));
 	fprintf(s, "os=%u, ", ntohs(chunk->os));
@@ -879,13 +894,12 @@ static int sctp_sack_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("SACK[", s);
-	if (flags != 0)
-		fprintf(s, "flags=0x%02x, ", flags);
+	fprintf(s, "flgs=0x%02x, ", flags);
 	fprintf(s, "cum_tsn=%u, ", ntohl(chunk->cum_tsn));
 	fprintf(s, "a_rwnd=%u, ", ntohl(chunk->a_rwnd));
 	fputs("gaps=[", s);
 	for (i = 0; i < nr_gaps; i++)
-		fprintf(s, "%s%u-%u",
+		fprintf(s, "%s%u:%u",
 			   i > 0 ? ", " : "",
 			   ntohs(chunk->block[i].gap.start),
 			   ntohs(chunk->block[i].gap.end));
@@ -902,18 +916,37 @@ static int sctp_heartbeat_chunk_to_string(FILE *s,
 					  struct sctp_heartbeat_chunk *chunk,
 					  char **error)
 {
-	u16 length;
+	u16 chunk_length, parameter_length, chunk_padding, parameter_padding;
+	struct sctp_parameter *parameter;
+	int result;
 	u8 flags;
 
 	flags = chunk->flags;
-	length = ntohs(chunk->length);
+	chunk_length = ntohs(chunk->length);
+	if (chunk_length < sizeof(struct sctp_heartbeat_chunk) +
+	                   sizeof(struct sctp_heartbeat_information_parameter)) {
+		asprintf(error, "HEARTBEAT chunk too short");
+		return STATUS_ERR;
+	}
+	chunk_padding = chunk_length & 0x0003;
+	if (chunk_padding != 0)
+		chunk_padding = 4 - chunk_padding;
+	parameter = (struct sctp_parameter *)chunk->value;
+	parameter_length = ntohs(parameter->length);
+	parameter_padding = parameter_length & 0x0003;
+	if (parameter_padding != 0)
+		parameter_padding = 4 - parameter_padding;
+	if (chunk_length + chunk_padding !=
+	    sizeof(struct sctp_heartbeat_chunk) +
+	    parameter_length + parameter_padding) {
+		asprintf(error, "HEARTBEAT chunk inconsistent");
+		return STATUS_ERR;
+	}
 	fputs("HEARTBEAT[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", flags);
-	fprintf(s, "info_len=%u",
-		length - (u16)sizeof(struct sctp_heartbeat_chunk));
+	fprintf(s, "flgs=0x%02x, ", flags);
+	result = sctp_parameter_to_string(s, parameter, error);
 	fputc(']', s);
-	return STATUS_OK;
+	return result;
 }
 
 static int sctp_heartbeat_ack_chunk_to_string(
@@ -921,18 +954,37 @@ static int sctp_heartbeat_ack_chunk_to_string(
 	struct sctp_heartbeat_ack_chunk *chunk,
 	char **error)
 {
-	u16 length;
+	u16 chunk_length, parameter_length, chunk_padding, parameter_padding;
+	struct sctp_parameter *parameter;
+	int result;
 	u8 flags;
 
 	flags = chunk->flags;
-	length = ntohs(chunk->length);
+	chunk_length = ntohs(chunk->length);
+	if (chunk_length < sizeof(struct sctp_heartbeat_ack_chunk) +
+	                   sizeof(struct sctp_heartbeat_information_parameter)) {
+		asprintf(error, "HEARTBEAT_ACK chunk too short");
+		return STATUS_ERR;
+	}
+	chunk_padding = chunk_length & 0x0003;
+	if (chunk_padding != 0)
+		chunk_padding = 4 - chunk_padding;
+	parameter = (struct sctp_parameter *)chunk->value;
+	parameter_length = ntohs(parameter->length);
+	parameter_padding = parameter_length & 0x0003;
+	if (parameter_padding != 0)
+		parameter_padding = 4 - parameter_padding;
+	if (chunk_length + chunk_padding !=
+	    sizeof(struct sctp_heartbeat_chunk) +
+	    parameter_length + parameter_padding) {
+		asprintf(error, "HEARTBEAT_ACK chunk inconsistent");
+		return STATUS_ERR;
+	}
 	fputs("HEARTBEAT_ACK[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", flags);
-	fprintf(s, "info_len=%u",
-		length - (u16)sizeof(struct sctp_heartbeat_chunk));
+	fprintf(s, "flgs=0x%02x, ", flags);
+	result = sctp_parameter_to_string(s, parameter, error);
 	fputc(']', s);
-	return STATUS_OK;
+	return result;
 }
 
 static int sctp_abort_chunk_to_string(FILE *s,
@@ -952,22 +1004,19 @@ static int sctp_abort_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("ABORT[", s);
-	if (flags != 0x00) {
-		fputs("flags=", s);
-		if (flags & ~SCTP_ABORT_CHUNK_T_BIT)
-			fprintf(s, "0x%02x", chunk->flags);
-		else
-			if (flags & SCTP_ABORT_CHUNK_T_BIT)
-				fputc('T', s);
-	}
+	fputs("flgs=", s);
+	if ((flags & ~SCTP_ABORT_CHUNK_T_BIT) || (flags == 0x00))
+		fprintf(s, "0x%02x", flags);
+	else
+		if (flags & SCTP_ABORT_CHUNK_T_BIT)
+			fputc('T', s);
 	index = 0;
 	for (cause = sctp_causes_begin((struct sctp_chunk *)chunk,
 				       SCTP_ABORT_CHUNK_CAUSE_OFFSET,
 				       &iter, error);
 	     cause != NULL;
 	     cause = sctp_causes_next(&iter, error)) {
-		if (((index == 0) && (flags != 0x00)) || (index > 0))
-			fputs(", ", s);
+		fputs(", ", s);
 		if (*error != NULL)
 			break;
 		result = sctp_cause_to_string(s, cause, error);
@@ -995,9 +1044,8 @@ static int sctp_shutdown_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("SHUTDOWN[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
-	fprintf(s, "tsn=%u", ntohl(chunk->cum_tsn));
+	fprintf(s, "flgs=0x%02x, ", chunk->flags);
+	fprintf(s, "cum_tsn=%u", ntohl(chunk->cum_tsn));
 	fputc(']', s);
 	return STATUS_OK;
 }
@@ -1018,8 +1066,7 @@ static int sctp_shutdown_ack_chunk_to_string(
 		return STATUS_ERR;
 	}
 	fputs("SHUTDOWN_ACK[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
+	fprintf(s, "flgs=0x%02x", chunk->flags);
 	fputc(']', s);
 	return STATUS_OK;
 }
@@ -1041,16 +1088,14 @@ static int sctp_error_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("ERROR[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x", chunk->flags);
+	fprintf(s, "flgs=0x%02x", chunk->flags);
 	index = 0;
 	for (cause = sctp_causes_begin((struct sctp_chunk *)chunk,
 				       SCTP_ERROR_CHUNK_CAUSE_OFFSET,
 				       &iter, error);
 	     cause != NULL;
 	     cause = sctp_causes_next(&iter, error)) {
-		if (((index == 0) && (flags != 0x00)) || (index > 0))
-			fputs(", ", s);
+		fputs(", ", s);
 		if (*error != NULL)
 			break;
 		result = sctp_cause_to_string(s, cause, error);
@@ -1075,10 +1120,8 @@ static int sctp_cookie_echo_chunk_to_string(
 	flags = chunk->flags;
 	length = ntohs(chunk->length);
 	fputs("COOKIE_ECHO[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", flags);
-	fprintf(s, "cookie_len=%u",
-		length - (u16)sizeof(struct sctp_cookie_echo_chunk));
+	fprintf(s, "flgs=0x%02x, ", flags);
+	fprintf(s, "len=%u", length);
 	fputc(']', s);
 	return STATUS_OK;
 }
@@ -1098,8 +1141,7 @@ static int sctp_cookie_ack_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("COOKIE_ACK[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x", chunk->flags);
+	fprintf(s, "flgs=0x%02x", chunk->flags);
 	fputc(']', s);
 	return STATUS_OK;
 }
@@ -1118,8 +1160,7 @@ static int sctp_ecne_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("ECNE[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
+	fprintf(s, "flgs=0x%02x, ", chunk->flags);
 	fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn));
 	fputc(']', s);
 	return STATUS_OK;
@@ -1139,8 +1180,7 @@ static int sctp_cwr_chunk_to_string(FILE *s,
 		return STATUS_ERR;
 	}
 	fputs("CWR[", s);
-	if (flags != 0x00)
-		fprintf(s, "flags=0x%02x, ", chunk->flags);
+	fprintf(s, "flgs=0x%02x, ", chunk->flags);
 	fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn));
 	fputc(']', s);
 	return STATUS_OK;
@@ -1162,14 +1202,12 @@ static int sctp_shutdown_complete_chunk_to_string(
 		return STATUS_ERR;
 	}
 	fputs("SHUTDOWN_COMPLETE[", s);
-	if (flags != 0x00) {
-		fputs("flags=", s);
-		if (flags & ~SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT)
-			fprintf(s, "0x%02x", chunk->flags);
-		else
-			if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT)
-				fputc('T', s);
-	}
+	fputs("flgs=", s);
+	if ((flags & ~SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) || (flags == 0x00))
+		fprintf(s, "0x%02x", flags);
+	else
+		if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT)
+			fputc('T', s);
 	fputc(']', s);
 	return STATUS_OK;
 }
diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c
index 21cbcfc8c9910fc8ce342e33a86b74a45d70b3df..886a921ca935dd9ed55004141e6c43d77a8b44d3 100644
--- a/gtests/net/packetdrill/sctp_packet.c
+++ b/gtests/net/packetdrill/sctp_packet.c
@@ -29,8 +29,6 @@
 
 /*
  * ToDo:
- * - Add support for chunk flags (fix hard coded flags for DATA chunk)
- * - Add support for user data length in DATA chunks (fix hard coded payload)
  * - Add support for parameters (fix hard coded state cookie in INIT-ACK)
  * - Add support for error causes
  */
@@ -66,7 +64,7 @@ sctp_sack_block_list_append(struct sctp_sack_block_list *list,
 }
 
 void
-sctp_sctp_sack_block_list_free(struct sctp_sack_block_list *list)
+sctp_sack_block_list_free(struct sctp_sack_block_list *list)
 {
 	struct sctp_sack_block_list_item *current_item, *next_item;
 
@@ -111,8 +109,72 @@ sctp_sack_block_list_item_dup_new(u32 tsn)
 	return item;
 }
 
+struct sctp_address_type_list *
+sctp_address_type_list_new(void)
+{
+	struct sctp_address_type_list *list;
+
+	list = malloc(sizeof(struct sctp_address_type_list));
+	assert(list != NULL);
+	list->first = NULL;
+	list->last = NULL;
+	list->nr_entries = 0;
+	return list;
+}
+
+void
+sctp_address_type_list_append(struct sctp_address_type_list *list,
+			      struct sctp_address_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_address_type_list_free(struct sctp_address_type_list *list)
+{
+	struct sctp_address_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_address_type_list_item *
+sctp_address_type_list_item_new(u16 address_type)
+{
+	struct sctp_address_type_list_item *item;
+
+	item = malloc(sizeof(struct sctp_address_type_list_item));
+	assert(item != NULL);
+	item->next = NULL;
+	item->address_type = address_type;
+	return item;
+}
+
 struct sctp_chunk_list_item *
-sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags)
+sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags,
+                         struct sctp_parameter_list *list)
 {
 	struct sctp_chunk_list_item *item;
 
@@ -120,6 +182,7 @@ 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->length = length;
 	item->flags = flags;
 	return item;
@@ -183,17 +246,33 @@ sctp_data_chunk_new(s64 flgs, s64 len, s64 tsn, s64 sid, s64 ssn, s64 ppid)
 	memset(chunk->data, 0,
 	       length + padding_length - sizeof(struct sctp_data_chunk));
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                length, flags);
+	                                length, flags,
+	                                sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
-sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
+sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn,
+                    struct sctp_parameter_list *list)
 {
 	struct sctp_init_chunk *chunk;
+	struct sctp_parameter_list_item *item;
 	u32 flags;
+	u16 offset, chunk_length, chunk_padding_length, parameter_padding_length;
 
 	flags = 0;
-	chunk = malloc(sizeof(struct sctp_init_chunk));
+	chunk_length = sizeof(struct sctp_init_chunk);
+	if (list != NULL) {
+		chunk_length += list->length;
+	} else {
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		flags |= FLAG_INIT_CHUNK_OPT_PARAM_NOCHECK;
+		list = sctp_parameter_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_INIT_CHUNK_TYPE;
 	if (flgs == -1) {
@@ -202,9 +281,7 @@ sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
 	} else {
 		chunk->flags = (u8)flgs;
 	}
-	/* FIXME */
-	flags |= FLAG_CHUNK_LENGTH_NOCHECK;
-	chunk->length = htons(sizeof(struct sctp_init_chunk));
+	chunk->length = htons(chunk_length);
 	chunk->initiate_tag = htonl((u32)tag);
 	if (a_rwnd == -1) {
 		chunk->a_rwnd = htonl(0);
@@ -225,21 +302,50 @@ sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
 		chunk->is = htons((u16)is);
 	}
 	chunk->initial_tsn = htonl((u32)tsn);
+	offset = 0;
+	for (item = list->first; item != NULL; item = item->next) {
+		parameter_padding_length = item->length % 4;
+		if (parameter_padding_length > 0) {
+			parameter_padding_length = 4 - parameter_padding_length;
+		}
+		memcpy(chunk->parameter + offset,
+		       item->parameter,
+		       item->length + parameter_padding_length);
+		free(item->parameter);
+		item->parameter = (struct sctp_parameter *)(chunk->parameter + offset);
+		if (item->flags == FLAG_PARAMETER_LENGTH_NOCHECK) {
+			flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		}
+		offset += item->length + parameter_padding_length;
+	}
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_init_chunk),
-	                                flags);
+	                                chunk_length + chunk_padding_length,
+	                                flags, list);
 }
 
-/* FIXME: Don't fake the cookie that way... */
 struct sctp_chunk_list_item *
-sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
+sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn,
+                        struct sctp_parameter_list *list)
 {
 	struct sctp_init_ack_chunk *chunk;
-	struct sctp_state_cookie_parameter state_cookie_parameter;
+	struct sctp_parameter_list_item *item;
 	u32 flags;
+	u16 offset, chunk_length, chunk_padding_length, parameter_padding_length;
 
 	flags = 0;
-	chunk = malloc(sizeof(struct sctp_init_ack_chunk) + sizeof(struct sctp_state_cookie_parameter));
+	chunk_length = sizeof(struct sctp_init_ack_chunk);
+	if (list != NULL) {
+		chunk_length += list->length;
+	} else {
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		flags |= FLAG_INIT_ACK_CHUNK_OPT_PARAM_NOCHECK;
+		list = sctp_parameter_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_INIT_ACK_CHUNK_TYPE;
 	if (flgs == -1) {
@@ -248,9 +354,7 @@ sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
 	} else {
 		chunk->flags = (u8)flgs;
 	}
-	/* FIXME */
-	flags |= FLAG_CHUNK_LENGTH_NOCHECK;
-	chunk->length = htons(sizeof(struct sctp_init_ack_chunk) + sizeof(struct sctp_state_cookie_parameter));
+	chunk->length = htons(chunk_length);
 	chunk->initiate_tag = htonl((u32)tag);
 	if (a_rwnd == -1) {
 		chunk->a_rwnd = htonl(0);
@@ -271,12 +375,25 @@ sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn)
 		chunk->is = htons((u16)is);
 	}
 	chunk->initial_tsn = htonl((u32)tsn);
-	state_cookie_parameter.type = htons(SCTP_STATE_COOKIE_PARAMETER_TYPE);
-	state_cookie_parameter.length = htons(sizeof(struct sctp_state_cookie_parameter));
-	memcpy(chunk->parameter, &state_cookie_parameter, sizeof(struct sctp_state_cookie_parameter));
+	offset = 0;
+	for (item = list->first; item != NULL; item = item->next) {
+		parameter_padding_length = item->length % 4;
+		if (parameter_padding_length > 0) {
+			parameter_padding_length = 4 - parameter_padding_length;
+		}
+		memcpy(chunk->parameter + offset,
+		       item->parameter,
+		       item->length + parameter_padding_length);
+		free(item->parameter);
+		item->parameter = (struct sctp_parameter *)(chunk->parameter + offset);
+		if (item->flags == FLAG_PARAMETER_LENGTH_NOCHECK) {
+			flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		}
+		offset += item->length + parameter_padding_length;
+	}
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_init_ack_chunk) + sizeof(struct sctp_state_cookie_parameter),
-	                                flags);
+	                                chunk_length + chunk_padding_length,
+	                                flags, list);
 }
 
 struct sctp_chunk_list_item *
@@ -353,17 +470,38 @@ sctp_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd,
 		assert((i == nr_dups) && (item == NULL));
 	}
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                length,  flags);
+	                                length, flags,
+	                                sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
-sctp_heartbeat_chunk_new(s64 flgs)
+sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info)
 {
 	struct sctp_heartbeat_chunk *chunk;
 	u32 flags;
+	u16 chunk_length, padding_length;
 
-	flags = FLAG_CHUNK_LENGTH_NOCHECK | FLAG_CHUNK_VALUE_NOCHECK;
-	chunk = malloc(sizeof(struct sctp_heartbeat_chunk));
+	flags = 0;
+	assert(info == NULL ||
+	       info->length + sizeof(struct sctp_heartbeat_chunk) <= MAX_SCTP_CHUNK_BYTES);
+	chunk_length = sizeof(struct sctp_heartbeat_chunk);
+	if (info != NULL) {
+		chunk_length += info->length;
+		if (info->flags & FLAG_PARAMETER_LENGTH_NOCHECK) {
+			flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		}
+		if (info->flags & FLAG_PARAMETER_VALUE_NOCHECK) {
+			flags |= FLAG_CHUNK_VALUE_NOCHECK;
+		}
+	} else {
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		flags |= FLAG_CHUNK_VALUE_NOCHECK;
+	}
+	padding_length = chunk_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	chunk = malloc(chunk_length + padding_length);
 	assert(chunk != NULL);
 	chunk->type = SCTP_HEARTBEAT_CHUNK_TYPE;
 	if (flgs == -1) {
@@ -372,20 +510,44 @@ sctp_heartbeat_chunk_new(s64 flgs)
 	} else {
 		chunk->flags = (u8)flgs;
 	}
-	chunk->length = htons(sizeof(struct sctp_heartbeat_chunk));
+	chunk->length = htons(chunk_length);
+	memcpy(chunk->value, info->parameter, info->length);
+	memset(chunk->value + info->length, 0, padding_length);
+	free(info->parameter);
+	free(info);
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_heartbeat_chunk), 
-	                                flags);
+	                                chunk_length + padding_length,
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
-sctp_heartbeat_ack_chunk_new(s64 flgs)
+sctp_heartbeat_ack_chunk_new(s64 flgs, struct sctp_parameter_list_item *info)
 {
 	struct sctp_heartbeat_ack_chunk *chunk;
 	u32 flags;
+	u16 chunk_length, padding_length;
 
-	flags = FLAG_CHUNK_LENGTH_NOCHECK | FLAG_CHUNK_VALUE_NOCHECK;
-	chunk = malloc(sizeof(struct sctp_heartbeat_ack_chunk));
+	flags = 0;
+	assert(info == NULL ||
+	       info->length + sizeof(struct sctp_heartbeat_ack_chunk) <= MAX_SCTP_CHUNK_BYTES);
+	chunk_length = sizeof(struct sctp_heartbeat_ack_chunk);
+	if (info != NULL) {
+		chunk_length += info->length;
+		if (info->flags & FLAG_PARAMETER_LENGTH_NOCHECK) {
+			flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		}
+		if (info->flags & FLAG_PARAMETER_VALUE_NOCHECK) {
+			flags |= FLAG_CHUNK_VALUE_NOCHECK;
+		}
+	} else {
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		flags |= FLAG_CHUNK_VALUE_NOCHECK;
+	}
+	padding_length = chunk_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	chunk = malloc(chunk_length + padding_length);
 	assert(chunk != NULL);
 	chunk->type = SCTP_HEARTBEAT_ACK_CHUNK_TYPE;
 	if (flgs == -1) {
@@ -394,10 +556,14 @@ sctp_heartbeat_ack_chunk_new(s64 flgs)
 	} else {
 		chunk->flags = (u8)flgs;
 	}
-	chunk->length = htons(sizeof(struct sctp_heartbeat_ack_chunk));
+	chunk->length = htons(chunk_length);
+	memcpy(chunk->value, info->parameter, info->length);
+	memset(chunk->value + info->length, 0, padding_length);
+	free(info->parameter);
+	free(info);
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_heartbeat_ack_chunk), 
-	                                flags);
+	                                chunk_length + padding_length,
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -418,8 +584,8 @@ sctp_abort_chunk_new(s64 flgs)
 	}
 	chunk->length = htons(sizeof(struct sctp_abort_chunk));
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_abort_chunk), 
-	                                flags);
+	                                (u32)sizeof(struct sctp_abort_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -447,8 +613,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);
+	                                (u32)sizeof(struct sctp_shutdown_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -469,8 +635,8 @@ 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);
+	                                (u32)sizeof(struct sctp_shutdown_ack_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -491,18 +657,33 @@ sctp_error_chunk_new(s64 flgs)
 	}
 	chunk->length = htons(sizeof(struct sctp_error_chunk));
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_error_chunk), 
-	                                flags);
+	                                (u32)sizeof(struct sctp_error_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
-sctp_cookie_echo_chunk_new(s64 flgs)
+sctp_cookie_echo_chunk_new(s64 flgs, s64 len, u8* cookie)
 {
 	struct sctp_cookie_echo_chunk *chunk;
 	u32 flags;
+	u16 chunk_length, cookie_length, padding_length;
 
-	flags = FLAG_CHUNK_LENGTH_NOCHECK | FLAG_CHUNK_VALUE_NOCHECK;
-	chunk = malloc(sizeof(struct sctp_cookie_echo_chunk));
+	assert((len == -1) || is_valid_u16(len));
+	assert((len != -1) || (cookie == NULL));
+	flags = 0;
+	if (len == -1) {
+		cookie_length = 0;
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+	} else {
+		assert(len <= MAX_SCTP_CHUNK_BYTES - sizeof(struct sctp_cookie_echo_chunk));
+		cookie_length = len - sizeof(struct sctp_cookie_echo_chunk);
+	}
+	chunk_length = cookie_length + sizeof(struct sctp_cookie_echo_chunk);
+	padding_length = chunk_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	chunk = malloc(chunk_length + padding_length);
 	assert(chunk != NULL);
 	chunk->type = SCTP_COOKIE_ECHO_CHUNK_TYPE;
 	if (flgs == -1) {
@@ -511,10 +692,18 @@ sctp_cookie_echo_chunk_new(s64 flgs)
 	} else {
 		chunk->flags = (u8)flgs;
 	}
-	chunk->length = htons(sizeof(struct sctp_cookie_echo_chunk));
+	chunk->length = htons(chunk_length);
+	if (cookie != NULL) {
+		memcpy(chunk->cookie, cookie, cookie_length);
+	} else {
+		flags |= FLAG_CHUNK_VALUE_NOCHECK;
+		memset(chunk->cookie, 'A', cookie_length);
+	}
+	/* Clear the padding */
+	memset(chunk->cookie + cookie_length, 0, padding_length);
 	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
-	                                (u32)sizeof(struct sctp_cookie_echo_chunk), 
-	                                flags);
+	                                chunk_length + padding_length,
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -535,8 +724,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);
+	                                (u32)sizeof(struct sctp_cookie_ack_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -564,8 +753,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);
+	                                (u32)sizeof(struct sctp_ecne_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -593,8 +782,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);
+	                                (u32)sizeof(struct sctp_cwr_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list_item *
@@ -615,8 +804,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);
+	                                (u32)sizeof(struct sctp_shutdown_complete_chunk),
+	                                flags, sctp_parameter_list_new());
 }
 
 struct sctp_chunk_list *
@@ -659,7 +848,361 @@ sctp_chunk_list_free(struct sctp_chunk_list *list)
 	while (current_item != NULL) {
 		next_item = current_item->next;
 		assert(next_item != NULL || current_item == list->last);
-		assert(current_item->chunk != NULL);
+		assert(current_item->parameter_list);
+		sctp_parameter_list_free(current_item->parameter_list);
+		free(current_item);
+		current_item = next_item;
+	}
+	free(list);
+}
+
+struct sctp_parameter_list_item *
+sctp_parameter_list_item_new(struct sctp_parameter *parameter, u32 length, u32 flags)
+{
+	struct sctp_parameter_list_item *item;
+
+	item = malloc(sizeof(struct sctp_parameter_list_item));
+	assert(item != NULL);
+	item->next = NULL;
+	item->parameter = parameter;
+	item->length = length;
+	item->flags = flags;
+	return item;
+}
+
+struct sctp_parameter_list_item *
+sctp_heartbeat_information_parameter_new(s64 len, u8 *information)
+{
+	struct sctp_heartbeat_information_parameter *parameter;
+	u32 flags;
+	u16 parameter_length, information_length, padding_length;
+
+	assert((len == -1) || is_valid_u16(len));
+	assert((len != -1) || (information == NULL));
+	flags = 0;
+	if (len == -1) {
+		information_length = 0;
+		flags |= FLAG_PARAMETER_LENGTH_NOCHECK;
+	} else {
+		assert(len <= MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_heartbeat_information_parameter));
+		information_length = len - sizeof(struct sctp_heartbeat_information_parameter);
+	}
+	parameter_length = information_length + sizeof(struct sctp_heartbeat_information_parameter);
+	padding_length = parameter_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	parameter = malloc(parameter_length + padding_length);
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_HEARTBEAT_INFORMATION_PARAMETER_TYPE);
+	parameter->length = htons(parameter_length);
+	if (information != NULL) {
+		memcpy(parameter->information, information, information_length);
+	} else {
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+		memset(parameter->information, 'A', information_length);
+	}
+	/* Clear the padding */
+	memset(parameter->information + information_length, 0, padding_length);
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    parameter_length, flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_ipv4_address_parameter_new(struct in_addr *addr)
+{
+	struct sctp_ipv4_address_parameter *parameter;
+	u32 flags;
+
+	flags = 0;
+	parameter = malloc(sizeof(struct sctp_ipv4_address_parameter));
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_IPV4_ADDRESS_PARAMETER_TYPE);
+	parameter->length = htons(sizeof(struct sctp_ipv4_address_parameter));
+	if (addr == NULL) {
+		parameter->addr.s_addr = htonl(INADDR_ANY);
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+	} else {
+		parameter->addr = *addr;
+	}
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    sizeof(struct sctp_ipv4_address_parameter),
+	                                    flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_ipv6_address_parameter_new(struct in6_addr *addr)
+{
+	struct sctp_ipv6_address_parameter *parameter;
+	u32 flags;
+
+	flags = 0;
+	parameter = malloc(sizeof(struct sctp_ipv6_address_parameter));
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_IPV6_ADDRESS_PARAMETER_TYPE);
+	parameter->length = htons(sizeof(struct sctp_ipv6_address_parameter));
+	if (addr == NULL) {
+		parameter->addr = in6addr_any;
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+	} else {
+		parameter->addr = *addr;
+	}
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    sizeof(struct sctp_ipv6_address_parameter),
+	                                    flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_state_cookie_parameter_new(s64 len, u8 *cookie)
+{
+	struct sctp_state_cookie_parameter *parameter;
+	u32 flags;
+	u16 parameter_length, cookie_length, padding_length;
+
+	assert((len == -1) || is_valid_u16(len));
+	assert((len != -1) || (cookie == NULL));
+	flags = 0;
+	if (len == -1) {
+		cookie_length = 0;
+		flags |= FLAG_PARAMETER_LENGTH_NOCHECK;
+	} else {
+		assert(len <= MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_state_cookie_parameter));
+		cookie_length = len - sizeof(struct sctp_state_cookie_parameter);
+	}
+	parameter_length = cookie_length + sizeof(struct sctp_state_cookie_parameter);
+	padding_length = parameter_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	parameter = malloc(parameter_length + padding_length);
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_STATE_COOKIE_PARAMETER_TYPE);
+	parameter->length = htons(parameter_length);
+	if (cookie != NULL) {
+		memcpy(parameter->cookie, cookie, cookie_length);
+	} else {
+		/* flags |= FLAG_PARAMETER_VALUE_NOCHECK; */
+		memset(parameter->cookie, 'A', cookie_length);
+	}
+	/* Clear the padding */
+	memset(parameter->cookie + cookie_length, 0, padding_length);
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    parameter_length, flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_unrecognized_parameters_parameter_new(struct sctp_parameter_list *list)
+{
+	struct sctp_unrecognized_parameter_parameter *parameter;
+	struct sctp_parameter_list_item *item;
+	u32 flags;
+	u16 parameter_length, padding_length, offset;
+
+	assert(list == NULL ||
+	       (list->length <
+	        MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_unrecognized_parameter_parameter)));
+	flags = 0;
+	parameter_length = sizeof(struct sctp_unrecognized_parameter_parameter);
+	if (list != NULL) {
+		parameter_length += list->length;
+	} else {
+		flags |= FLAG_PARAMETER_LENGTH_NOCHECK;
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+	}
+	padding_length = parameter_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	parameter = malloc(parameter_length + padding_length);
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE);
+	parameter->length = htons(parameter_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(parameter->value + offset, item->parameter, item->length + padding_length);
+			if (item->flags & FLAG_PARAMETER_LENGTH_NOCHECK) {
+				flags |= FLAG_PARAMETER_LENGTH_NOCHECK;
+			}
+			if (item->flags & FLAG_PARAMETER_VALUE_NOCHECK) {
+				flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+			}
+			offset += item->length + padding_length;
+		}
+	}
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    parameter_length, flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_cookie_preservative_parameter_new(s64 increment)
+{
+	struct sctp_cookie_preservative_parameter *parameter;
+	u32 flags;
+
+	flags = 0;
+	parameter = malloc(sizeof(struct sctp_cookie_preservative_parameter));
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE);
+	parameter->length = htons(sizeof(struct sctp_cookie_preservative_parameter));
+	if (increment == -1) {
+		parameter->increment = htonl(0);
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+	} else {
+		assert(is_valid_u32(increment));
+		parameter->increment = htonl((u32)increment);
+	}
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    sizeof(struct sctp_cookie_preservative_parameter),
+	                                    flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_hostname_address_parameter_new(char *hostname)
+{
+	struct sctp_hostname_address_parameter *parameter;
+	u32 flags;
+	u16 length, name_length, padding_length;
+
+	/* RFC 4960 requires that the hostname is NUL terminated */
+	assert(hostname == NULL ||
+	       (strlen(hostname) + 1 <=
+	        MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_hostname_address_parameter)));
+	flags = 0;
+	if (hostname == NULL) {
+		name_length = 1;
+	} else {
+		name_length = strlen(hostname) + 1;
+	}
+	length = name_length + sizeof(struct sctp_hostname_address_parameter);
+	padding_length = length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	parameter = malloc(length + padding_length);
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE);
+	parameter->length = htons(length);
+	if (hostname == NULL) {
+		parameter->hostname[0] = '\0';
+		flags |= FLAG_PARAMETER_LENGTH_NOCHECK;
+		flags |= FLAG_PARAMETER_VALUE_NOCHECK;
+	} else {
+		strcpy(parameter->hostname, hostname);
+	}
+	memset(parameter->hostname + name_length, 0, padding_length);
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    length, flags);
+}
+
+struct sctp_parameter_list_item *
+sctp_supported_address_types_parameter_new(struct sctp_address_type_list *list)
+{
+	struct sctp_supported_address_types_parameter *parameter;
+
+	u32 flags;
+	u16 i, parameter_length, padding_length;
+	struct sctp_address_type_list_item *item;
+
+	flags = 0;
+	parameter_length = sizeof(struct sctp_supported_address_types_parameter);
+	if (list != NULL) {
+		assert(list->nr_entries <=
+		       (MAX_SCTP_PARAMETER_BYTES - sizeof(struct sctp_supported_address_types_parameter)) / sizeof(u16));
+		parameter_length += list->nr_entries * sizeof(u16);
+	}
+	padding_length = parameter_length % 4;
+	if (padding_length > 0) {
+		padding_length = 4 - padding_length;
+	}
+	assert(padding_length == 0 || padding_length == 2);
+	parameter = malloc(parameter_length + padding_length);
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE);
+	parameter->length = htons(parameter_length);
+	if (list != NULL) {
+		for (i = 0, item = list->first;
+		     (i < list->nr_entries) && (item != NULL);
+		     i++, item = item->next) {
+			parameter->address_type[i] = htons(item->address_type);
+		}
+		assert((i == list->nr_entries) && (item == NULL));
+	}
+	if (padding_length == 2) {
+		parameter->address_type[list->nr_entries] = htons(0);
+	}
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    parameter_length, flags);
+}
+
+
+/* SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE	*/
+
+struct sctp_parameter_list_item *
+sctp_ecn_capable_parameter_new(void)
+{
+	struct sctp_ecn_capable_parameter *parameter;
+
+	parameter = malloc(sizeof(struct sctp_ecn_capable_parameter));
+	assert(parameter != NULL);
+	parameter->type = htons(SCTP_ECN_CAPABLE_PARAMETER_TYPE);
+	parameter->length = htons(sizeof(struct sctp_ecn_capable_parameter));
+	return sctp_parameter_list_item_new((struct sctp_parameter *)parameter,
+	                                    sizeof(struct sctp_ecn_capable_parameter),
+	                                    0);
+}
+
+struct sctp_parameter_list *
+sctp_parameter_list_new(void)
+{
+	struct sctp_parameter_list *list;
+
+	list = malloc(sizeof(struct sctp_parameter_list));
+	assert(list != NULL);
+	list->first = NULL;
+	list->last = NULL;
+	list->length = 0;
+	return list;
+}
+
+void
+sctp_parameter_list_append(struct sctp_parameter_list *list,
+                           struct sctp_parameter_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_parameter_list_free(struct sctp_parameter_list *list)
+{
+	struct sctp_parameter_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;
 	}
@@ -675,7 +1218,8 @@ new_sctp_packet(int address_family,
 {
 	struct packet *packet;  /* the newly-allocated result packet */
 	struct header *sctp_header;  /* the SCTP header info */
-	struct sctp_chunk_list_item *item;
+	struct sctp_chunk_list_item *chunk_item;
+	struct sctp_parameter_list_item *parameter_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) +
@@ -701,91 +1245,117 @@ new_sctp_packet(int address_family,
 	}
 
 	if (direction == DIRECTION_INBOUND) {
-		for (item = list->first; item != NULL; item = item->next) {
-			switch (item->chunk->type) {
+		for (chunk_item = list->first;
+		     chunk_item != NULL;
+		     chunk_item = chunk_item->next) {
+			for (parameter_item = chunk_item->parameter_list->first;
+			     parameter_item != NULL;
+			     parameter_item = parameter_item->next) {
+				if (parameter_item->flags & FLAG_PARAMETER_LENGTH_NOCHECK) {
+					asprintf(error,
+						 "parameter length must be specified for inbound packets");
+					return NULL;
+				}
+				if (parameter_item->flags & FLAG_PARAMETER_VALUE_NOCHECK) {
+					asprintf(error,
+						 "parameter value must be specified for inbound packets");
+					return NULL;
+				}
+			}
+			switch (chunk_item->chunk->type) {
 			case SCTP_DATA_CHUNK_TYPE:
-				if (item->flags & FLAG_CHUNK_FLAGS_NOCHECK) {
+				if (chunk_item->flags & FLAG_CHUNK_FLAGS_NOCHECK) {
 					asprintf(error,
 						 "chunk flags must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
+				if (chunk_item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
 					asprintf(error,
 						 "chunk length must be specified for inbound packets");
 					return NULL;
-				}				
-				if (item->flags & FLAG_DATA_CHUNK_TSN_NOCHECK) {
+				}
+				if (chunk_item->flags & FLAG_DATA_CHUNK_TSN_NOCHECK) {
 					asprintf(error,
 						 "TSN must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_DATA_CHUNK_SID_NOCHECK) {
+				if (chunk_item->flags & FLAG_DATA_CHUNK_SID_NOCHECK) {
 					asprintf(error,
 						 "SID must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_DATA_CHUNK_SSN_NOCHECK) {
+				if (chunk_item->flags & FLAG_DATA_CHUNK_SSN_NOCHECK) {
 					asprintf(error,
 						 "SSN must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_DATA_CHUNK_PPID_NOCHECK) {
+				if (chunk_item->flags & FLAG_DATA_CHUNK_PPID_NOCHECK) {
 					asprintf(error,
 						 "PPID must be specified for inbound packets");
 					return NULL;
-				}	
+				}
 				break;
 			case SCTP_INIT_CHUNK_TYPE:
-				if (item->flags & FLAG_INIT_CHUNK_A_RWND_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_CHUNK_A_RWND_NOCHECK) {
 					asprintf(error,
 						 "A_RWND must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_INIT_CHUNK_OS_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_CHUNK_OS_NOCHECK) {
 					asprintf(error,
 						 "OS must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_INIT_CHUNK_IS_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_CHUNK_IS_NOCHECK) {
 					asprintf(error,
 						 "IS must be specified for inbound packets");
 					return NULL;
 				}
+				if (chunk_item->flags & FLAG_INIT_CHUNK_OPT_PARAM_NOCHECK) {
+					asprintf(error,
+						 "list of optional parameters must be specified for inbound packets");
+					return NULL;
+				}
 				break;
 			case SCTP_INIT_ACK_CHUNK_TYPE:
-				if (item->flags & FLAG_INIT_ACK_CHUNK_A_RWND_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_ACK_CHUNK_A_RWND_NOCHECK) {
 					asprintf(error,
 						 "A_RWND must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_INIT_ACK_CHUNK_OS_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_ACK_CHUNK_OS_NOCHECK) {
 					asprintf(error,
 						 "OS must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_INIT_ACK_CHUNK_IS_NOCHECK) {
+				if (chunk_item->flags & FLAG_INIT_ACK_CHUNK_IS_NOCHECK) {
 					asprintf(error,
 						 "IS must be specified for inbound packets");
 					return NULL;
 				}
+				if (chunk_item->flags & FLAG_INIT_ACK_CHUNK_OPT_PARAM_NOCHECK) {
+					asprintf(error,
+						 "list of optional parameters must be specified for inbound packets");
+					return NULL;
+				}
 				break;
 			case SCTP_SACK_CHUNK_TYPE:
-				if (item->flags & FLAG_SACK_CHUNK_CUM_TSN_NOCHECK) {
+				if (chunk_item->flags & FLAG_SACK_CHUNK_CUM_TSN_NOCHECK) {
 					asprintf(error,
 						 "CUM_TSN must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_SACK_CHUNK_A_RWND_NOCHECK) {
+				if (chunk_item->flags & FLAG_SACK_CHUNK_A_RWND_NOCHECK) {
 					asprintf(error,
 						 "A_RWND must be specified for inbound packets");
 					return NULL;
-				}				
-				if (item->flags & FLAG_SACK_CHUNK_GAP_BLOCKS_NOCHECK) {
+				}
+				if (chunk_item->flags & FLAG_SACK_CHUNK_GAP_BLOCKS_NOCHECK) {
 					asprintf(error,
 						 "GAP_BLOCKS must be specified for inbound packets");
 					return NULL;
 				}
-				if (item->flags & FLAG_SACK_CHUNK_DUP_TSNS_NOCHECK) {
+				if (chunk_item->flags & FLAG_SACK_CHUNK_DUP_TSNS_NOCHECK) {
 					asprintf(error,
 						 "DUP_TSNS must be specified for inbound packets");
 					return NULL;
@@ -797,14 +1367,14 @@ new_sctp_packet(int address_family,
 				overbook = true;
 				break;
 			case SCTP_ABORT_CHUNK_TYPE:
-				if (item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
+				if (chunk_item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
 					asprintf(error,
 						 "error causes must be specified for inbound packets");
 					return NULL;
-				}				
+				}
 				break;
 			case SCTP_SHUTDOWN_CHUNK_TYPE:
-				if (item->flags & FLAG_SHUTDOWN_CHUNK_CUM_TSN_NOCHECK) {
+				if (chunk_item->flags & FLAG_SHUTDOWN_CHUNK_CUM_TSN_NOCHECK) {
 					asprintf(error,
 						 "TSN must be specified for inbound packets");
 					return NULL;
@@ -813,11 +1383,11 @@ new_sctp_packet(int address_family,
 			case SCTP_SHUTDOWN_ACK_CHUNK_TYPE:
 				break;
 			case SCTP_ERROR_CHUNK_TYPE:
-				if (item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
+				if (chunk_item->flags & FLAG_CHUNK_LENGTH_NOCHECK) {
 					asprintf(error,
 						 "error causes must be specified for inbound packets");
 					return NULL;
-				}				
+				}
 				break;
 			case SCTP_COOKIE_ECHO_CHUNK_TYPE:
 				overbook = true;
@@ -825,14 +1395,14 @@ new_sctp_packet(int address_family,
 			case SCTP_COOKIE_ACK_CHUNK_TYPE:
 				break;
 			case SCTP_ECNE_CHUNK_TYPE:
-				if (item->flags & FLAG_ECNE_CHUNK_LOWEST_TSN_NOCHECK) {
+				if (chunk_item->flags & FLAG_ECNE_CHUNK_LOWEST_TSN_NOCHECK) {
 					asprintf(error,
 						 "LOWEST_TSN must be specified for inbound packets");
 					return NULL;
 				}
 				break;
 			case SCTP_CWR_CHUNK_TYPE:
-				if (item->flags & FLAG_CWR_CHUNK_LOWEST_TSN_NOCHECK) {
+				if (chunk_item->flags & FLAG_CWR_CHUNK_LOWEST_TSN_NOCHECK) {
 					asprintf(error,
 						 "LOWEST_TSN must be specified for inbound packets");
 					return NULL;
@@ -841,12 +1411,12 @@ new_sctp_packet(int address_family,
 			case SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE:
 				break;
 			default:
-				asprintf(error, "Unknown chunk type 0x%02x", item->chunk->type);
+				asprintf(error, "Unknown chunk type 0x%02x", chunk_item->chunk->type);
 				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);
@@ -872,14 +1442,21 @@ new_sctp_packet(int address_family,
 	packet->sctp->v_tag = htonl(0);
 	packet->sctp->crc32c = htonl(0);
 
-	for (item = list->first; item != NULL; item = item->next) {
-		DEBUGP("Copy in a chunk of length %d\n", item->length);
-		memcpy(sctp_chunk_start, item->chunk, item->length);
-		DEBUGP("Old location: %p\n", (void *)item->chunk);
-		free(item->chunk);
-		item->chunk = (struct sctp_chunk *)sctp_chunk_start;
-		DEBUGP("New location: %p\n", (void *)item->chunk);
-		sctp_chunk_start += item->length;
+	for (chunk_item = list->first;
+	     chunk_item != NULL;
+	     chunk_item = chunk_item->next) {
+		memcpy(sctp_chunk_start, chunk_item->chunk, chunk_item->length);
+		for (parameter_item = chunk_item->parameter_list->first;
+		     parameter_item != NULL;
+		     parameter_item = parameter_item->next) {
+			parameter_item->parameter =
+			    (struct sctp_parameter *)(sctp_chunk_start +
+			                              ((u8 *)parameter_item->parameter -
+			                               (u8 *)chunk_item->chunk));
+		}
+		free(chunk_item->chunk);
+		chunk_item->chunk = (struct sctp_chunk *)sctp_chunk_start;
+		sctp_chunk_start += chunk_item->length;
 	}
 	free(packet->chunk_list);
 	packet->chunk_list = list;
diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h
index 6ac35603d36b5fb834fbb36b8865984ce96020bb..eacbe555a8a228b1bb1f7f08639a09b7d83b3daf 100644
--- a/gtests/net/packetdrill/sctp_packet.h
+++ b/gtests/net/packetdrill/sctp_packet.h
@@ -48,7 +48,7 @@ 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);
+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);
@@ -56,9 +56,50 @@ 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_address_type_list_item {
+	struct sctp_address_type_list_item *next;
+	u16 address_type;
+};
+
+struct sctp_address_type_list {
+	struct sctp_address_type_list_item *first;
+	struct sctp_address_type_list_item *last;
+	u16 nr_entries;
+};
+
+struct sctp_address_type_list *
+sctp_address_type_list_new(void);
+
+void
+sctp_address_type_list_append(struct sctp_address_type_list *list,
+			      struct sctp_address_type_list_item *item);
+
+void
+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_list_item {
+	struct sctp_parameter_list_item *next;
+	struct sctp_parameter *parameter;
+	/* total length in bytes */
+	u32 length;
+	/* metadata */
+	u32 flags;
+};
+
+struct sctp_parameter_list {
+	struct sctp_parameter_list_item *first;
+	struct sctp_parameter_list_item *last;
+	/* length in bytes excluding the padding of the last parameter*/
+	u32 length;
+};
+
 struct sctp_chunk_list_item {
 	struct sctp_chunk_list_item *next;
 	struct sctp_chunk *chunk;
+	struct sctp_parameter_list *parameter_list;
 	/* total length in bytes */
 	u32 length;
 	/* metadata */
@@ -78,7 +119,8 @@ struct sctp_chunk_list {
 #define FLAG_CHUNK_VALUE_NOCHECK                0x00000008
 
 struct sctp_chunk_list_item *
-sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags);
+sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags,
+                         struct sctp_parameter_list *list);
 
 #define FLAG_DATA_CHUNK_TSN_NOCHECK             0x00000100
 #define FLAG_DATA_CHUNK_SID_NOCHECK             0x00000200
@@ -91,16 +133,20 @@ sctp_data_chunk_new(s64 flgs, s64 len, s64 tsn, s64 sid, s64 ssn, s64 ppid);
 #define FLAG_INIT_CHUNK_A_RWND_NOCHECK          0x00000100
 #define FLAG_INIT_CHUNK_OS_NOCHECK              0x00000200
 #define FLAG_INIT_CHUNK_IS_NOCHECK              0x00000400
+#define FLAG_INIT_CHUNK_OPT_PARAM_NOCHECK       0x00000800
 
 struct sctp_chunk_list_item *
-sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn);
+sctp_init_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn,
+                    struct sctp_parameter_list *parameters);
 
 #define FLAG_INIT_ACK_CHUNK_A_RWND_NOCHECK      0x00000100
 #define FLAG_INIT_ACK_CHUNK_OS_NOCHECK          0x00000200
 #define FLAG_INIT_ACK_CHUNK_IS_NOCHECK          0x00000400
+#define FLAG_INIT_ACK_CHUNK_OPT_PARAM_NOCHECK   0x00000800
 
 struct sctp_chunk_list_item *
-sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn);
+sctp_init_ack_chunk_new(s64 flgs, s64 tag, s64 a_rwnd, s64 os, s64 is, s64 tsn,
+                        struct sctp_parameter_list *parameters);
 
 #define FLAG_SACK_CHUNK_CUM_TSN_NOCHECK         0x00000100
 #define FLAG_SACK_CHUNK_A_RWND_NOCHECK          0x00000200
@@ -113,10 +159,10 @@ sctp_sack_chunk_new(s64 flgs, s64 cum_tsn, s64 a_rwnd,
                     struct sctp_sack_block_list *dups);
 
 struct sctp_chunk_list_item *
-sctp_heartbeat_chunk_new(s64 flgs);
+sctp_heartbeat_chunk_new(s64 flgs, struct sctp_parameter_list_item *info);
 
 struct sctp_chunk_list_item *
-sctp_heartbeat_ack_chunk_new(s64 flgs);
+sctp_heartbeat_ack_chunk_new(s64 flgs, struct sctp_parameter_list_item *info);
 
 struct sctp_chunk_list_item *
 sctp_abort_chunk_new(s64 flgs);
@@ -133,7 +179,7 @@ struct sctp_chunk_list_item *
 sctp_error_chunk_new(s64 flgs);
 
 struct sctp_chunk_list_item *
-sctp_cookie_echo_chunk_new(s64 flgs);
+sctp_cookie_echo_chunk_new(s64 flgs, s64 len, u8* cookie);
 
 struct sctp_chunk_list_item *
 sctp_cookie_ack_chunk_new(s64 flgs);
@@ -151,12 +197,61 @@ sctp_cwr_chunk_new(s64 flgs, s64 lowest_tsn);
 struct sctp_chunk_list_item *
 sctp_shutdown_complete_chunk_new(s64 flgs);
 
-struct sctp_chunk_list *sctp_chunk_list_new(void);
+struct sctp_chunk_list *
+sctp_chunk_list_new(void);
 
-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);
+
+void
+sctp_chunk_list_free(struct sctp_chunk_list *list);
+
+#define FLAG_PARAMETER_TYPE_NOCHECK				0x00000001
+#define FLAG_PARAMETER_LENGTH_NOCHECK				0x00000002
+#define FLAG_PARAMETER_VALUE_NOCHECK				0x00000004
+
+struct sctp_parameter_list_item *
+sctp_parameter_list_item_new(struct sctp_parameter *parameter,
+                             u32 length, u32 flags);
+
+struct sctp_parameter_list_item *
+sctp_heartbeat_information_parameter_new(s64 len, u8 *information);
+
+struct sctp_parameter_list_item *
+sctp_ipv4_address_parameter_new(struct in_addr *addr);
+
+struct sctp_parameter_list_item *
+sctp_ipv6_address_parameter_new(struct in6_addr *addr);
+
+struct sctp_parameter_list_item *
+sctp_state_cookie_parameter_new(s64 len, u8 *cookie);
+
+struct sctp_parameter_list_item *
+sctp_unrecognized_parameters_parameter_new(struct sctp_parameter_list *list);
+
+struct sctp_parameter_list_item *
+sctp_cookie_preservative_parameter_new(s64 increment);
+
+struct sctp_parameter_list_item *
+sctp_hostname_address_parameter_new(char *hostname);
+
+struct sctp_parameter_list_item *
+sctp_supported_address_types_parameter_new(struct sctp_address_type_list *list);
+
+struct sctp_parameter_list_item *
+sctp_ecn_capable_parameter_new(void);
+
+struct sctp_parameter_list *
+sctp_parameter_list_new(void);
+
+void
+sctp_parameter_list_append(struct sctp_parameter_list *list,
+                           struct sctp_parameter_list_item *item);
+
+void
+sctp_parameter_list_free(struct sctp_parameter_list *list);
 
-void sctp_chunk_list_free(struct sctp_chunk_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_active.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_active.pkt
index 64a442fc3dd18117aae85dfed7c4df93e366f0f0..87c7266d8f3fe52275e1be8edbadee69cea3e549 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_active.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_active.pkt
@@ -1,27 +1,36 @@
+ 0.0 `sysctl -w net.inet.sctp.ecn_enable=1`
++0.0 `sysctl -w net.inet.sctp.pr_enable=0`
++0.0 `sysctl -w net.inet.sctp.asconf_enable=0`
++0.0 `sysctl -w net.inet.sctp.auth_enable=0`
++0.0 `sysctl -w net.inet.sctp.reconfig_enable=0`
++0.0 `sysctl -w net.inet.sctp.nrsack_enable=0`
++0.0 `sysctl -w net.inet.sctp.pktdrop_enable=0`
 +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 en empty(!) cookie
+// Check the handshake with an empty(!) cookie
 +0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
-+0.0 > sctp: INIT[tag=1 tsn=0]
-+0.1 < sctp: INIT_ACK[tag=2 a_rwnd=1500  os=1 is =1 tsn=3] // faked cookie
-+0.0 > sctp: COOKIE_ECHO[] // syntax not clear
-+0.1 < sctp: COOKIE_ACK[]
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ECN_CAPABLE[], SUPPORTED_ADDRESS_TYPES[types=[IPv4]]]
++0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=1500, os=1, is=1, tsn=3, STATE_COOKIE[len=4, val=...], SUPPORTED_ADDRESS_TYPES[types=[IPv4]]]
++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
-// Send some data.
+// Send some data
++0.5 < sctp: HEARTBEAT[flgs=0, HEARTBEAT_INFORMATION[len=9, val=...]]
++0.0 > sctp: HEARTBEAT_ACK[flgs=0, HEARTBEAT_INFORMATION[len=9, val=...]]
 +1.0 write(3, ..., 1000) = 1000
-+0.0 > sctp: DATA[tsn=0 sid=0 ssn=0 ppid=0]
-+0.1 < sctp: SACK[tsn=0 a_rwnd=1500 gaps=[] dups=[]]
++0.0 > sctp: DATA[flgs=BE, len=1016, tsn=0, sid=0, ssn=0, ppid=0]
++0.1 < sctp: SACK[flgs=0, cum_tsn=0, a_rwnd=1500, gaps=[], dups=[]]
 // Receive some data
-+1.0 < sctp: DATA[flgs=BE len=1016 tsn=3 sid=0 ssn=0 ppid=0]
++1.0 < sctp: DATA[flgs=BE, len=1016, tsn=3, sid=0, ssn=0, ppid=0]
 +0.0 read(3, ..., 2000) = 1000
-+0.0 > sctp: SACK[tsn=3]
-// Receive more data, observe delayed SACK
-+1.0 < sctp: DATA[flgs=IBE len=1016 tsn=4 sid=0 ssn=1 ppid=0]
++0.0 > sctp: SACK[flgs=..., cum_tsn=3, a_rwnd=..., gaps=..., dups=...]
+// Receive more data, observe a non-delayed SACK
++1.0 < sctp: DATA[flgs=IBE, len=1016, tsn=4, sid=0, ssn=1, ppid=0]
 +0.0 read(3, ..., 2000) = 1000
-+0.0 > sctp: SACK[tsn=4]
++0.0 > sctp: SACK[flgs=0, cum_tsn=4, a_rwnd=..., gaps=..., dups=...]
 // Tear down the association
 +0.0 close(3) = 0
-+0.0 > sctp: SHUTDOWN[tsn=4]
-+0.1 < sctp: SHUTDOWN_ACK[]
-+0.0 > sctp: SHUTDOWN_COMPLETE[]
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=4]
++0.1 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_handle_init.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_handle_init.pkt
index eafcaf511913f7ad930b38a0c0e6f22ff19d09d1..389c938f9c98b29eef11d5362b52e79f31f2305a 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_handle_init.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_handle_init.pkt
@@ -2,6 +2,6 @@
 +0.0  bind(3, ..., ...) = 0
 +0.0  listen(3, 1) = 0
 // The initiate tag in the INIT chunk MUST NOT be zero.
-+0.1 < sctp: INIT[tag=0, a_rwnd=1500, is=1, os=1, tsn=0]
-+0.0 > sctp: ABORT[INVALID_MANDATORY_PARAMETER[]]
++0.1 < sctp: INIT[flgs=0, tag=0, a_rwnd=1500, os=1, is=1, tsn=0]
++0.0 > sctp: ABORT[flgs=T]
 +0.0 close(3) = 0
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_hb.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_hb.pkt
index 0869034e21a5ede4a42d0f56a138f61156d0ebd1..d7278191e3a6a00553a113bdb747fc72d8da8f7b 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_hb.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_hb.pkt
@@ -4,18 +4,18 @@
 +0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
 +0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
 +0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
-+0.0 > sctp: INIT[tag=1 tsn=1]                            // FIXME
-+0.0 < sctp: INIT_ACK[tag=2 a_rwnd=1500 os=1 is =1 tsn=1] // FIXME 
-+0.0 > sctp: COOKIE_ECHO[]                                // FIXME 
-+0.0 < sctp: COOKIE_ACK[]
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=1, ...]
++0.0 < 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.0 < sctp: COOKIE_ACK[flgs=0]
 +0.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
 // then respond to two HEARTBEATs
- *   > sctp: HEARTBEAT[]
-+0.0 < sctp: HEARTBEAT_ACK[]
- *   > sctp: HEARTBEAT[]
-+0.0 < sctp: HEARTBEAT_ACK[]
+ *   > sctp: HEARTBEAT[flgs=0, HEARTBEAT_INFORMATION[len=..., val=...]]
++0.0 < sctp: HEARTBEAT_ACK[flgs=0, HEARTBEAT_INFORMATION[len=..., val=...]]
+ *   > sctp: HEARTBEAT[flgs=0, HEARTBEAT_INFORMATION[len=..., val=...]]
++0.0 < sctp: HEARTBEAT_ACK[flgs=0, HEARTBEAT_INFORMATION[len=..., val=...]]
 // and finally perform a graceful shutdown
 +0.0 close(3) = 0
-+0.0 > sctp: SHUTDOWN[tsn=0]
-+0.0 < sctp: SHUTDOWN_ACK[]
-+0.0 > sctp: SHUTDOWN_COMPLETE[]
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=0]
++0.0 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt
index c02a9e0898471f1f5a204caf750c01f2d5f31fa8..5aa6a0ee4d172c4f21430a113a15065063050ee8 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt
@@ -4,20 +4,20 @@
 +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[tag=1 tsn=0]
-+0.1 < sctp: INIT_ACK[tag=2 a_rwnd=4500 os=1 is =1 tsn=3] // faked cookie
-+0.0 > sctp: COOKIE_ECHO[] // faked cookie
-+0.1 < sctp: COOKIE_ACK[]
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=4500, os=1, is =1, tsn=3, 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
 // Reduce the PMTU.
 +0.5 < [sctp(2)] icmp unreachable frag_needed mtu 1500
 // Send some data and get it acknowledged.
 +0.5 write(3, ..., 2000) = 2000
-+0.0 > sctp: DATA[flgs=B len=1468 tsn=0 sid=0 ssn=0 ppid=0]
-+0.0 > sctp: DATA[flgs=E len=564  tsn=1 sid=0 ssn=0 ppid=0]
-+0.1 < sctp: SACK[tsn=1 a_rwnd=1500 gaps=[] dups=[]]
++0.0 > sctp: DATA[flgs=B, len=1468, tsn=0, sid=0, ssn=0, ppid=0]
++0.0 > sctp: DATA[flgs=E, len=564,  tsn=1, sid=0, ssn=0, ppid=0]
++0.1 < sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=1500, gaps=[], dups=[]]
 // Tear down the association
 +0.0 close(3) = 0
-+0.0 > sctp: SHUTDOWN[tsn=2]
-+0.1 < sctp: SHUTDOWN_ACK[]
-+0.0 > sctp: SHUTDOWN_COMPLETE[]
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=2]
++0.1 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt
index eb801515eba3609c7801ae284048b42700cfe8ed..82460960f70072d748c936f7ca48e61e2ac42c9a 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt
@@ -2,21 +2,21 @@
 // Check the handshake with en empty(!) cookie
 +0.0 bind(3, ..., ...) = 0
 +0.0 listen(3, 1) = 0
-+0.0 < sctp: INIT[tag=1 a_rwnd=4500 os=1 is=1 tsn=1]
-+0.0 > sctp: INIT_ACK[tag=2 tsn=0] // faked cookie
-+0.1 < sctp: COOKIE_ECHO[]
-+0.0 > sctp: COOKIE_ACK[]
++0.0 < sctp: INIT[flgs=0, tag=1, a_rwnd=4500, os=1, is=1, tsn=1]
++0.0 > sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=..., os=..., is=..., tsn=0, STATE_COOKIE[len=..., val=...]]
++0.1 < sctp: COOKIE_ECHO[flgs=0, len=..., val=...]
++0.0 > sctp: COOKIE_ACK[flgs=0]
 +0.0 accept(3, ..., ...) = 4
 +0.0 close(3) = 0
 // Reduce the PMTU.
 +0.5 < [sctp(1)] icmp unreachable frag_needed mtu 1500
 // Send some data and get it acknowledged.
 +0.5 write(4, ..., 2000) = 2000
-+0.0 > sctp: DATA[flgs=B len=1468 tsn=0 sid=0 ssn=0 ppid=0]
-+0.0 > sctp: DATA[flgs=E len=564  tsn=1 sid=0 ssn=0 ppid=0]
-+0.1 < sctp: SACK[tsn=1 a_rwnd=4500 gaps=[] dups=[]]
++0.0 > sctp: DATA[flgs=B, len=1468, tsn=0, sid=0, ssn=0, ppid=0]
++0.0 > sctp: DATA[flgs=E, len=564,  tsn=1, sid=0, ssn=0, ppid=0]
++0.1 < sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=4500, gaps=[], dups=[]]
 // Tear down the association
 +0.0 close(4) = 0
-+0.0 > sctp: SHUTDOWN[tsn=0]
-+0.1 < sctp: SHUTDOWN_ACK[]
-+0.0 > sctp: SHUTDOWN_COMPLETE[]
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=0]
++0.1 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_init_rtx.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_init_rtx.pkt
index 2254235e731b1dfe92447ee4507bf53b99d4f7cf..64b992533a5b723d22c17cba1fdabca0de3e0c8e 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_init_rtx.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_init_rtx.pkt
@@ -1,26 +1,18 @@
- 0.0 `sysctl -w net.inet.sctp.ecn_enable=0`
-+0.0 `sysctl -w net.inet.sctp.pr_enable=0`
-+0.0 `sysctl -w net.inet.sctp.asconf_enable=0`
-+0.0 `sysctl -w net.inet.sctp.auth_enable=0`
-+0.0 `sysctl -w net.inet.sctp.reconfig_enable=0`
-+0.0 `sysctl -w net.inet.sctp.nrsack_enable=0`
-+0.0 `sysctl -w net.inet.sctp.pktdrop_enable=0`
 +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
 +0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RTOINFO, {srto_initial=100, srto_max=800, srto_min=100}, 16) = 0
 +0.0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
 // Check the number and the timing of the restransmissions
-+0.0 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-+0.1 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...] 
-+0.2 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...] 
-+0.4 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-+0.8 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-+0.8 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-+0.8 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-+0.8 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...] 
-+0.8 > sctp: INIT[tag=..., a_rwnd=..., is=10, os=2048, tsn=0, ...]
-// Look as if we don't send a final ABORT. Shouldn't we?
-// +1.0 > sctp: ABORT[]
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.1 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.2 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.4 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=0, ...]
++0.8 > sctp: ABORT[flgs=T]
 +1.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [ETIMEDOUT], [4]) = 0
 +0.0 close(3) = 0
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_passive.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_passive.pkt
index 3ca6b026b4e2005dac3befa621a27d483e6cf946..e8b517f568f34a74db70bd4fff7f235ec510dcfc 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_passive.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_passive.pkt
@@ -1,28 +1,35 @@
+ 0.0 `sysctl -w net.inet.sctp.ecn_enable=1`
++0.0 `sysctl -w net.inet.sctp.pr_enable=0`
++0.0 `sysctl -w net.inet.sctp.asconf_enable=0`
++0.0 `sysctl -w net.inet.sctp.auth_enable=0`
++0.0 `sysctl -w net.inet.sctp.reconfig_enable=0`
++0.0 `sysctl -w net.inet.sctp.nrsack_enable=0`
++0.0 `sysctl -w net.inet.sctp.pktdrop_enable=0`
 +0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3
 // Check the handshake with en empty(!) cookie
 +0.0 bind(3, ..., ...) = 0
 +0.0 listen(3, 1) = 0
-+0.0 < sctp: INIT[tag=1 a_rwnd=1500 os=1 is=1 tsn=0]
-+0.0 > sctp: INIT_ACK[tag=2 tsn=10] // faked cookie
-+0.1 < sctp: COOKIE_ECHO[]; DATA[flgs=0x03 len=1016 tsn=0 sid=0 ssn=0 ppid=0]// syntax not clear
-+0.0 > sctp: COOKIE_ACK[]; SACK[tsn=0]
++0.0 < sctp: INIT[flgs=0, tag=1, a_rwnd=1500, os=1, is=1, tsn=0, ECN_CAPABLE[], COOKIE_PRESERVATIVE[incr=12345], SUPPORTED_ADDRESS_TYPES[types=[IPv4]]]
++0.0 > sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=..., os=..., is=..., tsn=10, STATE_COOKIE[len=..., val=...]]
++0.1 < sctp: COOKIE_ECHO[flgs=0, len=..., val=...]; DATA[flgs=BE, len=1016, tsn=0, sid=0, ssn=0, ppid=0]
++0.0 > sctp: COOKIE_ACK[flgs=0]; SACK[flgs=0, cum_tsn=0, a_rwnd=..., gaps=..., dups=...]
 +0.0 accept(3, ..., ...) = 4
 +0.0 read(4, ..., 1000) = 1000
 // Send some data.
 +1.0 write(4, ..., 1000) = 1000
-+0.0 > sctp: DATA[tsn=10 sid=0 ssn=0 ppid=0]
-+0.1 < sctp: SACK[tsn=10 a_rwnd=1500 gaps=[] dups=[]]
++0.0 > sctp: DATA[flgs=BE, len=1016, tsn=10, sid=0, ssn=0, ppid=0]
++0.1 < sctp: SACK[flgs=0, cum_tsn=10, a_rwnd=1500, gaps=[], dups=[]]
 // Receive some data
-+1.0 < sctp: DATA[flgs=BE len=1016 tsn=1 sid=0 ssn=1 ppid=0] 
++1.0 < sctp: DATA[flgs=BE, len=1016, tsn=1, sid=0, ssn=1, ppid=0]
 +0.0 read(4, ..., 2000) = 1000
-+0.2 > sctp: SACK[tsn=1]
++0.2 > sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=..., gaps=[], dups=[]]
 // Receive more data, observe delayed SACKi
-+1.0 < sctp: DATA[flgs=BE len=1016 tsn=2 sid=0 ssn=2 ppid=0]
++1.0 < sctp: DATA[flgs=BE, len=1016, tsn=2, sid=0, ssn=2, ppid=0]
 +0.0 read(4, ..., 2000) = 1000
-+0.2 > sctp: SACK[tsn=2]
++0.2 > sctp: SACK[flgs=0, cum_tsn=2, a_rwnd=..., gaps=[], dups=[]]
 // Tear down the association
-+1.0 < sctp: SHUTDOWN[tsn=10]
-+0.0 > sctp: SHUTDOWN_ACK[]
-+0.0 < sctp: SHUTDOWN_COMPLETE[]
++1.0 < sctp: SHUTDOWN[flgs=0, cum_tsn=10]
++0.0 > sctp: SHUTDOWN_ACK[flgs=0]
++0.0 < sctp: SHUTDOWN_COMPLETE[flgs=0]
 +0.0 close(4) = 0
 +0.0 close(3) = 0