From 01750660f27f7e9fbea544a00a6a30665d3bec4b Mon Sep 17 00:00:00 2001
From: hoelscher <jens.hoelscher@fh-muenster.de>
Date: Sat, 31 Oct 2015 08:07:23 +0100
Subject: [PATCH] add support for cmsg sctp_nxtinfo and sctp_rcvinfo

---
 gtests/net/packetdrill/lexer.l                |   2 +
 gtests/net/packetdrill/parser.y               |  40 ++-
 gtests/net/packetdrill/run_system_call.c      | 294 ++++++++++--------
 gtests/net/packetdrill/script.c               |  10 +
 gtests/net/packetdrill/script.h               |   2 +
 gtests/net/packetdrill/symbols_freebsd.c      |   3 +
 .../packetdrill/tests/bsd/sctp/recvmsg.pkt    |  54 ++++
 7 files changed, 277 insertions(+), 128 deletions(-)
 create mode 100644 gtests/net/packetdrill/tests/bsd/sctp/recvmsg.pkt

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 163f890a..c4b9e435 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -295,10 +295,12 @@ 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;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index b75dd85b..03cfa9bf 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -544,8 +544,8 @@ 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> RCV_SID RCV_SSN RCV_FLAGS RCV_PPID RCV_TSN RCV_CUMTSN RCV_CONTEXT RCV_ASSOC_ID
+%token <reserved> NXT_SID NXT_FLAGS NXT_PPID NXT_LENGTH NXT_ASSOC_ID
 %token <reserved> RECVV_RCVINFO RECVV_NXTINFO
 %token <reserved> SSE_TYPE SSE_FLAGS SSE_LENGTH
 %token <reserved> SENDER_DRY_TYPE SENDER_DRY_FLAGS SENDER_DRY_LENGTH SENDER_DRY_ASSOC_ID
@@ -617,8 +617,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 sinfo_assoc_id
 %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 <expression> sctp_rcvinfo rcv_sid rcv_ssn rcv_flags rcv_ppid rcv_tsn rcv_cumtsn rcv_context rcv_assoc_id
+%type <expression> sctp_nxtinfo nxt_sid nxt_flags nxt_ppid nxt_length nxt_assoc_id sctp_recvv_rn
 %type <expression> sctp_shutdown_event sse_type sse_flags sse_length
 %type <expression> sctp_sender_dry_event sender_dry_type sender_dry_flags sender_dry_length sender_dry_assoc_id
 %type <expression> sctp_event_subscribe
@@ -2681,6 +2681,8 @@ cmsg_data
 : CMSG_DATA '=' sctp_initmsg     { $$ = $3; }
 | CMSG_DATA '=' sctp_sndrcvinfo  { $$ = $3; }
 | CMSG_DATA '=' sctp_sndinfo     { $$ = $3; }
+| CMSG_DATA '=' sctp_rcvinfo     { $$ = $3; }
+| CMSG_DATA '=' sctp_nxtinfo     { $$ = $3; }
 | CMSG_DATA '=' sctp_prinfo      { $$ = $3; }
 | CMSG_DATA '=' sctp_authinfo    { $$ = $3; }
 | CMSG_DATA '=' sockaddr         { $$ = $3; }
@@ -3529,9 +3531,19 @@ rcv_context
 | RCV_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
+rcv_assoc_id
+: RCV_ASSOC_ID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("rcv_assoc_id out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| RCV_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
 
 sctp_rcvinfo
-: '{' rcv_sid ',' rcv_ssn ',' rcv_flags ',' rcv_ppid ',' rcv_tsn ',' rcv_cumtsn ',' rcv_context '}' {
+: '{' rcv_sid ',' rcv_ssn ',' rcv_flags ',' rcv_ppid ',' rcv_tsn ',' rcv_cumtsn ',' rcv_context ',' rcv_assoc_id'}' {
 	$$ = new_expression(EXPR_SCTP_RCVINFO);
 	$$->value.sctp_rcvinfo = calloc(1, sizeof(struct sctp_rcvinfo_expr));
 	$$->value.sctp_rcvinfo->rcv_sid = $2;
@@ -3541,6 +3553,7 @@ sctp_rcvinfo
 	$$->value.sctp_rcvinfo->rcv_tsn = $10;
 	$$->value.sctp_rcvinfo->rcv_cumtsn = $12;
 	$$->value.sctp_rcvinfo->rcv_context = $14;
+	$$->value.sctp_rcvinfo->rcv_assoc_id = $16;
 }
 ;
 
@@ -3607,6 +3620,10 @@ nxt_flags
 	}
 	$$ = new_integer_expression($3, "%u");
 }
