From 182ee6cbfd94c2bf0688862a670b183691abcfbc Mon Sep 17 00:00:00 2001
From: hoelscher <jens.hoelscher@fh-muenster.de>
Date: Sat, 31 Oct 2015 02:05:14 +0100
Subject: [PATCH] add support for cmsghdr for sendmsg syscall

---
 gtests/net/packetdrill/lexer.l                |   6 +
 gtests/net/packetdrill/parser.y               |  84 ++++-
 gtests/net/packetdrill/run_system_call.c      | 295 +++++++++++++-----
 gtests/net/packetdrill/script.c               |  48 +++
 gtests/net/packetdrill/script.h               |  13 +
 gtests/net/packetdrill/symbols_freebsd.c      |   6 +
 .../bsd/sctp/sctp_get_socket_options.pkt      |  16 +-
 .../packetdrill/tests/bsd/sctp/sctp_sendv.pkt |   5 +-
 8 files changed, 385 insertions(+), 88 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 8aa42a8e..08ff374e 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -164,7 +164,12 @@ sin_port			return SIN_PORT;
 sin_addr			return SIN_ADDR;
 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;
 fd				return FD;
 events				return EVENTS;
 revents				return REVENTS;
@@ -259,6 +264,7 @@ 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;
 ssb_adaptation_ind		return SSB_ADAPTATION_IND;
 sinfo_stream			return SINFO_STREAM;
 sinfo_ssn			return SINFO_SSN;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 42e6c192..357bdc61 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -494,7 +494,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
  */
 %token ELLIPSIS
 %token <reserved> SA_FAMILY SIN_PORT SIN_ADDR _HTONS_ _HTONL_ INET_ADDR
-%token <reserved> MSG_NAME MSG_IOV MSG_FLAGS
+%token <reserved> MSG_NAME MSG_IOV MSG_FLAGS MSG_CONTROL CMSG_LEN CMSG_LEVEL CMSG_TYPE CMSG_DATA
 %token <reserved> FD EVENTS REVENTS ONOFF LINGER
 %token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO
 %token <reserved> FAST_OPEN
@@ -538,7 +538,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> SPP_FLAGS SPP_IPV6_FLOWLABEL_ SPP_DSCP_
 %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> SND_SID SND_FLAGS SND_PPID SND_CONTEXT SND_ASSOC_ID SSB_ADAPTATION_IND
 %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
@@ -598,7 +598,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression_list> expression_list function_arguments
 %type <expression> expression binary_expression array
 %type <expression> decimal_integer hex_integer data
-%type <expression> inaddr sockaddr msghdr iovec pollfd opt_revents
+%type <expression> inaddr sockaddr msghdr cmsghdr cmsg_level cmsg_type cmsg_data 
+%type <expression> iovec pollfd opt_revents
 %type <expression> linger l_onoff l_linger
 %type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata
 %type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary
@@ -611,7 +612,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> spinfo_address spinfo_state spinfo_cwnd spinfo_srtt spinfo_rto spinfo_mtu
 %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_sndinfo snd_sid snd_flags snd_ppid snd_assoc_id snd_context
 %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
@@ -2458,6 +2459,9 @@ expression
 | msghdr            {
 	$$ = $1;
 }
+| cmsghdr           {
+	$$ = $1;
+}
 | iovec             {
 	$$ = $1;
 }
@@ -2630,6 +2634,7 @@ data
 msghdr
 : '{' MSG_NAME '(' ELLIPSIS ')' '=' ELLIPSIS ','
       MSG_IOV '(' decimal_integer ')' '=' array ','
+      MSG_CONTROL '('decimal_integer ')' '=' array ','
       MSG_FLAGS '=' expression '}' {
 	struct msghdr_expr *msg_expr = calloc(1, sizeof(struct msghdr_expr));
 	$$ = new_expression(EXPR_MSGHDR);
@@ -2638,11 +2643,60 @@ msghdr
 	msg_expr->msg_namelen	= new_expression(EXPR_ELLIPSIS);
 	msg_expr->msg_iov	= $14;
 	msg_expr->msg_iovlen	= $11;
-	msg_expr->msg_flags	= $18;
-	/* TODO(ncardwell): msg_control, msg_controllen */
+	msg_expr->msg_controllen= $18;
+	msg_expr->msg_control   = $21;
+	msg_expr->msg_flags	= $25;
+}
+;
+
+cmsg_level
+: CMSG_LEVEL '=' INTEGER {
+	if (!is_valid_s32($3)) {
+		semantic_error("cmsg_level out of range");
+	}
+	$$ = new_integer_expression($3, "%d");
+}
+| CMSG_LEVEL '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
 }
