diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common
index 6490f46f20c496dfb9a22d253b72beb576ec949d..06a865dbbd8a082002323ad88b67c0fa2b69e05b 100644
--- a/gtests/net/packetdrill/Makefile.common
+++ b/gtests/net/packetdrill/Makefile.common
@@ -1,6 +1,6 @@
 all: binaries
 
-CFLAGS = -g -Wall -Werror
+CFLAGS = -g -Wall -Werror -Wno-unknown-warning-option -Wno-address-of-packed-member
 
 parser.o: parser.y
 	bison --output=parser.c --defines=parser.h --report=state parser.y
@@ -8,7 +8,7 @@ parser.o: parser.y
 
 lexer.o: lexer.l parser.o
 	flex -olexer.c lexer.l
-	$(CC) -O2 -g -Wall -c lexer.c
+	$(CC) -O2 -g -Wall -Wno-unknown-warning-option -Wno-address-of-packed-member -c lexer.c
 
 packetdrill-lib := \
          checksum.o code.o config.o hash.o hash_map.o ip_address.o ip_prefix.o \
diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c
index 4df80329ee7e42e56ffdf7679be6c79c1ba86cfe..e79061c40be49f10ca9228845a027540f77b21dc 100644
--- a/gtests/net/packetdrill/config.c
+++ b/gtests/net/packetdrill/config.c
@@ -60,6 +60,7 @@ enum option_codes {
 	OPT_DRY_RUN,
 	OPT_VERBOSE = 'v',	/* our only single-letter option */
 	OPT_DEBUG,
+	OPT_UDP_ENCAPS,
 };
 
 /* Specification of command line options for getopt_long(). */
@@ -89,6 +90,7 @@ struct option options[] = {
 	{ "dry_run",		.has_arg = false, NULL, OPT_DRY_RUN },
 	{ "verbose",		.has_arg = false, NULL, OPT_VERBOSE },
 	{ "debug",		.has_arg = false, NULL, OPT_DEBUG },
+	{ "udp_encapsulation",	.has_arg = true,  NULL, OPT_UDP_ENCAPS },
 	{ NULL },
 };
 
@@ -120,6 +122,7 @@ void show_usage(void)
 		"\t[--dry_run]\n"
 		"\t[--verbose|-v]\n"
 		"\t[--debug] * requires compilation with DEBUG *\n"
+		"\t[--udp_encapsulation=[sctp,tcp]]\n"
 		"\tscript_path ...\n");
 }
 
