diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 5179907b893c25b9cac2f4b4614a5e57207cd101..5cebb2f7756b4b2a022ceaa3201d2958e1ef38b6 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -247,6 +247,14 @@ snd_flags			return SND_FLAGS;
 snd_ppid			return SND_PPID;
 snd_context			return SND_CONTEXT;
 ssb_adaptation_ind		return SSB_ADAPTATION_IND;
+sinfo_stream			return SINFO_STREAM;
+sinfo_ssn			return SINFO_SSN;
+sinfo_flags			return SINFO_FLAGS;
+sinfo_ppid			return SINFO_PPID;
+sinfo_context			return SINFO_CONTEXT;
+sinfo_timetolive		return SINFO_TIMETOLIVE;
+sinfo_tsn			return SINFO_TSN;
+sinfo_cumtsn			return SINFO_CUMTSN;
 CHUNK				return CHUNK;
 DATA				return DATA;
 INIT				return INIT;
@@ -319,6 +327,7 @@ staleness			return STALENESS;
 param				return PARAM;
 chk				return CHK;
 bad_crc32c			return BAD_CRC32C;
+NULL				return NULL_;
 --[a-zA-Z0-9_]+			yylval.string	= option(yytext); return OPTION;
 [-]?[0-9]*[.][0-9]+		yylval.floating	= atof(yytext);   return FLOAT;
 [-]?[0-9]+			yylval.integer	= atoll(yytext);  return INTEGER;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index bb2fb56b5a72dc63bcf7b54f14b2fa6e5b0f2e2b..6239ff2647b6a6627fbe4a3a4bcc3b6394a84fa5 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -538,7 +538,9 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> SASOC_ASOCMAXRXT SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
 %token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE SE_TYPE SE_ON
 %token <reserved> SND_SID SND_FLAGS SND_PPID SND_CONTEXT SSB_ADAPTATION_IND
-%token <reserved> BAD_CRC32C
+%token <reserved> BAD_CRC32C NULL_
+%token <reserved> SINFO_STREAM SINFO_SSN SINFO_FLAGS SINFO_PPID SINFO_CONTEXT
+%token <reserved> SINFO_TIMETOLIVE SINFO_TSN SINFO_CUMTSN
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -588,7 +590,9 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd
 %type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams
 %type <expression> sctp_sndinfo snd_sid snd_flags snd_ppid snd_context
-%type <expression> sctp_event se_type se_on sctp_setadaptation
+%type <expression> sctp_event se_type se_on sctp_setadaptation null
+%type <expression> sctp_sndrcvinfo sinfo_stream sinfo_ssn sinfo_flags sinfo_ppid sinfo_context 
+%type <expression> sinfo_timetolive sinfo_tsn sinfo_cumtsn
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
@@ -2398,6 +2402,12 @@ expression
 | sctp_setadaptation{
 	$$ = $1;
 }
+| null              {
+	$$ = $1;
+}
+| sctp_sndrcvinfo   {
+	$$ = $1;
+}
 ;
 
 decimal_integer
@@ -3059,17 +3069,14 @@ snd_context
 
 sctp_sndinfo
 : '{' snd_sid ',' snd_flags ',' snd_ppid ',' snd_context '}' {
-#ifdef SCTP_DEFAULT_SNDINFO
 	$$ = new_expression(EXPR_SCTP_SNDINFO);
-	$$->value.sctp_sndinfo = calloc(1, sizeof(struct sctp_sndinfo));
+	$$->value.sctp_sndinfo = calloc(1, sizeof(struct sctp_sndinfo_expr));
 	$$->value.sctp_sndinfo->snd_sid = $2;
 	$$->value.sctp_sndinfo->snd_flags = $4;
 	$$->value.sctp_sndinfo->snd_ppid = $6;
 	$$->value.sctp_sndinfo->snd_context = $8;
-#else
-	$$ = NULL;
-#endif
 }
