From 13bf8a4eda31730a026cf049ddf7c8b4a3e082f2 Mon Sep 17 00:00:00 2001
From: hoelscher <jens.hoelscher@fh-muenster.de>
Date: Mon, 19 Oct 2015 16:50:37 +0200
Subject: [PATCH] add Support for sctp_recvv

---
 gtests/net/packetdrill/lexer.l           |  13 +
 gtests/net/packetdrill/parser.y          | 159 +++++++++
 gtests/net/packetdrill/run_system_call.c | 422 ++++++++++++++++++++---
 gtests/net/packetdrill/script.c          | 137 ++++++++
 gtests/net/packetdrill/script.h          |  31 ++
 gtests/net/packetdrill/symbols_freebsd.c |   7 +-
 6 files changed, 715 insertions(+), 54 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 69487672..357e4722 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -265,6 +265,19 @@ sendv_flags			return SENDV_FLAGS;
 sendv_sndinfo			return SENDV_SNDINFO;
 sendv_prinfo			return SENDV_PRINFO;
 sendv_authinfo			return SENDV_AUTHINFO;
+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;
+nxt_sid                         return NXT_SID;
+nxt_flags                       return NXT_FLAGS;
+nxt_ppid                        return NXT_PPID;
+nxt_length                      return NXT_LENGTH;
+recvv_rcvinfo                   return RECVV_RCVINFO;
+recvv_nxtinfo                   return RECVV_NXTINFO;
 CHUNK				return CHUNK;
 DATA				return DATA;
 INIT				return INIT;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 1b74def5..634e6487 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -544,6 +544,9 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> SINFO_TIMETOLIVE SINFO_TSN SINFO_CUMTSN
 %token <reserved> PR_POLICY PR_VALUE AUTH_KEYNUMBER SENDV_FLAGS SENDV_SNDINFO
 %token <reserved> SENDV_PRINFO SENDV_AUTHINFO
+%token <reserved> RCV_SID RCV_SSN RCV_FLAGS RCV_PPID RCV_TSN RCV_CUMTSN RCV_CONTEXT
+%token <reserved> NXT_SID NXT_FLAGS NXT_PPID NXT_LENGTH
+%token <reserved> RECVV_RCVINFO RECVV_NXTINFO
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -597,6 +600,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> sctp_sndrcvinfo sinfo_stream sinfo_ssn sinfo_flags sinfo_ppid sinfo_context
 %type <expression> sinfo_timetolive sinfo_tsn sinfo_cumtsn
 %type <expression> sctp_prinfo sctp_authinfo pr_policy sctp_sendv_spa
+%type <expression> sctp_rcvinfo rcv_sid rcv_ssn rcv_flags rcv_ppid rcv_tsn rcv_cumtsn rcv_context
+%type <expression> sctp_nxtinfo nxt_sid nxt_flags nxt_ppid nxt_length sctp_recvv_rn
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
@@ -2472,6 +2477,15 @@ expression
 | sctp_sendv_spa    {
 	$$ = $1;
 }
+| sctp_rcvinfo      {
+	$$ = $1;
+}
+| sctp_nxtinfo      {
+	$$ = $1;
+}
+| sctp_recvv_rn     {
+	$$ = $1;
+}
 | null              {
 	$$ = $1;
 }
@@ -3255,6 +3269,91 @@ sctp_sndrcvinfo
 	$$->value.sctp_sndrcvinfo->sinfo_cumtsn = $16;
 };
 