@@ -365,8 +368,7 @@ static void process_option(int opt, char *optarg, struct config *config,
 	char *end = NULL;
 	unsigned long speed = 0;
 
-	DEBUGP("process_option %d ('%c') = %s\n",
-	       opt, (char)opt, optarg);
+	DEBUGP("process_option %d = %s\n", opt, optarg);
 
 	switch (opt) {
 	case OPT_IP_VERSION:
@@ -475,6 +477,14 @@ static void process_option(int opt, char *optarg, struct config *config,
 #endif
 		debug_logging = true;
 		break;
+	case OPT_UDP_ENCAPS:
+		if (strcmp(optarg, "sctp") == 0)
+			config->udp_encaps = IPPROTO_SCTP;
+		else if (strcmp(optarg, "tcp") == 0)
+			config->udp_encaps = IPPROTO_TCP;
+		else
+			die("%s: bad --udp_encapsulation: %s\n", where, optarg);
+		break;
 	default:
 		show_usage();
 		exit(EXIT_FAILURE);
diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h
index 55190ce1167cc988b974f9e3ba9dca5d6f8412cc..d789826123469ef781d328898de7c2dcafbdbfc6 100644
--- a/gtests/net/packetdrill/config.h
+++ b/gtests/net/packetdrill/config.h
@@ -36,7 +36,7 @@
 #include "script.h"
 
 #define TUN_DRIVER_SPEED_CUR	0	/* don't change current speed */
-#define TUN_DRIVER_DEFAULT_MTU 1500	/* default MTU for tun device */
+#define TUN_DRIVER_DEFAULT_MTU	1500	/* default MTU for tun device */
 
 extern struct option options[];
 
@@ -81,6 +81,9 @@ struct config {
 	bool dry_run;			/* parse script but don't execute? */
 
 	bool verbose;			/* print detailed debug info? */
+
+	u8 udp_encaps;			/* Protocol encapsulated in UDP */
+
 	char *script_path;		/* pathname of script file */
 
 	/* Shell command to invoke via system(3) to run post-processing code */
diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c
index 82533e3cfe7ed0bba3f7a9ebea473cef2eb1a130..89d60eb3676b9c82aae9887300607c3128ce314d 100644
--- a/gtests/net/packetdrill/icmp_packet.c
+++ b/gtests/net/packetdrill/icmp_packet.c
@@ -291,6 +291,8 @@ struct packet *new_icmp_packet(int address_family,
 				u16 udplite_checksum_coverage,
 				u32 sctp_verification_tag,
 				s64 mtu,
+				u16 udp_src_port,
+				u16 udp_dst_port,
 				char **error)
 {
 	s32 type = -1;	/* bad type; means "unknown so far" */
@@ -303,10 +305,13 @@ struct packet *new_icmp_packet(int address_family,
 	 * header and the first 8 bytes after that (which will
 	 * typically have the port info needed to demux the message).
 	 */
+	const bool encapsulated = ((protocol != IPPROTO_UDP) &&
+				   ((udp_src_port > 0) || (udp_dst_port > 0)));
 	const int ip_fixed_bytes = ip_header_min_len(address_family);
 	const int ip_option_bytes = 0;
 	const int ip_header_bytes = ip_fixed_bytes + ip_option_bytes;
-	const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES;
+	const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES +
+				 (encapsulated ? sizeof(struct udp) : 0);
 	const int icmp_bytes = icmp_header_len(address_family) + echoed_bytes;
 	const int ip_bytes = ip_header_bytes + icmp_bytes;
 
@@ -355,16 +360,30 @@ struct packet *new_icmp_packet(int address_family,
 	 */
 	u8 *echoed_ip = packet_echoed_ip_header(packet);
 	const int echoed_ip_bytes = (ip_fixed_bytes +
+				     (encapsulated ? sizeof(struct udp) : 0) +
 				     layer4_header_len(protocol) +
 				     payload_bytes);
-	set_ip_header(echoed_ip, address_family, echoed_ip_bytes,
-		      ecn, protocol);
+	if (encapsulated) {
+		struct udp *udp;
+
+		set_ip_header(echoed_ip, address_family, echoed_ip_bytes,
+			      ecn, IPPROTO_UDP);
+		udp = (struct udp *)(echoed_ip + ip_fixed_bytes);
+		udp->src_port = htons(udp_src_port);
+		udp->dst_port = htons(udp_dst_port);
+		udp->len = htons(sizeof(struct udp) +
+				 layer4_header_len(protocol) +
+				 payload_bytes);
+	} else {
+		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);
+		u32 *v_tag = packet_echoed_sctp_v_tag(packet, encapsulated);
 		*v_tag = htonl(sctp_verification_tag);
 	}
 	if (protocol == IPPROTO_TCP) {
-		u32 *seq = packet_echoed_tcp_seq(packet);
+		u32 *seq = packet_echoed_tcp_seq(packet, encapsulated);
 		*seq = htonl(tcp_start_sequence);
 	}
 	if (protocol == IPPROTO_UDP) {
diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h
index 609d3a66dda3e72e9cfae83f332067b4dd1f3ce6..6f20fc906ba8adcd28c0cfd36f1d89fb35061418 100644
--- a/gtests/net/packetdrill/icmp_packet.h
+++ b/gtests/net/packetdrill/icmp_packet.h
@@ -50,6 +50,8 @@ extern struct packet *new_icmp_packet(int address_family,
 				      u16 udplite_checksum_coverage,
 				      u32 sctp_verification_tag,
 				      s64 mtu,
+				      u16 udp_src_port,
+				      u16 udp_dst_port,
 				      char **error);
 
 #endif /* __ICMP_PACKET_H__ */
diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 372ebd4835d51b727073e2c6320cee387468abed..0b696791da302884e446ee78b4c5a039648f817e 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -288,10 +288,10 @@ msg_name			return MSG_NAME;
 msg_iov				return MSG_IOV;
 msg_control			return MSG_CONTROL;
 msg_flags			return MSG_FLAGS;
-cmsg_len                        return CMSG_LEN;
-cmsg_level                      return CMSG_LEVEL;
-cmsg_type                       return CMSG_TYPE;
-cmsg_data                       return CMSG_DATA;
+cmsg_len			return CMSG_LEN;
+cmsg_level			return CMSG_LEVEL;
+cmsg_type			return CMSG_TYPE;
+cmsg_data			return CMSG_DATA;
 fd				return FD;
 events				return EVENTS;
 revents				return REVENTS;
@@ -334,6 +334,8 @@ iov_len				return IOV_LEN;
 headers				return SF_HDTR_HEADERS;
 trailers			return SF_HDTR_TRAILERS;
 [.][.][.]			return ELLIPSIS;
+function_set_name		return FUNCTION_SET_NAME;
+pcbcnt				return PCBCNT;
 assoc_id			return ASSOC_ID;
 assoc_value			return ASSOC_VALUE;
 shmac_number_of_idents		return SHMAC_NUMBER_OF_IDENTS;
@@ -359,7 +361,7 @@ sinit_num_ostreams		return SINIT_NUM_OSTREAMS;
 sinit_max_instreams		return SINIT_MAX_INSTREAMS;
 sinit_max_attempts		return SINIT_MAX_ATTEMPTS;
 sinit_max_init_timeo		return SINIT_MAX_INIT_TIMEO;
-sstat_assoc_id                  return SSTAT_ASSOC_ID;
+sstat_assoc_id			return SSTAT_ASSOC_ID;
 sstat_state			return SSTAT_STATE;
 sstat_rwnd			return SSTAT_RWND;
 sstat_unackdata			return SSTAT_UNACKDATA;
@@ -386,21 +388,21 @@ spp_dscp			return SPP_DSCP_; /* avoid name clash */
 se_assoc_id			return SE_ASSOC_ID;
 se_type				return SE_TYPE;
 se_on				return SE_ON;
-sctp_data_io_event              return _SCTP_DATA_IO_EVENT_;
-sctp_association_event          return _SCTP_ASSOCIATION_EVENT_;
-sctp_address_event              return _SCTP_ADDRESS_EVENT_;
-sctp_send_failure_event         return _SCTP_SEND_FAILURE_EVENT_;
-sctp_peer_error_event           return _SCTP_PEER_ERROR_EVENT_;
-sctp_shutdown_event             return _SCTP_SHUTDOWN_EVENT_;
-sctp_partial_delivery_event     return _SCTP_PARTIAL_DELIVERY_EVENT_;
-sctp_adaptation_layer_event     return _SCTP_ADAPTATION_LAYER_EVENT_;
-sctp_authentication_event       return _SCTP_AUTHENTICATION_EVENT_;
-sctp_sender_dry_event           return _SCTP_SENDER_DRY_EVENT_;
+sctp_data_io_event		return _SCTP_DATA_IO_EVENT_;
+sctp_association_event		return _SCTP_ASSOCIATION_EVENT_;
+sctp_address_event		return _SCTP_ADDRESS_EVENT_;
+sctp_send_failure_event		return _SCTP_SEND_FAILURE_EVENT_;
+sctp_peer_error_event		return _SCTP_PEER_ERROR_EVENT_;
+sctp_shutdown_event		return _SCTP_SHUTDOWN_EVENT_;
+sctp_partial_delivery_event	return _SCTP_PARTIAL_DELIVERY_EVENT_;
+sctp_adaptation_layer_event	return _SCTP_ADAPTATION_LAYER_EVENT_;
+sctp_authentication_event	return _SCTP_AUTHENTICATION_EVENT_;
+sctp_sender_dry_event		return _SCTP_SENDER_DRY_EVENT_;
 snd_sid				return SND_SID;
 snd_flags			return SND_FLAGS;
 snd_ppid			return SND_PPID;
 snd_context			return SND_CONTEXT;
-snd_assoc_id                    return SND_ASSOC_ID;
+snd_assoc_id			return SND_ASSOC_ID;
 ssp_assoc_id			return SSP_ASSOC_ID;
 ssp_addr			return SSP_ADDR;
 ssb_adaptation_ind		return SSB_ADAPTATION_IND;
@@ -413,100 +415,100 @@ sinfo_timetolive		return SINFO_TIMETOLIVE;
 sinfo_tsn			return SINFO_TSN;
 sinfo_cumtsn			return SINFO_CUMTSN;
 sinfo_assoc_id			return SINFO_ASSOC_ID;
-sinfo_pr_value                  return SINFO_PR_VALUE;
-serinfo_next_flags              return SERINFO_NEXT_FLAGS;
-serinfo_next_stream             return SERINFO_NEXT_STREAM;
-serinfo_next_aid                return SERINFO_NEXT_AID;
-serinfo_next_length             return SERINFO_NEXT_LENGTH;
+sinfo_pr_value			return SINFO_PR_VALUE;
+serinfo_next_flags		return SERINFO_NEXT_FLAGS;
+serinfo_next_stream		return SERINFO_NEXT_STREAM;
+serinfo_next_aid		return SERINFO_NEXT_AID;
+serinfo_next_length		return SERINFO_NEXT_LENGTH;
 serinfo_next_ppid		return SERINFO_NEXT_PPID;
 pr_policy			return PR_POLICY;
 pr_value			return PR_VALUE;
-pr_assoc_id                     return PR_ASSOC_ID;
+pr_assoc_id			return PR_ASSOC_ID;
 sendv_flags			return SENDV_FLAGS;
 sendv_sndinfo			return SENDV_SNDINFO;
 sendv_prinfo			return SENDV_PRINFO;
 sendv_authinfo			return SENDV_AUTHINFO;
-ssfe_type                       return SSFE_TYPE;
-ssfe_flags                      return SSFE_FLAGS;
-ssfe_length                     return SSFE_LENGTH;
-ssfe_error                      return SSFE_ERROR;
-ssfe_info                       return SSFE_INFO;
-ssfe_assoc_id                   return SSFE_ASSOC_ID;
-ssfe_data                       return SSFE_DATA;
-rcv_sid                         return RCV_SID;
-rcv_ssn                         return RCV_SSN;
-rcv_flags                       return RCV_FLAGS;
-rcv_ppid                        return RCV_PPID;
-rcv_tsn                         return RCV_TSN;
-rcv_cumtsn                      return RCV_CUMTSN;
-rcv_context                     return RCV_CONTEXT;
-rcv_assoc_id                    return RCV_ASSOC_ID;
-nxt_sid                         return NXT_SID;
-nxt_flags                       return NXT_FLAGS;
-nxt_ppid                        return NXT_PPID;
-nxt_length                      return NXT_LENGTH;
-nxt_assoc_id                    return NXT_ASSOC_ID;
-recvv_rcvinfo                   return RECVV_RCVINFO;
-recvv_nxtinfo                   return RECVV_NXTINFO;
-sse_type                        return SSE_TYPE;
-sse_flags                       return SSE_FLAGS;
-sse_length                      return SSE_LENGTH;
-sse_assoc_id                    return SSE_ASSOC_ID;
-sac_type                        return SAC_TYPE;
-sac_flags                       return SAC_FLAGS;
-sac_length                      return SAC_LENGTH;
-sac_state                       return SAC_STATE;
-sac_error                       return SAC_ERROR;
-sac_outbound_streams            return SAC_OUTBOUND_STREAMS;
-sac_inbound_streams             return SAC_INBOUND_STREAMS;
-sac_assoc_id                    return SAC_ASSOC_ID;
-sac_info                        return SAC_INFO;
-sre_type                        return SRE_TYPE;
-sre_flags                       return SRE_FLAGS;
-sre_length                      return SRE_LENGTH;
-sre_error                       return SRE_ERROR;
-sre_assoc_id                    return SRE_ASSOC_ID;
-sre_data                        return SRE_DATA;
-auth_type                       return AUTH_TYPE;
-auth_flags                      return AUTH_FLAGS;
-auth_length                     return AUTH_LENGTH;
-auth_keynumber                  return AUTH_KEYNUMBER;
-auth_indication                 return AUTH_INDICATION;
-auth_assoc_id                   return AUTH_ASSOC_ID;
-sender_dry_type                 return SENDER_DRY_TYPE;
-sender_dry_flags                return SENDER_DRY_FLAGS;
-sender_dry_length               return SENDER_DRY_LENGTH;
-sender_dry_assoc_id             return SENDER_DRY_ASSOC_ID;
-pdapi_type                      return PDAPI_TYPE;
-pdapi_flags                     return PDAPI_FLAGS;
-pdapi_length                    return PDAPI_LENGTH;
-pdapi_indication                return PDAPI_INDICATION;
-pdapi_stream                    return PDAPI_STREAM;
-pdapi_seq                       return PDAPI_SEQ;
-pdapi_assoc_id                  return PDAPI_ASSOC_ID;
-spc_type                        return SPC_TYPE;
-spc_length                      return SPC_LENGTH;
-spc_flags                       return SPC_FLAGS;
-spc_aaddr                       return SPC_AADDR;
-spc_state                       return SPC_STATE;
-spc_error                       return SPC_ERROR;
-spc_assoc_id                    return SPC_ASSOC_ID;
-ssf_type                        return SSF_TYPE;
-ssf_length                      return SSF_LENGTH;
-ssf_flags                       return SSF_FLAGS;
-ssf_error                       return SSF_ERROR;
-ssf_info                        return SSF_INFO;
-ssf_assoc_id                    return SSF_ASSOC_ID;
-ssf_data                        return SSF_DATA;
-sai_type                        return SAI_TYPE;
-sai_length                      return SAI_LENGTH;
-sai_flags                       return SAI_FLAGS;
-sai_adaptation_ind              return SAI_ADAPTATION_IND;
-sai_assoc_id                    return SAI_ASSOC_ID;
-sn_type                         return SN_TYPE;
-sn_length                       return SN_LENGTH;
-sn_flags                        return SN_FLAGS;
-gaids_number_of_ids             return GAIDS_NUMBER_OF_IDS;
+ssfe_type			return SSFE_TYPE;
+ssfe_flags			return SSFE_FLAGS;
+ssfe_length			return SSFE_LENGTH;
+ssfe_error			return SSFE_ERROR;
+ssfe_info			return SSFE_INFO;
+ssfe_assoc_id			return SSFE_ASSOC_ID;
+ssfe_data			return SSFE_DATA;
+rcv_sid				return RCV_SID;
+rcv_ssn				return RCV_SSN;
+rcv_flags			return RCV_FLAGS;
+rcv_ppid			return RCV_PPID;
+rcv_tsn				return RCV_TSN;
+rcv_cumtsn			return RCV_CUMTSN;
+rcv_context			return RCV_CONTEXT;
+rcv_assoc_id			return RCV_ASSOC_ID;
+nxt_sid				return NXT_SID;
+nxt_flags			return NXT_FLAGS;
+nxt_ppid			return NXT_PPID;
+nxt_length			return NXT_LENGTH;
+nxt_assoc_id			return NXT_ASSOC_ID;
+recvv_rcvinfo			return RECVV_RCVINFO;
+recvv_nxtinfo			return RECVV_NXTINFO;
+sse_type			return SSE_TYPE;
+sse_flags			return SSE_FLAGS;
+sse_length			return SSE_LENGTH;
+sse_assoc_id			return SSE_ASSOC_ID;
+sac_type			return SAC_TYPE;
+sac_flags			return SAC_FLAGS;
+sac_length			return SAC_LENGTH;
+sac_state			return SAC_STATE;
+sac_error			return SAC_ERROR;
+sac_outbound_streams		return SAC_OUTBOUND_STREAMS;
+sac_inbound_streams		return SAC_INBOUND_STREAMS;
+sac_assoc_id			return SAC_ASSOC_ID;
+sac_info			return SAC_INFO;
+sre_type			return SRE_TYPE;
+sre_flags			return SRE_FLAGS;
+sre_length			return SRE_LENGTH;
+sre_error			return SRE_ERROR;
+sre_assoc_id			return SRE_ASSOC_ID;
+sre_data			return SRE_DATA;
+auth_type			return AUTH_TYPE;
+auth_flags			return AUTH_FLAGS;
+auth_length			return AUTH_LENGTH;
+auth_keynumber			return AUTH_KEYNUMBER;
+auth_indication			return AUTH_INDICATION;
+auth_assoc_id			return AUTH_ASSOC_ID;
+sender_dry_type			return SENDER_DRY_TYPE;
+sender_dry_flags		return SENDER_DRY_FLAGS;
+sender_dry_length		return SENDER_DRY_LENGTH;
+sender_dry_assoc_id		return SENDER_DRY_ASSOC_ID;
+pdapi_type			return PDAPI_TYPE;
+pdapi_flags			return PDAPI_FLAGS;
+pdapi_length			return PDAPI_LENGTH;
+pdapi_indication		return PDAPI_INDICATION;
+pdapi_stream			return PDAPI_STREAM;
+pdapi_seq			return PDAPI_SEQ;
+pdapi_assoc_id			return PDAPI_ASSOC_ID;
+spc_type			return SPC_TYPE;
+spc_length			return SPC_LENGTH;
+spc_flags			return SPC_FLAGS;
+spc_aaddr			return SPC_AADDR;
+spc_state			return SPC_STATE;
+spc_error			return SPC_ERROR;
+spc_assoc_id			return SPC_ASSOC_ID;
+ssf_type			return SSF_TYPE;
+ssf_length			return SSF_LENGTH;
+ssf_flags			return SSF_FLAGS;
+ssf_error			return SSF_ERROR;
+ssf_info			return SSF_INFO;
+ssf_assoc_id			return SSF_ASSOC_ID;
+ssf_data			return SSF_DATA;
+sai_type			return SAI_TYPE;
+sai_length			return SAI_LENGTH;
+sai_flags			return SAI_FLAGS;
+sai_adaptation_ind		return SAI_ADAPTATION_IND;
+sai_assoc_id			return SAI_ASSOC_ID;
+sn_type				return SN_TYPE;
+sn_length			return SN_LENGTH;
+sn_flags			return SN_FLAGS;
+gaids_number_of_ids		return GAIDS_NUMBER_OF_IDS;
 gaids_assoc_id			return GAIDS_ASSOC_ID;
 gauth_assoc_id			return GAUTH_ASSOC_ID;
 gauth_number_of_chunks		return GAUTH_NUMBER_OF_CHUNKS;
@@ -542,6 +544,9 @@ strchange_length		return STRCHANGE_LENGTH;
 strchange_assoc_id		return STRCHANGE_ASSOC_ID;
 strchange_instrms		return STRCHANGE_INSTRMS;
 strchange_outstrms		return STRCHANGE_OUTSTRMS;
+sue_assoc_id			return SUE_ASSOC_ID;
+sue_address			return SUE_ADDRESS;
+sue_port			return SUE_PORT;
 CHUNK				return CHUNK;
 DATA				return DATA;
 INIT				return INIT;
@@ -562,8 +567,8 @@ SHUTDOWN_COMPLETE		return SHUTDOWN_COMPLETE;
 I_DATA				return I_DATA;
 PAD				return PAD;
 RECONFIG			return RECONFIG;
-FORWARD_TSN                     return FORWARD_TSN;
-I_FORWARD_TSN                   return I_FORWARD_TSN;
+FORWARD_TSN			return FORWARD_TSN;
+I_FORWARD_TSN			return I_FORWARD_TSN;
 type				return TYPE;
 flgs				return FLAGS;
 len				return LEN;
@@ -609,7 +614,7 @@ HOSTNAME_ADDRESS		return HOSTNAME_ADDRESS;
 SUPPORTED_ADDRESS_TYPES		return SUPPORTED_ADDRESS_TYPES;
 ADAPTATION_INDICATION		return ADAPTATION_INDICATION;
 ECN_CAPABLE			return ECN_CAPABLE;
-FORWARD_TSN_SUPPORTED           return FORWARD_TSN_SUPPORTED;
+FORWARD_TSN_SUPPORTED		return FORWARD_TSN_SUPPORTED;
 SUPPORTED_EXTENSIONS		return SUPPORTED_EXTENSIONS;
 addr				return ADDR;
 incr				return INCR;
diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c
index d4ac7c8068b639a4cb311a97e82154823229eba7..a8cb5175a554e0d0e2858b43d9e3c4744a28cd66 100644
--- a/gtests/net/packetdrill/netdev.c
+++ b/gtests/net/packetdrill/netdev.c
@@ -84,13 +84,19 @@ static void cleanup_old_device(struct config *config,
 {
 #if defined(__NetBSD__)
 	char *cleanup_command = NULL;
+#ifdef DEBUG
 	int result;
+#endif
 
 	asprintf(&cleanup_command,
 		 "/sbin/ifconfig %s down delete > /dev/null 2>&1",
 		 TUN_DEV);
 	DEBUGP("running: '%s'\n", cleanup_command);
+#ifdef DEBUG
 	result = system(cleanup_command);
+#else
+	system(cleanup_command);
+#endif
 	DEBUGP("result: %d\n", result);
 	free(cleanup_command);
 #endif  /* defined(__NetBSD__) */
@@ -112,10 +118,20 @@ static void check_remote_address(struct config *config,
 static void create_device(struct config *config, struct local_netdev *netdev)
 {
 	/* Open the tun device, which "clones" it for our purposes. */
-	int tun_fd = open(TUN_PATH, O_RDWR);
-	if (tun_fd < 0)
-		die_perror("open tun device");
+	int tun_fd;
 
+	tun_fd = open(TUN_PATH, O_RDWR);
+#if defined(__FreeBSD__)
+	if ((tun_fd < 0) && (errno == ENOENT)) {
+		if (system("kldload -q if_tun") < 0) {
+			die_perror("kldload -q if_tun");
+		}
+		tun_fd = open(TUN_PATH, O_RDWR);
+	}
+#endif
+	if (tun_fd < 0) {
+		die_perror("open tun device");
+	}
 	netdev->tun_fd = tun_fd;
 
 #ifdef linux
@@ -405,10 +421,10 @@ static void local_netdev_read_queue(struct local_netdev *netdev,
 			else
 				die_perror("tun read()");
 		}
-       }
+	}
 }
 
-static int local_netdev_receive(struct netdev *a_netdev,
+static int local_netdev_receive(struct netdev *a_netdev, u8 udp_encaps,
 				struct packet **packet, char **error)
 {
 	struct local_netdev *netdev = to_local_netdev(a_netdev);
@@ -417,14 +433,15 @@ static int local_netdev_receive(struct netdev *a_netdev,
 
 	DEBUGP("local_netdev_receive\n");
 
-	status = netdev_receive_loop(netdev->psock, DIRECTION_OUTBOUND, packet,
-				     &num_packets, error);
+	status = netdev_receive_loop(netdev->psock, DIRECTION_OUTBOUND,
+				     udp_encaps,packet, &num_packets, error);
 	local_netdev_read_queue(netdev, num_packets);
 	return status;
 }
 
 int netdev_receive_loop(struct packet_socket *psock,
 			enum direction_t direction,
+			u8 udp_encaps,
 			struct packet **packet,
 			int *num_packets,
 			char **error)
@@ -446,7 +463,8 @@ int netdev_receive_loop(struct packet_socket *psock,
 			continue;
 
 		++*num_packets;
-		result = parse_packet(*packet, in_bytes, ether_type, error);
+		result = parse_packet(*packet, in_bytes, ether_type, udp_encaps,
+				      error);
 
 		if (result == PACKET_OK)
 			return STATUS_OK;
diff --git a/gtests/net/packetdrill/netdev.h b/gtests/net/packetdrill/netdev.h
index 115c3453edff6e002323ea824fd4ed891bcbad5b..3e479678e9ecd2d75c3feb245ef5e5e1d0e7a380 100644
--- a/gtests/net/packetdrill/netdev.h
+++ b/gtests/net/packetdrill/netdev.h
@@ -52,7 +52,7 @@ struct netdev_ops {
 	 * pointer to the newly-allocated packet. Caller must free the packet
 	 * with packet_free().
 	 */
-	int (*receive)(struct netdev *netdev,
+	int (*receive)(struct netdev *netdev, u8 udp_encaps,
 		       struct packet **packet, char **error);
 };
 
@@ -75,10 +75,11 @@ static inline int netdev_send(struct netdev *netdev,
  * with packet_free().
  */
 static inline int netdev_receive(struct netdev *netdev,
+				 u8 udp_encaps,
 				 struct packet **packet,
 				 char **error)
 {
-	return netdev->ops->receive(netdev, packet, error);
+	return netdev->ops->receive(netdev, udp_encaps, packet, error);
 }
 
 
@@ -88,6 +89,7 @@ static inline int netdev_receive(struct netdev *netdev,
  */
 extern int netdev_receive_loop(struct packet_socket *psock,
 			       enum direction_t direction,
+			       u8 udp_encaps,
 			       struct packet **packet,
 			       int *num_packets,
 			       char **error);
diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c
index 406e34659560622b0dfac625fca6490ffce94185..ebaebd5e0fcd06296b30115ae7bb4d8dfa1ed8af 100644
--- a/gtests/net/packetdrill/packet.c
+++ b/gtests/net/packetdrill/packet.c
@@ -33,6 +33,7 @@
 #include "ip_packet.h"
 #include "mpls_packet.h"
 #include "sctp_packet.h"
+#include "udp_packet.h"
 
 
 /* Info for all types of header we support. */
diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h
index a7510b34748cb9144996830c0660589aa9780a66..3d7de771dc8fe05ba098f31dacd6b8c01a793c7a 100644
--- a/gtests/net/packetdrill/packet.h
+++ b/gtests/net/packetdrill/packet.h
@@ -109,9 +109,11 @@ struct packet {
 #define FLAG_WIN_NOCHECK          0x1  /* don't check TCP receive window */
 #define FLAG_OPTIONS_NOCHECK      0x2  /* don't check TCP options */
 #define FLAG_ABSOLUTE_TS_ECR      0x4  /* don't adjust TCP TS ecr */
-#define FLAGS_SCTP_BAD_CRC32C     0x8  /* compute bad CRC32C for SCTP packets */
-#define FLAGS_SCTP_EXPLICIT_TAG   0x10 /* verification tag specified */
-#define FLAGS_SCTP_GENERIC_PACKET 0x20 /* set if it is a generic packet */
+#define FLAG_ABSOLUTE_SEQ         0x8  /* don't adjust TCP.SEQ */
+#define FLAGS_SCTP_BAD_CRC32C     0x10 /* compute bad CRC32C for SCTP packets */
+#define FLAGS_SCTP_EXPLICIT_TAG   0x20 /* verification tag specified */
+#define FLAGS_SCTP_GENERIC_PACKET 0x40 /* set if it is a generic packet */
+#define FLAGS_UDP_ENCAPSULATED    0x80 /* TCP/UDP or SCTP/UDP encapsulated */
 
 	enum ip_ecn_t ecn;	/* IPv4/IPv6 ECN treatment for packet */
 
@@ -216,14 +218,18 @@ static inline int ip_header_min_len(int address_family)
 }
 
 /* Return the layer4 protocol of the packet. */
-static inline int packet_ip_protocol(const struct packet *packet)
+static inline int packet_ip_protocol(const struct packet *packet, u8 udp_encaps)
 {
+	int protocol = 0;
+
+	assert(packet->ipv4 != NULL || packet->ipv6 != NULL);
 	if (packet->ipv4 != NULL)
-		return packet->ipv4->protocol;
+		protocol = packet->ipv4->protocol;
 	if (packet->ipv6 != NULL)
-		return packet->ipv6->next_header;
-	assert(!"no valid IP header");
-	return 0;
+		protocol = packet->ipv6->next_header;
+	if (protocol == IPPROTO_UDP && udp_encaps != 0)
+		protocol = udp_encaps;
+	return protocol;
 }
 
 /* Return the length of an optionless TCP or UDP header. */
@@ -381,27 +387,37 @@ static inline int packet_echoed_ip_protocol(struct packet *packet)
 }
 
 /* Return the location of the transport header echoed by an ICMP message. */
-static inline u8 *packet_echoed_layer4_header(struct packet *packet)
+static inline u8 *packet_echoed_layer4_header(struct packet *packet, bool encapsulated)
 {
 	u8 *echoed_ip = packet_echoed_ip_header(packet);
 	int ip_header_len = packet_echoed_ip_header_len(packet);
-	return echoed_ip + ip_header_len;
+	if (packet_echoed_ip_protocol(packet) == IPPROTO_UDP && encapsulated == true) {
+		return echoed_ip + ip_header_len + sizeof(struct udp);
+	} else {
+		return echoed_ip + ip_header_len;
+	}
 }
 
 /* Return the location of the SCTP common header echoed by an ICMP message. */
 static inline struct sctp_common_header *
-packet_echoed_sctp_header(struct packet *packet)
+packet_echoed_sctp_header(struct packet *packet, bool encapsulated)
 {
-	if (packet_echoed_ip_protocol(packet) == IPPROTO_SCTP)
+	int protocol;
+
+	protocol = packet_echoed_ip_protocol(packet);
+	if (protocol == IPPROTO_UDP && encapsulated == true) {
+		protocol = IPPROTO_SCTP;
+	}
+	if (protocol == IPPROTO_SCTP)
 		return (struct sctp_common_header *)
-		       (packet_echoed_layer4_header(packet));
+		       (packet_echoed_layer4_header(packet, encapsulated));
 	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)
+static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet, bool encapsulated)
 {
-	struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet);
+	struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet, encapsulated);
 	assert(echoed_sctp);
 	u32 *v_tag = &(echoed_sctp->v_tag);
 	/* Check that the v_tag field is actually in the space we
@@ -412,10 +428,16 @@ static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet)
 }
 
 /* Return the location of the TCP header echoed by an ICMP message. */
-static inline struct tcp *packet_echoed_tcp_header(struct packet *packet)
+static inline struct tcp *packet_echoed_tcp_header(struct packet *packet, bool encapsulated)
 {
-	if (packet_echoed_ip_protocol(packet) == IPPROTO_TCP)
-		return (struct tcp *)(packet_echoed_layer4_header(packet));
+	int protocol;
+
+	protocol = packet_echoed_ip_protocol(packet);
+	if (protocol == IPPROTO_UDP && encapsulated == true) {
+		protocol = IPPROTO_TCP;
+	}
+	if (protocol == IPPROTO_TCP)
+		return (struct tcp *)(packet_echoed_layer4_header(packet, encapsulated));
 	return NULL;
 }
 
@@ -423,7 +445,7 @@ static inline struct tcp *packet_echoed_tcp_header(struct packet *packet)
 static inline struct udp *packet_echoed_udp_header(struct packet *packet)
 {
 	if (packet_echoed_ip_protocol(packet) == IPPROTO_UDP)
-		return (struct udp *)(packet_echoed_layer4_header(packet));
+		return (struct udp *)(packet_echoed_layer4_header(packet, 0));
 	return NULL;
 }
 
@@ -432,14 +454,14 @@ static inline struct
 udplite *packet_echoed_udplite_header(struct packet *packet)
 {
 	if (packet_echoed_ip_protocol(packet) == IPPROTO_UDPLITE)
-		return (struct udplite *)(packet_echoed_layer4_header(packet));
+		return (struct udplite *)(packet_echoed_layer4_header(packet, 0));
 	return NULL;
 }
 
 /* Return the location of the TCP sequence number echoed by an ICMP message. */
-static inline u32 *packet_echoed_tcp_seq(struct packet *packet)
+static inline u32 *packet_echoed_tcp_seq(struct packet *packet, bool encapsulated)
 {
-	struct tcp *echoed_tcp = packet_echoed_tcp_header(packet);
+	struct tcp *echoed_tcp = packet_echoed_tcp_header(packet, encapsulated);
 	assert(echoed_tcp);
 	u32 *seq = &(echoed_tcp->seq);
 	/* Check that the seq field is actually in the space we
diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c
index a81a3d052ecff8920dc56b658dd2a09b51cc6bdd..9bb72a9b9dec28999fd4a9c87c683af7431a8df4 100644
--- a/gtests/net/packetdrill/packet_checksum.c
+++ b/gtests/net/packetdrill/packet_checksum.c
@@ -47,19 +47,52 @@ static void checksum_ipv4_packet(struct packet *packet)
 	/* Fill in IPv4-based layer 4 checksum. */
 	if (packet->sctp != NULL) {
 		struct sctp_common_header *sctp = packet->sctp;
+		struct udp *udp;
+
 		sctp->crc32c = htonl(0);
-		sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			sctp->crc32c = sctp_crc32c(sctp, l4_bytes - sizeof(struct udp));
+		} else {
+			sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		}
 		if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
 			sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1);
 		}
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			udp = ((struct udp *)sctp) - 1;
+			udp->check = 0;
+			udp->check = tcp_udp_v4_checksum(ipv4->src_ip,
+							 ipv4->dst_ip,
+							 IPPROTO_UDP, udp,
+							 l4_bytes);
+		}
 	} else if (packet->tcp != NULL) {
 		struct tcp *tcp = packet->tcp;
+		struct udp *udp;
+
 		tcp->check = 0;
-		tcp->check = tcp_udp_v4_checksum(ipv4->src_ip,
-						 ipv4->dst_ip,
-						 IPPROTO_TCP, tcp, l4_bytes);
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			tcp->check = tcp_udp_v4_checksum(ipv4->src_ip,
+							 ipv4->dst_ip,
+							 IPPROTO_TCP, tcp,
+							 l4_bytes - sizeof(struct udp));
+		} else {
+			tcp->check = tcp_udp_v4_checksum(ipv4->src_ip,
+							 ipv4->dst_ip,
+							 IPPROTO_TCP, tcp,
+							 l4_bytes);
+		}
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			udp = ((struct udp *)tcp) - 1;
+			udp->check = 0;
+			udp->check = tcp_udp_v4_checksum(ipv4->src_ip,
+							 ipv4->dst_ip,
+							 IPPROTO_UDP, udp,
+							 l4_bytes);
+		}
 	} else if (packet->udp != NULL) {
 		struct udp *udp = packet->udp;
+
 		udp->check = 0;
 		udp->check = tcp_udp_v4_checksum(ipv4->src_ip,
 						 ipv4->dst_ip,
@@ -79,6 +112,7 @@ static void checksum_ipv4_packet(struct packet *packet)
 						     l4_bytes, coverage);
 	} else if (packet->icmpv4 != NULL) {
 		struct icmpv4 *icmpv4 = packet->icmpv4;
+
 		icmpv4->checksum = 0;
 		icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes);
 	} else {
@@ -101,19 +135,52 @@ static void checksum_ipv6_packet(struct packet *packet)
 	/* Fill in IPv6-based layer 4 checksum. */
 	if (packet->sctp != NULL) {
 		struct sctp_common_header *sctp = packet->sctp;
+		struct udp *udp;
+
 		sctp->crc32c = htonl(0);
-		sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			sctp->crc32c = sctp_crc32c(sctp, l4_bytes - sizeof(struct udp));
+		} else {
+			sctp->crc32c = sctp_crc32c(sctp, l4_bytes);
+		}
 		if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
 			sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1);
 		}
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			udp = ((struct udp *)sctp) - 1;
+			udp->check = 0;
+			udp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
+							 &ipv6->dst_ip,
+							 IPPROTO_UDP, udp,
+							 l4_bytes);
+		}
 	} else if (packet->tcp != NULL) {
 		struct tcp *tcp = packet->tcp;
+		struct udp *udp;
+
 		tcp->check = 0;
-		tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
-						 &ipv6->dst_ip,
-						 IPPROTO_TCP, tcp, l4_bytes);
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
+							 &ipv6->dst_ip,
+							 IPPROTO_TCP, tcp,
+							 l4_bytes - sizeof(struct udp));
+		} else {
+			tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
+							 &ipv6->dst_ip,
+							 IPPROTO_TCP, tcp,
+							 l4_bytes);
+		}
+		if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			udp = ((struct udp *)tcp) - 1;
+			udp->check = 0;
+			udp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
+							 &ipv6->dst_ip,
+							 IPPROTO_UDP, udp,
+							 l4_bytes);
+		}
 	} else if (packet->udp != NULL) {
 		struct udp *udp = packet->udp;
+
 		udp->check = 0;
 		udp->check = tcp_udp_v6_checksum(&ipv6->src_ip,
 						 &ipv6->dst_ip,
diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c
index 03209545f813bef3db8fa1325ea5cd402d4386c0..f5ac7a5ae80c44559a946afe489bc0caa0fa7f54 100644
--- a/gtests/net/packetdrill/packet_parser.c
+++ b/gtests/net/packetdrill/packet_parser.c
@@ -44,22 +44,24 @@
 #include "packet.h"
 #include "tcp.h"
 
-static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error);
-static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error);
-static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error);
+static int parse_ipv4(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error);
+static int parse_ipv6(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error);
+static int parse_mpls(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error);
 static int parse_layer3_packet_by_proto(struct packet *packet,
-					u16 proto, u8 *header_start,
-					u8 *packet_end, char **error);
-static int parse_layer4(struct packet *packet, u8 *header_start,
+					u16 proto, u8 udp_encaps,
+					u8 *header_start, u8 *packet_end,
+					char **error);
+static int parse_layer4(struct packet *packet, u8 udp_encaps, u8 *header_start,
 			int layer4_protocol, int layer4_bytes,
 			u8 *packet_end, bool *is_inner, char **error);
 
 static int parse_layer3_packet_by_proto(struct packet *packet,
-					u16 proto, u8 *header_start,
-					u8 *packet_end, char **error)
+					u16 proto, u8 udp_encaps,
+					u8 *header_start, u8 *packet_end,
+					char **error)
 {
 	u8 *p = header_start;
 
@@ -77,7 +79,8 @@ static int parse_layer3_packet_by_proto(struct packet *packet,
 		 */
 		ip = (struct ipv4 *)p;
 		if (ip->version == 4) {
-			return parse_ipv4(packet, p, packet_end, error);
+			return parse_ipv4(packet, udp_encaps, p, packet_end,
+					  error);
 		} else {
 			asprintf(error, "Bad IP version for ETHERTYPE_IP");
 			goto error_out;
@@ -96,14 +99,15 @@ static int parse_layer3_packet_by_proto(struct packet *packet,
 		 */
 		ip = (struct ipv6 *)p;
 		if (ip->version == 6) {
-			return parse_ipv6(packet, p, packet_end, error);
+			return parse_ipv6(packet, udp_encaps, p, packet_end,
+					  error);
 		} else {
 			asprintf(error, "Bad IP version for ETHERTYPE_IPV6");
 			goto error_out;
 		}
 	} else if ((proto == ETHERTYPE_MPLS_UC) ||
 		   (proto == ETHERTYPE_MPLS_MC)) {
-		return parse_mpls(packet, p, packet_end, error);
+		return parse_mpls(packet, udp_encaps, p, packet_end, error);
 	} else {
 		return PACKET_UNKNOWN_L4;
 	}
@@ -112,7 +116,7 @@ error_out:
 	return PACKET_BAD;
 }
 
-static int parse_layer3_packet(struct packet *packet,
+static int parse_layer3_packet(struct packet *packet, u8 udp_encaps,
 			       u8 *header_start, u8 *packet_end,
 			       char **error)
 {
@@ -131,16 +135,16 @@ static int parse_layer3_packet(struct packet *packet,
 	 */
 	ip = (struct ipv4 *) (p);
 	if (ip->version == 4)
-		return parse_ipv4(packet, p, packet_end, error);
+		return parse_ipv4(packet, udp_encaps, p, packet_end, error);
 	else if (ip->version == 6)
-		return parse_ipv6(packet, p, packet_end, error);
+		return parse_ipv6(packet, udp_encaps, p, packet_end, error);
 
 	asprintf(error, "Unsupported IP version");
 	return PACKET_BAD;
 }
 
 int parse_packet(struct packet *packet, int in_bytes,
-		 u16 ether_type, char **error)
+		 u16 ether_type, u8 udp_encaps, char **error)
 {
 	assert(in_bytes <= packet->buffer_bytes);
 	char *message = NULL;		/* human-readable error summary */
@@ -150,7 +154,7 @@ int parse_packet(struct packet *packet, int in_bytes,
 	/* packet_end points to the byte beyond the end of packet. */
 	u8 *packet_end = packet->buffer + in_bytes;
 
-	result = parse_layer3_packet_by_proto(packet, ether_type,
+	result = parse_layer3_packet_by_proto(packet, ether_type, udp_encaps,
 					      header_start, packet_end, error);
 
 	if (result != PACKET_BAD)
@@ -170,8 +174,8 @@ int parse_packet(struct packet *packet, int in_bytes,
  * packet_parse_result_t.
  * Note that packet_end points to the byte beyond the end of packet.
  */
-static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error)
+static int parse_ipv4(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error)
 {
 	struct header *ip_header = NULL;
 	u8 *p = header_start;
@@ -247,8 +251,8 @@ static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end,
 	/* Examine the L4 header. */
 	const int layer4_bytes = ip_total_bytes - ip_header_bytes;
 	const int layer4_protocol = ipv4->protocol;
-	result = parse_layer4(packet, p, layer4_protocol, layer4_bytes,
-			      packet_end, &is_inner, error);
+	result = parse_layer4(packet, udp_encaps, p, layer4_protocol,
+			      layer4_bytes, packet_end, &is_inner, error);
 
 	/* If this is the innermost IP header then this is the primary. */
 	if (is_inner)
@@ -268,8 +272,8 @@ error_out:
  * protocol other than TCP. Return a packet_parse_result_t.
  * Note that packet_end points to the byte beyond the end of packet.
  */
-static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error)
+static int parse_ipv6(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error)
 {
 	struct header *ip_header = NULL;
 	u8 *p = header_start;
@@ -321,8 +325,8 @@ static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end,
 	/* Examine the L4 header. */
 	const int layer4_bytes = ip_total_bytes - ip_header_bytes;
 	const int layer4_protocol = ipv6->next_header;
-	result = parse_layer4(packet, p, layer4_protocol, layer4_bytes,
-			      packet_end, &is_inner, error);
+	result = parse_layer4(packet, udp_encaps, p, layer4_protocol,
+			      layer4_bytes, packet_end, &is_inner, error);
 
 	/* If this is the innermost IP header then this is the primary. */
 	if (is_inner)
@@ -422,19 +426,21 @@ error_out:
 }
 
 /* Parse the UDP header. Return a packet_parse_result_t. */
-static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes,
-		     u8 *packet_end, char **error)
+static int parse_udp(struct packet *packet, u8 udp_encaps,
+		     u8 *layer4_start, int layer4_bytes, u8 *packet_end,
+		     char **error)
 {
 	struct header *udp_header = NULL;
 	u8 *p = layer4_start;
+	struct udp *udp;
 
 	assert(layer4_bytes >= 0);
 	if (layer4_bytes < sizeof(struct udp)) {
 		asprintf(error, "Truncated UDP header");
 		goto error_out;
 	}
-	packet->udp = (struct udp *) p;
-	const int udp_len = ntohs(packet->udp->len);
+	udp = (struct udp *) p;
+	const int udp_len = ntohs(udp->len);
 	const int udp_header_len = sizeof(struct udp);
 	if (udp_len < udp_header_len) {
 		asprintf(error, "UDP datagram length too small for UDP header");
@@ -456,12 +462,25 @@ static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes,
 	}
 	udp_header->total_bytes = layer4_bytes;
 
-	p += layer4_bytes;
-	assert(p <= packet_end);
-
-	DEBUGP("UDP src port: %d\n", ntohs(packet->udp->src_port));
-	DEBUGP("UDP dst port: %d\n", ntohs(packet->udp->dst_port));
-	return PACKET_OK;
+	DEBUGP("UDP src port: %d\n", ntohs(udp->src_port));
+	DEBUGP("UDP dst port: %d\n", ntohs(udp->dst_port));
+	if (udp_encaps == IPPROTO_SCTP) {
+		packet->flags |= FLAGS_UDP_ENCAPSULATED;
+		return parse_sctp(packet, p + udp_header_len,
+				  layer4_bytes - udp_header_len,
+				  packet_end, error);
+	} else if (udp_encaps == IPPROTO_TCP) {
+		packet->flags |= FLAGS_UDP_ENCAPSULATED;
+		return parse_tcp(packet, p + udp_header_len,
+				 layer4_bytes - udp_header_len,
+				  packet_end, error);
+	} else {
+		assert(udp_encaps == 0);
+		packet->udp = udp;
+		p += layer4_bytes;
+		assert(p <= packet_end);
+		return PACKET_OK;
+	}
 
 error_out:
 	return PACKET_BAD;
@@ -585,8 +604,9 @@ error_out:
 }
 
 /* Parse the GRE header. Return a packet_parse_result_t. */
-static int parse_gre(struct packet *packet, u8 *layer4_start, int layer4_bytes,
-		     u8 *packet_end, char **error)
+static int parse_gre(struct packet *packet, u8 udp_encaps,
+		     u8 *layer4_start, int layer4_bytes, u8 *packet_end,
+		     char **error)
 {
 	struct header *gre_header = NULL;
 	u8 *p = layer4_start;
@@ -629,14 +649,14 @@ static int parse_gre(struct packet *packet, u8 *layer4_start, int layer4_bytes,
 	p += gre_header_len;
 	assert(p <= packet_end);
 	return parse_layer3_packet_by_proto(packet, ntohs(gre->protocol),
-					    p, packet_end, error);
+					    udp_encaps, p, packet_end, error);
 
 error_out:
 	return PACKET_BAD;
 }
 
-static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end,
-		      char **error)
+static int parse_mpls(struct packet *packet, u8 udp_encaps,
+		      u8 *header_start, u8 *packet_end, char **error)
 {
 	struct header *mpls_header = NULL;
 	u8 *p = header_start;
@@ -670,13 +690,13 @@ static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end,
 
 	/* Move on to the header inside the MPLS label stack. */
 	assert(p <= packet_end);
-	return parse_layer3_packet(packet, p, packet_end, error);
+	return parse_layer3_packet(packet, udp_encaps, p, packet_end, error);
 
 error_out:
 	return PACKET_BAD;
 }
 
-static int parse_layer4(struct packet *packet, u8 *layer4_start,
+static int parse_layer4(struct packet *packet, u8 udp_encaps, u8 *layer4_start,
 			int layer4_protocol, int layer4_bytes,
 			u8 *packet_end, bool *is_inner, char **error)
 {
@@ -690,8 +710,8 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start,
 				 error);
 	} else if (layer4_protocol == IPPROTO_UDP) {
 		*is_inner = true;	/* found inner-most layer 4 */
-		return parse_udp(packet, layer4_start, layer4_bytes, packet_end,
-				 error);
+		return parse_udp(packet, udp_encaps, layer4_start, layer4_bytes,
+				 packet_end, error);
 	} else if (layer4_protocol == IPPROTO_UDPLITE) {
 		*is_inner = true;	/* found inner-most layer 4 */
 		return parse_udplite(packet, layer4_start, layer4_bytes,
@@ -706,14 +726,16 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start,
 				    packet_end, error);
 	} else if (layer4_protocol == IPPROTO_GRE) {
 		*is_inner = false;
-		return parse_gre(packet, layer4_start, layer4_bytes, packet_end,
-				 error);
+		return parse_gre(packet, udp_encaps, layer4_start, layer4_bytes,
+				 packet_end, error);
 	} else if (layer4_protocol == IPPROTO_IPIP) {
 		*is_inner = false;
-		return parse_ipv4(packet, layer4_start, packet_end, error);
+		return parse_ipv4(packet, udp_encaps, layer4_start, packet_end,
+				  error);
 	} else if (layer4_protocol == IPPROTO_IPV6) {
 		*is_inner = false;
-		return parse_ipv6(packet, layer4_start, packet_end, error);
+		return parse_ipv6(packet, udp_encaps, layer4_start, packet_end,
+				  error);
 	}
 	return PACKET_UNKNOWN_L4;
 }
diff --git a/gtests/net/packetdrill/packet_parser.h b/gtests/net/packetdrill/packet_parser.h
index 07805553da28179d17db7a99196b7f1cd3677b16..3051d22475d4ea785d51889a9891c579d4a366cf 100644
--- a/gtests/net/packetdrill/packet_parser.h
+++ b/gtests/net/packetdrill/packet_parser.h
@@ -30,7 +30,7 @@
 enum packet_parse_result_t {
 	PACKET_OK,		/* no errors detected */
 	PACKET_BAD,		/* illegal header */
-	PACKET_UNKNOWN_L4,	/* not TCP or UDP or UDPLite */
+	PACKET_UNKNOWN_L4,	/* not SCTP or TCP or UDP or UDPLite */
 };
 
 /* Given an input packet of length 'in_bytes' stored in the buffer
@@ -42,6 +42,6 @@ enum packet_parse_result_t {
  * error message.
  */
 int parse_packet(struct packet *packet, int in_bytes,
-		 u16 ether_type, char **error);
+		 u16 ether_type, u8 udp_encaps, char **error);
 
 #endif /* __PACKET_PARSER_H__ */
diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c
index 07ef95920076721b9f91cb34434b416db81c4f27..46c8fca4cca29432962516d60489febaddf30c38 100644
--- a/gtests/net/packetdrill/packet_parser_test.c
+++ b/gtests/net/packetdrill/packet_parser_test.c
@@ -29,7 +29,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-int debug_logging=0;
+int debug_logging = 0;
 
 static void test_parse_sctp_ipv4_packet(void)
 {
@@ -50,7 +50,7 @@ static void test_parse_sctp_ipv4_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -96,7 +96,7 @@ static void test_parse_sctp_ipv6_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -121,6 +121,105 @@ static void test_parse_sctp_ipv6_packet(void)
 	packet_free(packet);
 }
 
+static void test_parse_sctp_udp_ipv4_packet(void)
+{
+	/* A SCTP/UDP/IPv4 packet. */
+	u8 data[] = {
+		/* 1.1.1.1:1234 > 192.168.0.1:60213
+		 * udp(9899 > 9899): sctp: ABORT[] */
+		0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x11, 0xf9, 0x15, 0x01, 0x01, 0x01, 0x01,
+		0xc0, 0xa8, 0x00, 0x01, 0x26, 0xab, 0x26, 0xab,
+		0x00, 0x18, 0x00, 0x00, 0x04, 0xd2, 0xeb, 0x35,
+		0x01, 0x02, 0x03, 0x04, 0xab, 0x0c, 0xeb, 0x7a,
+		0x06, 0x01, 0x00, 0x04
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data),
+			     ETHERTYPE_IP, IPPROTO_SCTP, &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	struct ipv4 *expected_ipv4 = (struct ipv4 *)(packet->buffer);
+	struct udp *expected_udp =
+		(struct udp *)(expected_ipv4 + 1);
+	struct sctp_common_header *expected_sctp =
+		(struct sctp_common_header *)(expected_udp + 1);
+
+	assert(packet->ip_bytes		== sizeof(data));
+	assert(packet->ipv4		== expected_ipv4);
+	assert(packet->ipv6		== NULL);
+	assert(packet->sctp		== expected_sctp);
+	assert(packet->tcp		== NULL);
+	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
+	assert(packet->icmpv4		== NULL);
+	assert(packet->icmpv6		== NULL);
+
+	assert(packet->time_usecs	== 0);
+	assert(packet->flags		== FLAGS_UDP_ENCAPSULATED);
+	assert(packet->ecn		== 0);
+
+	packet_free(packet);
+}
+
+static void test_parse_sctp_udp_ipv6_packet(void)
+{
+	/* A SCTP/UDP/IPv6 packet. */
+	u8 data[] = {
+		/* 2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080
+		 * udp(9899->9899): sctp: ABORT[] */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x11, 0xff,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00,
+		0xd3, 0xe2, 0x1f, 0x90, 0x01, 0x02, 0x03, 0x04,
+		0xc4, 0xb0, 0x47, 0x64, 0x06, 0x01, 0x00, 0x04
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data),
+			     ETHERTYPE_IPV6, IPPROTO_SCTP, &error);
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	struct ipv6 *expected_ipv6 = (struct ipv6 *)(packet->buffer);
+	struct udp *expected_udp =
+		(struct udp *)(expected_ipv6 + 1);
+	struct sctp_common_header *expected_sctp =
+		(struct sctp_common_header *)(expected_udp + 1);
+
+	assert(packet->ip_bytes		== sizeof(data));
+	assert(packet->ipv4		== NULL);
+	assert(packet->ipv6		== expected_ipv6);
+	assert(packet->sctp		== expected_sctp);
+	assert(packet->tcp		== NULL);
+	assert(packet->udp		== NULL);
+	assert(packet->udplite		== NULL);
+	assert(packet->icmpv4		== NULL);
+	assert(packet->icmpv6		== NULL);
+
+	assert(packet->time_usecs	== 0);
+	assert(packet->flags		== FLAGS_UDP_ENCAPSULATED);
+	assert(packet->ecn		== 0);
+
+	packet_free(packet);
+}
+
+
 static void test_parse_tcp_ipv4_packet(void)
 {
 	/* A TCP/IPv4 packet. */
@@ -145,7 +244,7 @@ static void test_parse_tcp_ipv4_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -193,7 +292,7 @@ static void test_parse_tcp_ipv6_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -234,7 +333,7 @@ static void test_parse_udp_ipv4_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -278,7 +377,7 @@ static void test_parse_udp_ipv6_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -320,7 +419,7 @@ static void test_parse_udplite_ipv4_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -366,7 +465,7 @@ static void test_parse_udplite_ipv6_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -418,7 +517,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -510,7 +609,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -588,7 +687,7 @@ static void test_parse_icmpv4_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -642,7 +741,7 @@ static void test_parse_icmpv6_packet(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -670,6 +769,8 @@ int main(void)
 {
 	test_parse_sctp_ipv4_packet();
 	test_parse_sctp_ipv6_packet();
+	test_parse_sctp_udp_ipv4_packet();
+	test_parse_sctp_udp_ipv6_packet();
 	test_parse_tcp_ipv4_packet();
 	test_parse_tcp_ipv6_packet();
 	test_parse_udp_ipv4_packet();
diff --git a/gtests/net/packetdrill/packet_socket_linux.c b/gtests/net/packetdrill/packet_socket_linux.c
index 8abf10666bc4422ab32e4c63cbb5489dcc5a249b..bab7f34555530424498aefd6231f803cf4a7accb 100644
--- a/gtests/net/packetdrill/packet_socket_linux.c
+++ b/gtests/net/packetdrill/packet_socket_linux.c
@@ -175,7 +175,7 @@ void packet_socket_set_filter(struct packet_socket *psock,
 	bpfcode.filter[3].k = ((client_ether[0] << 8)  |
 			       (client_ether[1]));
 
-	if (DEBUG_LOGGING) {
+	if (debug_logging) {
 		int i;
 		DEBUGP("filter constants:\n");
 		for (i = 0; i < bpfcode.len; ++i)
diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c
index 9b1dd53ef47fcdb9ec27c3ed69783ecb84591507..a992376008d9e789eb14c25997ccc70ac7e3dcee 100644
--- a/gtests/net/packetdrill/packet_to_string.c
+++ b/gtests/net/packetdrill/packet_to_string.c
@@ -120,7 +120,7 @@ static int mpls_header_to_string(FILE *s, struct packet *packet, int layer,
 	return STATUS_OK;
 }
 
-static int sctp_packet_to_string(FILE *s, struct packet *packet,
+static int sctp_packet_to_string(FILE *s, struct packet *packet, int i,
 				 enum dump_format_t format, char **error)
 {
 	struct sctp_chunks_iterator iter;
@@ -138,6 +138,13 @@ static int sctp_packet_to_string(FILE *s, struct packet *packet,
 	if (packet->flags & FLAGS_SCTP_BAD_CRC32C) {
 		fputs("(bad_crc32c)", s);
 	}
+
+	if (packet->headers[i + 1].type == HEADER_UDP) {
+		struct udp *udp = packet->headers[i + 1].h.udp;
+
+		fprintf(s, "/udp(%u > %u)",
+			ntohs(udp->src_port), ntohs(udp->dst_port));
+	}
 	fputc(':', s);
 
 	index = 0;
@@ -167,7 +174,7 @@ static int sctp_packet_to_string(FILE *s, struct packet *packet,
 /* Print a string representation of the TCP packet:
  *  direction opt_ip_info flags seq ack window tcp_options
  */
-static int tcp_packet_to_string(FILE *s, struct packet *packet,
+static int tcp_packet_to_string(FILE *s, struct packet *packet, int i,
 				enum dump_format_t format, char **error)
 {
 	int result = STATUS_OK;       /* return value */
@@ -177,7 +184,6 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet,
 		fputc(' ', s);
 	}
 
-
 	/* We print flags in the same order as tcpdump 4.1.1. */
 	if (packet->tcp->fin)
 		fputc('F', s);
@@ -216,6 +222,13 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet,
 		free(tcp_options);
 	}
 
+	if (packet->headers[i + 1].type == HEADER_UDP) {
+		struct udp *udp = packet->headers[i + 1].h.udp;
+
+		fprintf(s, "/udp(%u > %u)",
+			ntohs(udp->src_port), ntohs(udp->dst_port));
+	}
+
 	if (format == DUMP_VERBOSE)
 		packet_buffer_to_string(s, packet);
 
@@ -307,9 +320,14 @@ int packet_to_string(struct packet *packet,
 	FILE *s = open_memstream(ascii_string, &size);  /* output string */
 	int i;
 	int header_count = packet_header_count(packet);
+	int limit;
 
 	/* Print any encapsulation headers preceding layer 3 and 4 headers. */
-	for (i = 0; i < header_count - 2; ++i) {
+	if (packet->flags & FLAGS_UDP_ENCAPSULATED)
+		limit = header_count - 3;
+	else
+		limit = header_count - 2;
+	for (i = 0; i < limit; ++i) {
 		if (packet->headers[i].type == HEADER_NONE)
 			break;
 		if (encap_header_to_string(s, packet, i, format, error))
@@ -320,10 +338,12 @@ int packet_to_string(struct packet *packet,
 		fputs("[NO IP HEADER]", s);
 	} else {
 		if (packet->sctp != NULL) {
-			if (sctp_packet_to_string(s, packet, format, error))
+			if (sctp_packet_to_string(s, packet, limit, format,
+						  error))
 				goto out;
 		} else if (packet->tcp != NULL) {
-			if (tcp_packet_to_string(s, packet, format, error))
+			if (tcp_packet_to_string(s, packet, limit, format,
+						 error))
 				goto out;
 		} else if (packet->udp != NULL) {
 			if (udp_packet_to_string(s, packet, format, error))
diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c
index 560aa24d6f7d6c33a3a3373b09252dd93c00048a..c40aa0cebddb1d966fb46c368b269072fd28a4be 100644
--- a/gtests/net/packetdrill/packet_to_string_test.c
+++ b/gtests/net/packetdrill/packet_to_string_test.c
@@ -31,7 +31,8 @@
 #include "packet_parser.h"
 #include "logging.h"
 
-int debug_logging=0;
+int debug_logging = 0;
+#define DEBUG_LOGGING 1
 
 static void test_sctp_ipv4_packet_to_string(void)
 {
@@ -54,7 +55,12 @@ static void test_sctp_ipv4_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
+#if DEBUG_LOGGING == 1
+	if (result != PACKET_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -276,7 +282,7 @@ static void test_sctp_ipv6_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 #if DEBUG_LOGGING == 1
 	if (result != PACKET_OK) {
 		printf("error was: %s\n", error);
@@ -523,6 +529,357 @@ static void test_sctp_ipv6_packet_to_string(void)
 	packet_free(packet);
 }
 
+static void test_sctp_udp_ipv4_packet_to_string(void)
+{
+	/* An IPv4/UDP/SCTP packet. */
+	u8 data[] = {
+		/* IPv4: */
+		0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x11, 0xb5, 0xbb, 0x02, 0x02, 0x02, 0x02,
+		0x01, 0x01, 0x01, 0x01,
+		/* UDP Header */
+		0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00,
+		/* SCTP Common Header: */
+		0x04, 0xd2, 0x1f, 0x90,
+		0x01, 0x02, 0x03, 0x04,
+		0x3d, 0x99, 0xbf, 0xe3,
+		/* SCTP ABORT Chunk: */
+		0x06, 0x01, 0x00, 0x04
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, IPPROTO_SCTP,
+			     &error);
+#if DEBUG_LOGGING == 1
+	if (result != PACKET_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump,
+				  &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump,
+				  &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"2.2.2.2:1234 > 1.1.1.1:8080 "
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump,
+				  &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"2.2.2.2:1234 > 1.1.1.1:8080 "
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]"
+		"\n"
+		"0x0000: 45 00 00 2c 00 00 00 00 ff 11 b5 bb 02 02 02 02 " "\n"
+		"0x0010: 01 01 01 01 26 ab 26 ab 00 18 00 00 04 d2 1f 90 " "\n"
+		"0x0020: 01 02 03 04 3d 99 bf e3 06 01 00 04 " "\n";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	packet_free(packet);
+}
+
+static void test_sctp_udp_ipv6_packet_to_string(void)
+{
+	/* An IPv6/UDP/SCTP packet. */
+	u8 data[] = {
+		/* IPv6 Base Header: */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x11, 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,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+		/* UDP Header */
+		0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00,
+		/* SCTP Common Header: */
+		0x04, 0xd2, 0x1f, 0x90,
+		0x01, 0x02, 0x03, 0x04,
+		0x3d, 0x99, 0xbf, 0xe3,
+		/* SCTP ABORT Chunk: */
+		0x06, 0x01, 0x00, 0x04
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, IPPROTO_SCTP,
+			     &error);
+#if DEBUG_LOGGING == 1
+	if (result != PACKET_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump, &error);
+#if DEBUG_LOGGING == 1
+	if (status != STATUS_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]";
+
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"2::2222:1234 > 1::1111:8080 "
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"2::2222:1234 > 1::1111:8080 "
+		"sctp/udp(9899 > 9899): ABORT[flgs=T]"
+		"\n"
+		"0x0000: 60 00 00 00 00 18 11 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 26 ab 26 ab 00 18 00 00 " "\n"
+		"0x0030: 04 d2 1f 90 01 02 03 04 3d 99 bf e3 06 01 00 04 " "\n";
+
+	printf("expected = '%s'\n", expected);
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+	packet_free(packet);
+}
+
+static void test_tcp_udp_ipv4_packet_to_string(void)
+{
+	/* An IPv4/GRE/IPv4/UDP/TCP packet. */
+	u8 data[] = {
+		/* IPv4: */
+		0x45, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x2f, 0xb5, 0x6d, 0x02, 0x02, 0x02, 0x02,
+		0x01, 0x01, 0x01, 0x01,
+		/* GRE: */
+		0x00, 0x00, 0x08, 0x00,
+		/* IPv4, UDP, TCP: */
+		0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
+		0xff, 0x11, 0x38, 0xfe, 0xc0, 0x00, 0x02, 0x01,
+		0xc0, 0xa8, 0x00, 0x01, 0x26, 0xab, 0x26, 0xab,
+		0x00, 0x30, 0x00, 0x00, 0xcf, 0x3f, 0x1f, 0x90,
+		0x00, 0x00, 0x00, 0x01, 0x83, 0x4d, 0xa5, 0x5b,
+		0xa0, 0x10, 0x01, 0x01, 0xdb, 0x2d, 0x00, 0x00,
+		0x05, 0x0a, 0x83, 0x4d, 0xab, 0x03, 0x83, 0x4d,
+		0xb0, 0xab, 0x08, 0x0a, 0x00, 0x00, 0x01, 0x2c,
+		0x60, 0xc2, 0x18, 0x20
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, IPPROTO_TCP,
+			     &error);
+#if DEBUG_LOGGING == 1
+	if (result != PACKET_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		". 1:1(0) ack 2202903899 win 257 "
+		"<sack 2202905347:2202906795,TS val 300 ecr 1623332896>"
+		"/udp(9899 > 9899)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		"192.0.2.1:53055 > 192.168.0.1:8080 "
+		". 1:1(0) ack 2202903899 win 257 "
+		"<sack 2202905347:2202906795,TS val 300 ecr 1623332896>"
+		"/udp(9899 > 9899)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv4 2.2.2.2 > 1.1.1.1: gre: "
+		"192.0.2.1:53055 > 192.168.0.1:8080 "
+		". 1:1(0) ack 2202903899 win 257 "
+		"<sack 2202905347:2202906795,TS val 300 ecr 1623332896>"
+		"/udp(9899 > 9899)"
+		"\n"
+		"0x0000: 45 00 00 5c 00 00 00 00 ff 2f b5 6d 02 02 02 02 " "\n"
+		"0x0010: 01 01 01 01 00 00 08 00 45 00 00 44 00 00 00 00 " "\n"
+		"0x0020: ff 11 38 fe c0 00 02 01 c0 a8 00 01 26 ab 26 ab " "\n"
+		"0x0030: 00 30 00 00 cf 3f 1f 90 00 00 00 01 83 4d a5 5b " "\n"
+		"0x0040: a0 10 01 01 db 2d 00 00 05 0a 83 4d ab 03 83 4d " "\n"
+		"0x0050: b0 ab 08 0a 00 00 01 2c 60 c2 18 20 " "\n";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	packet_free(packet);
+}
+
+static void test_tcp_udp_ipv6_packet_to_string(void)
+{
+	/* An IPv6/GRE/IPv6/UDP/TCP packet. */
+	u8 data[] = {
+		/* IPv6: */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0x2f, 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,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+		/* GRE: */
+		0x00, 0x00, 0x86, 0xdd,
+		/* IPv6, UDP, TCP: */
+		0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11, 0xff,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0x26, 0xab, 0x26, 0xab, 0x00, 0x28, 0x00, 0x00,
+		0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, 0x18,
+		0x06, 0x60, 0x00, 0x00, 0x02, 0x04, 0x03, 0xe8,
+		0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07
+	};
+
+	struct packet *packet = packet_new(sizeof(data));
+
+	/* Populate and parse a packet */
+	memcpy(packet->buffer, data, sizeof(data));
+	char *error = NULL;
+	enum packet_parse_result_t result =
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, IPPROTO_TCP,
+			     &error);
+#if DEBUG_LOGGING == 1
+	if (result != PACKET_OK) {
+		printf("error was: %s\n", error);
+	}
+#endif
+	assert(result == PACKET_OK);
+	assert(error == NULL);
+
+	int status = 0;
+	char *dump = NULL, *expected = NULL;
+
+	/* Test a DUMP_SHORT dump */
+	status = packet_to_string(packet, DUMP_SHORT, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>"
+		"/udp(9899 > 9899)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_FULL dump */
+	status = packet_to_string(packet, DUMP_FULL, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 "
+		"S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>"
+		"/udp(9899 > 9899)";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	/* Test a DUMP_VERBOSE dump */
+	status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error);
+	assert(status == STATUS_OK);
+	assert(error == NULL);
+	printf("dump = '%s'\n", dump);
+	expected =
+		"ipv6 2::2222 > 1::1111: gre: "
+		"2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 "
+		"S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>"
+		"/udp(9899 > 9899)"
+		"\n"
+		"0x0000: 60 00 00 00 00 54 2f 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 00 00 86 dd 60 00 00 00 " "\n"
+		"0x0030: 00 28 11 ff 20 01 0d b8 00 00 00 00 00 00 00 00 " "\n"
+		"0x0040: 00 00 00 01 fd 3d fa 7b d1 7d 00 00 00 00 00 00 " "\n"
+		"0x0050: 00 00 00 01 26 ab 26 ab 00 28 00 00 d3 e2 1f 90 " "\n"
+		"0x0060: 00 00 00 00 00 00 00 00 80 02 80 18 06 60 00 00 " "\n"
+		"0x0070: 02 04 03 e8 04 02 01 01 01 03 03 07 " "\n";
+	assert(strcmp(dump, expected) == 0);
+	free(dump);
+
+	packet_free(packet);
+}
+
 static void test_tcp_ipv4_packet_to_string(void)
 {
 	/* An IPv4/GRE/IPv4/TCP packet. */
@@ -550,7 +907,7 @@ static void test_tcp_ipv4_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -635,7 +992,7 @@ static void test_tcp_ipv6_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -716,7 +1073,7 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -764,7 +1121,7 @@ static void test_udplite_ipv4_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -842,7 +1199,7 @@ static void test_udplite_ipv6_packet_to_string(void)
 	memcpy(packet->buffer, data, sizeof(data));
 	char *error = NULL;
 	enum packet_parse_result_t result =
-		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error);
+		parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error);
 	assert(result == PACKET_OK);
 	assert(error == NULL);
 
@@ -895,10 +1252,15 @@ static void test_udplite_ipv6_packet_to_string(void)
 
 int main(void)
 {
+	test_tcp_udp_ipv4_packet_to_string();
 	test_sctp_ipv4_packet_to_string();
 	test_sctp_ipv6_packet_to_string();
+	test_sctp_udp_ipv4_packet_to_string();
+	test_sctp_udp_ipv6_packet_to_string();
 	test_tcp_ipv4_packet_to_string();
 	test_tcp_ipv6_packet_to_string();
+	test_tcp_udp_ipv4_packet_to_string();
+	test_tcp_udp_ipv6_packet_to_string();
 	test_gre_mpls_tcp_ipv4_packet_to_string();
 	test_udplite_ipv4_packet_to_string();
 	test_udplite_ipv6_packet_to_string();
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 4f2b04edbb3b422506c037a7e065ae1dfca4f890..a25ffb5b994d6488f27dd643c59a565a4bc7a649 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -460,6 +460,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	struct {
 		u32 start_sequence;
 		u16 payload_bytes;
+		bool absolute;
 	} tcp_sequence_info;
 	struct {
 		int protocol;
@@ -467,7 +468,17 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 		u32 verification_tag;	/* used for SCTP */
 		u32 start_sequence;	/* used for TCP */
 		u16 checksum_coverage;	/* used for UDPLite */
+		u16 udp_src_port;
+		u16 udp_dst_port;
 	} transport_info;
+	struct {
+		u16 udp_src_port;
+		u16 udp_dst_port;
+	} udp_encaps_info;
+	struct {
+		bool bad_crc32c;
+		s64 tag;
+	} sctp_header_spec;
 	struct option_list *option;
 	struct event *event;
 	struct packet *packet;
@@ -517,6 +528,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> IPV4 IPV6 ICMP SCTP UDP UDPLITE GRE MTU
 %token <reserved> MPLS LABEL TC TTL
 %token <reserved> OPTION
+%token <reserved> FUNCTION_SET_NAME PCBCNT
 %token <reserved> SRTO_ASSOC_ID SRTO_INITIAL SRTO_MAX SRTO_MIN
 %token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
 %token <reserved> SINIT_MAX_INIT_TIMEO
@@ -590,6 +602,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> STRRESET_TYPE STRRESET_FLAGS STRRESET_LENGTH STRRESET_ASSOC_ID STRRESET_STREAM_LIST
 %token <reserved> ASSOCRESET_TYPE ASSOCRESET_FLAGS ASSOCRESET_LENGTH ASSOCRESET_ASSOC_ID ASSOCRESET_LOCAL_TSN ASSOCRESET_REMOTE_TSN
 %token <reserved> STRCHANGE_TYPE STRCHANGE_FLAGS STRCHANGE_LENGTH STRCHANGE_ASSOC_ID STRCHANGE_INSTRMS STRCHANGE_OUTSTRMS
+%token <reserved> SUE_ASSOC_ID SUE_ADDRESS SUE_PORT
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -628,7 +641,11 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> decimal_integer hex_integer data
 %type <expression> inaddr sockaddr msghdr cmsghdr cmsg_level cmsg_type cmsg_data
 %type <expression> sf_hdtr iovec pollfd opt_revents
-%type <expression> linger l_onoff l_linger sctp_assoc_id
+%type <expression> linger l_onoff l_linger
+%type <expression> tcp_function_set function_set_name pcbcnt
+%type <udp_encaps_info> opt_udp_encaps_info
+%type <sctp_header_spec> sctp_header_spec
+%type <expression> sctp_assoc_id
 %type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata
 %type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary
 %type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts
@@ -670,6 +687,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> assocreset_local_tsn assocreset_remote_tsn
 %type <expression> sctp_stream_change_event strchange_type strchange_flags strchange_length strchange_instrms strchange_outstrms
 %type <expression> sctp_add_streams
+%type <expression> sctp_udpencaps sue_address sue_port
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
@@ -908,113 +926,58 @@ packet_spec
 | icmp_packet_spec    { $$ = $1; }
 ;
 
-sctp_packet_spec
-: packet_prefix opt_ip_info SCTP ':' sctp_chunk_list_spec {
-	char *error = NULL;
-	struct packet *outer = $1, *inner = NULL;
-	enum direction_t direction = outer->direction;
-
-	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
-	                        -1, false, $5, &error);
-	if (inner == NULL) {
-		assert(error != NULL);
-		semantic_error(error);
-		free(error);
-	}
-
-	$$ = packet_encapsulate_and_free(outer, inner);
+opt_udp_encaps_info
+:	{
+	$$.udp_src_port = 0;
+	$$.udp_dst_port = 0;
 }
-| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':' sctp_chunk_list_spec {
-	char *error = NULL;
-	struct packet *outer = $1, *inner = NULL;
-	enum direction_t direction = outer->direction;
-
-	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
-	                        -1, true, $8, &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 ')' ':' sctp_chunk_list_spec {
-	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");
+| '/' UDP '(' INTEGER '>' INTEGER ')' {
+	if (!is_valid_u16($4)) {
+		semantic_error("UDP source port out of range");
 	}
-	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
-	                        $7, false, $10, &error);
-	if (inner == NULL) {
-		assert(error != NULL);
-		semantic_error(error);
-		free(error);
+	if (!is_valid_u16($6)) {
+		semantic_error("UDP destination port out of range");
 	}
-
-	$$ = packet_encapsulate_and_free(outer, inner);
+	$$.udp_src_port = $4;
+	$$.udp_dst_port = $6;
 }
-| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' sctp_chunk_list_spec {
-	char *error = NULL;
-	struct packet *outer = $1, *inner = NULL;
-	enum direction_t direction = outer->direction;
+;
 
-	if (!is_valid_u32($9)) {
+sctp_header_spec
+: SCTP {
+	$$.bad_crc32c = false;
+	$$.tag = -1;
+}
+| SCTP '(' BAD_CRC32C ')' {
+	$$.bad_crc32c = true;
+	$$.tag = -1;
+}
+| SCTP '(' TAG '=' INTEGER ')' {
+	if (!is_valid_u32($5)) {
 		semantic_error("tag value out of range");
 	}
-	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
-	                        $9, true, $12, &error);
-	if (inner == NULL) {
-		assert(error != NULL);
-		semantic_error(error);
-		free(error);
-	}
-
-	$$ = packet_encapsulate_and_free(outer, inner);
+	$$.bad_crc32c = false;
+	$$.tag = $5;
 }
-| 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);
+| SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' {
+	if (!is_valid_u32($7)) {
+		semantic_error("tag value out of range");
 	}
-
-	$$ = packet_encapsulate_and_free(outer, inner);
+	$$.bad_crc32c = true;
+	$$.tag = $7;
 }
-| 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 ']' {
+sctp_packet_spec
+: packet_prefix opt_ip_info sctp_header_spec opt_udp_encaps_info ':' sctp_chunk_list_spec {
 	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);
+	inner = new_sctp_packet(in_config->wire_protocol, direction, $2,
+	                        $3.tag, $3.bad_crc32c, $6,
+	                        $4.udp_src_port, $4.udp_dst_port,
+	                        &error);
 	if (inner == NULL) {
 		assert(error != NULL);
 		semantic_error(error);
@@ -1023,16 +986,15 @@ sctp_packet_spec
 
 	$$ = packet_encapsulate_and_free(outer, inner);
 }
-| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' '[' byte_list ']' {
+| packet_prefix opt_ip_info sctp_header_spec opt_udp_encaps_info ':' '[' 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);
+	                                $3.tag, $3.bad_crc32c, $7,
+	                                $4.udp_src_port, $4.udp_dst_port,
+	                                &error);
 	if (inner == NULL) {
 		assert(error != NULL);
 		semantic_error(error);
@@ -2360,7 +2322,7 @@ sctp_protocol_violation_cause_spec
 }
 
 tcp_packet_spec
-: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options {
+: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options opt_udp_encaps_info {
 	char *error = NULL;
 	struct packet *outer = $1, *inner = NULL;
 	enum direction_t direction = outer->direction;
@@ -2376,6 +2338,8 @@ tcp_packet_spec
 			       $4.start_sequence, $4.payload_bytes,
 			       $5, $6, $7,
 			       absolute_ts_ecr,
+			       $4.absolute,
+			       $8.udp_src_port, $8.udp_dst_port,
 			       &error);
 	absolute_ts_ecr = false;
 	free($3);
@@ -2445,7 +2409,8 @@ 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,
-				$2.verification_tag, $6, &error);
+				$2.verification_tag, $6,
+				$2.udp_src_port, $2.udp_dst_port, &error);
 	free($4);
 	free($5);
 	if (inner == NULL) {
@@ -2557,26 +2522,36 @@ opt_icmp_echoed
 	$$.protocol		= IPPROTO_TCP;
 	$$.payload_bytes	= 0;
 	$$.start_sequence	= 0;
+	$$.udp_src_port		= 0;
+	$$.udp_dst_port		= 0;
 }
-| '[' SCTP '(' INTEGER ')' ']'	{
+| '[' SCTP '(' INTEGER ')' opt_udp_encaps_info ']'	{
 	$$.protocol		= IPPROTO_SCTP;
 	$$.payload_bytes	= 0;
 	$$.verification_tag	= $4;
+	$$.udp_src_port		= $6.udp_src_port;
+	$$.udp_dst_port		= $6.udp_dst_port;
 }
 | '[' UDP '(' INTEGER ')' ']'	{
 	$$.protocol		= IPPROTO_UDP;
 	$$.payload_bytes	= $4;
 	$$.start_sequence	= 0;
+	$$.udp_src_port		= 0;
+	$$.udp_dst_port		= 0;
 }
 | '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']'	{
 	$$.protocol		= IPPROTO_UDPLITE;
 	$$.payload_bytes	= $4;
 	$$.checksum_coverage	= $6;
+	$$.udp_src_port		= 0;
+	$$.udp_dst_port		= 0;
 }
-| '[' seq ']'		{
+| '[' seq opt_udp_encaps_info ']'	{
 	$$.protocol		= IPPROTO_TCP;
 	$$.payload_bytes	= $2.payload_bytes;
 	$$.start_sequence	= $2.start_sequence;
+	$$.udp_src_port		= $3.udp_src_port;
+	$$.udp_dst_port		= $3.udp_dst_port;
 }
 ;
 
@@ -2627,6 +2602,25 @@ seq
 	}
 	$$.start_sequence = $1;
 	$$.payload_bytes = $5;
+	$$.absolute = false;
+}
+| INTEGER ':' INTEGER '(' INTEGER ')' '!' {
+	if (!is_valid_u32($1)) {
+		semantic_error("TCP start sequence number out of range");
+	}
+	if (!is_valid_u32($3)) {
+		semantic_error("TCP end sequence number out of range");
+	}
+	if (!is_valid_u16($5)) {
+		semantic_error("TCP payload size out of range");
+	}
+	if ($3 != ($1 +$5)) {
+		semantic_error("inconsistent TCP sequence numbers and "
+			       "payload size");
+	}
+	$$.start_sequence = $1;
+	$$.payload_bytes = $5;
+	$$.absolute = true;
 }
 ;
 
@@ -2858,6 +2852,9 @@ expression
 | linger            {
 	$$ = $1;
 }
+| tcp_function_set  {
+	$$ = $1;
+}
 | sf_hdtr           {
 	$$ = $1;
 }
@@ -2954,6 +2951,9 @@ expression
 | sctp_add_streams  {
 	$$ = $1;
 }
+| sctp_udpencaps  {
+	$$ = $1;
+}
 | null              {
 	$$ = $1;
 }
@@ -3168,7 +3168,9 @@ l_onoff
 	}
 	$$ = new_integer_expression($3, "%ld");
 }
-| ONOFF '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+| ONOFF '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
 ;
 
 l_linger
@@ -3178,7 +3180,9 @@ l_linger
 	}
 	$$ = new_integer_expression($3, "%ld");
 }
-| LINGER '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+| LINGER '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
 ;
 
 linger
@@ -3190,6 +3194,42 @@ linger
 }
 ;
 
+function_set_name
+: FUNCTION_SET_NAME '=' STRING {
+	$$ = new_expression(EXPR_STRING);
+	$$->value.string = $3;
+	$$->format = "\"%s\"";
+}
+| FUNCTION_SET_NAME '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+pcbcnt
+: PCBCNT '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("linger out of range");
+	}
+	$$ = new_integer_expression($3, "%lu");
+}
+| PCBCNT '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+tcp_function_set
+: '{' function_set_name ',' pcbcnt '}' {
+#if defined(__FreeBSD__)
+	$$ = new_expression(EXPR_TCP_FUNCTION_SET);
+	$$->value.tcp_function_set = calloc(1, sizeof(struct tcp_function_set_expr));
+	$$->value.tcp_function_set->function_set_name = $2;
+	$$->value.tcp_function_set->pcbcnt = $4;
+#else
+	$$ = NULL;
+#endif
+}
+;
+
 sf_hdtr
 : '{' SF_HDTR_HEADERS '(' decimal_integer ')' '=' array ','
       SF_HDTR_TRAILERS '('decimal_integer ')' '=' array '}' {
@@ -5563,7 +5603,44 @@ sctp_stream_change_event
 	$$->value.sctp_stream_change_event->strchange_assoc_id = $10;
 	$$->value.sctp_stream_change_event->strchange_instrms = $12;
 	$$->value.sctp_stream_change_event->strchange_outstrms = $14;
+}
+;
 
+sue_address
+: SUE_ADDRESS '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+| SUE_ADDRESS '=' sockaddr {
+	$$ = $3;
+}
+;
+
+sue_port
+: SUE_PORT '=' _HTONS_ '(' INTEGER ')' {
+	if (!is_valid_u16($5)) {
+		semantic_error("sue_port out of range");
+	}
+	$$ = new_integer_expression(htons($5), "%u");
+}
+| SUE_PORT '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+sctp_udpencaps
+: '{' SUE_ASSOC_ID '=' sctp_assoc_id ',' sue_address ',' sue_port '}' {
+	$$ = new_expression(EXPR_SCTP_UDPENCAPS);
+	$$->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+	$$->value.sctp_udpencaps->sue_assoc_id = $4;
+	$$->value.sctp_udpencaps->sue_address = $6;
+	$$->value.sctp_udpencaps->sue_port = $8;
+}
+| '{' sue_address ',' sue_port '}' {
+	$$ = new_expression(EXPR_SCTP_UDPENCAPS);
+	$$->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+	$$->value.sctp_udpencaps->sue_assoc_id = new_expression(EXPR_ELLIPSIS);
+	$$->value.sctp_udpencaps->sue_address = $2;
+	$$->value.sctp_udpencaps->sue_port = $4;
 }
 ;
 
diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c
index 07ebe30a5e95ef864bab4bd468aa00d0e8c6d6e4..3debc5d7c29d03251c35e1bb857dd1cb88c3f9db 100644
--- a/gtests/net/packetdrill/run_packet.c
+++ b/gtests/net/packetdrill/run_packet.c
@@ -189,7 +189,7 @@ static struct socket *setup_new_child_socket(struct state *state, const struct p
 	assert(socket->state == SOCKET_INIT);
 	socket->state = SOCKET_PASSIVE_PACKET_RECEIVED;
 	socket->address_family = packet_address_family(packet);
-	socket->protocol = packet_ip_protocol(packet);
+	socket->protocol = packet_ip_protocol(packet, config->udp_encaps);
 
 	/* Set script info for this socket using script packet. */
 	struct tuple tuple;
@@ -330,6 +330,7 @@ static struct socket *handle_listen_for_script_packet(
 
 #if defined(DEBUG)
 	if (debug_logging) {
+#if 0
 		char local_string[ADDR_STR_LEN];
 		char remote_string[ADDR_STR_LEN];
 		DEBUGP("live: local: %s.%d\n",
@@ -338,6 +339,7 @@ static struct socket *handle_listen_for_script_packet(
 		DEBUGP("live: remote: %s.%d\n",
 		       ip_to_string(&socket->live.remote.ip, remote_string),
 		       ntohs(socket->live.remote.port));
+#endif
 		if (packet->tcp != NULL) {
 			DEBUGP("live: ISN: %u\n", socket->live.remote_isn);
 		} else {
@@ -413,7 +415,7 @@ static struct socket *handle_connect_for_script_packet(
 		state->socket_under_test = socket;
 		assert(socket->state == SOCKET_INIT);
 		socket->address_family = packet_address_family(packet);
-		socket->protocol = packet_ip_protocol(packet);
+		socket->protocol = packet_ip_protocol(packet, config->udp_encaps);
 
 		socket->script.fd	 = -1;
 
@@ -587,9 +589,12 @@ static int offset_sack_blocks(struct packet *packet,
 }
 
 static int map_inbound_icmp_sctp_packet(
-	struct socket *socket, struct packet *live_packet, char **error)
+	struct socket *socket,
+	struct packet *live_packet,
+	bool encapsulated,
+	char **error)
 {
-	u32 *v_tag = packet_echoed_sctp_v_tag(live_packet);
+	u32 *v_tag = packet_echoed_sctp_v_tag(live_packet, encapsulated);
 	*v_tag = htonl(socket->live.remote_initiate_tag);
 	return STATUS_OK;
 }
@@ -598,9 +603,12 @@ static int map_inbound_icmp_sctp_packet(
  * The Linux TCP layer ignores ICMP messages with bogus sequence numbers.
  */
 static int map_inbound_icmp_tcp_packet(
-	struct socket *socket, struct packet *live_packet, char **error)
+	struct socket *socket,
+	struct packet *live_packet,
+	bool encapsulated,
+	char **error)
 {
-	u32 *seq = packet_echoed_tcp_seq(live_packet);
+	u32 *seq = packet_echoed_tcp_seq(live_packet, encapsulated);
 	/* FIXME: There is currently no way to access the TCP flags */
 	bool is_syn = false;
 	u32 seq_offset = local_seq_script_to_live_offset(socket, is_syn);
@@ -623,15 +631,24 @@ static int map_inbound_icmp_udplite_packet(
 }
 
 static int map_inbound_icmp_packet(
-	struct socket *socket, struct packet *live_packet, char **error)
+	struct socket *socket,
+	struct packet *live_packet,
+	u8 udp_encaps,
+	char **error)
 {
-	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)
+	int protocol;
+
+	protocol = packet_echoed_ip_protocol(live_packet);
+	if ((protocol == IPPROTO_UDP) && (udp_encaps != 0)) {
+		protocol = udp_encaps;
+	}
+	if (protocol == IPPROTO_SCTP)
+		return map_inbound_icmp_sctp_packet(socket, live_packet, udp_encaps == IPPROTO_SCTP, error);
+	else if (protocol == IPPROTO_TCP)
+		return map_inbound_icmp_tcp_packet(socket, live_packet, udp_encaps == IPPROTO_TCP, error);
+	else if (protocol == IPPROTO_UDP)
 		return map_inbound_icmp_udp_packet(socket, live_packet, error);
-	else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDPLITE)
+	else if (protocol == IPPROTO_UDPLITE)
 		return map_inbound_icmp_udplite_packet(socket, live_packet,
 						       error);
 	else
@@ -899,17 +916,18 @@ static int map_inbound_sctp_packet(
  * on failure returns STATUS_ERR and sets error message.
  */
 static int map_inbound_packet(
-	struct socket *socket, struct packet *live_packet, char **error)
+	struct socket *socket, struct packet *live_packet, u8 udp_encaps,
+	char **error)
 {
 	DEBUGP("map_inbound_packet\n");
 
 	/* Remap packet to live values. */
 	struct tuple live_inbound;
 	socket_get_inbound(&socket->live, &live_inbound);
-	set_packet_tuple(live_packet, &live_inbound);
+	set_packet_tuple(live_packet, &live_inbound, udp_encaps != 0);
 
 	if ((live_packet->icmpv4 != NULL) || (live_packet->icmpv6 != NULL))
-		return map_inbound_icmp_packet(socket, live_packet, error);
+		return map_inbound_icmp_packet(socket, live_packet, udp_encaps, error);
 
 	if (live_packet->sctp) {
 		return map_inbound_sctp_packet(socket, live_packet, error);
@@ -922,8 +940,10 @@ static int map_inbound_packet(
 	/* Remap the sequence number from script sequence number to live. */
 	const bool is_syn = live_packet->tcp->syn;
 	const u32 seq_offset = remote_seq_script_to_live_offset(socket, is_syn);
-	live_packet->tcp->seq =
-	    htonl(ntohl(live_packet->tcp->seq) + seq_offset);
+	if ((live_packet->flags & FLAG_ABSOLUTE_SEQ) == 0) {
+		live_packet->tcp->seq =
+		    htonl(ntohl(live_packet->tcp->seq) + seq_offset);
+	}
 
 	/* Remap the ACK and SACKs from script sequence number to live. */
 	const u32 ack_offset = local_seq_script_to_live_offset(socket, is_syn);
@@ -1143,6 +1163,7 @@ static int map_outbound_live_packet(
 	struct packet *live_packet,
 	struct packet *actual_packet,
 	struct packet *script_packet,
+	u8 udp_encaps,
 	char **error)
 {
 	DEBUGP("map_outbound_live_packet\n");
@@ -1156,7 +1177,7 @@ static int map_outbound_live_packet(
 
 	/* Rewrite 4-tuple to be outbound script values. */
 	socket_get_outbound(&socket->script, &script_outbound);
-	set_packet_tuple(actual_packet, &script_outbound);
+	set_packet_tuple(actual_packet, &script_outbound, udp_encaps != 0);
 
 	if (live_packet->sctp) {
 		return map_outbound_live_sctp_packet(socket, live_packet, actual_packet, script_packet, error);
@@ -1169,9 +1190,12 @@ static int map_outbound_live_packet(
 	/* Rewrite TCP sequence number from live to script space. */
 	const bool is_syn = live_packet->tcp->syn;
 	const u32 seq_offset = local_seq_live_to_script_offset(socket, is_syn);
-	actual_packet->tcp->seq =
-	    htonl(ntohl(live_packet->tcp->seq) + seq_offset);
-
+	if ((script_packet->flags & FLAG_ABSOLUTE_SEQ) == 0) {
+		actual_packet->tcp->seq =
+		    htonl(ntohl(live_packet->tcp->seq) + seq_offset);
+	} else {
+		actual_packet->tcp->seq = live_packet->tcp->seq;
+	}
 	/* Rewrite ACKs and SACKs from live to script space. */
 	const u32 ack_offset = remote_seq_live_to_script_offset(socket, is_syn);
 	if (actual_packet->tcp->ack)
@@ -1312,7 +1336,7 @@ static int tcp_options_allowance(const struct packet *actual_packet,
 static int verify_ipv4(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct ipv4 *actual_ipv4 = actual_packet->headers[layer].h.ipv4;
 	const struct ipv4 *script_ipv4 = script_packet->headers[layer].h.ipv4;
@@ -1339,6 +1363,18 @@ static int verify_ipv4(
 				ntohs(actual_ipv4->tot_len), error))
 			return STATUS_ERR;
 		break;
+	case IPPROTO_UDP:
+		if (udp_encaps == IPPROTO_TCP) {
+			if (check_field("ipv4_total_length",
+					(ntohs(script_ipv4->tot_len) +
+					 tcp_options_allowance(actual_packet,
+							       script_packet)),
+					ntohs(actual_ipv4->tot_len), error))
+				return STATUS_ERR;
+			break;
+		} else if (udp_encaps == IPPROTO_SCTP) {
+			break;
+		}
 	default:
 		if (check_field("ipv4_total_length",
 				ntohs(script_ipv4->tot_len),
@@ -1360,7 +1396,7 @@ static int verify_ipv4(
 static int verify_ipv6(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct ipv6 *actual_ipv6 = actual_packet->headers[layer].h.ipv6;
 	const struct ipv6 *script_ipv6 = script_packet->headers[layer].h.ipv6;
@@ -1384,6 +1420,18 @@ static int verify_ipv6(
 				ntohs(actual_ipv6->payload_len), error))
 			return STATUS_ERR;
 		break;
+	case IPPROTO_UDP:
+		if (udp_encaps != 0) {
+			if (check_field("ipv6_payload_len",
+					(ntohs(script_ipv6->payload_len) +
+					 tcp_options_allowance(actual_packet,
+							       script_packet)),
+					ntohs(actual_ipv6->payload_len), error))
+				return STATUS_ERR;
+			break;
+		} else if (udp_encaps == IPPROTO_SCTP) {
+			break;
+		}
 	default:
 		if (check_field("ipv6_payload_len",
 				ntohs(script_ipv6->payload_len),
@@ -1456,7 +1504,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length,
 			    (flags & FLAG_RECONFIG_RESP_SN_NOCHECK ? STATUS_OK :
 			    check_field("outgoing_ssn_reset_request_parameter.resp_sn",
 			                ntohl(script_reset->respsn),
-		                        ntohl(live_reset->respsn),
+			                ntohl(live_reset->respsn),
 			                error)) ||
 			    (flags & FLAG_RECONFIG_LAST_TSN_NOCHECK ? STATUS_OK :
 			    check_field("outgoing_ssn_reset_request_parameter.last_tsn",
@@ -1472,7 +1520,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length,
 						    ntohs(live_reset->sids[i]),
 						    error)) {
 					return STATUS_ERR;
-                        	}
+				}
 			}
 			break;
 		}
@@ -1496,7 +1544,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length,
 						    ntohs(live_reset->sids[i]),
 						    error)) {
 					return STATUS_ERR;
-                        	}
+				}
 			}
 			break;
 		}
@@ -2286,7 +2334,7 @@ static int verify_i_forward_tsn_chunk(struct sctp_i_forward_tsn_chunk *actual_ch
 static int verify_sctp(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	struct sctp_chunks_iterator iter;
 	struct sctp_chunk *actual_chunk;
@@ -2469,11 +2517,23 @@ static int verify_sctp(
 static int verify_tcp(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct tcp *actual_tcp = actual_packet->headers[layer].h.tcp;
 	const struct tcp *script_tcp = script_packet->headers[layer].h.tcp;
-
+	const struct udp *actual_udp =  (struct udp *)actual_tcp - 1;
+	const struct udp *script_udp =  (struct udp *)script_tcp - 1;
+
+	if (udp_encaps != 0) {
+		if (check_field("udp_src_port",
+				ntohs(script_udp->src_port),
+				ntohs(actual_udp->src_port), error) ||
+		    check_field("udp_dst_port",
+				ntohs(script_udp->dst_port),
+				ntohs(actual_udp->dst_port), error)) {
+			return STATUS_ERR;
+		}
+	}
 	if (check_field("tcp_data_offset",
 			(script_tcp->doff +
 			 tcp_options_allowance(actual_packet,
@@ -2528,11 +2588,14 @@ static int verify_tcp(
 static int verify_udp(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct udp *actual_udp = actual_packet->headers[layer].h.udp;
 	const struct udp *script_udp = script_packet->headers[layer].h.udp;
 
+	if (udp_encaps != 0) {
+		return STATUS_OK;
+	}
 	if (check_field("udp_len",
 			ntohs(script_udp->len),
 			ntohs(actual_udp->len), error))
@@ -2545,7 +2608,7 @@ static int verify_udp(
 static int verify_udplite(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct udplite *actual_udplite =
 	    actual_packet->headers[layer].h.udplite;
@@ -2562,7 +2625,7 @@ static int verify_udplite(
 static int verify_gre(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct gre *actual_gre = actual_packet->headers[layer].h.gre;
 	const struct gre *script_gre = script_packet->headers[layer].h.gre;
@@ -2579,7 +2642,7 @@ static int verify_gre(
 static int verify_mpls(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	const struct header *actual_header = &actual_packet->headers[layer];
 	const struct header *script_header = &script_packet->headers[layer];
@@ -2608,13 +2671,13 @@ static int verify_mpls(
 typedef int (*verifier_func)(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error);
+	int layer, u8 udp_encaps, char **error);
 
 /* Verify that required actual header fields are as the script expected. */
 static int verify_header(
 	const struct packet *actual_packet,
 	const struct packet *script_packet,
-	int layer, char **error)
+	int layer, u8 udp_encaps, char **error)
 {
 	verifier_func verifiers[HEADER_NUM_TYPES] = {
 		[HEADER_IPV4]		= verify_ipv4,
@@ -2644,13 +2707,13 @@ static int verify_header(
 	assert(type < HEADER_NUM_TYPES);
 	verifier = verifiers[type];
 	assert(verifier != NULL);
-	return verifier(actual_packet, script_packet, layer, error);
+	return verifier(actual_packet, script_packet, layer, udp_encaps, error);
 }
 
 /* Verify that required actual header fields are as the script expected. */
 static int verify_outbound_live_headers(
 	const struct packet *actual_packet,
-	const struct packet *script_packet, char **error)
+	const struct packet *script_packet, u8 udp_encaps, char **error)
 {
 	const int actual_headers = packet_header_count(actual_packet);
 	const int script_headers = packet_header_count(script_packet);
@@ -2676,7 +2739,8 @@ static int verify_outbound_live_headers(
 		if (script_packet->headers[i].type == HEADER_NONE)
 			break;
 
-		if (verify_header(actual_packet, script_packet, i, error))
+		if (verify_header(actual_packet, script_packet, i,
+				  udp_encaps, error))
 			return STATUS_ERR;
 	}
 
@@ -2795,11 +2859,13 @@ static int verify_outbound_live_packet(
 
 	/* Map live packet values into script space for easy comparison. */
 	if (map_outbound_live_packet(
-		    socket, live_packet, actual_packet, script_packet, error))
+		    socket, live_packet, actual_packet, script_packet,
+		    state->config->udp_encaps, error))
 		goto out;
 
 	/* Verify actual IP, TCP/UDP header values matched expected ones. */
-	if (verify_outbound_live_headers(actual_packet, script_packet, error)) {
+	if (verify_outbound_live_headers(actual_packet, script_packet,
+					 state->config->udp_encaps, error)) {
 		non_fatal = true;
 		goto out;
 	}
@@ -2835,8 +2901,8 @@ out:
 	add_packet_dump(error, "script", script_packet, script_usecs,
 			DUMP_SHORT);
 	if (actual_packet != NULL) {
-		add_packet_dump(error, "actual", actual_packet, actual_usecs,
-				DUMP_SHORT);
+		add_packet_dump(error, "actual", actual_packet,
+				actual_usecs, DUMP_SHORT);
 		packet_free(actual_packet);
 	}
 	if (result == STATUS_ERR &&
@@ -2857,7 +2923,8 @@ static int sniff_outbound_live_packet(
 	enum direction_t direction = DIRECTION_INVALID;
 	assert(*packet == NULL);
 	while (1) {
-		if (netdev_receive(state->netdev, packet, error))
+		if (netdev_receive(state->netdev, state->config->udp_encaps,
+				   packet, error))
 			return STATUS_ERR;
 		/* See if the packet matches an existing, known socket. */
 		socket = find_socket_for_live_packet(state, *packet,
@@ -3113,8 +3180,24 @@ static int do_outbound_script_packet(
 				    state, live_packet->time_usecs));
 
 	/* Save the TCP header so we can reset the connection at the end. */
-	if (live_packet->tcp)
+	if (live_packet->tcp) {
 		socket->last_outbound_tcp_header = *(live_packet->tcp);
+		if (live_packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			struct udp *udp = (struct udp *)(live_packet->tcp) - 1;
+
+			socket->last_outbound_udp_encaps_src_port = ntohs(udp->src_port);
+			socket->last_outbound_udp_encaps_dst_port = ntohs(udp->dst_port);
+		}
+	}
+
+	if (live_packet->sctp) {
+		if (live_packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			struct udp *udp = (struct udp *)(live_packet->sctp) - 1;
+
+			socket->last_outbound_udp_encaps_src_port = ntohs(udp->src_port);
+			socket->last_outbound_udp_encaps_dst_port = ntohs(udp->dst_port);
+		}
+	}
 
 	/* Verify the bits the kernel sent were what the script expected. */
 	result = verify_outbound_live_packet(
@@ -3211,9 +3294,21 @@ static int do_inbound_script_packet(
 								break;
 							}
 						}
-						assert(packet->headers[i + 1].type == HEADER_SCTP);
-						packet->headers[i].total_bytes += temp_offset;
-						packet->headers[i + 1].total_bytes += temp_offset;
+						if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+							struct udp *udp;
+
+							assert(packet->headers[i + 1].type == HEADER_UDP);
+							assert(packet->headers[i + 2].type == HEADER_SCTP);
+							packet->headers[i].total_bytes += temp_offset;
+							packet->headers[i + 1].total_bytes += temp_offset;
+							packet->headers[i + 2].total_bytes += temp_offset;
+							udp = ((struct udp *)packet->sctp) - 1;
+							udp->len = htons(ntohs(udp->len) + temp_offset);
+						} else {
+							assert(packet->headers[i + 1].type == HEADER_SCTP);
+							packet->headers[i].total_bytes += temp_offset;
+							packet->headers[i + 1].total_bytes += temp_offset;
+						}
 						offset += temp_offset;
 					}
 					if (((packet->flags & FLAGS_SCTP_BAD_CRC32C) == 0) &&
@@ -3248,9 +3343,21 @@ static int do_inbound_script_packet(
 								break;
 							}
 						}
-						assert(packet->headers[i + 1].type == HEADER_SCTP);
-						packet->headers[i].total_bytes += temp_offset;
-						packet->headers[i + 1].total_bytes += temp_offset;
+						if (packet->flags & FLAGS_UDP_ENCAPSULATED) {
+							struct udp *udp;
+
+							assert(packet->headers[i + 1].type == HEADER_UDP);
+							assert(packet->headers[i + 2].type == HEADER_SCTP);
+							packet->headers[i].total_bytes += temp_offset;
+							packet->headers[i + 1].total_bytes += temp_offset;
+							packet->headers[i + 2].total_bytes += temp_offset;
+							udp = ((struct udp *)packet->sctp) - 1;
+							udp->len = htons(ntohs(udp->len) + temp_offset);
+						} else {
+							assert(packet->headers[i + 1].type == HEADER_SCTP);
+							packet->headers[i].total_bytes += temp_offset;
+							packet->headers[i + 1].total_bytes += temp_offset;
+						}
 						offset += temp_offset;
 					}
 					break;
@@ -3265,7 +3372,8 @@ static int do_inbound_script_packet(
 	/* Start with a bit-for-bit copy of the packet from the script. */
 	struct packet *live_packet = packet_copy(packet);
 	/* Map packet fields from script values to live values. */
-	if (map_inbound_packet(socket, live_packet, error))
+	if (map_inbound_packet(socket, live_packet, state->config->udp_encaps,
+			       error))
 		goto out;
 
 	verbose_packet_dump(state, "inbound injected", live_packet,
@@ -3277,7 +3385,22 @@ static int do_inbound_script_packet(
 		socket->last_injected_tcp_header = *(live_packet->tcp);
 		socket->last_injected_tcp_payload_len =
 			packet_payload_len(live_packet);
+		if (live_packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			struct udp *udp = (struct udp *)(live_packet->tcp) - 1;
+
+			socket->last_injected_udp_encaps_src_port = ntohs(udp->src_port);
+			socket->last_injected_udp_encaps_dst_port = ntohs(udp->dst_port);
+		}
+	}
+	if (live_packet->sctp) {
+		if (live_packet->flags & FLAGS_UDP_ENCAPSULATED) {
+			struct udp *udp = (struct udp *)(live_packet->sctp) - 1;
+
+			socket->last_injected_udp_encaps_src_port = ntohs(udp->src_port);
+			socket->last_injected_udp_encaps_dst_port = ntohs(udp->dst_port);
+		}
 	}
+
 	if (((live_packet->ipv4 != NULL) &&
 	     (live_packet->ipv4->src_ip.s_addr == htonl(INADDR_ANY))) ||
 	    ((live_packet->ipv6 != NULL) &&
@@ -3288,7 +3411,7 @@ static int do_inbound_script_packet(
 			DEBUGP("socket_under_test = %p\n", state->socket_under_test);
 			state->socket_under_test = setup_new_child_socket(state, packet);
 			socket_get_inbound(&state->socket_under_test->live, &live_inbound);
-			set_packet_tuple(live_packet, &live_inbound);
+			set_packet_tuple(live_packet, &live_inbound, state->config->udp_encaps != 0);
 	}
 
 	/* Inject live packet into kernel. */
@@ -3360,7 +3483,17 @@ int reset_connection(struct state *state, struct socket *socket)
 	struct packet *packet = NULL;
 	struct tuple live_inbound;
 	int result = STATUS_OK;
+	u16 udp_src_port;
+	u16 udp_dst_port;
 
+	if (socket->last_outbound_udp_encaps_src_port != 0 ||
+	    socket->last_outbound_udp_encaps_dst_port != 0) {
+		udp_src_port = socket->last_outbound_udp_encaps_dst_port;
+		udp_dst_port = socket->last_outbound_udp_encaps_src_port;
+	} else {
+		udp_src_port = socket->last_injected_udp_encaps_src_port;
+		udp_dst_port = socket->last_injected_udp_encaps_dst_port;
+	}
 	/* Pick TCP header fields to be something the kernel will accept. */
 	if (socket->last_injected_tcp_header.ack) {
 		/* If we've already injected something, then use a sequence
@@ -3393,13 +3526,14 @@ int reset_connection(struct state *state, struct socket *socket)
 
 	packet = new_tcp_packet(socket->address_family,
 				DIRECTION_INBOUND, ECN_NONE,
-				"R.", seq, 0, ack_seq, window, NULL, false, &error);
+				"R.", seq, 0, ack_seq, window, NULL, false,
+				false, udp_src_port, udp_dst_port, &error);
 	if (packet == NULL)
 		die("%s", error);
 
 	/* Rewrite addresses and port to match inbound live traffic. */
 	socket_get_inbound(&socket->live, &live_inbound);
-	set_packet_tuple(packet, &live_inbound);
+	set_packet_tuple(packet, &live_inbound, state->config->udp_encaps != 0);
 
 	/* Inject live packet into kernel. */
 	result = send_live_ip_packet(state->netdev, packet);
@@ -3421,8 +3555,10 @@ int abort_association(struct state *state, struct socket *socket)
 	struct sctp_chunk_list *chunk_list;
 	struct sctp_cause_list *cause_list;
 	struct tuple live_inbound;
-	int result = STATUS_OK;
+	int result;
 	s64 flgs;
+	u16 udp_src_port;
+	u16 udp_dst_port;
 
 	if ((socket->live.local_initiate_tag == 0) &&
 	    (socket->live.remote_initiate_tag == 0)) {
@@ -3433,6 +3569,14 @@ int abort_association(struct state *state, struct socket *socket)
 	} else {
 		flgs = SCTP_ABORT_CHUNK_T_BIT;
 	}
+	if (socket->last_outbound_udp_encaps_src_port != 0 ||
+	    socket->last_outbound_udp_encaps_dst_port != 0) {
+		udp_src_port = socket->last_outbound_udp_encaps_dst_port;
+		udp_dst_port = socket->last_outbound_udp_encaps_src_port;
+	} else {
+		udp_src_port = socket->last_injected_udp_encaps_src_port;
+		udp_dst_port = socket->last_injected_udp_encaps_dst_port;
+	}
 	cause_list = sctp_cause_list_new();
 	sctp_cause_list_append(cause_list,
 	                       sctp_user_initiated_abort_cause_new("packetdrill cleaning up"));
@@ -3440,12 +3584,13 @@ int abort_association(struct state *state, struct socket *socket)
 	sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(flgs, cause_list));
 	packet = new_sctp_packet(socket->address_family,
 				 DIRECTION_INBOUND, ECN_NONE, -1, false,
-				 chunk_list, &error);
+				 chunk_list, udp_src_port, udp_dst_port,
+				 &error);
 	if (packet == NULL)
 		die("%s", error);
 	/* Rewrite addresses and port to match inbound live traffic. */
 	socket_get_inbound(&socket->live, &live_inbound);
-	set_packet_tuple(packet, &live_inbound);
+	set_packet_tuple(packet, &live_inbound, state->config->udp_encaps != 0);
 	/* Rewrite the verification tag in the SCTP common header */
 	if (socket->live.local_initiate_tag != 0) {
 		packet->sctp->v_tag = htonl(socket->live.local_initiate_tag);
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 88db2335e4567549ae218ac672644f5e29d0fc13..2b33cc82a57c05a9ba33a39dcac59e7c57093bd2 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -696,6 +696,24 @@ int check_u8_expr(struct expression *expr, u8 value, char *val_name, char **erro
 #endif
 
 #if defined(__FreeBSD__) || defined(linux)
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+int check_u16_htons_expr(struct expression *expr, u16 value, char *val_name, char **error) {
+	if (expr->type != EXPR_ELLIPSIS) {
+		u16 script_val;
+
+		if (get_u16(expr, &script_val, error)) {
+			return STATUS_ERR;
+		}
+		if (ntohs(script_val) != ntohs(value)) {
+			asprintf(error, "%s: expected: %hu actual: %hu",
+				 val_name, ntohs(script_val), ntohs(value));
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+}
+#endif
+
 int check_u16_expr(struct expression *expr, u16 value, char *val_name, char **error) {
 	if (expr->type != EXPR_ELLIPSIS) {
 		u16 script_val;
@@ -736,7 +754,7 @@ int check_u32_hton_expr(struct expression *expr, u32 value, char *val_name, char
 		}
 		if (htonl(value) != htonl(script_val)) {
 			asprintf(error, "%s: expected: %u actual: %u", val_name,
-				 htonl(script_val), htonl(value));
+				 ntohl(script_val), ntohl(value));
 			return STATUS_ERR;
 		}
 	}
@@ -1999,7 +2017,14 @@ static int syscall_socket(struct state *state, struct syscall_spec *syscall,
 		return STATUS_ERR;
 
 	if (result >= 0) {
+		const int off = 0;
+
 		live_fd = result;
+		/* If IPv4-mapped IPv6 addresses are used, disable IPV6_V6ONLY */
+		if (state->config->socket_domain == AF_INET6 &&
+		    state->config->wire_protocol == AF_INET) {
+			setsockopt(live_fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(int));
+		}
 		if (get_s32(syscall->result, &script_fd, error))
 			return STATUS_ERR;
 		if (run_syscall_socket(state, domain, protocol,
@@ -3138,6 +3163,41 @@ static int check_sctp_authchunks(struct sctp_authchunks_expr *expr,
 }
 #endif
 
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+static int check_sctp_udpencaps(struct sctp_udpencaps_expr *expr,
+			        struct sctp_udpencaps *sctp_udpencaps,
+			        char **error) {
+	if (check_u16_htons_expr(expr->sue_port, sctp_udpencaps->sue_port,
+				 "sctp_udpencaps.sue_port", error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+#endif
+
+#ifdef TCP_FUNCTION_BLK
+static int check_tcp_function_set(struct tcp_function_set_expr *expr,
+			          struct tcp_function_set *tcp_function_set,
+			          char **error) {
+	if (expr->function_set_name->type != EXPR_ELLIPSIS) {
+		if (strncmp(expr->function_set_name->value.string,
+		            tcp_function_set->function_set_name,
+		            TCP_FUNCTION_NAME_LEN_MAX)) {
+			asprintf(error, "tcp_function_set.function_set_name: expected: %s, actual: %.*s\n",
+			         expr->function_set_name->value.string,
+			         TCP_FUNCTION_NAME_LEN_MAX,
+			         tcp_function_set->function_set_name);
+			return STATUS_ERR;
+		}
+	}
+	if (check_u32_expr(expr->pcbcnt, tcp_function_set->pcbcnt,
+	                   "tcp_function_set.pcbcnt", error))
+		return STATUS_ERR;
+	return STATUS_OK;
+}
+
+#endif
+
 static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 			      struct expression_list *args, char **error)
 {
@@ -3428,6 +3488,37 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 			return STATUS_ERR;
 		}
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS: {
+		struct sctp_udpencaps_expr *expr_udpencaps = val_expression->value.sctp_udpencaps;
+		struct sctp_udpencaps *live_udpencaps = malloc(sizeof(struct sctp_udpencaps));
+
+		live_optlen = (socklen_t) sizeof(struct sctp_udpencaps);
+		memset(live_udpencaps, 0, sizeof(struct sctp_udpencaps));
+		if (get_sctp_assoc_t(expr_udpencaps->sue_assoc_id,
+				     &(live_udpencaps->sue_assoc_id), error)) {
+			free(live_udpencaps);
+			return STATUS_ERR;
+		}
+		if (get_sockstorage_arg(expr_udpencaps->sue_address,
+					&(live_udpencaps->sue_address), live_fd)) {
+			asprintf(error, "can't determine sue_address");
+			free(live_udpencaps);
+			return STATUS_ERR;
+		}
+		live_optval = live_udpencaps;
+		break;
+	}
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET: {
+		struct tcp_function_set *live_tcp_function_set = malloc(sizeof(struct tcp_function_set));
+
+		memset(live_tcp_function_set, 0, sizeof(struct tcp_function_set));
+		live_optval = live_tcp_function_set;
+		break;
+	}
 #endif
 	case EXPR_LIST:
 		s32_bracketed_arg(args, 3, &script_optval, error);
@@ -3575,6 +3666,16 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_ADD_STREAMS:
 		// SCTP_ADD_STREAMS should not be a successfull option
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS:
+		result = check_sctp_udpencaps(val_expression->value.sctp_udpencaps, live_optval, error);
+		break;
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET:
+		result = check_tcp_function_set(val_expression->value.tcp_function_set, live_optval, error);
+		break;
 #endif
 	case EXPR_LIST:
 		if (*(int*)live_optval != script_optval) {
@@ -3672,6 +3773,13 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_ADD_STREAMS
 	struct sctp_add_streams add_streams;
 #endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	struct sctp_udpencaps udpencaps;
+#endif
+#ifdef TCP_FUNCTION_BLK
+	struct tcp_function_set tcp_function_set;
+#endif
+
 	if (check_arg_count(args, 5, error))
 		return STATUS_ERR;
 	if (s32_arg(args, 0, &script_fd, error))
@@ -3857,7 +3965,7 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_GET_PEER_ADDR_INFO
 	case EXPR_SCTP_PADDRINFO:
 		if (get_sctp_assoc_t(val_expression->value.sctp_paddrinfo->spinfo_assoc_id,
-				    &paddrinfo.spinfo_assoc_id, error)) {
+				     &paddrinfo.spinfo_assoc_id, error)) {
 			return STATUS_ERR;
 		}
 		if (get_sockstorage_arg(val_expression->value.sctp_paddrinfo->spinfo_address,
@@ -4164,7 +4272,6 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 			expr = get_arg(list, i, error);
 			get_u16(expr, &(reset_streams->srs_stream_list[i]), error);
 		}
-
 		optval = reset_streams;
 		break;
 	}
@@ -4183,9 +4290,41 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 			    &add_streams.sas_outstrms, error)) {
 			return STATUS_ERR;
 		}
-
 		optval = &add_streams;
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS:
+		if (get_sctp_assoc_t(val_expression->value.sctp_udpencaps->sue_assoc_id,
+				     &udpencaps.sue_assoc_id, error)) {
+			return STATUS_ERR;
+		}
+		if (get_sockstorage_arg(val_expression->value.sctp_udpencaps->sue_address,
+					&udpencaps.sue_address, live_fd)) {
+			asprintf(error, "can't determine sue_address");
+			return STATUS_ERR;
+		}
+		if (get_u16(val_expression->value.sctp_udpencaps->sue_port,
+			    &udpencaps.sue_port, error)) {
+			return STATUS_ERR;
+		}
+		optval = &udpencaps;
+		break;
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET:
+		if (check_type(val_expression->value.tcp_function_set->function_set_name, EXPR_STRING, error)) {
+			return STATUS_ERR;
+		}
+		strncpy(tcp_function_set.function_set_name,
+		        val_expression->value.tcp_function_set->function_set_name->value.string,
+		        TCP_FUNCTION_NAME_LEN_MAX);
+		if (get_u32(val_expression->value.tcp_function_set->pcbcnt,
+			    &tcp_function_set.pcbcnt, error)) {
+			return STATUS_ERR;
+		}
+		optval = &tcp_function_set;
+		break;
 #endif
 	default:
 		asprintf(error, "unsupported value type: %s",
@@ -4193,6 +4332,8 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 		return STATUS_ERR;
 		break;
 	}
+	assert(optval != NULL);
+
 	begin_syscall(state, syscall);
 
 	result = setsockopt(live_fd, level, optname, optval, optlen);
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 5a0e16d73a7723b44a0a6e9d6fef9824a639755a..571d21d051da6d503a43322ffc2e6e3c8274e7e1 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -53,71 +53,73 @@ struct expression_type_entry {
 	const char *name;
 };
 struct expression_type_entry expression_type_table[] = {
-	{ EXPR_NONE,                 "none" },
-	{ EXPR_NULL,		     "null" },
-	{ EXPR_ELLIPSIS,             "ellipsis" },
-	{ EXPR_INTEGER,              "integer" },
-	{ EXPR_WORD,                 "word" },
-	{ EXPR_STRING,               "string" },
-	{ EXPR_SOCKET_ADDRESS_IPV4,  "sockaddr_in" },
-	{ EXPR_SOCKET_ADDRESS_IPV6,  "sockaddr_in6" },
-	{ EXPR_LINGER,               "linger" },
-	{ EXPR_BINARY,               "binary_expression" },
-	{ EXPR_LIST,                 "list" },
-	{ EXPR_IOVEC,                "iovec" },
-	{ EXPR_MSGHDR,               "msghdr" },
-	{ EXPR_CMSGHDR,              "cmsghdr"},
-	{ EXPR_POLLFD,               "pollfd" },
+	{ EXPR_NONE,                        "none"                            },
+	{ EXPR_NULL,                        "null"                            },
+	{ EXPR_ELLIPSIS,                    "ellipsis"                        },
+	{ EXPR_INTEGER,                     "integer"                         },
+	{ EXPR_WORD,                        "word"                            },
+	{ EXPR_STRING,                      "string"                          },
+	{ EXPR_SOCKET_ADDRESS_IPV4,         "sockaddr_in"                     },
+	{ EXPR_SOCKET_ADDRESS_IPV6,         "sockaddr_in6"                    },
+	{ EXPR_LINGER,                      "linger"                          },
+	{ EXPR_BINARY,                      "binary_expression"               },
+	{ EXPR_LIST,                        "list"                            },
+	{ EXPR_IOVEC,                       "iovec"                           },
+	{ EXPR_MSGHDR,                      "msghdr"                          },
+	{ EXPR_CMSGHDR,                     "cmsghdr"                         },
+	{ EXPR_POLLFD,                      "pollfd"                          },
 #if defined(__FreeBSD__)
-	{ EXPR_SF_HDTR,              "sf_hdtr" },
+	{ EXPR_SF_HDTR,                     "sf_hdtr"                         },
+	{ EXPR_TCP_FUNCTION_SET,            "tcp_function_set"                },
 #endif
-	{ EXPR_SCTP_RTOINFO,         "sctp_rtoinfo"},
-	{ EXPR_SCTP_INITMSG,         "sctp_initmsg"},
-	{ EXPR_SCTP_ASSOC_VALUE,     "sctp_assoc_value"},
-	{ EXPR_SCTP_HMACALGO,        "sctp_hmacalgo"},
-	{ EXPR_SCTP_AUTHKEYID,       "sctp_authkeyid"},
-	{ EXPR_SCTP_SACKINFO,        "sctp_sackinfo"},
-	{ EXPR_SCTP_STATUS,          "sctp_status"},
-	{ EXPR_SCTP_PADDRINFO,	     "sctp_paddrinfo"},
-	{ EXPR_SCTP_PEER_ADDR_PARAMS,"sctp_peer_addr_params"},
-	{ EXPR_SCTP_STREAM_VALUE,    "sctp_stream_value"},
-	{ EXPR_SCTP_ASSOCPARAMS,     "sctp_assocparams"},
-	{ EXPR_SCTP_EVENT,	     "sctp_event"      },
-	{ EXPR_SCTP_EVENT_SUBSCRIBE, "sctp_event_subscribe"},
-	{ EXPR_SCTP_SNDINFO,         "sctp_sndinfo"    },
-	{ EXPR_SCTP_SETPRIM,         "sctp_setprim"    },
-	{ EXPR_SCTP_SETADAPTATION,   "sctp_setadaptation"},
-	{ EXPR_SCTP_SNDRCVINFO,      "sctp_sndrcvinfo" },
-	{ EXPR_SCTP_PRINFO,          "sctp_prinfo"     },
-	{ EXPR_SCTP_DEFAULT_PRINFO,  "sctp_default_prinfo"},
-	{ EXPR_SCTP_AUTHINFO,        "sctp_authinfo"   },
-	{ EXPR_SCTP_SENDV_SPA,       "sctp_sendv_spa"  },
-	{ EXPR_SCTP_RCVINFO,         "sctp_rcvinfo"    },
-	{ EXPR_SCTP_NXTINFO,         "sctp_nxtinfo"    },
-	{ EXPR_SCTP_RECVV_RN,        "sctp_recvv_rn "  },
-	{ EXPR_SCTP_ASSOC_CHANGE,    "sctp_assoc_change"},
-	{ EXPR_SCTP_PADDR_CHANGE,    "sctp_paddr_change"},
-	{ EXPR_SCTP_REMOTE_ERROR,    "sctp_remote_error"},
-	{ EXPR_SCTP_SEND_FAILED,     "sctp_send_failed"},
-	{ EXPR_SCTP_SHUTDOWN_EVENT,  "sctp_shutdown_event"},
-	{ EXPR_SCTP_ADAPTATION_EVENT,"sctp_adaptation_event"},
-	{ EXPR_SCTP_PDAPI_EVENT,     "sctp_pdapi_event"},
-	{ EXPR_SCTP_AUTHKEY_EVENT,   "sctp_authkey_event"},
-	{ EXPR_SCTP_SENDER_DRY_EVENT,"sctp_sender_dry_event"},
-	{ EXPR_SCTP_SEND_FAILED_EVENT,"sctp_send_failed_event"},
-	{ EXPR_SCTP_TLV,             "sctp_tlv"        },
-	{ EXPR_SCTP_EXTRCVINFO,      "sctp_extrcvinfo" },
-	{ EXPR_SCTP_ASSOC_IDS,       "sctp_assoc_ids"  },
-	{ EXPR_SCTP_AUTHCHUNKS,      "sctp_authchunks" },
-	{ EXPR_SCTP_SETPEERPRIM,     "sctp_setpeerprim"},
-	{ EXPR_SCTP_AUTHCHUNK,       "sctp_authchunk"  },
-	{ EXPR_SCTP_AUTHKEY,         "sctp_authkey"    },
-	{ EXPR_SCTP_RESET_STREAMS,   "sctp_reset_streams"},
-	{ EXPR_SCTP_ADD_STREAMS,     "sctp_add_streams"},
-	{ EXPR_SCTP_STREAM_RESET_EVENT, "sctp_stream_reset_event"},
-	{ EXPR_SCTP_ASSOC_RESET_EVENT, "sctp_assoc_reset_event"},
-	{ EXPR_SCTP_STREAM_CHANGE_EVENT, "sctp_stream_change_event"},
-	{ NUM_EXPR_TYPES,            NULL}
+	{ EXPR_SCTP_RTOINFO,                "sctp_rtoinfo"                    },
+	{ EXPR_SCTP_INITMSG,                "sctp_initmsg"                    },
+	{ EXPR_SCTP_ASSOC_VALUE,            "sctp_assoc_value"                },
+	{ EXPR_SCTP_HMACALGO,               "sctp_hmacalgo"                   },
+	{ EXPR_SCTP_AUTHKEYID,              "sctp_authkeyid"                  },
+	{ EXPR_SCTP_SACKINFO,               "sctp_sackinfo"                   },
+	{ EXPR_SCTP_STATUS,                 "sctp_status"                     },
+	{ EXPR_SCTP_PADDRINFO,              "sctp_paddrinfo"                  },
+	{ EXPR_SCTP_PEER_ADDR_PARAMS,       "sctp_peer_addr_params"           },
+	{ EXPR_SCTP_STREAM_VALUE,           "sctp_stream_value"               },
+	{ EXPR_SCTP_ASSOCPARAMS,            "sctp_assocparams"                },
+	{ EXPR_SCTP_EVENT,                  "sctp_event"                      },
+	{ EXPR_SCTP_EVENT_SUBSCRIBE,        "sctp_event_subscribe"            },
+	{ EXPR_SCTP_SNDINFO,                "sctp_sndinfo"                    },
+	{ EXPR_SCTP_SETPRIM,                "sctp_setprim"                    },
+	{ EXPR_SCTP_SETADAPTATION,          "sctp_setadaptation"              },
+	{ EXPR_SCTP_SNDRCVINFO,             "sctp_sndrcvinfo"                 },
+	{ EXPR_SCTP_PRINFO,                 "sctp_prinfo"                     },
+	{ EXPR_SCTP_DEFAULT_PRINFO,         "sctp_default_prinfo"             },
+	{ EXPR_SCTP_AUTHINFO,               "sctp_authinfo"                   },
+	{ EXPR_SCTP_SENDV_SPA,              "sctp_sendv_spa"                  },
+	{ EXPR_SCTP_RCVINFO,                "sctp_rcvinfo"                    },
+	{ EXPR_SCTP_NXTINFO,                "sctp_nxtinfo"                    },
+	{ EXPR_SCTP_RECVV_RN,               "sctp_recvv_rn"                   },
+	{ EXPR_SCTP_ASSOC_CHANGE,           "sctp_assoc_change"               },
+	{ EXPR_SCTP_PADDR_CHANGE,           "sctp_paddr_change"               },
+	{ EXPR_SCTP_REMOTE_ERROR,           "sctp_remote_error"               },
+	{ EXPR_SCTP_SEND_FAILED,            "sctp_send_failed"                },
+	{ EXPR_SCTP_SHUTDOWN_EVENT,         "sctp_shutdown_event"             },
+	{ EXPR_SCTP_ADAPTATION_EVENT,       "sctp_adaptation_event"           },
+	{ EXPR_SCTP_PDAPI_EVENT,            "sctp_pdapi_event"                },
+	{ EXPR_SCTP_AUTHKEY_EVENT,          "sctp_authkey_event"              },
+	{ EXPR_SCTP_SENDER_DRY_EVENT,       "sctp_sender_dry_event"           },
+	{ EXPR_SCTP_SEND_FAILED_EVENT,      "sctp_send_failed_event"          },
+	{ EXPR_SCTP_TLV,                    "sctp_tlv"                        },
+	{ EXPR_SCTP_EXTRCVINFO,             "sctp_extrcvinfo"                 },
+	{ EXPR_SCTP_ASSOC_IDS,              "sctp_assoc_ids"                  },
+	{ EXPR_SCTP_AUTHCHUNKS,             "sctp_authchunks"                 },
+	{ EXPR_SCTP_SETPEERPRIM,            "sctp_setpeerprim"                },
+	{ EXPR_SCTP_AUTHCHUNK,              "sctp_authchunk"                  },
+	{ EXPR_SCTP_AUTHKEY,                "sctp_authkey"                    },
+	{ EXPR_SCTP_RESET_STREAMS,          "sctp_reset_streams"              },
+	{ EXPR_SCTP_ADD_STREAMS,            "sctp_add_streams"                },
+	{ EXPR_SCTP_STREAM_RESET_EVENT,     "sctp_stream_reset_event"         },
+	{ EXPR_SCTP_ASSOC_RESET_EVENT,      "sctp_assoc_reset_event"          },
+	{ EXPR_SCTP_STREAM_CHANGE_EVENT,    "sctp_stream_change_event"        },
+	{ EXPR_SCTP_UDPENCAPS,              "sctp_udpencaps"                  },
+	{ NUM_EXPR_TYPES,                   NULL                              }
 };
 
 const char *expression_type_to_string(enum expression_t type)
@@ -219,7 +221,7 @@ struct flag_name poll_flags[] = {
 #endif
 
 #ifdef POLLINIGNEOF
-	{ POLLINIGNEOF, "POLLINIGNEOF"                    },
+	{ POLLINIGNEOF, "POLLINIGNEOF" },
 #endif
 
 	{ POLLERR,	"POLLERR" },
@@ -330,6 +332,13 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.linger->l_onoff);
 		free_expression(expression->value.linger->l_linger);
 		break;
+#if defined(__FreeBSD__)
+	case EXPR_TCP_FUNCTION_SET:
+		assert(expression->value.tcp_function_set);
+		free_expression(expression->value.tcp_function_set->function_set_name);
+		free_expression(expression->value.tcp_function_set->pcbcnt);
+		break;
+#endif
 	case EXPR_SCTP_RTOINFO:
 		assert(expression->value.sctp_rtoinfo);
 		free_expression(expression->value.sctp_rtoinfo->srto_assoc_id);
@@ -651,6 +660,11 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_stream_change_event->strchange_instrms);
 		free_expression(expression->value.sctp_stream_change_event->strchange_outstrms);
 		break;
+	case EXPR_SCTP_UDPENCAPS:
+		free_expression(expression->value.sctp_udpencaps->sue_assoc_id);
+		free_expression(expression->value.sctp_udpencaps->sue_address);
+		free_expression(expression->value.sctp_udpencaps->sue_port);
+		break;
 	case EXPR_WORD:
 		assert(expression->value.string);
 		free(expression->value.string);
@@ -2695,6 +2709,39 @@ static int evaluate_sctp_stream_change_event_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_udpencaps_expression(struct expression *in,
+					      struct expression *out,
+					      char **error)
+{
+	struct sctp_udpencaps_expr *in_udpencaps;
+	struct sctp_udpencaps_expr *out_udpencaps;
+
+	assert(in->type == EXPR_SCTP_UDPENCAPS);
+	assert(in->value.sctp_udpencaps);
+	assert(out->type == EXPR_SCTP_UDPENCAPS);
+
+	out->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+
+	in_udpencaps = in->value.sctp_udpencaps;
+	out_udpencaps = out->value.sctp_udpencaps;
+
+	if (evaluate(in_udpencaps->sue_assoc_id,
+		     &out_udpencaps->sue_assoc_id,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_udpencaps->sue_address,
+		     &out_udpencaps->sue_address,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_udpencaps->sue_port,
+		     &out_udpencaps->sue_port,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
+
 static int evaluate(struct expression *in,
 		    struct expression **out_ptr, char **error)
 {
@@ -2720,6 +2767,12 @@ static int evaluate(struct expression *in,
 		memcpy(&out->value.linger, &in->value.linger,
 		       sizeof(in->value.linger));
 		break;
+#if defined(__FreeBSD__)
+	case EXPR_TCP_FUNCTION_SET:		/* copy as-is */
+		memcpy(&out->value.tcp_function_set, &in->value.tcp_function_set,
+		       sizeof(in->value.tcp_function_set));
+		break;
+#endif
 	case EXPR_SCTP_RTOINFO:
 		result = evaluate_sctp_rtoinfo_expression(in, out, error);
 		break;
@@ -2858,6 +2911,9 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_STREAM_CHANGE_EVENT:
 		result = evaluate_sctp_stream_change_event_expression(in, out, error);
 		break;
+	case EXPR_SCTP_UDPENCAPS:
+		result = evaluate_sctp_udpencaps_expression(in, out, error);
+		break;
 	case EXPR_WORD:
 		out->type = EXPR_INTEGER;
 		if (symbol_to_int(in->value.string,
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 41bf74a618604f0aff1430b4f53bb34ee47bb02d..b4d75eaf18eb815cb3d663c52689711d939f85d3 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -49,6 +49,7 @@ enum expression_t {
 	EXPR_POLLFD,		  /* expression tree for a pollfd struct */
 #if defined(__FreeBSD__)
 	EXPR_SF_HDTR,		  /* struct sf_hdtr for sendfile */
+	EXPR_TCP_FUNCTION_SET,	  /* struct tcp_function_set */
 #endif
 	EXPR_SCTP_RTOINFO,	  /* struct sctp_rtoinfo for SCTP_RTOINFO */
 	EXPR_SCTP_INITMSG,	  /* struct sctp_initmsg for SCTP_INITMSG */
@@ -96,6 +97,7 @@ enum expression_t {
 	EXPR_SCTP_STREAM_RESET_EVENT, /* expression tree for sctp_stream_reset_event struct for sctp notifications */
 	EXPR_SCTP_ASSOC_RESET_EVENT,  /* expression tree for sctp_assoc_reset_event struct for sctp notifications */
 	EXPR_SCTP_STREAM_CHANGE_EVENT, /* expression tree for sctp_stream_change_event struct for sctp notifications */
+	EXPR_SCTP_UDPENCAPS,      /* expression tree for sctp_udpencaps struct for [gs]etsockopt */
 	NUM_EXPR_TYPES,
 };
 /* Convert an expression type to a human-readable string */
@@ -118,6 +120,7 @@ struct expression {
 		struct pollfd_expr *pollfd;
 #if defined(__FreeBSD__)
 		struct sf_hdtr_expr *sf_hdtr;
+		struct tcp_function_set_expr *tcp_function_set;
 #endif
 		struct sctp_rtoinfo_expr *sctp_rtoinfo;
 		struct sctp_initmsg_expr *sctp_initmsg;
@@ -165,6 +168,7 @@ struct expression {
 		struct sctp_stream_reset_event_expr *sctp_stream_reset_event;
 		struct sctp_assoc_reset_event_expr *sctp_assoc_reset_event;
 		struct sctp_stream_change_event_expr *sctp_stream_change_event;
+		struct sctp_udpencaps_expr *sctp_udpencaps;
 	} value;
 	const char *format;	/* the printf format for printing the value */
 };
@@ -230,6 +234,11 @@ struct sf_hdtr_expr {
 	struct expression *trailers;
 	struct expression *trl_cnt;
 };
+
+struct tcp_function_set_expr {
+	struct expression *function_set_name;
+	struct expression *pcbcnt;
+};
 #endif
 
 /* Parse tree for a sctp_rtoinfo struct in a [gs]etsockopt syscall. */
@@ -635,6 +644,13 @@ struct sctp_stream_change_event_expr {
 	struct expression *strchange_outstrms;
 };
 
+/* Parse tree for sctp_udpencaps struct */
+struct sctp_udpencaps_expr {
+	struct expression *sue_assoc_id;
+	struct expression *sue_address;
+	struct expression *sue_port;
+};
+
 /* The errno-related info from strace to summarize a system call error */
 struct errno_spec {
 	const char *errno_macro;	/* errno symbol (C macro name) */
diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c
index 8be43c6e81cd68a191467c364eec22f7dc3184db..e49f5f6b42c689f29c465149d73dfce4c27e1bc4 100644
--- a/gtests/net/packetdrill/sctp_packet.c
+++ b/gtests/net/packetdrill/sctp_packet.c
@@ -3013,10 +3013,12 @@ new_sctp_packet(int address_family,
                 s64 tag,
                 bool bad_crc32c,
                 struct sctp_chunk_list *list,
+                u16 udp_src_port,
+                u16 udp_dst_port,
                 char **error)
 {
 	struct packet *packet;  /* the newly-allocated result packet */
-	struct header *sctp_header;  /* the SCTP header info */
+	struct header *sctp_header, *udp_header;
 	struct sctp_chunk_list_item *chunk_item;
 	struct sctp_parameter_list_item *parameter_item;
 	struct sctp_cause_list_item *cause_item;
@@ -3024,11 +3026,12 @@ new_sctp_packet(int address_family,
 	const int ip_option_bytes = 0;
 	const int ip_header_bytes = (ip_header_min_len(address_family) +
 				     ip_option_bytes);
+	const int udp_header_bytes = sizeof(struct udp);
 	const int sctp_header_bytes = sizeof(struct sctp_common_header);
 	const int sctp_chunk_bytes = list->length;
-	const int ip_bytes =
-		 ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+	int ip_bytes;
 	bool overbook = false;
+	bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0);
 
 	/* Sanity-check all the various lengths */
 	if (ip_option_bytes & 0x3) {
@@ -3039,6 +3042,11 @@ new_sctp_packet(int address_family,
 	}
 	assert((ip_header_bytes & 0x3) == 0);
 
+	ip_bytes = ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+	if (encapsulate) {
+		ip_bytes += udp_header_bytes;
+	}
+
 	if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) {
 		asprintf(error, "SCTP packet too large");
 		return NULL;
@@ -3383,9 +3391,8 @@ new_sctp_packet(int address_family,
 	/* 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 = 0;
+	packet->flags = encapsulate ? FLAGS_UDP_ENCAPSULATED : 0;
 	if (bad_crc32c) {
 		packet->flags |= FLAGS_SCTP_BAD_CRC32C;
 	}
@@ -3395,14 +3402,29 @@ new_sctp_packet(int address_family,
 	packet->ecn = ecn;
 
 	/* Set IP header fields */
-	set_packet_ip_header(packet, address_family, ip_bytes, ecn,
-			     IPPROTO_SCTP);
+	if (encapsulate) {
+		set_packet_ip_header(packet, address_family, ip_bytes, ecn,
+				     IPPROTO_UDP);
+		udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes);
+		udp_header->total_bytes = udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+		udp_header->h.udp->src_port = htons(udp_src_port);
+		udp_header->h.udp->dst_port = htons(udp_dst_port);
+		udp_header->h.udp->len = htons(udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes);
+		udp_header->h.udp->check = htons(0);
+	} else {
+		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);
+	if (encapsulate) {
+		packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes + udp_header_bytes);
+	} else {
+		packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes);
+	}
 	u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1);
 
 	/* Set SCTP header fields */
@@ -3436,12 +3458,12 @@ new_sctp_packet(int address_family,
 		sctp_chunk_start += chunk_item->length;
 	}
 	free(packet->chunk_list);
+	packet->ip_bytes += sctp_chunk_bytes;
 	packet->chunk_list = list;
-	packet->ip_bytes = ip_bytes;
 	return packet;
 }
 
-#ifdef DEBUG_LOGGING
+#ifdef DEBUG
 static void print_sctp_byte_list(struct sctp_byte_list *list) {
 	struct sctp_byte_list_item *item;
 
@@ -3453,27 +3475,30 @@ static void print_sctp_byte_list(struct sctp_byte_list *list) {
 
 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) {
+			enum direction_t direction,
+			enum ip_ecn_t ecn,
+			s64 tag,
+			bool bad_crc32c,
+			struct sctp_byte_list *bytes,
+			u16 udp_src_port,
+			u16 udp_dst_port,
+			char **error) {
 	struct packet *packet;  /* the newly-allocated result packet */
-	struct header *sctp_header;  /* the SCTP header info */
+	struct header *sctp_header, *udp_header;
 	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 udp_header_bytes = sizeof(struct udp);
 	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;
+	int ip_bytes;
 	bool overbook = false;
+	bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0);
 	u16 i;
 
-#ifdef DEBUG_LOGGING
+#ifdef DEBUG
 	print_sctp_byte_list(bytes);
 #endif
 
@@ -3492,6 +3517,11 @@ new_sctp_generic_packet(int address_family,
 	}
 	assert((ip_header_bytes & 0x3) == 0);
 
+	ip_bytes = ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+	if (encapsulate) {
+		ip_bytes += udp_header_bytes;
+	}
+
 	if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) {
 		asprintf(error, "SCTP packet too large");
 		return NULL;
@@ -3509,17 +3539,35 @@ new_sctp_generic_packet(int address_family,
 	if (tag != -1) {
 		packet->flags |= FLAGS_SCTP_EXPLICIT_TAG;
 	}
+	if (encapsulate) {
+		packet->flags |= FLAGS_SCTP_BAD_CRC32C;
+	}
 	packet->ecn = ecn;
 
 	/* Set IP header fields */
-	set_packet_ip_header(packet, address_family, ip_bytes, ecn,
-			     IPPROTO_SCTP);
+	if (encapsulate) {
+		set_packet_ip_header(packet, address_family, ip_bytes, ecn,
+				     IPPROTO_UDP);
+		udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes);
+		udp_header->total_bytes = udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes;
+		udp_header->h.udp->src_port = htons(udp_src_port);
+		udp_header->h.udp->dst_port = htons(udp_dst_port);
+		udp_header->h.udp->len = htons(udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes);
+		udp_header->h.udp->check = htons(0);
+	} else {
+		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);
+	if (encapsulate) {
+		packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes + udp_header_bytes);
+	} else {
+		packet->sctp = (struct sctp_common_header *) (ip_start(packet) + udp_header_bytes);
+	}
 	u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1);
 
 	/* Set SCTP header fields */
diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h
index 6fece3a3f5de69163eadb8e49f379df1eb8462db..33f420e5a538264837d47b0749831d23fe7a7be6 100644
--- a/gtests/net/packetdrill/sctp_packet.h
+++ b/gtests/net/packetdrill/sctp_packet.h
@@ -564,14 +564,18 @@ extern struct packet *new_sctp_packet(int address_family,
 				      s64 tag,
 				      bool bad_crc32c,
 				      struct sctp_chunk_list *chunk_list,
+				      u16 udp_src_port,
+				      u16 udp_dst_port,
 				      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);
+			enum direction_t direction,
+			enum ip_ecn_t ecn,
+			s64 tag,
+			bool bad_crc32c,
+			struct sctp_byte_list *bytes,
+			u16 udp_src_port,
+			u16 udp_dst_port,
+			char **error);
 #endif /* __SCTP_PACKET_H__ */
diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h
index 96c6f6bf0bec76590f6bbfdcc36a3f8674e761ab..07fc13364462a5c3c765a771355ce531d28988dd 100644
--- a/gtests/net/packetdrill/socket.h
+++ b/gtests/net/packetdrill/socket.h
@@ -116,6 +116,11 @@ struct socket {
 	struct tcp last_injected_tcp_header;
 	u32 last_injected_tcp_payload_len;
 
+	u16 last_outbound_udp_encaps_dst_port;
+	u16 last_outbound_udp_encaps_src_port;
+	u16 last_injected_udp_encaps_src_port;
+	u16 last_injected_udp_encaps_dst_port;
+
 	struct sctp_cookie_echo_chunk *prepared_cookie_echo;
 	u16 prepared_cookie_echo_length;
 	struct sctp_heartbeat_ack_chunk *prepared_heartbeat_ack;
@@ -233,14 +238,15 @@ static inline void set_headers_tuple(struct ipv4 *ipv4,
 
 /* Set the tuple for a packet header echoed inside an ICMPv4/ICMPv6 message. */
 static inline void set_icmp_echoed_tuple(struct packet *packet,
-					 const struct tuple *tuple)
+					 const struct tuple *tuple,
+					 bool encapsulated)
 {
 	/* All currently supported ICMP message types include a copy
 	 * of the outbound IP header and the first few bytes inside,
 	 * which so far always means the first ICMP_ECHO_BYTES of
 	 * TCP header.
 	 */
-	DEBUGP("set_icmp_echoed_tuple");
+	DEBUGP("set_icmp_echoed_tuple encapsulated: %d\n", encapsulated);
 
 	/* Flip the direction of the tuple, since the ICMP message is
 	 * flowing in the direction opposite that of the echoed TCP/IP
@@ -250,8 +256,8 @@ static inline void set_icmp_echoed_tuple(struct packet *packet,
 	reverse_tuple(tuple, &echoed_tuple);
 	set_headers_tuple(packet_echoed_ipv4_header(packet),
 			  packet_echoed_ipv6_header(packet),
-			  packet_echoed_sctp_header(packet),
-			  packet_echoed_tcp_header(packet),
+			  packet_echoed_sctp_header(packet, encapsulated),
+			  packet_echoed_tcp_header(packet, encapsulated),
 			  packet_echoed_udp_header(packet),
 			  packet_echoed_udplite_header(packet),
 			  &echoed_tuple);
@@ -259,12 +265,12 @@ static inline void set_icmp_echoed_tuple(struct packet *packet,
 
 /* Set the tuple for a packet. */
 static inline void set_packet_tuple(struct packet *packet,
-				    const struct tuple *tuple)
+				    const struct tuple *tuple, bool encapsulated)
 {
 	set_headers_tuple(packet->ipv4, packet->ipv6, packet->sctp, packet->tcp,
 			  packet->udp, packet->udplite, tuple);
 	if ((packet->icmpv4 != NULL) || (packet->icmpv6 != NULL))
-		set_icmp_echoed_tuple(packet, tuple);
+		set_icmp_echoed_tuple(packet, tuple, encapsulated);
 }
 
 
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index c919906083a06f91fe32bc93b8128d6a07ccf23e..350aa3ed419f2ade54d60818afce2afb95f8ced7 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -193,6 +193,7 @@ struct int_symbol platform_symbols_table[] = {
 #if defined(SCTP_INTERLEAVING_SUPPORTED)
 	{ SCTP_INTERLEAVING_SUPPORTED,      "SCTP_INTERLEAVING_SUPPORTED"     },
 #endif
+	{ SCTP_REMOTE_UDP_ENCAPS_PORT,      "SCTP_REMOTE_UDP_ENCAPS_PORT"     },
 	{ SCTP_CLOSED,                      "SCTP_CLOSED"                     },
 	{ SCTP_BOUND,                       "SCTP_BOUND"                      },
 	{ SCTP_LISTEN,                      "SCTP_LISTEN"                     },
@@ -365,18 +366,24 @@ struct int_symbol platform_symbols_table[] = {
 	{ TCP_MD5SIG,                       "TCP_MD5SIG"                      },
 	{ TCP_INFO,                         "TCP_INFO"                        },
 	{ TCP_CONGESTION,                   "TCP_CONGESTION"                  },
-#if __FreeBSD_version >=1100000
+#if defined(TCP_CCALGOOPT)
 	{ TCP_CCALGOOPT,                    "TCP_CCALGOOPT"                   },
 #endif
 	{ TCP_KEEPINIT,                     "TCP_KEEPINIT"                    },
 	{ TCP_KEEPIDLE,                     "TCP_KEEPIDLE"                    },
 	{ TCP_KEEPINTVL,                    "TCP_KEEPINTVL"                   },
 	{ TCP_KEEPCNT,                      "TCP_KEEPCNT"                     },
-#if __FreeBSD_version >= 1003000
+#if defined(TCP_FASTOPEN)
 	{ TCP_FASTOPEN,                     "TCP_FASTOPEN"                    },
 #endif
+#if defined(TCP_FUNCTION_BLK)
+	{ TCP_FUNCTION_BLK,                 "TCP_FUNCTION_BLK"                },
+#endif
+#if defined(TCP_REMOTE_UDP_ENCAPS_PORT)
+	{ TCP_REMOTE_UDP_ENCAPS_PORT,       "TCP_REMOTE_UDP_ENCAPS_PORT"      },
+#endif
 
-#if __FreeBSD_version >= 1001000
+#if defined(UDPLITE_RECV_CSCOV) && defined(UDPLITE_SEND_CSCOV)
 	/* /usr/include/netinet/udplite.h */
 	{ UDPLITE_RECV_CSCOV,               "UDPLITE_RECV_CSCOV"              },
 	{ UDPLITE_SEND_CSCOV,               "UDPLITE_SEND_CSCOV"              },
diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c
index de415f9811e2bafe8e7f6ac6325cb1f64145777a..2bbe13f89fcd4cff4522b27350a0279583087651 100644
--- a/gtests/net/packetdrill/tcp_packet.c
+++ b/gtests/net/packetdrill/tcp_packet.c
@@ -60,18 +60,22 @@ struct packet *new_tcp_packet(int address_family,
 			       s32 window,
 			       const struct tcp_options *tcp_options,
 			       bool abs_ts_ecr,
+			       bool abs_seq,
+			       u16 udp_src_port,
+			       u16 udp_dst_port,
 			       char **error)
 {
 	struct packet *packet = NULL;  /* the newly-allocated result packet */
-	struct header *tcp_header = NULL;  /* the TCP header info */
+	struct header *tcp_header, *udp_header;
 	/* Calculate lengths in bytes of all sections of the packet */
 	const int ip_option_bytes = 0;
 	const int tcp_option_bytes = tcp_options ? tcp_options->length : 0;
 	const int ip_header_bytes = (ip_header_min_len(address_family) +
 				     ip_option_bytes);
+	const int udp_header_bytes = sizeof(struct udp);
 	const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes;
-	const int ip_bytes =
-		 ip_header_bytes + tcp_header_bytes + tcp_payload_bytes;
+	int ip_bytes;
+	bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0);
 
 	/* Sanity-check all the various lengths */
 	if (ip_option_bytes & 0x3) {
@@ -95,6 +99,10 @@ struct packet *new_tcp_packet(int address_family,
 		return NULL;
 	}
 
+	ip_bytes = ip_header_bytes + tcp_header_bytes + tcp_payload_bytes;
+	if (encapsulate) {
+		ip_bytes += udp_header_bytes;
+	}
 	if (ip_bytes > MAX_TCP_DATAGRAM_BYTES) {
 		asprintf(error, "TCP segment too large");
 		return NULL;
@@ -108,18 +116,33 @@ struct packet *new_tcp_packet(int address_family,
 	memset(packet->buffer, 0, ip_bytes);
 
 	packet->direction = direction;
-	packet->flags = 0;
+	packet->flags = encapsulate ? FLAGS_UDP_ENCAPSULATED : 0;
 	packet->ecn = ecn;
 
 	/* Set IP header fields */
-	set_packet_ip_header(packet, address_family, ip_bytes, ecn,
-			     IPPROTO_TCP);
+	if (encapsulate) {
+		set_packet_ip_header(packet, address_family, ip_bytes, ecn,
+				     IPPROTO_UDP);
+		udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes);
+		udp_header->total_bytes = udp_header_bytes + tcp_header_bytes + tcp_payload_bytes;
+		udp_header->h.udp->src_port = htons(udp_src_port);
+		udp_header->h.udp->dst_port = htons(udp_dst_port);
+		udp_header->h.udp->len = htons(udp_header_bytes + tcp_header_bytes + tcp_payload_bytes);
+		udp_header->h.udp->check = htons(0);
+	} else {
+		set_packet_ip_header(packet, address_family, ip_bytes, ecn,
+				     IPPROTO_TCP);
+	}
 
 	tcp_header = packet_append_header(packet, HEADER_TCP, tcp_header_bytes);
 	tcp_header->total_bytes = tcp_header_bytes + tcp_payload_bytes;
 
 	/* Find the start of TCP sections of the packet */
-	packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes);
+	if (encapsulate) {
+		packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes + udp_header_bytes);
+	} else {
+		packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes);
+	}
 	u8 *tcp_option_start = (u8 *) (packet->tcp + 1);
 
 	/* Set TCP header fields */
@@ -161,6 +184,9 @@ struct packet *new_tcp_packet(int address_family,
 	if (abs_ts_ecr) {
 		packet->flags |= FLAG_ABSOLUTE_TS_ECR;
 	}
+	if (abs_seq) {
+		packet->flags |= FLAG_ABSOLUTE_SEQ;
+	}
 
 	packet->ip_bytes = ip_bytes;
 	return packet;
diff --git a/gtests/net/packetdrill/tcp_packet.h b/gtests/net/packetdrill/tcp_packet.h
index 0c065c0e77291df076c950da2456d49486f1d6b3..deacd96f98bd65f40ea9597ff1c2749de079d70e 100644
--- a/gtests/net/packetdrill/tcp_packet.h
+++ b/gtests/net/packetdrill/tcp_packet.h
@@ -45,5 +45,8 @@ extern struct packet *new_tcp_packet(int address_family,
 				     s32 window,
 				     const struct tcp_options *tcp_options,
 				     bool abs_ts_ecr,
+				     bool abs_seq,
+				     u16 udp_src_port,
+				     u16 udp_dst_port,
 				     char **error);
 #endif /* __TCP_PACKET_H__ */
diff --git a/gtests/net/packetdrill/wire_client_netdev.c b/gtests/net/packetdrill/wire_client_netdev.c
index ce4ebefc8468d2ae1fc7a00f6ff16d2a7bfea003..049f982117fa0d91af739bbfee57d584eec4697c 100644
--- a/gtests/net/packetdrill/wire_client_netdev.c
+++ b/gtests/net/packetdrill/wire_client_netdev.c
@@ -150,7 +150,7 @@ static int wire_client_netdev_send(struct netdev *a_netdev,
 	return STATUS_ERR;
 }
 
-static int wire_client_netdev_receive(struct netdev *a_netdev,
+static int wire_client_netdev_receive(struct netdev *a_netdev, u8 udp_encaps,
 				      struct packet **packet, char **error)
 {
 	DEBUGP("wire_client_netdev_receive\n");
diff --git a/gtests/net/packetdrill/wire_server_netdev.c b/gtests/net/packetdrill/wire_server_netdev.c
index 8ef26f3a7ce3eeec04b45fde6a91fa74c37a514d..02905614f03b20a1a19946acba25a2bf301abef2 100644
--- a/gtests/net/packetdrill/wire_server_netdev.c
+++ b/gtests/net/packetdrill/wire_server_netdev.c
@@ -184,7 +184,7 @@ static int wire_server_netdev_send(struct netdev *a_netdev,
 	return result;
 }
 
-static int wire_server_netdev_receive(struct netdev *a_netdev,
+static int wire_server_netdev_receive(struct netdev *a_netdev, u8 udp_encaps,
 				      struct packet **packet, char **error)
 {
 	struct wire_server_netdev *netdev = to_server_netdev(a_netdev);
@@ -192,8 +192,8 @@ static int wire_server_netdev_receive(struct netdev *a_netdev,
 
 	DEBUGP("wire_server_netdev_receive\n");
 
-	return netdev_receive_loop(netdev->psock, DIRECTION_INBOUND, packet,
-				   &num_packets, error);
+	return netdev_receive_loop(netdev->psock, DIRECTION_INBOUND, udp_encaps,
+				   packet, &num_packets, error);
 }
 
 struct netdev_ops wire_server_netdev_ops = {