diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h
index 8e931ec28528670813bf29749eb3f205f0d84528..75a922106425fc812e6621d9feebcb3cdd9be200 100644
--- a/gtests/net/packetdrill/packet.h
+++ b/gtests/net/packetdrill/packet.h
@@ -105,11 +105,12 @@ struct packet {
 
 	s64 time_usecs;		/* wall time of receive/send if non-zero */
 
-	u32 flags;		/* various meta-flags */
-#define FLAG_WIN_NOCHECK	0x1  /* don't check TCP receive window */
-#define FLAG_OPTIONS_NOCHECK	0x2  /* don't check TCP options */
-#define FLAGS_SCTP_BAD_CRC32C   0x4  /* compute bad CRC32C for SCTP packets */
-#define FLAGS_SCTP_EXPLICIT_TAG 0x8  /* verification tag specified */
+	u32 flags;		  /* various meta-flags */
+#define FLAG_WIN_NOCHECK	  0x1  /* don't check TCP receive window */
+#define FLAG_OPTIONS_NOCHECK	  0x2  /* don't check TCP options */
+#define FLAGS_SCTP_BAD_CRC32C     0x4  /* compute bad CRC32C for SCTP packets */
+#define FLAGS_SCTP_EXPLICIT_TAG   0x8  /* verification tag specified */
+#define FLAGS_SCTP_GENERIC_PACKET 0x10 /* set if it is a generic packet */
 
 	enum ip_ecn_t ecn;	/* IPv4/IPv6 ECN treatment for packet */
 
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 7799260aaf7490f53a17d1b1746e016c33446474..8f76636f3f3e57ff7a9c0e0c04a85b627174a057 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -637,7 +637,6 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
-%type <chunk_list_item> sctp_generic_spec
 %type <chunk_list_item> sctp_generic_chunk_spec
 %type <chunk_list_item> sctp_data_chunk_spec
 %type <chunk_list_item> sctp_init_chunk_spec sctp_init_ack_chunk_spec
@@ -928,6 +927,72 @@ sctp_packet_spec
 
 	$$ = packet_encapsulate_and_free(outer, inner);
 }
+| packet_prefix opt_ip_info SCTP ':' '[' byte_list ']' {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2,
+	                        -1, false, $6, &error);
+	if (inner == NULL) {
+		assert(error != NULL);
+        semantic_error(error);
+		free(error);
+	}
+
+	$$ = packet_encapsulate_and_free(outer, inner);
+}
+| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':'  '[' byte_list ']' {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2,
+	                        -1, true, $9, &error);
+	if (inner == NULL) {
+		assert(error != NULL);
+		semantic_error(error);
+		free(error);
+	}
+
+	$$ = packet_encapsulate_and_free(outer, inner);
+}
+| packet_prefix opt_ip_info SCTP '(' TAG '=' INTEGER ')' ':' '[' byte_list ']' {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	if (!is_valid_u32($7)) {
+		semantic_error("tag value out of range");
+	}
+	inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2,
+	                        $7, false, $11, &error);
+	if (inner == NULL) {
+		assert(error != NULL);
+		semantic_error(error);
+		free(error);
+	}
+
+	$$ = packet_encapsulate_and_free(outer, inner);
+}
+| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' '[' byte_list ']' {
+	char *error = NULL;
+	struct packet *outer = $1, *inner = NULL;
+	enum direction_t direction = outer->direction;
+
+	if (!is_valid_u32($9)) {
+		semantic_error("tag value out of range");
+	}
+	inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2,
+	                        $9, true, $13, &error);
+	if (inner == NULL) {
+		assert(error != NULL);
+		semantic_error(error);
+		free(error);
+	}
+
+	$$ = packet_encapsulate_and_free(outer, inner);
+}
 ;
 
 sctp_chunk_list_spec
@@ -940,7 +1005,6 @@ sctp_chunk_list_spec
 
 sctp_chunk_spec
 : sctp_generic_chunk_spec           { $$ = $1; }
-| sctp_generic_spec                 { $$ = $1; }
 | sctp_data_chunk_spec              { $$ = $1; }
 | sctp_init_chunk_spec              { $$ = $1; }
 | sctp_init_ack_chunk_spec          { $$ = $1; }
@@ -1449,19 +1513,6 @@ dup
 }
 ;
 
