From 9826f4cabdb36794264e6f888345f895795df82b Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Sat, 23 May 2015 13:46:05 +0200
Subject: [PATCH] Add support for SCTP chunk parameters

This patch adds support for the SCTP parameters defined in RFC 4960.
While there, make the syntax more consistent and make sure that
the packet printing is in line with the syntax being used.
This fixes https://github.com/nplab/packetdrill/issues/6
---
 gtests/net/packetdrill/lexer.l                |  17 +
 gtests/net/packetdrill/packet.c               |  35 +-
 .../net/packetdrill/packet_to_string_test.c   | 218 ++---
 gtests/net/packetdrill/parser.y               | 385 +++++++--
 gtests/net/packetdrill/run_packet.c           |  88 +-
 gtests/net/packetdrill/sctp_chunk_to_string.c | 222 ++---
 gtests/net/packetdrill/sctp_packet.c          | 775 +++++++++++++++---
 gtests/net/packetdrill/sctp_packet.h          | 117 ++-
 .../tests/bsd/sctp/sctp_active.pkt            |  41 +-
 .../tests/bsd/sctp/sctp_handle_init.pkt       |   4 +-
 .../packetdrill/tests/bsd/sctp/sctp_hb.pkt    |  22 +-
 .../tests/bsd/sctp/sctp_icmp_active.pkt       |  20 +-
 .../tests/bsd/sctp/sctp_icmp_passive.pkt      |  20 +-
 .../tests/bsd/sctp/sctp_init_rtx.pkt          |  28 +-
 .../tests/bsd/sctp/sctp_passive.pkt           |  33 +-
 15 files changed, 1538 insertions(+), 487 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 36fa98fd..d5fb23b8 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 19b9e6a7..97331617 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 0e0f0788..b81befab 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 ec044345..2e1d93f1 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 331903c5..3eea2703 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 842978bf..c365e7c3 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 21cbcfc8..886a921c 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 6ac35603..eacbe555 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 64a442fc..87c7266d 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 eafcaf51..389c938f 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 0869034e..d7278191 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 c02a9e08..5aa6a0ee 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 eb801515..82460960 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 2254235e..64b99253 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 3ca6b026..e8b517f5 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
-- 
GitLab