From 6a361a6d0e7bb0a9a8ec1a28e588714dc253a5bb Mon Sep 17 00:00:00 2001
From: hoelscher <jens.hoelscher@fh-muenster.de>
Date: Thu, 26 Nov 2015 21:50:03 +0100
Subject: [PATCH] add [gs]etsockopt SCTP_PRIMARY_ADDR

---
 gtests/net/packetdrill/lexer.l                |   2 +
 gtests/net/packetdrill/parser.y               |  27 +++-
 gtests/net/packetdrill/run_system_call.c      | 143 ++++++++++++------
 gtests/net/packetdrill/script.c               |  36 +++++
 gtests/net/packetdrill/script.h               |   8 +
 gtests/net/packetdrill/symbols_freebsd.c      |   1 +
 .../bsd/sctp/sctp_get_socket_options.pkt      |   5 +-
 7 files changed, 177 insertions(+), 45 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 213bae88..3f564c4c 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -268,6 +268,8 @@ snd_flags			return SND_FLAGS;
 snd_ppid			return SND_PPID;
 snd_context			return SND_CONTEXT;
 snd_assoc_id                    return SND_ASSOC_ID;
+ssp_assoc_id			return SSP_ASSOC_ID;
+ssp_addr			return SSP_ADDR;
 ssb_adaptation_ind		return SSB_ADAPTATION_IND;
 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 3f6a6fa0..91f05e8d 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -538,6 +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_ASSOC_ID SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
 %token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE SE_ASSOC_ID SE_TYPE SE_ON
+%token <reserved> SSP_ASSOC_ID SSP_ADDR
 %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 SINFO_ASSOC_ID
@@ -614,7 +615,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> sasoc_assoc_id 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_assoc_id snd_context
-%type <expression> sctp_event se_assoc_id se_type se_on sctp_setadaptation null
+%type <expression> sctp_event se_assoc_id se_type se_on sctp_setadaptation sctp_setprim ssp_assoc_id null
 %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 sinfo_pr_value serinfo_next_flags
 %type <expression> serinfo_next_stream serinfo_next_aid serinfo_next_length serinfo_next_ppid sctp_extrcvinfo
@@ -2509,6 +2510,9 @@ expression
 | sctp_sndinfo      {
 	$$ = $1;
 }
+| sctp_setprim      {
+	$$ = $1;
+}
 | sctp_setadaptation{
 	$$ = $1;
 }
@@ -3371,7 +3375,28 @@ sctp_sndinfo
 	$$->value.sctp_sndinfo->snd_assoc_id = $10;
 }
 ;