+| CMSG_LEVEL '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
+cmsg_type
+: CMSG_TYPE '=' INTEGER {
+	if (!is_valid_s32($3)) {
+		semantic_error("cmsg_level out of range");
+	}
+	$$ = new_integer_expression($3, "%d");
+}
+| CMSG_TYPE '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| CMSG_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+cmsg_data
+: CMSG_DATA '=' sctp_sndinfo     { $$ = $3; }
+| CMSG_DATA '=' sctp_prinfo      { $$ = $3; }
+| CMSG_DATA '=' sctp_authinfo    { $$ = $3; }
+| CMSG_DATA '=' sockaddr         { $$ = $3; }
+;
+
+cmsghdr
+: '{' CMSG_LEN '=' INTEGER ',' cmsg_level ',' cmsg_type ',' cmsg_data '}' {
+	$$ = new_expression(EXPR_CMSGHDR);
+	$$->value.cmsghdr = calloc(1, sizeof(struct cmsghdr_expr));
+	if (!is_valid_s32($4)) {
+		semantic_error("cmsg_len out of range");
+	}
+	$$->value.cmsghdr->cmsg_len = new_integer_expression($4, "%u");
+	$$->value.cmsghdr->cmsg_level = $6;
+	$$->value.cmsghdr->cmsg_type = $8;
+	$$->value.cmsghdr->cmsg_data = $10;
+};
+
 iovec
 : '{' data ',' decimal_integer '}' {
 	struct iovec_expr *iov_expr = calloc(1, sizeof(struct iovec_expr));
@@ -3260,14 +3314,30 @@ snd_context
 | SND_CONTEXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 ;
 
+snd_assoc_id
+: SND_ASSOC_ID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("snd_assoc_id out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SND_ASSOC_ID '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| SND_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+
 sctp_sndinfo
-: '{' snd_sid ',' snd_flags ',' snd_ppid ',' snd_context '}' {
+: '{' snd_sid ',' snd_flags ',' snd_ppid ',' snd_context ',' snd_assoc_id'}' {
 	$$ = new_expression(EXPR_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;
+	$$->value.sctp_sndinfo->snd_assoc_id = $10;
 }
 ;
 
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index c54a41b5..97b00666 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -52,7 +52,14 @@ static int to_live_fd(struct state *state, int script_fd, int *live_fd,
 static int check_sctp_notification(struct iovec *iov, struct expression *iovec_expr,
 				   char **error);
 #endif
-
+#if defined(__FreeBSD__)
+static int parse_expression_to_sctp_sndinfo(struct expression *expr, struct sctp_sndinfo *info,
+				            char **error);
+static int parse_expression_to_sctp_prinfo(struct expression *expr, struct sctp_prinfo *info,
+				            char **error);
+static int parse_expression_to_sctp_authinfo(struct expression *expr, struct sctp_authinfo *info,
+				             char **error);
+#endif
 
 /* Provide a wrapper for the Linux gettid() system call (glibc does not). */
 static pid_t gettid(void)
@@ -413,6 +420,69 @@ static int get_sockstorage_arg(struct expression *arg, struct sockaddr_storage *
 }
 #endif
 
+static int check_sockaddr(struct expression *sockaddr_expr, struct sockaddr *live_addr, char **error) {
+
+	if (sockaddr_expr->type != EXPR_ELLIPSIS) {
+		struct sockaddr *script_addr;
+		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, "Bad type for sockaddr");
+			return STATUS_ERR;
+		}
+		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_addr;
+				if (live_sockaddr->sin_port != script_sockaddr->sin_port) {
+					asprintf(error, "sockaddr_in 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, "sockaddr_in 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_addr;
+				if (live_sockaddr->sin6_port != script_sockaddr->sin6_port) {
+					asprintf(error, "sockaddr_in6 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, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
+						 expected_addr, live_addr);
+					return STATUS_ERR;
+				}
+			}
+			break;
+		}
+	}
+	return STATUS_OK;
+}
+
 #if defined(__FreeBSD__) || defined(linux)
 int check_u8_expr(struct expression *expr, u8 value, char *val_name, char **error) {
 	if (expr->type != EXPR_ELLIPSIS) {
@@ -463,7 +533,7 @@ int check_s32_expr(struct expression *expr, s16 value, char *val_name, char **er
 }
 
 #if defined(__FreeBSD__) || defined(linux)
-int check_u32_expr(struct expression *expr, u16 value, char *val_name, char **error) {
+int check_u32_expr(struct expression *expr, u32 value, char *val_name, char **error) {
 	if (expr->type != EXPR_ELLIPSIS) {
 		u32 script_val;
 
@@ -594,6 +664,123 @@ error_out:
 	return status;
 }
 
+/* Allocate and fill in an 
+cmsghdr described by the given expression.
+ * Return STATUS_OK if the expression is a valid cmsghdr. Otherwise
+ * fill in the error with a human-readable error message and return
+ * STATUS_ERR.
+ */
+static int cmsg_new(struct expression *expression,
+		    void **cmsg_ptr, size_t *cmsg_len_ptr, char **error)
+{
+	struct expression_list *list;
+	int list_len = 0, i = 0;
+	size_t cmsg_size = 0;
+	struct cmsghdr *cmsg;
+
+	if (check_type(expression, EXPR_LIST, error))
+		return STATUS_ERR;
+	list = expression->value.list;
+	list_len = expression_list_length(list);
+	//calc size of cmsg in list
+	if (list_len == 0){
+		cmsg_ptr = NULL;
+		return STATUS_OK;
+	}
+	for (i = 0; i < list_len; i++) {
+		struct expression *cmsg_expr;
+		cmsg_expr = get_arg(list, i, error);
+		switch (cmsg_expr->value.cmsghdr->cmsg_data->type) {
+		case EXPR_SCTP_SNDINFO:
+			cmsg_size += CMSG_SPACE(sizeof(struct sctp_sndinfo));
+			break;
+		case EXPR_SCTP_PRINFO:
+			cmsg_size += CMSG_SPACE(sizeof(struct sctp_prinfo));
+			break;
+		case EXPR_SCTP_AUTHINFO:
+			cmsg_size += CMSG_SPACE(sizeof(struct sctp_authinfo));
+			break;
+		case EXPR_SOCKET_ADDRESS_IPV4:
+			cmsg_size += CMSG_SPACE(sizeof(struct in_addr));
+			break;
+		case EXPR_SOCKET_ADDRESS_IPV6:
+			cmsg_size += CMSG_SPACE(sizeof(struct in6_addr));
+			break;
+		default:
+			asprintf(error,"cmsg %d type not valid", i);
+			return STATUS_ERR;
+		}
+	}
+
+	*cmsg_len_ptr = cmsg_size;
+	cmsg = calloc(1, cmsg_size);
+	*cmsg_ptr = (void *)cmsg;
+		
+	for (i = 0; i < list_len; i++) {
+		struct expression *expr;
+		struct cmsghdr_expr *cmsg_expr;
+
+		expr = get_arg(list, i, error);
+		if(check_type(expr, EXPR_CMSGHDR, error))
+			goto error_out;
+		cmsg_expr = expr->value.cmsghdr;
+		if (get_u32(cmsg_expr->cmsg_len, &cmsg->cmsg_len, error))
+			goto error_out;
+		if (get_s32(cmsg_expr->cmsg_level, &cmsg->cmsg_level, error))
+			goto error_out;
+		if (get_s32(cmsg_expr->cmsg_type, &cmsg->cmsg_type, error))
+			goto error_out;
+
+		switch(cmsg_expr->cmsg_data->type) {
+		case EXPR_SCTP_SNDINFO: {
+			struct sctp_sndinfo info;			
+			if (parse_expression_to_sctp_sndinfo(cmsg_expr->cmsg_data, &info, error)) {
+				goto error_out;
+			}
+			memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_sndinfo)); 
+			cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
+			break;
+		}
+		case EXPR_SCTP_PRINFO: {
+			struct sctp_prinfo info;			
+			if (parse_expression_to_sctp_prinfo(cmsg_expr->cmsg_data, &info, error)) {
+				goto error_out;
+			}
+			memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_prinfo)); 
+			cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
+			break;
+		}		
+		case EXPR_SCTP_AUTHINFO: {
+			struct sctp_authinfo info;			
+			if (parse_expression_to_sctp_authinfo(cmsg_expr->cmsg_data, &info, error)) {
+				goto error_out;
+			}
+			memcpy(CMSG_DATA(cmsg), &info, sizeof(struct sctp_authinfo)); 
+			cmsg = (struct cmsghdr *) ((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
+			break;
+		}		
+		case EXPR_SOCKET_ADDRESS_IPV4:
+			memcpy(CMSG_DATA(cmsg), &cmsg_expr->cmsg_data->value.socket_address_ipv4->sin_addr, sizeof(struct in_addr));
+			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr)));
+			break;
+		case EXPR_SOCKET_ADDRESS_IPV6:
+			memcpy(CMSG_DATA(cmsg), &cmsg_expr->cmsg_data->value.socket_address_ipv6->sin6_addr, sizeof(struct in6_addr));
+			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr)));
+			break;
+		default:
+			asprintf(error,"cmsg.cmsg_data %d type not valid", i);
+			goto error_out;
+		}
+	}
+
+	return STATUS_OK;
+error_out:
+	free(*cmsg_ptr);
+	*cmsg_ptr = NULL;
+	*cmsg_len_ptr = 0;
+	return STATUS_ERR;
+}
+
 /* Free all the space used by the given msghdr. */
 static void msghdr_free(struct msghdr *msg, size_t iov_len)
 {
@@ -615,6 +802,7 @@ static int msghdr_new(struct expression *expression,
 	struct msghdr_expr *msg_expr;	/* input expression from script */
 	socklen_t name_len = sizeof(struct sockaddr_storage);
 	struct msghdr *msg = NULL;	/* live output */
+	size_t cmsg_len = 0;
 
 	if (check_type(expression, EXPR_MSGHDR, error))
 		goto error_out;
@@ -652,14 +840,29 @@ static int msghdr_new(struct expression *expression,
 		goto error_out;
 	}
 
+	if (msg_expr->msg_control != NULL) {
+		if (cmsg_new(msg_expr->msg_control, &msg->msg_control, &cmsg_len, error))
+			goto error_out;
+	}
+
+	if (msg_expr->msg_controllen != NULL) {
+		if (get_u32(msg_expr->msg_controllen, &msg->msg_controllen, error))
+			goto error_out;
+	}
+
+	if (msg->msg_controllen != cmsg_len) {
+		asprintf(error,
+			 "msg_controllen %u does not match %u size of cmsghdr array",
+			 msg->msg_controllen, cmsg_len);
+		goto error_out;
+	}
+
 	if (msg_expr->msg_flags != NULL) {
 		if (get_s32(msg_expr->msg_flags, &s32_val, error))
 			goto error_out;
 		msg->msg_flags = s32_val;
 	}
 
-	/* TODO(ncardwell): msg_control, msg_controllen */
-
 	status = STATUS_OK;
 
 error_out:
@@ -1707,7 +1910,7 @@ static int syscall_sendmsg(struct state *state, struct syscall_spec *syscall,
 	}
 
 	begin_syscall(state, syscall);
-
+	
 	result = sendmsg(live_fd, msg, flags);
 
 	status = end_syscall(state, syscall, CHECK_EXACT, result, error);
@@ -2162,6 +2365,9 @@ static int check_sctp_sndinfo(struct sctp_sndinfo_expr *expr,
 	if (check_u32_expr(expr->snd_context, sctp_sndinfo->snd_context,
 			   "sctp_sndinfo.snd_context", error))
 		return STATUS_ERR;
+	if (check_u32_expr(expr->snd_assoc_id, sctp_sndinfo->snd_assoc_id,
+			   "sctp_sndinfo.snd_assoc_id", error))
+		return STATUS_ERR;
 
 	return STATUS_OK;
 }
@@ -2323,7 +2529,12 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_SNDINFO:
 		live_optval = malloc(sizeof(struct sctp_sndinfo));
 		live_optlen = sizeof(struct sctp_sndinfo);
-		((struct sctp_sndinfo *)live_optval)->snd_assoc_id = 0;
+		if (get_u32(val_expression->value.sctp_sndinfo->snd_assoc_id,
+			    &((struct sctp_sndinfo *)live_optval)->snd_assoc_id,
+			    error)) {
+			free(live_optval);
+			return STATUS_ERR;
+		}
 		break;
 #endif
 #ifdef SCTP_ADAPTATION_LAYER
@@ -2707,7 +2918,6 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #endif
 #ifdef SCTP_DEFAULT_SNDINFO
 	case EXPR_SCTP_SNDINFO:
-		sndinfo.snd_assoc_id = 0;
 		if (get_u16(val_expression->value.sctp_sndinfo->snd_sid,
 			    &sndinfo.snd_sid, error)) {
 			return STATUS_ERR;
@@ -2724,6 +2934,10 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 			    &sndinfo.snd_context, error)) {
 			return STATUS_ERR;
 		}
+		if (get_u32(val_expression->value.sctp_sndinfo->snd_assoc_id,
+			    &sndinfo.snd_assoc_id, error)) {
+			return STATUS_ERR;
+		}
 		optval = &sndinfo;
 		break;
 #endif
@@ -2974,68 +3188,6 @@ static int check_sctp_sndrcvinfo(struct sctp_sndrcvinfo_expr *expr,
 	return STATUS_OK;
 }
 
-static int check_sockaddr(struct expression *sockaddr_expr, struct sockaddr *live_addr, char **error) {
-
-	if (sockaddr_expr->type != EXPR_ELLIPSIS) {
-		struct sockaddr *script_addr;
-		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, "Bad type for sockaddr");
-			return STATUS_ERR;
-		}
-		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_addr;
-				if (live_sockaddr->sin_port != script_sockaddr->sin_port) {
-					asprintf(error, "sockaddr_in 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, "sockaddr_in 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_addr;
-				if (live_sockaddr->sin6_port != script_sockaddr->sin6_port) {
-					asprintf(error, "sockaddr_in6 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, "sockaddr_in6 from.sin6_addr. expected: %s actual %s",
-						 expected_addr, live_addr);
-					return STATUS_ERR;
-				}
-			}
-			break;
-		}
-	}
-	return STATUS_OK;
-}
 #endif
 
 static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscall,