+;
 
 sctp_setadaptation
 : '{' SSB_ADAPTATION_IND '=' INTEGER '}' {
@@ -3082,6 +3089,99 @@ sctp_setadaptation
 }
 ;
 
+sinfo_stream
+: SINFO_STREAM '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("sinfo_stream out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_STREAM '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_ssn
+: SINFO_SSN '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("sinfo_ssn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_SSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_flags
+: SINFO_FLAGS '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("sinfo_flags out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_ppid
+: SINFO_PPID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("sinfo_ppid out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_context
+: SINFO_CONTEXT '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("sinfo_context out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_timetolive
+: SINFO_TIMETOLIVE '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("snd_timetolive out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_TIMETOLIVE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_tsn
+: SINFO_TSN '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("sinfo_tsn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_TSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sinfo_cumtsn
+: SINFO_CUMTSN '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("sinfo_cumtsn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SINFO_CUMTSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sctp_sndrcvinfo
+: '{' sinfo_stream ',' sinfo_ssn ',' sinfo_flags ',' sinfo_ppid ',' sinfo_context ',' sinfo_timetolive ',' sinfo_tsn ',' sinfo_cumtsn '}' {
+	$$ = new_expression(EXPR_SCTP_SNDRCVINFO);
+	$$->value.sctp_sndrcvinfo = calloc(1, sizeof(struct sctp_sndrcvinfo));
+	$$->value.sctp_sndrcvinfo->sinfo_stream = $2;
+	$$->value.sctp_sndrcvinfo->sinfo_ssn = $4;
+	$$->value.sctp_sndrcvinfo->sinfo_flags = $6;
+	$$->value.sctp_sndrcvinfo->sinfo_ppid = $8;
+	$$->value.sctp_sndrcvinfo->sinfo_context = $10;
+	$$->value.sctp_sndrcvinfo->sinfo_timetolive = $12;
+	$$->value.sctp_sndrcvinfo->sinfo_tsn = $14;
+	$$->value.sctp_sndrcvinfo->sinfo_cumtsn = $16;
+};
 opt_errno
 :                   { $$ = NULL; }
 | WORD note         {
@@ -3121,3 +3221,8 @@ code_spec
  }
 ;
 
+null
+: NULL_ {
+	$$ = new_expression(EXPR_NULL);
+}
+;
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 626d692684adc5d3ed925cc2d5c440793ca1a82c..cf947c92d4d96da00c157ed536a9dd9558937c07 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -2971,6 +2971,329 @@ error_out:
 	return status;
 }
 