+ssp_assoc_id
+: SSP_ASSOC_ID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("ssp_assoc_id out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SSP_ASSOC_ID '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| SSP_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
 
+sctp_setprim
+: '{' ssp_assoc_id ',' SSP_ADDR '=' sockaddr'}' {
+	$$ = new_expression(EXPR_SCTP_SETPRIM);
+	$$->value.sctp_setprim = calloc(1, sizeof(struct sctp_setprim_expr));
+	$$->value.sctp_setprim->ssp_assoc_id = $2;
+	$$->value.sctp_setprim->ssp_addr = $6;
+}
+;
 sctp_setadaptation
 : '{' SSB_ADAPTATION_IND '=' INTEGER '}' {
 	$$ = new_expression(EXPR_SCTP_SETADAPTATION);
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 4e1d3cd8..e06ef241 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -269,6 +269,27 @@ static int get_size_t(struct expression *expression,
 }
 #endif
 
+#if defined(linux) || defined(__FreeBSD__)
+/* Sets the value from the expression argument, checking that it is a
+ * valid sctp_assoc_t, and matches the expected type. Returns STATUS_OK on
+ * success; on failure returns STATUS_ERR and sets error message.
+ */
+static int get_sctp_assoc_t(struct expression *expression,
+			    sctp_assoc_t *value, char **error)
+{
+	if (check_type(expression, EXPR_INTEGER, error))
+		return STATUS_ERR;
+	if (expression->value.num < 0) {
+		asprintf(error,
+			 "Value out of range for sctp_assoc_t: %lld",
+			 expression->value.num);
+		return STATUS_ERR;
+	}
+	*value = expression->value.num;
+	return STATUS_OK;
+}
+#endif
+
 /* Sets the value from the expression argument, checking that it is a
  * valid u32, and matches the expected type. Returns STATUS_OK on
  * success; on failure returns STATUS_ERR and sets error message.
@@ -2795,6 +2816,21 @@ static int check_sctp_sndinfo(struct sctp_sndinfo_expr *expr,
 }
 #endif
 
+#ifdef SCTP_PRIMARY_ADDR
+static int check_sctp_setprim(struct sctp_setprim_expr *expr,
+			      struct sctp_setprim *sctp_setprim,
+			      char **error)
+{
+	if (check_u32_expr(expr->ssp_assoc_id, sctp_setprim->ssp_assoc_id,
+			   "sctp_setprim.ssp_assoc_id", error))
+		return STATUS_ERR;
+	if (check_sockaddr(expr->ssp_addr, (struct sockaddr *)&sctp_setprim->ssp_addr, error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+#endif
+
 #ifdef SCTP_ADAPTATION_LAYER
 static int check_sctp_setadaptation(struct sctp_setadaptation_expr *expr,
 				    struct sctp_setadaptation *sctp_setadaptation,
@@ -2844,15 +2880,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_RTOINFO:
 		live_optval = malloc(sizeof(struct sctp_rtoinfo));
 		live_optlen = (socklen_t)sizeof(struct sctp_rtoinfo);
-		if (val_expression->value.sctp_rtoinfo->srto_assoc_id->type != EXPR_ELLIPSIS) {
-			if (get_u32(val_expression->value.sctp_rtoinfo->srto_assoc_id,
-				    &((struct sctp_rtoinfo*)live_optval)->srto_assoc_id,
-				    error)) {
-				free(live_optval);
-				return STATUS_ERR;
-			}
-		} else {
-			((struct sctp_rtoinfo*)live_optval)->srto_assoc_id = 0;
+		if (get_sctp_assoc_t(val_expression->value.sctp_rtoinfo->srto_assoc_id,
+				     &((struct sctp_rtoinfo*)live_optval)->srto_assoc_id,
+				     error)) {
+			free(live_optval);
+			return STATUS_ERR;
 		}
 		break;
 #endif
@@ -2860,15 +2892,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_ASSOCPARAMS:
 		live_optval = malloc(sizeof(struct sctp_assocparams));
 		live_optlen = (socklen_t)sizeof(struct sctp_assocparams);
-		if (val_expression->value.sctp_rtoinfo->srto_assoc_id->type != EXPR_ELLIPSIS) {
-			if (get_u32(val_expression->value.sctp_assocparams->sasoc_assoc_id,
+		if (get_sctp_assoc_t(val_expression->value.sctp_assocparams->sasoc_assoc_id,
 				    &((struct sctp_assocparams*) live_optval)->sasoc_assoc_id,
 				    error)) {
-				free(live_optval);
-				return STATUS_ERR;
-			}
-		} else {
-			((struct sctp_assocparams*) live_optval)->sasoc_assoc_id = 0;
+			free(live_optval);
+			return STATUS_ERR;
 		}
 		break;
 #endif
@@ -2888,7 +2916,7 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_STATUS
 	case EXPR_SCTP_STATUS:
 		live_optval = malloc(sizeof(struct sctp_status));
-		live_optlen = (socklen_t)sizeof(struct sctp_status);
+		live_optlen = (socklen_t) sizeof(struct sctp_status);
 		((struct sctp_status*) live_optval)->sstat_assoc_id = 0;
 		break;
 #endif
@@ -2896,7 +2924,7 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_PADDRINFO: {
 		struct sctp_paddrinfo_expr *expr_paddrinfo = val_expression->value.sctp_paddrinfo;
 		struct sctp_paddrinfo *live_paddrinfo = malloc(sizeof(struct sctp_paddrinfo));
-		live_optlen = (socklen_t)sizeof(struct sctp_paddrinfo);
+		live_optlen = (socklen_t) sizeof(struct sctp_paddrinfo);
 		memset(live_paddrinfo, 0, sizeof(struct sctp_paddrinfo));
 		live_paddrinfo->spinfo_assoc_id = 0;
 		if (get_sockstorage_arg(expr_paddrinfo->spinfo_address,
@@ -2950,15 +2978,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_EVENT:
 		live_optval = malloc(sizeof(struct sctp_event));
 		live_optlen = sizeof(struct sctp_event);
-		if (val_expression->value.sctp_event->se_assoc_id->type != EXPR_ELLIPSIS) {
-			if (get_u32(val_expression->value.sctp_event->se_assoc_id,
-				    &((struct sctp_event *)live_optval)->se_assoc_id,
-				    error)) {
-				free(live_optval);
-				return STATUS_ERR;
-			}
-		} else {
-			((struct sctp_event *)live_optval)->se_assoc_id = 0;
+		if (get_sctp_assoc_t(val_expression->value.sctp_event->se_assoc_id,
+				     &((struct sctp_event *)live_optval)->se_assoc_id,
+				     error)) {
+			free(live_optval);
+			return STATUS_ERR;
 		}
 		if (get_u16(val_expression->value.sctp_event->se_type,
 			    &((struct sctp_event *)live_optval)->se_type,
@@ -2978,9 +3002,21 @@ 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);
-		if (get_u32(val_expression->value.sctp_sndinfo->snd_assoc_id,
-			    &((struct sctp_sndinfo *)live_optval)->snd_assoc_id,
-			    error)) {
+		if (get_sctp_assoc_t(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_PRIMARY_ADDR
+	case EXPR_SCTP_SETPRIM:
+		live_optval = malloc(sizeof(struct sctp_setprim));
+		live_optlen = sizeof(struct sctp_setprim);
+		if (get_sctp_assoc_t(val_expression->value.sctp_setprim->ssp_assoc_id,
+				     &((struct sctp_setprim *)live_optval)->ssp_assoc_id,
+				     error)) {
 			free(live_optval);
 			return STATUS_ERR;
 		}
@@ -3083,6 +3119,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 		result = check_sctp_sndinfo(val_expression->value.sctp_sndinfo, live_optval, error);
 		break;
 #endif
+#ifdef SCTP_PRIMARY_ADDR
+	case EXPR_SCTP_SETPRIM:
+		result = check_sctp_setprim(val_expression->value.sctp_setprim, live_optval, error);
+		break;
+#endif
 #ifdef SCTP_ADAPTATION_LAYER
 	case EXPR_SCTP_SETADAPTATION:
 		result = check_sctp_setadaptation(val_expression->value.sctp_setadaptation, live_optval, error);
@@ -3144,6 +3185,9 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_DEFAULT_SNDINFO
 	struct sctp_sndinfo sndinfo;
 #endif
+#ifdef SCTP_PRIMARY_ADDR
+	struct sctp_setprim setprim;
+#endif
 #ifdef SCTP_ADAPTATION_LAYER
 	struct sctp_setadaptation setadaptation;
 #endif
@@ -3189,8 +3233,8 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_RTOINFO
 	case EXPR_SCTP_RTOINFO:
 		if (val_expression->value.sctp_rtoinfo->srto_assoc_id->type != EXPR_ELLIPSIS) {
-			if (get_u32(val_expression->value.sctp_rtoinfo->srto_assoc_id,
-				    &rtoinfo.srto_assoc_id, error)) {
+			if (get_sctp_assoc_t(val_expression->value.sctp_rtoinfo->srto_assoc_id,
+					      &rtoinfo.srto_assoc_id, error)) {
 				return STATUS_ERR;
 			}
 		} else {
@@ -3213,8 +3257,8 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #endif
 #ifdef SCTP_ASSOCINFO
 	case EXPR_SCTP_ASSOCPARAMS:
-		if (get_u32(val_expression->value.sctp_assocparams->sasoc_assoc_id,
-			    &assocparams.sasoc_assoc_id, error)) {
+		if (get_sctp_assoc_t(val_expression->value.sctp_assocparams->sasoc_assoc_id,
+				     &assocparams.sasoc_assoc_id, error)) {
 			return STATUS_ERR;
 		}
 		if (get_u16(val_expression->value.sctp_assocparams->sasoc_asocmaxrxt,
@@ -3306,8 +3350,8 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_EVENT
 	case EXPR_SCTP_EVENT:
 		if (val_expression->value.sctp_event->se_assoc_id->type != EXPR_ELLIPSIS) {
-			if (get_u16(val_expression->value.sctp_event->se_assoc_id,
-				    &event.se_type, error)) {
+			if (get_sctp_assoc_t(val_expression->value.sctp_event->se_assoc_id,
+					     &event.se_assoc_id, error)) {
 				return STATUS_ERR;
 			}
 		} else {
@@ -3387,13 +3431,26 @@ 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)) {
+		if (get_sctp_assoc_t(val_expression->value.sctp_sndinfo->snd_assoc_id,
+				     &sndinfo.snd_assoc_id, error)) {
 			return STATUS_ERR;
 		}
 		optval = &sndinfo;
 		break;
 #endif
+#ifdef SCTP_PRIMARY_ADDR
+	case EXPR_SCTP_SETPRIM:
+		if (get_sctp_assoc_t(val_expression->value.sctp_setprim->ssp_assoc_id,
+				     &setprim.ssp_assoc_id, error)) {
+			return STATUS_ERR;
+		}
+		if (get_sockstorage_arg(val_expression->value.sctp_setprim->ssp_addr,
+			    	&setprim.ssp_addr, live_fd)) {
+			return STATUS_ERR;
+		}
+		optval = &setprim;		
+		break;
+#endif
 #ifdef SCTP_ADAPTATION_LAYER
 	case EXPR_SCTP_SETADAPTATION:
 		if (get_u32(val_expression->value.sctp_setadaptation->ssb_adaptation_ind,
@@ -3915,7 +3972,7 @@ static int parse_expression_to_sctp_sndrcvinfo(struct expression *expr,
 				info->sinfo_assoc_id = 0;
 			}
 		} else {
-			if (get_u32(sndrcvinfo_expr->sinfo_assoc_id, (u32 *)&info->sinfo_assoc_id, error)) {
+			if (get_sctp_assoc_t(sndrcvinfo_expr->sinfo_assoc_id, &info->sinfo_assoc_id, error)) {
 				return STATUS_ERR;
 			}
 		}
@@ -3942,7 +3999,7 @@ 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)) {
+		if (get_sctp_assoc_t(sndinfo_expr->snd_assoc_id, &info->snd_assoc_id, error)) {
 			return STATUS_ERR;
 		}
 	} else {
@@ -5047,7 +5104,7 @@ static int syscall_sctp_peeloff(struct state *state, struct syscall_spec *syscal
 	if (to_live_fd(state, script_fd, &live_fd, error))
 		return STATUS_ERR;
 	expr_assoc = get_arg(args, 1, error);
-	if (get_u32(expr_assoc, (u32 *)&assoc_id, error))
+	if (get_sctp_assoc_t(expr_assoc, &assoc_id, error))
 		return STATUS_ERR;
 
 	//check connection Type and set assoc_id if one-to-many style socket
@@ -5094,7 +5151,7 @@ static int syscall_sctp_getpaddrs(struct state *state, struct syscall_spec *sysc
 	if (to_live_fd(state, script_fd, &live_fd, error))
 		return STATUS_ERR;
 	assoc_expr = get_arg(args, 1, error);
-	if (get_u32(assoc_expr, (u32 *)&assoc_id, error))
+	if (get_sctp_assoc_t(assoc_expr, (u32 *)&assoc_id, error))
 		return STATUS_ERR;
 
 	begin_syscall(state, syscall);
@@ -5179,7 +5236,7 @@ static int syscall_sctp_getladdrs(struct state *state, struct syscall_spec *sysc
 	if (to_live_fd(state, script_fd, &live_fd, error))
 		return STATUS_ERR;
 	assoc_expr = get_arg(args, 1, error);
-	if (get_u32(assoc_expr, (u32 *)&assoc_id, error))
+	if (get_sctp_assoc_t(assoc_expr, &assoc_id, error))
 		return STATUS_ERR;
 
 	begin_syscall(state, syscall);
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 6e3b76ec..3cb25578 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -79,6 +79,7 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_SCTP_EVENT,	     "sctp_event"      },
 	{ EXPR_SCTP_EVENT_SUBSCRIBE, "sctp_event_subscribe"},
 	{ EXPR_SCTP_SNDINFO,         "sctp_sndinfo"    },
+	{ EXPR_SCTP_SETPRIM,         "sctp_setprim"    },
 	{ EXPR_SCTP_SETADAPTATION,   "sctp_setadaptation"},
 	{ EXPR_SCTP_SNDRCVINFO,      "sctp_sndrcvinfo" },
 	{ EXPR_SCTP_PRINFO,          "sctp_prinfo"     },
@@ -400,6 +401,10 @@ void free_expression(struct expression *expression)
 		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_SETPRIM:
+		free_expression(expression->value.sctp_setprim->ssp_assoc_id);
+		free_expression(expression->value.sctp_setprim->ssp_addr);
 		break;		
 	case EXPR_SCTP_SETADAPTATION:
 		free_expression(expression->value.sctp_setadaptation->ssb_adaptation_ind);
@@ -1243,6 +1248,34 @@ static int evaluate_sctp_sndinfo_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_setprim_expression(struct expression *in,
+					    struct expression *out,
+					    char **error)
+{
+        struct sctp_setprim_expr *in_prim;
+        struct sctp_setprim_expr *out_prim;
+
+        assert(in->type == EXPR_SCTP_SETPRIM);
+        assert(in->value.sctp_setprim);
+        assert(out->type == EXPR_SCTP_SETPRIM);
+
+        out->value.sctp_setprim = calloc(1, sizeof(struct sctp_setprim_expr));
+                     
+        in_prim = in->value.sctp_setprim;
+        out_prim = out->value.sctp_setprim;
+                     
+        if (evaluate(in_prim->ssp_assoc_id,
+		     &out_prim->ssp_assoc_id,
+		     error))
+		return STATUS_ERR;
+        if (evaluate(in_prim->ssp_addr,
+		     &out_prim->ssp_addr,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
 static int evaluate_sctp_setadaptation_expression(struct expression *in,
 						  struct expression *out,
 						  char **error)
@@ -2145,6 +2178,9 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_SNDINFO:
 		result = evaluate_sctp_sndinfo_expression(in, out, error);
 		break;
+	case EXPR_SCTP_SETPRIM:
+		result = evaluate_sctp_setprim_expression(in, out, error);
+		break;
 	case EXPR_SCTP_SETADAPTATION:
 		result = evaluate_sctp_setadaptation_expression(in, out, error);
 		break;
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 8b9cbf0c..d5951572 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -59,6 +59,7 @@ enum expression_t {
 	EXPR_SCTP_EVENT,	  /* struct sctp_event for SCTP_EVENT */
 	EXPR_SCTP_EVENT_SUBSCRIBE,/* struct sctp_event_subscribe for SCTP_EVENTS */
 	EXPR_SCTP_SNDINFO,	  /* struct sctp_sndinfo for SCTP_DEFAULT_SNDINFO */
+	EXPR_SCTP_SETPRIM,        /* expression tree for sctp_setprim SCTP_PRIMARY_ADDR */
 	EXPR_SCTP_SETADAPTATION,  /* struct sctp_setadaptation for SCTP_ADATTATION_LAYER */
 	EXPR_SCTP_SNDRCVINFO,     /* struct sctp_sndrcvinfo for syscall sctp_recvmsg */
 	EXPR_SCTP_PRINFO,	  /* struct sctp_prinfo for syscall sctp_sendv */
@@ -111,6 +112,7 @@ struct expression {
 		struct sctp_event_expr *sctp_event;
 		struct sctp_event_subscribe_expr *sctp_event_subscribe;
 		struct sctp_sndinfo_expr *sctp_sndinfo;
+		struct sctp_setprim_expr *sctp_setprim;
 		struct sctp_setadaptation_expr *sctp_setadaptation;
 		struct sctp_sndrcvinfo_expr *sctp_sndrcvinfo;
 		struct sctp_prinfo_expr *sctp_prinfo;
@@ -294,6 +296,12 @@ struct sctp_sndinfo_expr {
 	struct expression *snd_assoc_id;
 };
 
+/* Parse tree for sctp_setadaptation struct in [gs]etsockopt syscall. */
+struct sctp_setprim_expr {
+	struct expression *ssp_assoc_id;
+	struct expression *ssp_addr;
+};
+
 /* Parse tree for sctp_setadaptation struct in [gs]etsockopt syscall. */
 struct sctp_setadaptation_expr {
 	struct expression *ssb_adaptation_ind;
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index a3adfe35..ae52fa39 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -84,6 +84,7 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_INITMSG,                     "SCTP_INITMSG"                    },
 	{ SCTP_INIT,                        "SCTP_INIT"                       },
 	{ SCTP_NODELAY,                     "SCTP_NODELAY"                    },
+	{ SCTP_PRIMARY_ADDR,                "SCTP_PRIMARY_ADDR"               },
 	{ SCTP_ADAPTATION_LAYER,            "SCTP_ADAPTATION_LAYER"           },
 	{ SCTP_MAXSEG,                      "SCTP_MAXSEG"                     },
 	{ SCTP_DELAYED_SACK,                "SCTP_DELAYED_SACK"               },
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 e83ad2af..90677ab3 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
@@ -8,7 +8,10 @@
 +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
++0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
+
++0 setsockopt(3, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, {ssp_assoc_id=0, ssp_addr={sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.0.2.1")}}, 136) = 0
++0 getsockopt(3, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, {ssp_assoc_id=0, ssp_addr={sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.0.2.1")}}, [136]) = 0
 
 +0 setsockopt(3, IPPROTO_SCTP, SCTP_STATUS, {sstat_state=..., sstat_rwnd=..., sstat_unackdata=..., sstat_penddata=...,
 sstat_instrms=..., sstat_outstrms=..., sstat_fragmentation_point=..., sstat_primary=...}, 176) = -1
-- 
GitLab