@@ -3120,7 +3272,6 @@ static int syscall_sctp_recvmsg(struct state *state, struct syscall_spec *syscal
 static int parse_expression_to_sctp_sndinfo(struct expression *expr, struct sctp_sndinfo *info, char **error) {
 	if (expr->type == EXPR_SCTP_SNDINFO) {
 		struct sctp_sndinfo_expr *sndinfo_expr = expr->value.sctp_sndinfo;
-		info->snd_assoc_id = 0;
 		if (get_u16(sndinfo_expr->snd_sid, &info->snd_sid, error)) {
 			return STATUS_ERR;
 		}
@@ -3133,6 +3284,9 @@ static int parse_expression_to_sctp_sndinfo(struct expression *expr, struct sctp
 		if (get_u32(sndinfo_expr->snd_context, &info->snd_context, error)) {
 			return STATUS_ERR;
 		}
+		if (get_u32(sndinfo_expr->snd_assoc_id, &info->snd_assoc_id, error)) {
+			return STATUS_ERR;
+		}
 	} else {
 		return STATUS_ERR;
 	}
@@ -3785,7 +3939,6 @@ static int check_sctp_notification(struct iovec *iov,
 				return STATUS_ERR;
 			break;
 		case EXPR_ELLIPSIS:
-			printf("check Ellipsis\n");
 			break;
 		default:
 			asprintf(error, "Bad type for iov_base. Can't check type %s",
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 33637435..58604198 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -65,6 +65,7 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_LIST,                 "list" },
 	{ EXPR_IOVEC,                "iovec" },
 	{ EXPR_MSGHDR,               "msghdr" },
+	{ EXPR_CMSGHDR,              "cmsghdr"},
 	{ EXPR_POLLFD,               "pollfd" },
 	{ EXPR_SCTP_RTOINFO,         "sctp_rtoinfo"},
 	{ EXPR_SCTP_INITMSG,         "sctp_initmsg"},
@@ -394,6 +395,7 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_sndinfo->snd_flags);
 		free_expression(expression->value.sctp_sndinfo->snd_ppid);
 		free_expression(expression->value.sctp_sndinfo->snd_context);
+		free_expression(expression->value.sctp_sndinfo->snd_assoc_id);
 		break;		
 	case EXPR_SCTP_SETADAPTATION:
 		free_expression(expression->value.sctp_setadaptation->ssb_adaptation_ind);
@@ -563,8 +565,16 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.msghdr->msg_namelen);
 		free_expression(expression->value.msghdr->msg_iov);
 		free_expression(expression->value.msghdr->msg_iovlen);
+		free_expression(expression->value.msghdr->msg_control);
+		free_expression(expression->value.msghdr->msg_controllen);
 		free_expression(expression->value.msghdr->msg_flags);
 		break;
+	case EXPR_CMSGHDR:
+		free_expression(expression->value.cmsghdr->cmsg_len);
+		free_expression(expression->value.cmsghdr->cmsg_level);
+		free_expression(expression->value.cmsghdr->cmsg_type);
+		free_expression(expression->value.cmsghdr->cmsg_data);
+		break;
 	case EXPR_POLLFD:
 		assert(expression->value.pollfd);
 		free_expression(expression->value.pollfd->fd);
@@ -656,6 +666,33 @@ static int evaluate_iovec_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_cmsghdr_expression(struct expression *in,
+				       struct expression *out, char **error)
+{
+	struct cmsghdr_expr *in_cmsg;
+	struct cmsghdr_expr *out_cmsg;
+
+	assert(in->type == EXPR_CMSGHDR);
+	assert(in->value.msghdr);
+	assert(out->type == EXPR_CMSGHDR);
+
+	out->value.cmsghdr = calloc(1, sizeof(struct cmsghdr_expr));
+
+	in_cmsg = in->value.cmsghdr;
+	out_cmsg = out->value.cmsghdr;
+
+	if (evaluate(in_cmsg->cmsg_len,		&out_cmsg->cmsg_len,	error))
+		return STATUS_ERR;
+	if (evaluate(in_cmsg->cmsg_level,	&out_cmsg->cmsg_level,	error))
+		return STATUS_ERR;
+	if (evaluate(in_cmsg->cmsg_type,	&out_cmsg->cmsg_type,	error))
+		return STATUS_ERR;
+	if (evaluate(in_cmsg->cmsg_data,	&out_cmsg->cmsg_data,	error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
 static int evaluate_msghdr_expression(struct expression *in,
 				      struct expression *out, char **error)
 {
@@ -679,6 +716,10 @@ static int evaluate_msghdr_expression(struct expression *in,
 		return STATUS_ERR;
 	if (evaluate(in_msg->msg_iovlen,	&out_msg->msg_iovlen,	error))
 		return STATUS_ERR;
+	if (evaluate(in_msg->msg_control,	&out_msg->msg_control,	error))
+		return STATUS_ERR;
+	if (evaluate(in_msg->msg_controllen,	&out_msg->msg_controllen,error))
+		return STATUS_ERR;
 	if (evaluate(in_msg->msg_flags,		&out_msg->msg_flags,	error))
 		return STATUS_ERR;
 
@@ -1159,6 +1200,10 @@ static int evaluate_sctp_sndinfo_expression(struct expression *in,
 		     &out_sndinfo->snd_context,
 		     error))
 		return STATUS_ERR;
+	if (evaluate(in_sndinfo->snd_assoc_id,
+		     &out_sndinfo->snd_assoc_id,
+		     error))
+		return STATUS_ERR;
 	return STATUS_OK;
 }
 
@@ -2065,6 +2110,9 @@ static int evaluate(struct expression *in,
 	case EXPR_MSGHDR:
 		result = evaluate_msghdr_expression(in, out, error);
 		break;
+	case EXPR_CMSGHDR:
+		result = evaluate_cmsghdr_expression(in, out, error);
+		break;
 	case EXPR_POLLFD:
 		result = evaluate_pollfd_expression(in, out, error);
 		break;
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index bb60524f..5dccd49c 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -45,6 +45,7 @@ enum expression_t {
 	EXPR_LIST,		  /* list of expressions */
 	EXPR_IOVEC,		  /* expression tree for an iovec struct */
 	EXPR_MSGHDR,		  /* expression tree for a msghdr struct */
+	EXPR_CMSGHDR,             /* expression tree for a cmsghdr struct */
 	EXPR_POLLFD,		  /* expression tree for a pollfd struct */
 	EXPR_SCTP_RTOINFO,	  /* struct sctp_rtoinfo for SCTP_RTOINFO */
 	EXPR_SCTP_INITMSG,	  /* struct sctp_initmsg for SCTP_INITMSG */
@@ -95,6 +96,7 @@ struct expression {
 		struct expression_list *list;
 		struct iovec_expr *iovec;
 		struct msghdr_expr *msghdr;
+		struct cmsghdr_expr *cmsghdr;
 		struct pollfd_expr *pollfd;
 		struct sctp_rtoinfo_expr *sctp_rtoinfo;
 		struct sctp_initmsg_expr *sctp_initmsg;
@@ -158,9 +160,19 @@ struct msghdr_expr {
 	struct expression *msg_namelen;
 	struct expression *msg_iov;
 	struct expression *msg_iovlen;
+	struct expression *msg_control;
+	struct expression *msg_controllen;
 	struct expression *msg_flags;
 };
 
+/* Parse tree for a cmsghdr struct in a struct msghdr. */
+struct cmsghdr_expr {
+	struct expression *cmsg_len;
+	struct expression *cmsg_level;
+	struct expression *cmsg_type;
+	struct expression *cmsg_data;
+};
+
 /* Parse tree for a pollfd struct in a poll syscall. */
 struct pollfd_expr {
 	struct expression *fd;		/* file descriptor */
@@ -274,6 +286,7 @@ struct sctp_sndinfo_expr {
 	struct expression *snd_flags;
 	struct expression *snd_ppid;
 	struct expression *snd_context;
+	struct expression *snd_assoc_id;
 };
 
 /* Parse tree for sctp_setadaptation struct in [gs]etsockopt syscall. */
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index 9b0bc90b..b8d5288b 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -82,6 +82,7 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_RTOINFO,                     "SCTP_RTOINFO"                    },
 	{ SCTP_ASSOCINFO,                   "SCTP_ASSOCINFO"                  },
 	{ SCTP_INITMSG,                     "SCTP_INITMSG"                    },
+	{ SCTP_INIT,                        "SCTP_INIT"                       },
 	{ SCTP_NODELAY,                     "SCTP_NODELAY"                    },
 	{ SCTP_ADAPTATION_LAYER,	    "SCTP_ADAPTATION_LAYER"           },
 	{ SCTP_MAXSEG,                      "SCTP_MAXSEG"                     },
@@ -221,6 +222,11 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_ADDR_UNREACHABLE,            "SCTP_ADDR_UNREACHABLE"           },
 	{ SCTP_ADDR_REMOVED,                "SCTP_ADDR_REMOVED"               },
 	{ SCTP_ADDR_MADE_PRIM,              "SCTP_ADDR_MADE_PRIM"             },
+	{ SCTP_SNDINFO,                     "SCTP_SNDINFO"                    },
+	{ SCTP_PRINFO,                      "SCTP_PRINFO"                     },
+	{ SCTP_AUTHINFO,                    "SCTP_AUTHINFO"                   },
+	{ SCTP_DSTADDRV4,                   "SCTP_DSTADDRV4"                  },
+	{ SCTP_DSTADDRV6,                   "SCTP_DSTADDRV6"                  },
 	/* /usr/include/netinet/tcp.h */
 	{ TCP_NODELAY,                      "TCP_NODELAY"                     },
 	{ TCP_MAXSEG,                       "TCP_MAXSEG"                      },
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_get_socket_options.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_get_socket_options.pkt
index 2b9f57a4..a33a6d19 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_get_socket_options.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_get_socket_options.pkt
@@ -115,14 +115,14 @@ sasoc_cookie_life=...}, [20]) = 0
 +0 setsockopt(3, IPPROTO_SCTP, SCTP_EVENT, {se_type=SCTP_SHUTDOWN_EVENT, se_on=1}, 8) = 0
 +0 getsockopt(3, IPPROTO_SCTP, SCTP_EVENT, {se_type=SCTP_SHUTDOWN_EVENT, se_on=1}, [8]) = 0
 
-+0 setsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=0, snd_ppid=htonl(1), snd_context=1}, 16) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=0, snd_ppid=htonl(1), snd_context=1}, [16]) = 0
-+0 setsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2}, 16) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2}, [16]) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=...}, [16]) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=..., snd_context=2}, [16]) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=..., snd_ppid=htonl(2), snd_context=2}, [16]) = 0
-+0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=..., snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2}, [16]) = 0
++0 setsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=0, snd_ppid=htonl(1), snd_context=1, snd_assoc_id=0}, 16) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=0, snd_ppid=htonl(1), snd_context=1, snd_assoc_id=0}, [16]) = 0
++0 setsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2, snd_assoc_id=0}, 16) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2, snd_assoc_id=0}, [16]) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=..., snd_assoc_id=0}, [16]) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=SCTP_UNORDERED, snd_ppid=..., snd_context=2, snd_assoc_id=0},[16]) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=0, snd_flags=..., snd_ppid=htonl(2), snd_context=2, snd_assoc_id=0}, [16]) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_DEFAULT_SNDINFO, {snd_sid=..., snd_flags=SCTP_UNORDERED, snd_ppid=htonl(2), snd_context=2, snd_assoc_id=0}, [16]) = 0
 
 +0 setsockopt(3, IPPROTO_SCTP, SCTP_ADAPTATION_LAYER, {ssb_adaptation_ind=2}, 4) = 0
 +0 getsockopt(3, IPPROTO_SCTP, SCTP_ADAPTATION_LAYER, {ssb_adaptation_ind=2}, [4]) = 0
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendv.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendv.pkt
index 114a4768..b5dbb6f0 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendv.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_sendv.pkt
@@ -50,14 +50,15 @@
 
 //test with all sctp_sendv_spainfo
 +1.0 sctp_sendv(3, [{..., 500}, {..., 500}], 2, ..., 1, {sendv_flags=SCTP_SEND_SNDINFO_VALID,
-sendv_sndinfo={snd_sid=2, snd_flags=0, snd_ppid=htonl(0), snd_context=0},
+sendv_sndinfo={snd_sid=2, snd_flags=0, snd_ppid=htonl(0), snd_context=0, snd_assoc_id=0},
 sendv_prinfo={pr_policy=SCTP_PR_SCTP_TTL, pr_value=10},
 sendv_authinfo={auth_keynumber=123}}, 32, SCTP_SENDV_SPA, 0) = 1000
 *    > sctp: DATA[flgs=BE, len=1016, tsn=8, sid=2, ssn=0, ppid=0]
 +0.0 < sctp: SACK[flgs=0, cum_tsn=8, a_rwnd=1500, gaps=[], dups=[]]
 
 //test with sctp_sendv_sndinfo
-+1.0 sctp_sendv(3, [{iov_base=..., iov_len=1000}], 1, ..., 1, {snd_sid=1, snd_flags=0, snd_ppid=htonl(1234), snd_context=0}, 16, SCTP_SENDV_SNDINFO, 0) = 1000
++1.0 sctp_sendv(3, [{iov_base=..., iov_len=1000}], 1, ..., 1, {snd_sid=1, snd_flags=0, snd_ppid=htonl(1234), snd_context=0, snd_assoc_id=0}, 16, SCTP_SENDV_SNDINFO, 0) 
+= 1000
 *    > sctp: DATA[flgs=BE, len=1016, tsn=9, sid=1, ssn=0, ppid=1234]
 +0.0 < sctp: SACK[flgs=0, cum_tsn=9, a_rwnd=1500, gaps=[], dups=[]]
 
-- 
GitLab