+static int syscall_sctp_sendmsg(struct state *state, struct syscall_spec *syscall,
+			struct expression_list *args, char **error)
+{
+#if defined(__FreeBSD__) || defined(__Linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
+	int result, script_fd, live_fd, len;
+	void *msg = NULL;
+	struct sockaddr_storage to;
+	struct sockaddr_storage *to_ptr = &to;
+	socklen_t tolen = 0;
+	u32 ppid, flags, timetolive, context;
+	u16 stream_no;
+	struct expression *sockaddr_expr, *tolen_expr, *ppid_expr, *flags_expr, *ttl_expr, *stream_no_expr, *context_expr;
+	
+	if (check_arg_count(args, 10, error))
+		return STATUS_ERR;
+	if (s32_arg(args, 0, &script_fd, error))
+		return STATUS_ERR;
+	if (to_live_fd(state, script_fd, &live_fd, error))
+		return STATUS_ERR;
+	if (ellipsis_arg(args, 1, error))
+		return STATUS_ERR;
+	if (s32_arg(args, 2, &len, error))
+		return STATUS_ERR;
+	sockaddr_expr = get_arg(args, 3, error);
+	if (sockaddr_expr->type == EXPR_ELLIPSIS) {
+		socklen_t len = (socklen_t)sizeof(struct sockaddr_storage);
+		if (getpeername(live_fd, (struct sockaddr *)to_ptr, &len)) {
+			return STATUS_ERR;
+		}
+		tolen = len;
+	} else if (sockaddr_expr->type == EXPR_NULL) {
+		to_ptr = NULL;
+		tolen = 0;
+	} else {
+		if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV4) {
+			memcpy(to_ptr, sockaddr_expr->value.socket_address_ipv4, sizeof(struct sockaddr_in));
+			tolen = sizeof(struct sockaddr_in);
+		} else if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV6) {
+			memcpy(to_ptr, sockaddr_expr->value.socket_address_ipv6, sizeof(struct sockaddr_in6));
+			tolen = sizeof(struct sockaddr_in6);
+		} else {
+			asprintf(error, "Bad input for reciever in sctp_sentmsg");
+			return STATUS_ERR;
+		}
+	}
+	tolen_expr = get_arg(args, 4, error);	
+	if (tolen_expr->type != EXPR_ELLIPSIS)
+		if (get_u32(tolen_expr, &tolen, error))
+			return STATUS_ERR;
+	ppid_expr = get_arg(args, 5, error);
+	if (get_u32(ppid_expr, &ppid, error))
+		return STATUS_ERR;
+	flags_expr = get_arg(args, 6, error);
+	if (get_u32(flags_expr, &flags, error))
+		return STATUS_ERR;
+	stream_no_expr =get_arg(args, 7, error);
+	if (get_u16(stream_no_expr, &stream_no, error))
+		return STATUS_ERR;
+	ttl_expr = get_arg(args, 8, error);
+	if (get_u32(ttl_expr, &timetolive, error))
+		return STATUS_ERR;
+	context_expr = get_arg(args, 9, error);
+	if (get_u32(context_expr, &context, error))
+		return STATUS_ERR;
+
+	msg = calloc(len, 1);
+	assert(msg != NULL);	
+
+	begin_syscall(state, syscall);
+	result = sctp_sendmsg(live_fd, msg, (size_t)len, (struct sockaddr*) to_ptr, 
+			      tolen, ppid, flags, stream_no, timetolive, context);
+
+	if (end_syscall(state, syscall, CHECK_EXACT, result, error)) {
+		free(msg);
+		return STATUS_ERR;
+	}
+	free(msg);
+	return STATUS_OK;
+#else
+	asprintf(error, "sctp_sendmsg is not supported");
+	return STATUS_ERR;
+#endif
+}
+
+#if defined(__FreeBSD__) || defined(__Linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
+static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr,
+				 struct sctp_sndrcvinfo *sctp_sndrcvinfo, 
+				 char** error) {
+	if (expr->sinfo_stream->type != EXPR_ELLIPSIS) {
+		u16 sinfo_stream;
+
+		if (get_u16(expr->sinfo_stream, &sinfo_stream, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_stream != sinfo_stream) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_stream: expected: %hu actual: %hu",
+				 sinfo_stream, sctp_sndrcvinfo->sinfo_stream);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_ssn->type != EXPR_ELLIPSIS) {
+		u16 sinfo_ssn;
+
+		if (get_u16(expr->sinfo_ssn, &sinfo_ssn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_ssn != sinfo_ssn) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_ssn: expected: %hu actual: %hu",
+				 sinfo_ssn, sctp_sndrcvinfo->sinfo_ssn);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_flags->type != EXPR_ELLIPSIS) {
+		u16 sinfo_flags;
+
+		if (get_u16(expr->sinfo_flags, &sinfo_flags, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_flags != sinfo_flags) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_flags: expected: %hu actual: %hu",
+				 sinfo_flags, sctp_sndrcvinfo->sinfo_flags);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_ppid->type != EXPR_ELLIPSIS) {
+		u32 sinfo_ppid;
+
+		if (get_u32(expr->sinfo_ppid, &sinfo_ppid, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_ppid != sinfo_ppid) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_ppid: expected: %u actual: %u",
+				 sinfo_ppid, sctp_sndrcvinfo->sinfo_ppid);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_context->type != EXPR_ELLIPSIS) {
+		u32 sinfo_context;
+
+		if (get_u32(expr->sinfo_context, &sinfo_context, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_context != sinfo_context) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_context: expected: %u actual: %u",
+				 sinfo_context, sctp_sndrcvinfo->sinfo_context);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_timetolive->type != EXPR_ELLIPSIS) {
+		u32 sinfo_timetolive;
+
+		if (get_u32(expr->sinfo_timetolive, &sinfo_timetolive, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_timetolive != sinfo_timetolive) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_timetolive: expected: %u actual: %u",
+				 sinfo_timetolive, sctp_sndrcvinfo->sinfo_timetolive);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_tsn->type != EXPR_ELLIPSIS) {
+		u32 sinfo_tsn;
+
+		if (get_u32(expr->sinfo_tsn, &sinfo_tsn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_tsn != sinfo_tsn) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_tsn: expected: %u actual: %u",
+				 sinfo_tsn, sctp_sndrcvinfo->sinfo_tsn);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->sinfo_cumtsn->type != EXPR_ELLIPSIS) {
+		u32 sinfo_cumtsn;
+
+		if (get_u32(expr->sinfo_cumtsn, &sinfo_cumtsn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_sndrcvinfo->sinfo_cumtsn != sinfo_cumtsn) {
+			asprintf(error, "sctp_sndrcvinfo.sinfo_cumtsn: expected: %u actual: %u",
+				 sinfo_cumtsn, sctp_sndrcvinfo->sinfo_cumtsn);
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+}
+#endif
+
+static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscall,
+				struct expression_list *args,
+				char **error)
+{
+#if defined(__FreeBSD__) || defined(__Linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
+	int script_fd, live_fd, live_msg_flags, result;
+	void *msg;
+	u32 len;
+	struct sockaddr live_from;
+	socklen_t live_fromlen;
+	struct sctp_sndrcvinfo live_sinfo;
+	struct expression *len_expr, *script_sinfo_expr, *script_msg_flags_expr;
+	struct expression *script_fromlen_expr, *script_from_expr;
+
+	if (check_arg_count(args, 7, error))
+		return STATUS_ERR;
+	if (s32_arg(args, 0, &script_fd, error))
+		return STATUS_ERR;
+	if (to_live_fd(state, script_fd, &live_fd, error))
+		return STATUS_ERR;
+	if (ellipsis_arg(args, 1, error))
+		return STATUS_ERR;
+	len_expr = get_arg(args, 2, error);
+	if (get_u32(len_expr, &len, error))
+		return STATUS_ERR;
+
+	msg = calloc(len, 1);
+	assert(msg != NULL);
+
+	begin_syscall(state, syscall);
+	result = sctp_recvmsg(live_fd, msg, len, (struct sockaddr*) &live_from,
+			      &live_fromlen, &live_sinfo, &live_msg_flags);
+	free(msg);
+	
+	if (end_syscall(state, syscall, CHECK_EXACT, result, error)) {
+		return STATUS_ERR;
+	}
+
+	script_from_expr = get_arg(args, 3, error);
+	if (script_from_expr->type != EXPR_ELLIPSIS) {
+		struct sockaddr *script_addr;
+		if (script_from_expr->type == EXPR_SOCKET_ADDRESS_IPV4) {
+			script_addr = (struct sockaddr*)script_from_expr->value.socket_address_ipv4;
+		} else if( script_from_expr->type == EXPR_SOCKET_ADDRESS_IPV6) {
+			script_addr = (struct sockaddr*)script_from_expr->value.socket_address_ipv6;
+		} else {
+			asprintf(error, "sctp_recvmsg fromlen: can't check sctp_recvmsg from");
+			return STATUS_ERR;			
+		}
+		if (script_addr->sa_family != live_from.sa_family) {
+			asprintf(error, "sctp_recvmsg from.sa_family: expected: %d actual: %d",
+				 script_addr->sa_family, live_from.sa_family);
+			return STATUS_ERR;
+		}
+		switch(script_addr->sa_family) {
+		case AF_INET: 
+			{
+				struct sockaddr_in *script_sockaddr = (struct sockaddr_in*)script_addr;
+				struct sockaddr_in *live_sockaddr = (struct sockaddr_in*)&live_from;
+				if (live_sockaddr->sin_port != script_sockaddr->sin_port) {
+					asprintf(error, "sctp_recvmsg from.sinport. expected: %d actual %d",
+						ntohs(script_sockaddr->sin_port), ntohs(live_sockaddr->sin_port));
+					return STATUS_ERR;
+				}
+				if (live_sockaddr->sin_addr.s_addr != script_sockaddr->sin_addr.s_addr) {
+					int len = strnlen(inet_ntoa(script_sockaddr->sin_addr), 16);
+					char *expected_addr = malloc(sizeof(char) * len);
+					memcpy(expected_addr, inet_ntoa(script_sockaddr->sin_addr), len);
+					asprintf(error, "sctp_recvmsg from.sin_addr. expected: %s actual %s",
+						expected_addr, inet_ntoa(live_sockaddr->sin_addr));
+					free(expected_addr);
+					return STATUS_ERR;
+				}
+			}
+			break;
+		case AF_INET6: 
+			{
+				struct sockaddr_in6 *script_sockaddr = (struct sockaddr_in6*)script_addr;
+				struct sockaddr_in6 *live_sockaddr = (struct sockaddr_in6*)&live_from;
+				if (live_sockaddr->sin6_port != script_sockaddr->sin6_port) {
+					asprintf(error, "sctp_recvmsg from.sinport. expected: %d actual %d",
+						ntohs(script_sockaddr->sin6_port), ntohs(live_sockaddr->sin6_port));
+					return STATUS_ERR;
+				}
+				if (live_sockaddr->sin6_addr.s6_addr != script_sockaddr->sin6_addr.s6_addr) {
+					char expected_addr[INET6_ADDRSTRLEN];
+					char live_addr[INET6_ADDRSTRLEN];
+					inet_ntop(AF_INET6, &script_sockaddr->sin6_addr, expected_addr, INET6_ADDRSTRLEN);
+					inet_ntop(AF_INET6, &live_sockaddr->sin6_addr, live_addr, INET6_ADDRSTRLEN);
+					asprintf(error, "sctp_recvmsg from.sin6_addr. expected: %s actual %s",
+						 expected_addr, live_addr);
+					return STATUS_ERR;
+				}
+			}
+			break;
+		}
+
+	}
+
+	script_fromlen_expr = get_arg(args, 4, error);
+	if (script_fromlen_expr->type != EXPR_ELLIPSIS) {
+		int script_fromlen;
+		if (get_s32(script_fromlen_expr, &script_fromlen, error))
+			return STATUS_ERR;
+		if (script_fromlen != live_fromlen) {
+			asprintf(error, "sctp_recvmsg fromlen: expected: %d actual: %d",
+				 script_fromlen, live_fromlen);
+			return STATUS_ERR;
+		}
+	}
+
+	script_sinfo_expr = get_arg(args, 5, error);
+	if (script_sinfo_expr->type != EXPR_ELLIPSIS) {
+		if (check_sctp_sndrcvinfo(script_sinfo_expr->value.sctp_sndrcvinfo, &live_sinfo, error)) {
+			return STATUS_ERR;
+		}
+	}
+	script_msg_flags_expr = get_arg(args, 6, error);
+	if (script_msg_flags_expr->type != EXPR_ELLIPSIS) {
+		int script_msg_flags;
+		if (get_s32(script_msg_flags_expr, &script_msg_flags, error))
+			return STATUS_ERR;
+		if (script_msg_flags != live_msg_flags) {
+			asprintf(error, "sctp_recvmsg msg_flags: expected: %d actual: %d",
+				 script_msg_flags, live_msg_flags);
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+#else
+	asprintf(error, "sctp_sendmsg is not supported");
+	return STATUS_ERR;
+#endif
+}
+
 /* A dispatch table with all the system calls that we support... */
 struct system_call_entry {
 	const char *name;
@@ -3002,6 +3325,8 @@ struct system_call_entry system_call_table[] = {
 	{"getsockopt", syscall_getsockopt},
 	{"setsockopt", syscall_setsockopt},
 	{"poll",       syscall_poll},
+	{"sctp_sendmsg", syscall_sctp_sendmsg},
+	{"sctp_recvmsg", syscall_sctp_recvmsg},
 };
 
 /* Evaluate the system call arguments and invoke the system call. */
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 5c07f3ee5053bf66edcf3a688dba82a8c9bb82dd..1b766b8271d94046728e6806b95f304f37dcf008 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -53,6 +53,7 @@ struct expression_type_entry {
 };
 struct expression_type_entry expression_type_table[] = {
 	{ EXPR_NONE,                 "none" },
+	{ EXPR_NULL,		     "null" },
 	{ EXPR_ELLIPSIS,             "ellipsis" },
 	{ EXPR_INTEGER,              "integer" },
 	{ EXPR_WORD,                 "word" },
@@ -77,6 +78,7 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_SCTP_EVENT,	     "sctp_event"      },
 	{ EXPR_SCTP_SNDINFO,         "sctp_sndinfo"    },
 	{ EXPR_SCTP_SETADAPTATION,   "sctp_setadaptation"},
+	{ EXPR_SCTP_SNDRCVINFO,      "sctp_sndrcvinfo" },
 	{ NUM_EXPR_TYPES,            NULL}
 };
 
@@ -280,6 +282,7 @@ void free_expression(struct expression *expression)
 	    (expression->type >= NUM_EXPR_TYPES))
 		assert(!"bad expression type");
 	switch (expression->type) {
+	case EXPR_NULL:
 	case EXPR_ELLIPSIS:
 	case EXPR_INTEGER:
 		break;
@@ -365,6 +368,16 @@ void free_expression(struct expression *expression)
 	case EXPR_SCTP_SETADAPTATION:
 		free_expression(expression->value.sctp_setadaptation->ssb_adaptation_ind);
 		break;
+	case EXPR_SCTP_SNDRCVINFO:
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_stream);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_ssn);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_flags);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_ppid);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_context);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_timetolive);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_tsn);
+		free_expression(expression->value.sctp_sndrcvinfo->sinfo_cumtsn);
+		break;
 	case EXPR_WORD:
 		assert(expression->value.string);
 		free(expression->value.string);