-sctp_generic_spec
-: '[' byte_list ']' {
-	if ($2 == NULL) {
-		semantic_error("byte field can not be empty");
-	}
-
-	if ($2->nr_entries < sizeof(struct sctp_chunk)) {
-		semantic_error("generic sctp chunk must at least contain type, flags and length");
-	}
-
-	$$ = sctp_generic_new($2);
-}
-
 sctp_generic_chunk_spec
 : CHUNK '[' opt_chunk_type ',' opt_flags ',' opt_len ',' opt_val ']' {
 	if (($7 != -1) &&
diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c
index 8197bb60e90924350d6760a6484bf422a2ab4f1c..0f5c66fd63e569879a90944b1b46a03692f3f611 100644
--- a/gtests/net/packetdrill/sctp_packet.c
+++ b/gtests/net/packetdrill/sctp_packet.c
@@ -315,59 +315,6 @@ sctp_chunk_list_item_new(struct sctp_chunk *chunk, u32 length, u32 flags,
 	return item;
 }
 
-static void print_sctp_byte_list(struct sctp_byte_list *list) {
-	struct sctp_byte_list_item *item;
-
-	for (item = list->first; item != NULL; item = item->next) {
-		DEBUGP("0x%.2x,", item->byte);
-	}
-}
-
-struct sctp_chunk_list_item *
-sctp_generic_new(struct sctp_byte_list *bytes)
-{
-	struct sctp_chunk *chunk;
-	struct sctp_byte_list_item *item;
-	u16 length, padding_length, i;
-	u32 flags = 0;
-
-	print_sctp_byte_list(bytes);
-
-	length = bytes->nr_entries;
-
-	padding_length = length % 4;
-	if (padding_length > 0) {
-		padding_length = 4 - padding_length;
-	}
-
-	chunk = malloc(length + padding_length);
-	assert(chunk != NULL);
-
-	item = bytes->first;
-	chunk->type = item->byte;
-	item = item->next;
-	chunk->flags = item->byte;
-
-	item = item->next;
-	chunk->length = (item->byte << 8) | item->next->byte;
-	chunk->length = htons(chunk->length);
-
-	item = item->next->next;
-
-	for (i = 0; item != NULL; i++, item = item->next) {
-		chunk->value[i] = item->byte;
-	}
-
-	memset(chunk->value + (length - sizeof(struct sctp_chunk)),
-	       0, padding_length);
-
-	return sctp_chunk_list_item_new(chunk,
-	                                length + padding_length,
-	                                flags,
-	                                sctp_parameter_list_new(),
-	                                sctp_cause_list_new());
-}
-
 struct sctp_chunk_list_item *
 sctp_generic_chunk_new(s64 type, s64 flgs, s64 len,
                        struct sctp_byte_list *bytes)
@@ -2644,3 +2591,99 @@ new_sctp_packet(int address_family,
 	packet->ip_bytes = ip_bytes;
 	return packet;
 }
+
+#ifdef DEBUG_LOGGING
+static void print_sctp_byte_list(struct sctp_byte_list *list) {
+	struct sctp_byte_list_item *item;
+
+	for (item = list->first; item != NULL; item = item->next) {
+		DEBUGP("0x%.2x,", item->byte);
+	}
+}
+#endif
+
+struct packet *
+new_sctp_generic_packet(int address_family,
+                enum direction_t direction,
+                enum ip_ecn_t ecn,
+                s64 tag,
+                bool bad_crc32c,
+                struct sctp_byte_list *bytes,
+                char **error) {
+	struct packet *packet;  /* the newly-allocated result packet */
+	struct header *sctp_header;  /* the SCTP header info */
+	struct sctp_byte_list_item *item = NULL;
+	/* 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) +
+				     ip_option_bytes);
+	const int sctp_header_bytes = sizeof(struct sctp_common_header);
+	const int sctp_chunk_bytes = bytes->nr_entries;
+	const int ip_bytes =
+		 ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+	bool overbook = false;
+	u16 i;
+
+#ifdef DEBUG_LOGGING
+	print_sctp_byte_list(bytes);
+#endif
+
+	if (direction == DIRECTION_OUTBOUND) {
+		asprintf(error,
+			"generic packets can only be specified as inbound.");
+		return NULL;
+	}
+
+	/* Sanity-check all the various lengths */
+	if (ip_option_bytes & 0x3) {
+		asprintf(error, "IP options are not padded correctly "
+			 "to ensure IP header is a multiple of 4 bytes: "
+			 "%d excess bytes", ip_option_bytes & 0x3);
+		return NULL;
+	}
+	assert((ip_header_bytes & 0x3) == 0);
+
+	if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) {
+		asprintf(error, "SCTP packet too large");
+		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);
+
+	packet->direction = direction;
+	packet->flags = FLAGS_SCTP_GENERIC_PACKET;
+	if (bad_crc32c) {
+		packet->flags |= FLAGS_SCTP_BAD_CRC32C;
+	}
+	if (tag != -1) {
+		packet->flags |= FLAGS_SCTP_EXPLICIT_TAG;
+	}
+	packet->ecn = ecn;
+
+	/* Set IP header fields */
+	set_packet_ip_header(packet, address_family, ip_bytes, ecn,
+			     IPPROTO_SCTP);
+
+	sctp_header = packet_append_header(packet, HEADER_SCTP, sctp_header_bytes);
+	sctp_header->total_bytes = sctp_header_bytes + sctp_chunk_bytes;
+
+	/* Find the start of the SCTP common header of the packet */
+	packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes);
+	u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1);
+
+	/* Set SCTP header fields */
+	packet->sctp->src_port = htons(0);
+	packet->sctp->dst_port = htons(0);
+	packet->sctp->v_tag = htonl((u32)tag);
+	packet->sctp->crc32c = htonl(0);
+
+	for (i = 0, item = bytes->first; item != NULL; i++, item = item->next) {
+		sctp_chunk_start[i] = item->byte;
+	}
+
+	packet->chunk_list = sctp_chunk_list_new();
+	packet->ip_bytes = ip_bytes;
+	return packet;
+}
\ No newline at end of file
diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h
index 0c7e57c4d7f60bec2760afdc968b138baae56495..a90d0a1581a5f907ceac519566943bd9eb5b876f 100644
--- a/gtests/net/packetdrill/sctp_packet.h
+++ b/gtests/net/packetdrill/sctp_packet.h
@@ -433,4 +433,13 @@ extern struct packet *new_sctp_packet(int address_family,
 				      bool bad_crc32c,
 				      struct sctp_chunk_list *chunk_list,
 				      char **error);
+
+struct packet *
+new_sctp_generic_packet(int address_family,
+                enum direction_t direction,
+                enum ip_ecn_t ecn,
+                s64 tag,
+                bool bad_crc32c,
+                struct sctp_byte_list *bytes,
+                char **error);
 #endif /* __SCTP_PACKET_H__ */