+rcv_sid
+: RCV_SID '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("rcv_sid out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_ssn
+: RCV_SSN '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("rcv_ssn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_SSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_flags
+: RCV_FLAGS '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("rcv_flags out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_ppid
+: RCV_PPID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("sinfo_cumtsn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_tsn
+: RCV_TSN '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("rcv_tsn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_TSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_cumtsn
+: RCV_CUMTSN '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("rcv_cumtsn out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_CUMTSN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+rcv_context
+: RCV_CONTEXT '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("rcv_context out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+
+sctp_rcvinfo
+: '{' rcv_sid ',' rcv_ssn ',' rcv_flags ',' rcv_ppid ',' rcv_tsn ',' rcv_cumtsn ',' rcv_context '}' {
+	$$ = new_expression(EXPR_SCTP_RCVINFO);
+	$$->value.sctp_rcvinfo = calloc(1, sizeof(struct sctp_rcvinfo_expr));
+	$$->value.sctp_rcvinfo->rcv_sid = $2;
+	$$->value.sctp_rcvinfo->rcv_ssn = $4;
+	$$->value.sctp_rcvinfo->rcv_flags = $6;
+	$$->value.sctp_rcvinfo->rcv_ppid = $8;
+	$$->value.sctp_rcvinfo->rcv_tsn = $10;
+	$$->value.sctp_rcvinfo->rcv_cumtsn = $12;
+	$$->value.sctp_rcvinfo->rcv_context = $14;
+}
+;
+
 pr_policy
 : PR_POLICY '=' WORD {
 	$$ = new_expression(EXPR_WORD);
@@ -3301,6 +3400,66 @@ sctp_sendv_spa
 }
 ;
 
+nxt_sid
+: NXT_SID '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("nxt_sid out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| NXT_SID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+nxt_flags
+: NXT_FLAGS '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("nxt_flags out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| NXT_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+nxt_ppid
+: NXT_PPID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("nxt_ppid out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| NXT_PPID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+nxt_length 
+: NXT_LENGTH '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("nxt_length out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| NXT_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sctp_nxtinfo
+: '{' nxt_sid ',' nxt_flags ',' nxt_ppid ',' nxt_length '}' {
+	$$ = new_expression(EXPR_SCTP_NXTINFO);
+	$$->value.sctp_sendv_spa = calloc(1, sizeof(struct sctp_nxtinfo_expr));
+	$$->value.sctp_nxtinfo->nxt_sid = $2;
+	$$->value.sctp_nxtinfo->nxt_flags = $4;
+	$$->value.sctp_nxtinfo->nxt_ppid = $6;
+	$$->value.sctp_nxtinfo->nxt_length = $8;
+}
+;
+
+sctp_recvv_rn
+: '{' RECVV_RCVINFO '=' sctp_rcvinfo ',' RECVV_NXTINFO '=' sctp_nxtinfo '}' {
+	$$ = new_expression(EXPR_SCTP_RECVV_RN);
+	$$->value.sctp_sendv_spa = calloc(1, sizeof(struct sctp_recvv_rn_expr));
+	$$->value.sctp_recvv_rn->recvv_rcvinfo = $4;
+	$$->value.sctp_recvv_rn->recvv_nxtinfo = $8;
+}
+;
+
 opt_errno
 :                   { $$ = NULL; }
 | WORD note         {
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 32bc900f..f82e14f5 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -3158,67 +3158,30 @@ static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr,
 }
 #endif
 
-static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscall,
-				struct expression_list *args,
-				char **error)
-{
-#if defined(__FreeBSD__) || defined(__Linux__)
-	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;
+static int check_sockaddr(struct expression *sockaddr_expr, struct sockaddr *live_addr, char **error) {
 
-	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) {
+	if (sockaddr_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;
+		if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV4) {
+			script_addr = (struct sockaddr*)sockaddr_expr->value.socket_address_ipv4;
+		} else if (sockaddr_expr->type == EXPR_SOCKET_ADDRESS_IPV6) {
+			script_addr = (struct sockaddr*)sockaddr_expr->value.socket_address_ipv6;
 		} else {
-			asprintf(error, "sctp_recvmsg fromlen: can't check sctp_recvmsg from");
+			asprintf(error, "Bad type for sockaddr");
 			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);
+		if (script_addr->sa_family != live_addr->sa_family) {
+			asprintf(error, "sockaddr sa_family expected: %d actual: %d",
+				 script_addr->sa_family, live_addr->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;
+				struct sockaddr_in *live_sockaddr = (struct sockaddr_in*)live_addr;
 				if (live_sockaddr->sin_port != script_sockaddr->sin_port) {
-					asprintf(error, "sctp_recvmsg from.sinport. expected: %d actual %d",
+					asprintf(error, "sockaddr_in from.sinport. expected: %d actual %d",
 						ntohs(script_sockaddr->sin_port), ntohs(live_sockaddr->sin_port));
 					return STATUS_ERR;
 				}
@@ -3226,7 +3189,7 @@ static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscal
 					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",
+					asprintf(error, "sockaddr_in from.sin_addr. expected: %s actual %s",
 						expected_addr, inet_ntoa(live_sockaddr->sin_addr));
 					free(expected_addr);
 					return STATUS_ERR;
@@ -3236,9 +3199,9 @@ static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscal
 		case AF_INET6:
 			{
 				struct sockaddr_in6 *script_sockaddr = (struct sockaddr_in6*)script_addr;
-				struct sockaddr_in6 *live_sockaddr = (struct sockaddr_in6*)&live_from;
+				struct sockaddr_in6 *live_sockaddr = (struct sockaddr_in6*)live_addr;
 				if (live_sockaddr->sin6_port != script_sockaddr->sin6_port) {
-					asprintf(error, "sctp_recvmsg from.sinport. expected: %d actual %d",
+					asprintf(error, "sockaddr_in6 from.sinport. expected: %d actual %d",
 						ntohs(script_sockaddr->sin6_port), ntohs(live_sockaddr->sin6_port));
 					return STATUS_ERR;
 				}
@@ -3247,16 +3210,60 @@ static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscal
 					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",
+					asprintf(error, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
 						 expected_addr, live_addr);
 					return STATUS_ERR;
 				}
 			}
 			break;
 		}
+	}
+	return STATUS_OK;
+}
+
+
+static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscall,
+				struct expression_list *args,
+				char **error)
+{
+#if defined(__FreeBSD__) || defined(__Linux__)
+	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 (check_sockaddr(script_from_expr, &live_from, error))
+		return STATUS_ERR;
+
 	script_fromlen_expr = get_arg(args, 4, error);
 	if (script_fromlen_expr->type != EXPR_ELLIPSIS) {
 		int script_fromlen;
@@ -3498,6 +3505,314 @@ static int syscall_sctp_sendv(struct state *state, struct syscall_spec *syscall,
 #endif
 }
 
+#ifdef SCTP_RECVV_RCVINFO
+static int check_sctp_rcvinfo(struct sctp_rcvinfo_expr *expr,
+			      struct sctp_rcvinfo *sctp_rcvinfo,
+			      char **error)
+{
+	if (expr->rcv_sid->type != EXPR_ELLIPSIS) {
+		u16 rcv_sid;
+
+		if (get_u16(expr->rcv_sid, &rcv_sid, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_sid != rcv_sid) {
+			asprintf(error, "sctp_rcvinfo.rcv_sid: expected: %hu actual: %hu",
+				 rcv_sid, sctp_rcvinfo->rcv_sid);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_ssn->type != EXPR_ELLIPSIS) {
+		u16 rcv_ssn;
+
+		if (get_u16(expr->rcv_ssn, &rcv_ssn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_ssn != rcv_ssn) {
+			asprintf(error, "sctp_rcvinfo.rcv_ssn: expected: %hu actual: %hu",
+				 rcv_ssn, sctp_rcvinfo->rcv_ssn);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_flags->type != EXPR_ELLIPSIS) {
+		u16 rcv_flags;
+
+		if (get_u16(expr->rcv_flags, &rcv_flags, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_flags != rcv_flags) {
+			asprintf(error, "sctp_rcvinfo.rcv_flags: expected: %hu actual: %hu",
+				 rcv_flags, sctp_rcvinfo->rcv_flags);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_ppid->type != EXPR_ELLIPSIS) {
+		u32 rcv_ppid;
+
+		if (get_u32(expr->rcv_ppid, &rcv_ppid, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_ppid != rcv_ppid) {
+			asprintf(error, "sctp_rcvinfo.rcv_ppid: expected: %u actual: %u",
+				 rcv_ppid, sctp_rcvinfo->rcv_ppid);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_tsn->type != EXPR_ELLIPSIS) {
+		u32 rcv_tsn;
+
+		if (get_u32(expr->rcv_tsn, &rcv_tsn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_tsn != rcv_tsn) {
+			asprintf(error, "sctp_rcvinfo.rcv_tsn: expected: %u actual: %u",
+				 rcv_tsn, sctp_rcvinfo->rcv_tsn);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_cumtsn->type != EXPR_ELLIPSIS) {
+		u32 rcv_cumtsn;
+
+		if (get_u32(expr->rcv_cumtsn, &rcv_cumtsn, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_cumtsn != rcv_cumtsn) {
+			asprintf(error, "sctp_rcvinfo.rcv_cumtsn: expected: %u actual: %u",
+				 rcv_cumtsn, sctp_rcvinfo->rcv_cumtsn);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->rcv_context->type != EXPR_ELLIPSIS) {
+		u32 rcv_context;
+
+		if (get_u32(expr->rcv_context, &rcv_context, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_rcvinfo->rcv_context != rcv_context) {
+			asprintf(error, "sctp_rcvinfo.rcv_context: expected: %u actual: %u",
+				 rcv_context, sctp_rcvinfo->rcv_context);
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+}
+#endif
+
+#ifdef SCTP_RECVV_NXTINFO
+static int check_sctp_nxtinfo(struct sctp_nxtinfo_expr *expr,
+			      struct sctp_nxtinfo *sctp_nxtinfo,
+			      char **error)
+{
+	if (expr->nxt_sid->type != EXPR_ELLIPSIS) {
+		u16 nxt_sid;
+
+		if (get_u16(expr->nxt_sid, &nxt_sid, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_nxtinfo->nxt_sid != nxt_sid) {
+			asprintf(error, "sctp_nxtinfo.nxt_sid: expected: %hu actual: %hu",
+				 nxt_sid, sctp_nxtinfo->nxt_sid);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->nxt_flags->type != EXPR_ELLIPSIS) {
+		u16 nxt_flags;
+
+		if (get_u16(expr->nxt_flags, &nxt_flags, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_nxtinfo->nxt_flags != nxt_flags) {
+			asprintf(error, "sctp_nxtinfo.nxt_flags: expected: %hu actual: %hu",
+				 nxt_flags, sctp_nxtinfo->nxt_flags);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->nxt_ppid->type != EXPR_ELLIPSIS) {
+		u32 nxt_ppid;
+
+		if (get_u32(expr->nxt_ppid, &nxt_ppid, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_nxtinfo->nxt_ppid != nxt_ppid) {
+			asprintf(error, "sctp_nxtinfo.nxt_ppid: expected: %u actual: %u",
+				 nxt_ppid, sctp_nxtinfo->nxt_ppid);
+			return STATUS_ERR;
+		}
+	}
+	if (expr->nxt_length->type != EXPR_ELLIPSIS) {
+		u32 nxt_length;
+
+		if (get_u32(expr->nxt_length, &nxt_length, error)) {
+			return STATUS_ERR;
+		}
+		if (sctp_nxtinfo->nxt_length != nxt_length) {
+			asprintf(error, "sctp_nxtinfo.nxt_length: expected: %u actual: %u",
+				 nxt_length, sctp_nxtinfo->nxt_length);
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+}
+#endif
+
+#ifdef SCTP_RECVV_RN
+static int check_sctp_recvv_rn(struct sctp_recvv_rn_expr *expr,
+			       struct sctp_recvv_rn *sctp_recvv_rn,
+			       char **error)
+{
+	if (expr->recvv_rcvinfo->type != EXPR_ELLIPSIS) {
+		if (check_sctp_rcvinfo((struct sctp_rcvinfo_expr *)expr->recvv_rcvinfo,
+				       &sctp_recvv_rn->recvv_rcvinfo, error))
+			return STATUS_ERR;
+		if (check_sctp_nxtinfo((struct sctp_nxtinfo_expr *)expr->recvv_nxtinfo,
+				       &sctp_recvv_rn->recvv_nxtinfo, error))
+			return STATUS_ERR;
+	}
+	return STATUS_OK;
+}
+#endif
+
+static int syscall_sctp_recvv(struct state *state, struct syscall_spec *syscall,
+			      struct expression_list *args,
+			      char **error)
+{
+#if defined(__FreeBSD__)
+	int flags, iovlen, script_fd, live_fd, result_val, result = STATUS_OK;
+	size_t script_iovec_list_len = 0;
+	unsigned int infotype = 0;
+	socklen_t infolen, fromlen;
+	void *info;
+	struct iovec *iov;
+	struct sockaddr *from = NULL;
+	begin_syscall(state, syscall);
+	struct expression *iovec_expr_list, *iovcnt_expr, *addr_expr, *fromlen_expr;
+	struct expression *infolen_expr, *info_expr, *infotype_expr, *flags_expr;
+	struct sctp_recvv_rn recvv_rn;
+	struct sctp_rcvinfo rcvinfo;
+	struct sctp_nxtinfo nxtinfo;
+
+	if (check_arg_count(args, 9, 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;
+	iovec_expr_list = get_arg(args, 1, error);
+	iovec_new(iovec_expr_list, &iov,  &script_iovec_list_len, error);
+	iovcnt_expr = get_arg(args, 2, error);
+	if (get_s32(iovcnt_expr, &iovlen, error))
+		return STATUS_ERR;
+	fromlen_expr = get_arg(args, 4, error);
+	if (get_u32(fromlen_expr, &fromlen, error))
+		return STATUS_ERR;
+
+	info_expr = get_arg(args, 5, error);
+	if (info_expr->type == EXPR_NULL) {
+		info = NULL;
+	} else if (info_expr->type == EXPR_SCTP_RCVINFO) {
+		info = &rcvinfo;
+	} else if (info_expr->type == EXPR_SCTP_NXTINFO) {
+		info = &nxtinfo;
+	} else if (info_expr->type == EXPR_SCTP_RECVV_RN) {
+		info = &recvv_rn;
+	} else {
+		return STATUS_ERR;
+	}
+	infolen_expr = get_arg(args, 6, error);
+	if (get_u32(infolen_expr, &infolen, error))
+		return STATUS_ERR;
+	infotype = 0;
+	flags = 0;
+	addr_expr = get_arg(args, 3, error);
+	if (addr_expr->type == EXPR_NULL) {
+		from = NULL;
+	} else {
+		from = malloc(fromlen);
+	}
+	printf("fd=%d, iovlen=%d, fromlen=%d, infolen=%d, infotype=%d, flags=%d\n",live_fd, iovlen, fromlen, infolen, infotype, flags);
+
+	begin_syscall(state, syscall);
+
+	result_val = sctp_recvv(live_fd, iov, iovlen, (struct sockaddr *)from, &fromlen, info, &infolen, &infotype, &flags);
+
+	iovec_free(iov, script_iovec_list_len);
+	
+	if (end_syscall(state, syscall, CHECK_EXACT, result_val, error)) {
+		free(from);
+		return STATUS_ERR;
+	}
+	if (from != NULL) {
+		if (check_sockaddr(addr_expr, from, error)) {
+			free(from);
+			return STATUS_ERR;
+		}
+	}
+	free(from);
+	infotype_expr = get_arg(args, 7, error);
+	if (infotype_expr->type != EXPR_ELLIPSIS) {
+		s32 script_infotype;
+
+		if (get_s32(infotype_expr, &script_infotype, error)) {
+			return STATUS_ERR;
+		}
+		if (infotype != script_infotype) {
+			asprintf(error, "sctp_recvv infotype: expected: %u actual: %u",
+				 script_infotype, infotype);
+			return STATUS_ERR;
+		}
+	}
+//check info
+	switch(infotype){
+	case SCTP_RECVV_NOINFO:
+		if (infolen != 0){
+			asprintf(error, "infolen returned bad size for null. expected 0, actual %u", infolen);
+			return STATUS_ERR;
+		}
+		break;
+	case SCTP_RECVV_RCVINFO:
+		if (infolen != sizeof(struct sctp_rcvinfo)){
+			asprintf(error, "infolen returned bad size for sctp_rcvinfo. expected %u, actual %u",
+				 sizeof(struct sctp_rcvinfo), infolen);
+			return STATUS_ERR;
+		}
+		result = check_sctp_rcvinfo(info_expr->value.sctp_rcvinfo, info, error);
+		break;
+	case SCTP_RECVV_NXTINFO:
+		if (infolen != sizeof(struct sctp_nxtinfo)){
+			asprintf(error, "infolen returned bad size for sctp_nxtinfo. expected %u, actual %u",
+				 sizeof(struct sctp_nxtinfo), infolen);
+			return STATUS_ERR;
+		}
+		result = check_sctp_nxtinfo(info_expr->value.sctp_nxtinfo, info, error);
+		break;
+	case SCTP_RECVV_RN:
+		if (infolen != sizeof(struct sctp_recvv_rn)){
+			asprintf(error, "infolen returned bad size for sctp_recvv_rn. expected %u, actual %u",
+				 sizeof(struct sctp_recvv_rn), infolen);
+			return STATUS_ERR;
+		}
+		result = check_sctp_recvv_rn(info_expr->value.sctp_recvv_rn, info, error);
+		break;
+	default:
+		result = STATUS_ERR;
+		break;
+	}
+	flags_expr = get_arg(args, 8, error);
+	if (flags_expr->type != EXPR_ELLIPSIS) {
+		s32 script_flags;
+		if (get_s32(flags_expr, &script_flags, error)) {
+			asprintf(error, "sctp_recvv flags bad return value. expected %d, actual %d",
+				 script_flags, flags);
+			return STATUS_ERR;
+		}
+	}
+	return result;
+#else
+	asprintf(error, "sctp_recvv 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;
@@ -3532,6 +3847,7 @@ struct system_call_entry system_call_table[] = {
 	{"sctp_sendmsg", syscall_sctp_sendmsg},
 	{"sctp_recvmsg", syscall_sctp_recvmsg},
 	{"sctp_sendv", syscall_sctp_sendv},
+	{"sctp_recvv", syscall_sctp_recvv}
 };
 
 /* 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 c5b493ea..21d3a1c9 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -82,6 +82,9 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_SCTP_PRINFO,          "sctp_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 "  },
 	{ NUM_EXPR_TYPES,            NULL}
 };
 
@@ -394,6 +397,25 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_sendv_spa->sendv_prinfo);
 		free_expression(expression->value.sctp_sendv_spa->sendv_authinfo);
 		break;
+	case EXPR_SCTP_RCVINFO:
+		free_expression(expression->value.sctp_rcvinfo->rcv_sid);
+		free_expression(expression->value.sctp_rcvinfo->rcv_ssn);
+		free_expression(expression->value.sctp_rcvinfo->rcv_flags);
+		free_expression(expression->value.sctp_rcvinfo->rcv_ppid);
+		free_expression(expression->value.sctp_rcvinfo->rcv_tsn);
+		free_expression(expression->value.sctp_rcvinfo->rcv_cumtsn);
+		free_expression(expression->value.sctp_rcvinfo->rcv_context);
+		break;
+	case EXPR_SCTP_NXTINFO:
+		free_expression(expression->value.sctp_nxtinfo->nxt_sid);
+		free_expression(expression->value.sctp_nxtinfo->nxt_flags);
+		free_expression(expression->value.sctp_nxtinfo->nxt_ppid);
+		free_expression(expression->value.sctp_nxtinfo->nxt_length);
+		break;
+	case EXPR_SCTP_RECVV_RN:
+		free_expression(expression->value.sctp_recvv_rn->recvv_rcvinfo);
+		free_expression(expression->value.sctp_recvv_rn->recvv_nxtinfo);
+		break;
 	case EXPR_WORD:
 		assert(expression->value.string);
 		free(expression->value.string);
@@ -1135,6 +1157,112 @@ static int evaluate_sctp_sendv_spa_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_rcvinfo_expression(struct expression *in,
+					    struct expression *out,
+					    char **error)
+{
+        struct sctp_rcvinfo_expr *in_info;
+        struct sctp_rcvinfo_expr *out_info;
+
+        assert(in->type == EXPR_SCTP_RCVINFO);
+        assert(in->value.sctp_rcvinfo);
+        assert(out->type == EXPR_SCTP_RCVINFO);
+
+        out->value.sctp_rcvinfo = calloc(1, sizeof(struct sctp_rcvinfo_expr));
+                     
+        in_info = in->value.sctp_rcvinfo;
+        out_info = out->value.sctp_rcvinfo;
+                     
+        if (evaluate(in_info->rcv_sid,
+		     &out_info->rcv_sid,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_ssn,
+		     &out_info->rcv_ssn,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_flags,
+		     &out_info->rcv_flags,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_ppid,
+		     &out_info->rcv_ppid,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_tsn,
+		     &out_info->rcv_tsn,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_cumtsn,
+		     &out_info->rcv_cumtsn,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->rcv_context,
+		     &out_info->rcv_context,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
+static int evaluate_sctp_nxtinfo_expression(struct expression *in,
+					    struct expression *out,
+					    char **error)
+{
+        struct sctp_nxtinfo_expr *in_info;
+        struct sctp_nxtinfo_expr *out_info;
+
+        assert(in->type == EXPR_SCTP_NXTINFO);
+        assert(in->value.sctp_nxtinfo);
+        assert(out->type == EXPR_SCTP_NXTINFO);
+
+        out->value.sctp_nxtinfo = calloc(1, sizeof(struct sctp_nxtinfo_expr));
+                     
+        in_info = in->value.sctp_nxtinfo;
+        out_info = out->value.sctp_nxtinfo;
+                     
+        if (evaluate(in_info->nxt_sid,
+		     &out_info->nxt_sid,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->nxt_flags,
+		     &out_info->nxt_flags,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->nxt_ppid,
+		     &out_info->nxt_ppid,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_info->nxt_length,
+		     &out_info->nxt_length,
+		     error))
+		return STATUS_ERR;
+	return STATUS_OK;
+}
+
+static int evaluate_sctp_recvv_rn_expression(struct expression *in,
+					    struct expression *out,
+					    char **error)
+{
+        struct sctp_recvv_rn_expr *in_rn;
+        struct sctp_recvv_rn_expr *out_rn;
+
+        assert(in->type == EXPR_SCTP_RECVV_RN);
+        assert(in->value.sctp_recvv_rn);
+        assert(out->type == EXPR_SCTP_RECVV_RN);
+
+        out->value.sctp_recvv_rn = calloc(1, sizeof(struct sctp_recvv_rn_expr));
+                     
+        in_rn = in->value.sctp_recvv_rn;
+        out_rn = out->value.sctp_recvv_rn;
+                     
+        if (evaluate(in_rn->recvv_rcvinfo,
+		     &out_rn->recvv_rcvinfo,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
 static int evaluate(struct expression *in,
 		    struct expression **out_ptr, char **error)
 {
@@ -1208,6 +1336,15 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_SENDV_SPA:
 		result = evaluate_sctp_sendv_spa_expression(in, out, error);
 		break;
+	case EXPR_SCTP_RCVINFO:
+		result = evaluate_sctp_rcvinfo_expression(in, out, error);
+		break;
+	case EXPR_SCTP_NXTINFO:
+		result = evaluate_sctp_nxtinfo_expression(in, out, error);
+		break;
+	case EXPR_SCTP_RECVV_RN:
+		result = evaluate_sctp_recvv_rn_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 35de4e87..c47ec124 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -62,6 +62,9 @@ enum expression_t {
 	EXPR_SCTP_PRINFO,	  /* struct sctp_prinfo for syscall sctp_sendv */
 	EXPR_SCTP_AUTHINFO,	  /* struct sctp_authinfo for syscall sctp_sendv */
 	EXPR_SCTP_SENDV_SPA,	  /* struct sctp_sendv_spa for syscall sctp_sendv */
+	EXPR_SCTP_RCVINFO,        /* struct sctp_rcvinfo for syscall sctp_recvv */
+	EXPR_SCTP_NXTINFO,        /* struct sctp_nxtinfo for syscall sctp_recvv */
+	EXPR_SCTP_RECVV_RN,       /* struct sctp_recvv_rn for syscall sctp_recvv */
 	NUM_EXPR_TYPES,
 };
 /* Convert an expression type to a human-readable string */
@@ -97,6 +100,9 @@ struct expression {
 		struct sctp_prinfo_expr *sctp_prinfo;
 		struct sctp_authinfo_expr *sctp_authinfo;
 		struct sctp_sendv_spa_expr *sctp_sendv_spa;
+		struct sctp_rcvinfo_expr *sctp_rcvinfo;
+		struct sctp_nxtinfo_expr *sctp_nxtinfo;
+		struct sctp_recvv_rn_expr *sctp_recvv_rn;
 	} value;
 	const char *format;	/* the printf format for printing the value */
 };
@@ -268,6 +274,31 @@ struct sctp_sendv_spa_expr {
 	struct expression *sendv_authinfo;
 };
 
+/* Parse tree for sctp_rcvinfo in sctp_recvv syscall. */
+struct sctp_rcvinfo_expr {
+	struct expression *rcv_sid;
+	struct expression *rcv_ssn;
+	struct expression *rcv_flags;
+	struct expression *rcv_ppid;
+	struct expression *rcv_tsn;
+	struct expression *rcv_cumtsn;
+	struct expression *rcv_context;
+};
+
+/* Parse tree for sctp_nxtinfo in sctp_recvv syscall. */
+struct sctp_nxtinfo_expr {
+	struct expression *nxt_sid;
+	struct expression *nxt_flags;
+	struct expression *nxt_ppid;
+	struct expression *nxt_length;
+};
+
+/* Parse tree for sctp_recvv_rn in sctp_recvv syscall. */
+struct sctp_recvv_rn_expr {
+	struct expression *recvv_rcvinfo;
+	struct expression *recvv_nxtinfo;
+};
+
 /* 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/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index e6e4d75c..284e156c 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -195,7 +195,12 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_SEND_SNDINFO_VALID,          "SCTP_SEND_SNDINFO_VALID"         },
 	{ SCTP_SEND_PRINFO_VALID,           "SCTP_SEND_PRINFO_VALID"          },
 	{ SCTP_SEND_AUTHINFO_VALID,         "SCTP_SEND_AUTHINFO_VALID"        },
-
+	{ SCTP_RECVV_NOINFO,                "SCTP_RECVV_NOINFO"               },
+	{ SCTP_RECVV_RCVINFO,               "SCTP_RECVV_RCVINFO"              },
+	{ SCTP_RECVV_NXTINFO,               "SCTP_RECVV_NXTINFO"              },
+	{ SCTP_RECVV_RN,                    "SCTP_RECVV_RN"                   },
+	{ SCTP_RECVRCVINFO,                 "SCTP_RECVRCVINFO"                },
+	{ SCTP_RECVNXTINFO,                 "SCTP_RECVNXTINFO"                },
 	/* /usr/include/netinet/tcp.h */
 	{ TCP_NODELAY,                      "TCP_NODELAY"                     },
 	{ TCP_MAXSEG,                       "TCP_MAXSEG"                      },
-- 
GitLab