+| NXT_FLAGS '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
 | NXT_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
@@ -3630,14 +3647,25 @@ nxt_length
 | NXT_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
+nxt_assoc_id
+: NXT_ASSOC_ID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("nxt_assoc_id out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| NXT_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
 sctp_nxtinfo
-: '{' nxt_sid ',' nxt_flags ',' nxt_ppid ',' nxt_length '}' {
+: '{' nxt_sid ',' nxt_flags ',' nxt_ppid ',' nxt_length ',' nxt_assoc_id '}' {
 	$$ = 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;
+	$$->value.sctp_nxtinfo->nxt_assoc_id = $10;
 }
 ;
 
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 467b5908..5c8b2b9b 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -80,8 +80,15 @@ static int check_sctp_initmsg(struct sctp_initmsg_expr *expr, struct sctp_initms
 			      char **error);
 #endif
 #if defined(Linux) || defined(__FreeBSD__)
-static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr,
-				 struct sctp_sndrcvinfo *sctp_sndrcvinfo,
+static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr, struct sctp_sndrcvinfo *sctp_sndrcvinfo,
+				 char** error);
+#endif
+#if defined(Linux) || defined(__FreeBSD__)
+static int check_sctp_rcvinfo(struct sctp_rcvinfo_expr *expr, struct sctp_rcvinfo *sctp_rcvinfo,
+				 char** error);
+#endif
+#if defined(Linux) || defined(__FreeBSD__)
+static int check_sctp_nxtinfo(struct sctp_nxtinfo_expr *expr, struct sctp_nxtinfo *sctp_nxtinfo,
 				 char** error);
 #endif
 
@@ -728,6 +735,16 @@ static int cmsg_new(struct expression *expression,
 			cmsg_size += CMSG_SPACE(sizeof(struct sctp_sndinfo));
 			break;
 #endif
+#if defined(SCTP_RCVINFO)
+		case EXPR_SCTP_RCVINFO:
+			cmsg_size += CMSG_SPACE(sizeof(struct sctp_rcvinfo));
+			break;
+#endif
+#if defined(SCTP_NXTINFO)
+		case EXPR_SCTP_NXTINFO:
+			cmsg_size += CMSG_SPACE(sizeof(struct sctp_nxtinfo));
+			break;
+#endif
 #if defined(SCTP_PRINFO)
 		case EXPR_SCTP_PRINFO:
 			cmsg_size += CMSG_SPACE(sizeof(struct sctp_prinfo));
@@ -806,6 +823,16 @@ static int cmsg_new(struct expression *expression,
 			break;
 		}
 #endif
+#if defined(SCTP_RCVINFO)
+		case EXPR_SCTP_RCVINFO:
+			cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_rcvinfo)));
+			break;
+#endif
+#if defined(SCTP_NXTINFO)
+		case EXPR_SCTP_NXTINFO:
+			cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_nxtinfo)));
+			break;
+#endif
 #if defined(SCTP_PRINFO)
 		case EXPR_SCTP_PRINFO: {
 			struct sctp_prinfo info;
@@ -853,6 +880,143 @@ error_out:
 	*cmsg_len_ptr = 0;
 	return STATUS_ERR;
 }
