diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c
index 73a890cacf76d592238e199bce08fe8ffcb661e5..36c0b9fc76ac6de1306b981fe9c8f17f2a3b794a 100644
--- a/gtests/net/packetdrill/icmp_packet.c
+++ b/gtests/net/packetdrill/icmp_packet.c
@@ -283,6 +283,7 @@ struct packet *new_icmp_packet(int address_family,
 				u16 payload_bytes,
 				u32 tcp_start_sequence,
 				u16 udplite_checksum_coverage,
+				u32 sctp_verification_tag,
 				s64 mtu,
 				char **error)
 {
@@ -352,6 +353,10 @@ struct packet *new_icmp_packet(int address_family,
 				     payload_bytes);
 	set_ip_header(echoed_ip, address_family, echoed_ip_bytes,
 		      ecn, protocol);
+	if (protocol == IPPROTO_SCTP) {
+		u32 *v_tag = packet_echoed_sctp_v_tag(packet);
+		*v_tag = htonl(sctp_verification_tag);
+	}
 	if (protocol == IPPROTO_TCP) {
 		u32 *seq = packet_echoed_tcp_seq(packet);
 		*seq = htonl(tcp_start_sequence);
diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h
index ba3db691bb23668257c3c9493131aa3faeec0f38..609d3a66dda3e72e9cfae83f332067b4dd1f3ce6 100644
--- a/gtests/net/packetdrill/icmp_packet.h
+++ b/gtests/net/packetdrill/icmp_packet.h
@@ -48,6 +48,7 @@ extern struct packet *new_icmp_packet(int address_family,
 				      u16 payload_bytes,
 				      u32 tcp_start_sequence,
 				      u16 udplite_checksum_coverage,
+				      u32 sctp_verification_tag,
 				      s64 mtu,
 				      char **error);
 
diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h
index dfedcd8d5c9b9c0b1121b31810c242fe90232588..6348cc4ea24d69b6b8f999b6441068b031d4e754 100644
--- a/gtests/net/packetdrill/packet.h
+++ b/gtests/net/packetdrill/packet.h
@@ -394,6 +394,19 @@ packet_echoed_sctp_header(struct packet *packet)
 	return NULL;
 }
 
+/* Return the location of the SCTP verification tag echoed by an ICMP message. */
+static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet)
+{
+	struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet);
+	assert(echoed_sctp);
+	u32 *v_tag = &(echoed_sctp->v_tag);
+	/* Check that the v_tag field is actually in the space we
+	 * reserved for the echoed prefix of the SCTP common header.
+	 */
+	assert((char *) (v_tag + 1) <= (char *) echoed_sctp + ICMP_ECHO_BYTES);
+	return v_tag;
+}
+
 /* Return the location of the TCP header echoed by an ICMP message. */
 static inline struct tcp *packet_echoed_tcp_header(struct packet *packet)
 {
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index f3f8f32826c3a0cd861bb8ea0ac01c73bbe76760..ec0443458324c848d317c5427193d515f8f2fd11 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -457,6 +457,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	struct {
 		int protocol;
 		u16 payload_bytes;
+		u32 verification_tag;	/* used for SCTP */
 		u32 start_sequence;	/* used for TCP */
 		u16 checksum_coverage;	/* used for UDPLite */
 	} transport_info;
@@ -1174,7 +1175,7 @@ icmp_packet_spec
 	inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5,
 				$2.protocol, $2.payload_bytes,
 				$2.start_sequence, $2.checksum_coverage,
-				$6, &error);
+				$2.verification_tag, $6, &error);
 	free($4);
 	free($5);
 	if (inner == NULL) {
@@ -1287,6 +1288,11 @@ opt_icmp_echoed
 	$$.payload_bytes	= 0;
 	$$.start_sequence	= 0;
 }
+| '[' SCTP '(' INTEGER ')' ']'	{
+	$$.protocol		= IPPROTO_SCTP;
+	$$.payload_bytes	= 0;
+	$$.verification_tag	= $4;
+}
 | '[' UDP '(' INTEGER ')' ']'	{
 	$$.protocol		= IPPROTO_UDP;
 	$$.payload_bytes	= $4;
diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c
index 27b3e66fab658553b9de466f5bb50a8d06b6aa28..4f5e0b6a1f0feebf87df0d3064f51678114af37d 100644
--- a/gtests/net/packetdrill/run_packet.c
+++ b/gtests/net/packetdrill/run_packet.c
@@ -517,6 +517,13 @@ static int offset_sack_blocks(struct packet *packet,
 	return *error ? STATUS_ERR : STATUS_OK;
 }
 
+static int map_inbound_icmp_sctp_packet(
+	struct socket *socket, struct packet *live_packet, char **error)
+{
+	u32 *v_tag = packet_echoed_sctp_v_tag(live_packet);
+	*v_tag = htonl(socket->live.remote_initiate_tag);
+	return STATUS_OK;
+}
 
 /* Rewrite the TCP sequence number echoed by the ICMP packet.
  * The Linux TCP layer ignores ICMP messages with bogus sequence numbers.
@@ -548,7 +555,9 @@ static int map_inbound_icmp_udplite_packet(
 static int map_inbound_icmp_packet(
 	struct socket *socket, struct packet *live_packet, char **error)
 {
-	if (packet_echoed_ip_protocol(live_packet) == IPPROTO_TCP)
+	if (packet_echoed_ip_protocol(live_packet) == IPPROTO_SCTP)
+		return map_inbound_icmp_sctp_packet(socket, live_packet, error);
+	else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_TCP)
 		return map_inbound_icmp_tcp_packet(socket, live_packet, error);
 	else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDP)
 		return map_inbound_icmp_udp_packet(socket, live_packet, error);
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt
new file mode 100644
index 0000000000000000000000000000000000000000..c02a9e0898471f1f5a204caf750c01f2d5f31fa8
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt
@@ -0,0 +1,23 @@
+// Create a non-blocking socket.
++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3
++0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
++0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+// Check the handshake with an empty(!) cookie.
++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
++0.0 > sctp: INIT[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 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=[]]
+// 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[]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt
new file mode 100644
index 0000000000000000000000000000000000000000..9daa9c7fa09bfeb9dc8531c66f90ee9fa4994935
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt
@@ -0,0 +1,22 @@
++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=4500 os=1 is=1 tsn=0]
++0.0 > sctp: INIT_ACK[tag=2 tsn=0] // faked cookie
++0.1 < sctp: COOKIE_ECHO[]
++0.0 > sctp: COOKIE_ACK[]
++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=[]]
+// Tear down the association
++0.0 close(4) = 0
++0.0 > sctp: SHUTDOWN[tsn=2]
++0.1 < sctp: SHUTDOWN_ACK[]
++0.0 > sctp: SHUTDOWN_COMPLETE[]