@@ -966,6 +979,58 @@ static int evaluate_sctp_setadaptation_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_sndrcvinfo_expression(struct expression *in,
+						  struct expression *out,
+						  char **error)
+{
+        struct sctp_sndrcvinfo_expr *in_info;
+        struct sctp_sndrcvinfo_expr *out_info;
+
+        assert(in->type == EXPR_SCTP_SNDRCVINFO);
+        assert(in->value.sctp_sndrcvinfo);
+        assert(out->type == EXPR_SCTP_SNDRCVINFO);
+
+        out->value.sctp_sndrcvinfo = calloc(1, sizeof(struct sctp_sndrcvinfo_expr));
+                     
+        in_info = in->value.sctp_sndrcvinfo;
+        out_info = out->value.sctp_sndrcvinfo;
+                     
+        if (evaluate(in_info->sinfo_stream,
+		     &out_info->sinfo_stream,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_ssn,
+		     &out_info->sinfo_ssn,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_flags,
+		     &out_info->sinfo_flags,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_ppid,
+		     &out_info->sinfo_ppid,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_context,
+		     &out_info->sinfo_context,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_timetolive,
+		     &out_info->sinfo_timetolive,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_tsn,
+		     &out_info->sinfo_tsn,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->sinfo_cumtsn,
+		     &out_info->sinfo_cumtsn,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
 static int evaluate(struct expression *in,
 		    struct expression **out_ptr, char **error)
 {
@@ -980,6 +1045,8 @@ static int evaluate(struct expression *in,
 		return STATUS_ERR;
 	}
 	switch (in->type) {
+	case EXPR_NULL:
+		break;
 	case EXPR_ELLIPSIS:
 		break;
 	case EXPR_INTEGER:		/* copy as-is */
@@ -1025,6 +1092,9 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_SETADAPTATION:
 		result = evaluate_sctp_setadaptation_expression(in, out, error);
 		break;
+	case EXPR_SCTP_SNDRCVINFO:
+		result = evaluate_sctp_sndrcvinfo_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 c614f9d7053d8e3363e8b3bbe254f2f84cc362fd..fbcf7fe705e49abb54e73dde4a944c3ec54e8e52 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -33,6 +33,7 @@
 /* The types of expressions in a script */
 enum expression_t {
 	EXPR_NONE,
+	EXPR_NULL,	          /* Expression to handle NULL */
 	EXPR_ELLIPSIS,		  /* ... but no value */
 	EXPR_INTEGER,		  /* integer in 'num' */
 	EXPR_LINGER,		  /* struct linger for SO_LINGER */
@@ -56,7 +57,8 @@ enum expression_t {
 	EXPR_SCTP_ASSOCPARAMS,    /* struct sctp_assocparams for SCTP_ASSOCINFO */
 	EXPR_SCTP_EVENT,	  /* struct sctp_event for SCTP_EVENT */
 	EXPR_SCTP_SNDINFO,	  /* struct sctp_sndinfo for SCTP_DEFAULT_SNDINFO */
-	EXPR_SCTP_SETADAPTATION, /* struct sctp_setadaptation for SCTP_ADATTATION_LAYER */
+	EXPR_SCTP_SETADAPTATION,  /* struct sctp_setadaptation for SCTP_ADATTATION_LAYER */
+	EXPR_SCTP_SNDRCVINFO,     /* struct sctp_sndrcvinfo for syscall sctp_recvmsg */
 	NUM_EXPR_TYPES,
 };
 /* Convert an expression type to a human-readable string */
@@ -88,6 +90,7 @@ struct expression {
 		struct sctp_event_expr *sctp_event;
 		struct sctp_sndinfo_expr *sctp_sndinfo;
 		struct sctp_setadaptation_expr *sctp_setadaptation;
+		struct sctp_sndrcvinfo_expr *sctp_sndrcvinfo;
 	} value;
 	const char *format;	/* the printf format for printing the value */
 };
@@ -227,6 +230,18 @@ struct sctp_setadaptation_expr {
 	struct expression *ssb_adaptation_ind;
 };
 
+/* Parse tree for sctp_sndrcvinfo in sctp_recvmsg syscall. */
+struct sctp_sndrcvinfo_expr {
+	struct expression *sinfo_stream;
+	struct expression *sinfo_ssn;
+	struct expression *sinfo_flags;
+	struct expression *sinfo_ppid;
+	struct expression *sinfo_context;
+	struct expression *sinfo_timetolive;
+	struct expression *sinfo_tsn;
+	struct expression *sinfo_cumtsn;
+};
+
 /* 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/tests/bsd/sctp/sctp_recvmsg.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_recvmsg.pkt
new file mode 100644
index 0000000000000000000000000000000000000000..c4df402369587dea4affade5c2f9b9af31b14d48
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_recvmsg.pkt
@@ -0,0 +1,30 @@
++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3
++0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
++0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+// Check the handshake with an empty(!) cookie
++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=1, ...]
++0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=1500, os=1, is=1, tsn=1, STATE_COOKIE[len=4, val=...]]
++0.0 > sctp: COOKIE_ECHO[flgs=0, len=4, val=...]
++0.1 < sctp: COOKIE_ACK[flgs=0]
++0.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
+//sctp_recvmsg(int sd, void * msg, size_t len, struct sockaddr * from, socklen_t * fromlen, struct sctp_sndrcvinfo* sinfo, int * msg_flags);
+
++0.0 < sctp: DATA[flgs=BE, len=1016, tsn=1, sid=0, ssn=0, ppid=0]
+*    > sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=..., gaps=[], dups=[]]
++0.0 sctp_recvmsg(3, ..., 1000, ..., ..., ..., 8) = 1000
+
++0.0 < sctp: DATA[flgs=BE, len=1016, tsn=2, sid=0, ssn=1, ppid=0]
+*    > sctp: SACK[flgs=0, cum_tsn=2, a_rwnd=..., gaps=[], dups=[]]
++0.0 sctp_recvmsg(3, ..., 1000, ..., ..., {sinfo_stream=0, sinfo_ssn=1, sinfo_flags=0,
+sinfo_ppid=0, sinfo_context=0, sinfo_timetolive=0, sinfo_tsn=2, sinfo_cumtsn=2}, 8) = 1000
+
++0.0 < sctp: DATA[flgs=BE, len=1016, tsn=3, sid=0, ssn=2, ppid=0]
+*    > sctp: SACK[flgs=0, cum_tsn=3, a_rwnd=..., gaps=[], dups=[]]
++0.0 sctp_recvmsg(3, ..., 1000, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.0.2.1")}, 16,
+{sinfo_stream=0, sinfo_ssn=2, sinfo_flags=0, sinfo_ppid=0, sinfo_context=0, sinfo_timetolive=0, sinfo_tsn=3, sinfo_cumtsn=3}, 8) = 1000
+
++0.0 close(3) = 0
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=3]
++0.1 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendmsg.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendmsg.pkt
new file mode 100644
index 0000000000000000000000000000000000000000..e9d6ec47dddfe82400ecb61c1122c941fe843ec9
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendmsg.pkt
@@ -0,0 +1,25 @@
++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3
++0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
++0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+// Check the handshake with an empty(!) cookie
++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
++0.0 > sctp: INIT[flgs=0, tag=1, a_rwnd=..., os=..., is=..., tsn=1, ...]
++0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=1500, os=1, is=1, tsn=1, STATE_COOKIE[len=4, val=...]]
++0.0 > sctp: COOKIE_ECHO[flgs=0, len=4, val=...]
++0.1 < sctp: COOKIE_ACK[flgs=0]
++0.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
+//sctp_sendmsg(int sd, const void * msg, size_t len, struct sockaddr *to, socklen_t tolen, 
+//             uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context);
++0.0 sctp_sendmsg(3, ..., 1000, ..., ..., 0, 0, 0, 0, 0) = 1000
++0.0 > sctp: DATA[flgs=BE, len=1016, tsn=1, sid=0, ssn=0, ppid=0]
++0.0 < sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=1500, gaps=[], dups=[]]
++0.0 sctp_sendmsg(3, ..., 1000, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.0.2.1")}, 16, 0, 0, 0, 0, 0) = 1000
++0.0 > sctp: DATA[flgs=BE, len=1016, tsn=2, sid=0, ssn=1, ppid=0]
++0.0 < sctp: SACK[flgs=0, cum_tsn=2, a_rwnd=1500, gaps=[], dups=[]]
++0.0 sctp_sendmsg(3, ..., 1000, NULL, 0, 0, 0, 0, 0, 0) = 1000
++0.0 > sctp: DATA[flgs=BE, len=1016, tsn=3, sid=0, ssn=2, ppid=0]
++0.0 < sctp: SACK[flgs=0, cum_tsn=3, a_rwnd=1500, gaps=[], dups=[]]
++0.0 close(3) = 0
++0.0 > sctp: SHUTDOWN[flgs=0, cum_tsn=0]
++0.1 < sctp: SHUTDOWN_ACK[flgs=0]
++0.0 > sctp: SHUTDOWN_COMPLETE[flgs=0]