+static int check_cmsghdr(struct expression *expr_list, struct msghdr *msg, char  **error) {
+	struct expression_list *list;
+	struct expression *cmsg_expr;
+	struct cmsghdr *cmsg_ptr;
+	int cnt = 0;
+
+	assert(expr_list->type == EXPR_LIST);
+
+	list = expr_list->value.list;
+	for (cmsg_ptr = CMSG_FIRSTHDR(msg); cmsg_ptr != NULL; cmsg_ptr = CMSG_NXTHDR(msg, cmsg_ptr)) {
+		cmsg_expr = get_arg(list, cnt, error);
+		if (cmsg_expr->type != EXPR_ELLIPSIS) {
+			struct cmsghdr_expr *expr;
+			expr = cmsg_expr->value.cmsghdr;
+			if (check_u32_expr(expr->cmsg_len, cmsg_ptr->cmsg_len,
+					   "cmsghdr.cmsg_len", error))
+				return STATUS_ERR;
+			if (check_s32_expr(expr->cmsg_level, cmsg_ptr->cmsg_level,
+					   "cmsghdr.cmsg_level", error))
+				return STATUS_ERR;
+			if (check_s32_expr(expr->cmsg_type, cmsg_ptr->cmsg_type,
+					   "cmsghdr.cmsg_type", error))
+				return STATUS_ERR;
+
+			if (expr->cmsg_data->type == EXPR_ELLIPSIS) {
+				continue;
+			}
+			switch(cmsg_ptr->cmsg_type) {
+#ifdef SCTP_INIT
+			case SCTP_INIT:
+				if (check_sctp_initmsg(expr->cmsg_data->value.sctp_initmsg,
+						       (struct sctp_initmsg *) CMSG_DATA(cmsg_ptr),
+						       error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_SNDRCV
+			case SCTP_SNDRCV:
+				if (check_sctp_sndrcvinfo(expr->cmsg_data->value.sctp_sndrcvinfo,
+							  (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg_ptr),
+							  error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_SNDINFO
+			case SCTP_SNDINFO:
+				if (check_sctp_sndinfo(expr->cmsg_data->value.sctp_sndinfo,
+						       (struct sctp_sndinfo *) CMSG_DATA(cmsg_ptr),
+						       error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_RCVINFO
+			case SCTP_RCVINFO:
+				if (check_sctp_rcvinfo(expr->cmsg_data->value.sctp_rcvinfo,
+						       (struct sctp_rcvinfo *) CMSG_DATA(cmsg_ptr),
+						       error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_NXTINFO
+			case SCTP_NXTINFO:
+				if (check_sctp_nxtinfo(expr->cmsg_data->value.sctp_nxtinfo,
+						       (struct sctp_nxtinfo *) CMSG_DATA(cmsg_ptr),
+						       error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_PRINFO
+			case SCTP_PRINFO:
+				if (check_u16_expr(expr->cmsg_data->value.sctp_prinfo->pr_policy,
+					   ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_policy,
+					   "prinfo.pr_policy", error)) {
+					return STATUS_ERR;
+				}
+				if (check_u32_expr(expr->cmsg_data->value.sctp_prinfo->pr_value,
+					   ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_value,
+					   "prinfo.pr_value", error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_AUTHINFO
+			case SCTP_AUTHINFO:
+				if (check_u16_expr(expr->cmsg_data->value.sctp_authinfo->auth_keynumber,
+					   ((struct sctp_authinfo *)CMSG_DATA(cmsg_ptr))->auth_keynumber,
+					   "authinfo.auth_keynumber", error)) {
+					return STATUS_ERR;
+				}
+				break;
+#endif
+#ifdef SCTP_DSTADDRV4
+			case SCTP_DSTADDRV4:
+				if (expr->cmsg_data->type != EXPR_ELLIPSIS) {
+					struct sockaddr_in *addr = expr->cmsg_data->value.socket_address_ipv4;
+					struct in_addr *cmsg_addr = (struct in_addr *) CMSG_DATA(cmsg_ptr);
+					if (addr->sin_addr.s_addr != cmsg_addr->s_addr) {
+						asprintf(error, "cmsg_data for SCTP_DSTADDRV4: expected: %s actual: %s",
+							 inet_ntoa(addr->sin_addr),
+							 inet_ntoa(*cmsg_addr));
+						return STATUS_ERR;
+					}
+				}
+				break;
+#endif
+#ifdef SCTP_DSTADDRV6
+			case SCTP_DSTADDRV6:
+				if (expr->cmsg_data->type != EXPR_ELLIPSIS) {
+					struct sockaddr_in6 *addr = expr->cmsg_data->value.socket_address_ipv6;
+					struct in6_addr *cmsg_addr = (struct in6_addr *) CMSG_DATA(cmsg_ptr);
+					if (memcmp(&addr->sin6_addr, cmsg_addr, sizeof(struct in6_addr))) {
+						char expected_addr[INET6_ADDRSTRLEN];
+						char live_addr[INET6_ADDRSTRLEN];
+						inet_ntop(AF_INET6, &addr->sin6_addr, expected_addr, INET6_ADDRSTRLEN);
+						inet_ntop(AF_INET6, cmsg_addr, live_addr, INET6_ADDRSTRLEN);
+						asprintf(error, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
+							 expected_addr, live_addr);
+						return STATUS_ERR;
+					}
+				}
+				break;
+#endif
+			default:
+				asprintf(error, "can't check cmsg type");
+				return STATUS_ERR;
+			}
+		}
+		cnt++;
+	}
+	return STATUS_OK;
+}
+
 
 /* Free all the space used by the given msghdr. */
 static void msghdr_free(struct msghdr *msg, size_t iov_len)
@@ -1796,7 +1960,7 @@ static int syscall_recvmsg(struct state *state, struct syscall_spec *syscall,
 			goto error_out;
 	}
 #endif
-	status = STATUS_OK;
+	status = check_cmsghdr(msg_expression->value.msghdr->msg_control, msg, error);
 
 error_out:
 	msghdr_free(msg, iov_len);
@@ -1948,125 +2112,6 @@ static int syscall_sendto(struct state *state, struct syscall_spec *syscall,
 	return status;
 }
 
-static int check_cmsghdr(struct expression *expr_list, struct msghdr *msg, char  **error) {
-	struct expression_list *list;
-	struct expression *cmsg_expr;
-	struct cmsghdr *cmsg_ptr;
-	int cnt = 0;
-
-	assert(expr_list->type == EXPR_LIST);
-
-	list = expr_list->value.list;
-	for (cmsg_ptr = CMSG_FIRSTHDR(msg); cmsg_ptr != NULL; cmsg_ptr = CMSG_NXTHDR(msg, cmsg_ptr)) {
-		cmsg_expr = get_arg(list, cnt, error);
-		if (cmsg_expr->type != EXPR_ELLIPSIS) {
-			struct cmsghdr_expr *expr;
-			expr = cmsg_expr->value.cmsghdr;
-			if (check_u32_expr(expr->cmsg_len, cmsg_ptr->cmsg_len,
-					   "cmsghdr.cmsg_len", error))
-				return STATUS_ERR;
-			if (check_s32_expr(expr->cmsg_level, cmsg_ptr->cmsg_level,
-					   "cmsghdr.cmsg_level", error))
-				return STATUS_ERR;
-			if (check_s32_expr(expr->cmsg_type, cmsg_ptr->cmsg_type,
-					   "cmsghdr.cmsg_type", error))
-				return STATUS_ERR;
-
-			if (expr->cmsg_data->type == EXPR_ELLIPSIS) {
-				continue;
-			}
-			switch(cmsg_ptr->cmsg_type) {
-#ifdef SCTP_INIT
-			case SCTP_INIT:
-				if (check_sctp_initmsg(expr->cmsg_data->value.sctp_initmsg,
-						       (struct sctp_initmsg *) CMSG_DATA(cmsg_ptr),
-						       error)) {
-					return STATUS_ERR;
-				}
-				break;
-#endif
-#ifdef SCTP_SNDRCV
-			case SCTP_SNDRCV:
-				if (check_sctp_sndrcvinfo(expr->cmsg_data->value.sctp_sndrcvinfo,
-							  (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg_ptr),
-							  error)) {
-					return STATUS_ERR;
-				}
-				break;
-#endif
-#ifdef SCTP_SNDINFO
-			case SCTP_SNDINFO:
-				if (check_sctp_sndinfo(expr->cmsg_data->value.sctp_sndinfo,
-						       (struct sctp_sndinfo *) CMSG_DATA(cmsg_ptr),
-						       error)) {
-					return STATUS_ERR;
-				}
-				break;
-#endif
-#ifdef SCTP_PRINFO
-			case SCTP_PRINFO:
-				if (check_u16_expr(expr->cmsg_data->value.sctp_prinfo->pr_policy,
-					   ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_policy,
-					   "prinfo.pr_policy", error)) {
-					return STATUS_ERR;
-				}
-				if (check_u32_expr(expr->cmsg_data->value.sctp_prinfo->pr_value,
-					   ((struct sctp_prinfo *)CMSG_DATA(cmsg_ptr))->pr_value,
-					   "prinfo.pr_value", error)) {
-					return STATUS_ERR;
-				}
-				break;
-#endif
-#ifdef SCTP_AUTHINFO
-			case SCTP_AUTHINFO:
-				if (check_u16_expr(expr->cmsg_data->value.sctp_authinfo->auth_keynumber,
-					   ((struct sctp_authinfo *)CMSG_DATA(cmsg_ptr))->auth_keynumber,
-					   "authinfo.auth_keynumber", error)) {
-					return STATUS_ERR;
-				}
-				break;
-#endif
-#ifdef SCTP_DSTADDRV4
-			case SCTP_DSTADDRV4:
-				if (expr->cmsg_data->type != EXPR_ELLIPSIS) {
-					struct sockaddr_in *addr = expr->cmsg_data->value.socket_address_ipv4;
-					struct in_addr *cmsg_addr = (struct in_addr *) CMSG_DATA(cmsg_ptr);
-					if (addr->sin_addr.s_addr != cmsg_addr->s_addr) {
-						asprintf(error, "cmsg_data for SCTP_DSTADDRV4: expected: %s actual: %s",
-							 inet_ntoa(addr->sin_addr),
-							 inet_ntoa(*cmsg_addr));
-						return STATUS_ERR;
-					}
-				}
-				break;
-#endif
-#ifdef SCTP_DSTADDRV6
-			case SCTP_DSTADDRV6:
-				if (expr->cmsg_data->type != EXPR_ELLIPSIS) {
-					struct sockaddr_in6 *addr = expr->cmsg_data->value.socket_address_ipv6;
-					struct in6_addr *cmsg_addr = (struct in6_addr *) CMSG_DATA(cmsg_ptr);
-					if (memcmp(&addr->sin6_addr, cmsg_addr, sizeof(struct in6_addr))) {
-						char expected_addr[INET6_ADDRSTRLEN];
-						char live_addr[INET6_ADDRSTRLEN];
-						inet_ntop(AF_INET6, &addr->sin6_addr, expected_addr, INET6_ADDRSTRLEN);
-						inet_ntop(AF_INET6, cmsg_addr, live_addr, INET6_ADDRSTRLEN);
-						asprintf(error, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
-							 expected_addr, live_addr);
-						return STATUS_ERR;
-					}
-				}
-				break;
-#endif
-			default:
-				asprintf(error, "can't check cmsg type");
-				return STATUS_ERR;
-			}
-		}
-		cnt++;
-	}
-	return STATUS_OK;
-}
-
 static int syscall_sendmsg(struct state *state, struct syscall_spec *syscall,
 			   struct expression_list *args, char **error)
 {
@@ -3749,6 +3794,9 @@ static int check_sctp_rcvinfo(struct sctp_rcvinfo_expr *expr,
 	if (check_u32_expr(expr->rcv_context, sctp_rcvinfo->rcv_context,
 			   "sctp_rcvinfo.rcv_context", error))
 		return STATUS_ERR;
+	if (check_u32_expr(expr->rcv_assoc_id, sctp_rcvinfo->rcv_assoc_id,
+			   "sctp_rcvinfo.rcv_assoc_id", error))
+		return STATUS_ERR;
 
 	return STATUS_OK;
 }
@@ -3777,6 +3825,8 @@ static int check_sctp_nxtinfo(struct sctp_nxtinfo_expr *expr,
 	}
 	if (check_u32_expr(expr->nxt_length, sctp_nxtinfo->nxt_length, "sctp_nxtinfo.nxt_length", error))
 		return STATUS_ERR;
+	if (check_u32_expr(expr->nxt_assoc_id, sctp_nxtinfo->nxt_assoc_id, "sctp_nxtinfo.nxt_assoc_id", error))
+		return STATUS_ERR;
 
 	return STATUS_OK;
 }
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 4508038f..2073a5ed 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -432,12 +432,14 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_rcvinfo->rcv_tsn);
 		free_expression(expression->value.sctp_rcvinfo->rcv_cumtsn);
 		free_expression(expression->value.sctp_rcvinfo->rcv_context);
+		free_expression(expression->value.sctp_rcvinfo->rcv_assoc_id);
 		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);
+		free_expression(expression->value.sctp_nxtinfo->nxt_assoc_id);
 		break;
 	case EXPR_SCTP_RECVV_RN:
 		free_expression(expression->value.sctp_recvv_rn->recvv_rcvinfo);
@@ -1420,6 +1422,10 @@ static int evaluate_sctp_rcvinfo_expression(struct expression *in,
 		     &out_info->rcv_context,
 		     error))
 		return STATUS_ERR;
+        if (evaluate(in_info->rcv_assoc_id,
+		     &out_info->rcv_assoc_id,
+		     error))
+		return STATUS_ERR;
 
 	return STATUS_OK;
 }
@@ -1456,6 +1462,10 @@ static int evaluate_sctp_nxtinfo_expression(struct expression *in,
 		     &out_info->nxt_length,
 		     error))
 		return STATUS_ERR;
+        if (evaluate(in_info->nxt_assoc_id,
+		     &out_info->nxt_assoc_id,
+		     error))
+		return STATUS_ERR;
 	return STATUS_OK;
 }
 
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 3a9942d5..f14ce435 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -335,6 +335,7 @@ struct sctp_rcvinfo_expr {
 	struct expression *rcv_tsn;
 	struct expression *rcv_cumtsn;
 	struct expression *rcv_context;
+	struct expression *rcv_assoc_id;
 };
 
 /* Parse tree for sctp_nxtinfo in sctp_recvv syscall. */
@@ -343,6 +344,7 @@ struct sctp_nxtinfo_expr {
 	struct expression *nxt_flags;
 	struct expression *nxt_ppid;
 	struct expression *nxt_length;
+	struct expression *nxt_assoc_id;
 };
 
 /* Parse tree for sctp_recvv_rn in sctp_recvv syscall. */
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index 135d9e19..d4907b7f 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -182,6 +182,7 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_STREAM_CHANGE_EVENT,         "SCTP_STREAM_CHANGE_EVENT"        },
 	{ SCTP_SEND_FAILED_EVENT,           "SCTP_SEND_FAILED_EVENT"          },
         { SCTP_UNORDERED,                   "SCTP_UNORDERED"                  },
+        { SCTP_COMPLETE,                    "SCTP_COMPLETE"                   },
         { SCTP_ADDR_OVER,                   "SCTP_ADDR_OVER"                  },
         { SCTP_ABORT,                       "SCTP_ABORT"                      },
         { SCTP_EOF,                         "SCTP_EOF"                        },
@@ -224,6 +225,8 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_ADDR_MADE_PRIM,              "SCTP_ADDR_MADE_PRIM"             },
 	{ SCTP_SNDRCV,                      "SCTP_SNDRCV"                     },
 	{ SCTP_SNDINFO,                     "SCTP_SNDINFO"                    },
+	{ SCTP_RCVINFO,                     "SCTP_RCVINFO"                    },
+	{ SCTP_NXTINFO,                     "SCTP_NXTINFO"                    },
 	{ SCTP_PRINFO,                      "SCTP_PRINFO"                     },
 	{ SCTP_AUTHINFO,                    "SCTP_AUTHINFO"                   },
 	{ SCTP_DSTADDRV4,                   "SCTP_DSTADDRV4"                  },
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/recvmsg.pkt b/gtests/net/packetdrill/tests/bsd/sctp/recvmsg.pkt
new file mode 100644
index 00000000..a991b0b3
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/recvmsg.pkt
@@ -0,0 +1,54 @@
+--tolerance_usecs=100000
+
+ 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
++0.0 bind(3, ..., ...) = 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=16, is=16, 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
+//recvmsg(sd, msghdr, flags)
+
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, {spp_address={sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.0.2.1")},
+spp_hbinterval=0, spp_pathmaxrxt=8, spp_pathmtu=1468, spp_flags=SPP_HB_DISABLE, spp_ipv6_flowlabel=0, spp_dscp=0}, 152) = 0
+
+//base test
++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=[]]
++1.0 recvmsg(3, {msg_name(...)=..., msg_iov(1)=[{iov_base=..., iov_len=1000}], msg_control(0)=[], msg_flags=MSG_EOR}, 0) = 1000
+
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVRCVINFO, [1], 4) = 0
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVNXTINFO, [0], 4) = 0
++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 recvmsg(3, {msg_name(...)=..., msg_iov(1)=[{iov_base=..., iov_len=1000}], msg_control(40)=
+	[{cmsg_len=40, cmsg_level=IPPROTO_SCTP, cmsg_type=SCTP_RCVINFO, cmsg_data=
+	 {rcv_sid=0, rcv_ssn=1, rcv_flags=0, rcv_ppid=htonl(0), rcv_tsn=2, rcv_cumtsn=2, rcv_context=0, rcv_assoc_id=...}
+	}],
+	msg_flags=MSG_EOR}, 0) = 1000
+
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVRCVINFO, [0], 4) = 0
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVNXTINFO, [1], 4) = 0
++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: DATA[flgs=BE, len=1016, tsn=4, sid=0, ssn=3, ppid=1234]
+*    > sctp: SACK[flgs=0, cum_tsn=4, a_rwnd=..., gaps=[], dups=[]]
++0.0 recvmsg(3, {msg_name(...)=..., msg_iov(1)=[{iov_base=..., iov_len=1000}],msg_control(28)=
+	[{cmsg_len=28, cmsg_level=IPPROTO_SCTP, cmsg_type=SCTP_NXTINFO, cmsg_data=
+	 {nxt_sid=0, nxt_flags=SCTP_COMPLETE, nxt_ppid=htonl(1234), nxt_length=1000, nxt_assoc_id=3}
+	}] ,msg_flags=MSG_EOR}, 0) = 1000
+
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVRCVINFO, [1], 4) = 0
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_RECVNXTINFO, [1], 4) = 0
++0.0 < sctp: DATA[flgs=BE, len=1016, tsn=5, sid=0, ssn=4, ppid=9876]
+*    > sctp: SACK[flgs=0, cum_tsn=5, a_rwnd=..., gaps=[], dups=[]]
++0.0 recvmsg(3, {msg_name(...)=..., msg_iov(1)=[{iov_base=..., iov_len=1000}],msg_control(68)=
+	[{cmsg_len=40, cmsg_level=IPPROTO_SCTP, cmsg_type=SCTP_RCVINFO, cmsg_data=
+         {rcv_sid=0, rcv_ssn=3, rcv_flags=0, rcv_ppid=htonl(1234), rcv_tsn=4, rcv_cumtsn=5, rcv_context=0, rcv_assoc_id=...}},
+	{cmsg_len=28, cmsg_level=IPPROTO_SCTP, cmsg_type=SCTP_NXTINFO, cmsg_data=
+	 {nxt_sid=0, nxt_flags=SCTP_COMPLETE, nxt_ppid=htonl(9876), nxt_length=1000, nxt_assoc_id=3}
+	}] ,msg_flags=MSG_EOR}, 0) = 1000
-- 
GitLab