From 7ce8d25a82d0537bab1d81ff9eb7e61ca21f2d7e Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Thu, 1 Oct 2015 11:12:12 +0200
Subject: [PATCH] Add support for the SCTP_SS_VALUE socket option.

---
 gtests/net/packetdrill/lexer.l           |  2 +
 gtests/net/packetdrill/parser.y          | 18 ++++++
 gtests/net/packetdrill/run_system_call.c | 82 ++++++++++++++++++++++++
 gtests/net/packetdrill/script.c          | 40 ++++++++++++
 gtests/net/packetdrill/script.h          | 14 ++++
 gtests/net/packetdrill/symbols_freebsd.c |  1 +
 6 files changed, 157 insertions(+)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 8e8e7f89..4e9e3e84 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -203,6 +203,8 @@ noecn				return NO_ECN;
 ce				return CE;
 [.][.][.]			return ELLIPSIS;
 assoc_value			return ASSOC_VALUE;
+stream_id			return STREAM_ID;
+stream_value			return STREAM_VALUE;
 sack_delay			return SACK_DELAY;
 sack_freq			return SACK_FREQ;
 srto_initial			return SRTO_INITIAL;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 88deb4f9..07bee01a 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -506,6 +506,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
 %token <reserved> SINIT_MAX_INIT_TIMEO
 %token <reserved> ASSOC_VALUE
+%token <reserved> STREAM_ID STREAM_VALUE
 %token <reserved> SACK_DELAY SACK_FREQ
 %token <reserved> SSTAT_STATE SSTAT_RWND SSTAT_UNACKDATA SSTAT_PENDDATA
 %token <reserved> SSTAT_INSTRMS SSTAT_OUTSTRMS SSTAT_FRAGMENTATION_POINT
@@ -569,6 +570,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> linger l_onoff l_linger
 %type <expression> sctp_status sctp_initmsg sctp_sackinfo
 %type <expression> sctp_assoc_value
+%type <expression> sctp_stream_value
 %type <expression> sctp_rtoinfo srto_initial srto_max srto_min
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
@@ -2337,6 +2339,9 @@ expression
 | sctp_assoc_value  {
 	$$ = $1;
 }
+| sctp_stream_value  {
+	$$ = $1;
+}
 | sctp_sackinfo     {
 	$$ = $1;
 }
@@ -2561,6 +2566,19 @@ sctp_initmsg
 }
 ;
 
+sctp_stream_value
+: '{' STREAM_ID '=' expression ',' STREAM_VALUE '=' expression '}' {
+#if defined(SCTP_SS_VALUE)
+	$$ = new_expression(EXPR_SCTP_STREAM_VALUE);
+	$$->value.sctp_stream_value = calloc(1, sizeof(struct sctp_stream_value_expr));
+	$$->value.sctp_stream_value->stream_id = $4;
+	$$->value.sctp_stream_value->stream_value = $8;
+#else
+	$$ = NULL;
+#endif
+}
+;
+
 sctp_assoc_value
 : '{' ASSOC_VALUE '=' expression '}' {
 #if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED)
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index a130404f..f3a3d1bb 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -180,6 +180,26 @@ static int check_type(struct expression *expression,
 	}
 }
 
+/* Sets the value from the expression argument, checking that it is a
+ * valid u16, and matches the expected type. Returns STATUS_OK on
+ * success; on failure returns STATUS_ERR and sets error message.
+ */
+static int get_u16(struct expression *expression,
+		   u16 *value, char **error)
+{
+	if (check_type(expression, EXPR_INTEGER, error))
+		return STATUS_ERR;
+	if ((expression->value.num > UINT16_MAX) ||
+	    (expression->value.num < 0)) {
+		asprintf(error,
+			 "Value out of range for 16-bit unsigned integer: %lld",
+			 expression->value.num);
+		return STATUS_ERR;
+	}
+	*value = expression->value.num;
+	return STATUS_OK;
+}
+
 /* 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.
@@ -1636,6 +1656,18 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 		live_optval = malloc(sizeof(struct sctp_assoc_value));
 		live_optlen = (socklen_t)sizeof(struct sctp_assoc_value);
 		((struct sctp_assoc_value *) live_optval)->assoc_id = 0;
+#endif
+#ifdef SCTP_SS_VALUE
+	} else if (val_expression->type == EXPR_SCTP_STREAM_VALUE) {
+		live_optval = malloc(sizeof(struct sctp_stream_value));
+		live_optlen = (socklen_t)sizeof(struct sctp_stream_value);
+		((struct sctp_stream_value *) live_optval)->assoc_id = 0;
+		if (get_u16(val_expression->value.sctp_stream_value->stream_id,
+		            &((struct sctp_stream_value *) live_optval)->stream_id,
+		            error)) {
+			free(live_optval);
+			return STATUS_ERR;
+		}
 #endif
 	} else {
 		s32_bracketed_arg(args, 3, &script_optval, error);
@@ -1788,6 +1820,38 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 				return STATUS_ERR;
 			}
 		}
+#endif
+#ifdef SCTP_SS_VALUE
+	} else if (val_expression->type == EXPR_SCTP_STREAM_VALUE) {
+		struct expression *stream_id = val_expression->value.sctp_stream_value->stream_id;
+		struct expression *stream_value = val_expression->value.sctp_stream_value->stream_value;
+		struct sctp_stream_value *sctp_stream_value = live_optval;
+		u16 value;
+
+		if (stream_id->type != EXPR_ELLIPSIS) {
+			if (get_u16(stream_id, &value, error)) {
+				free(live_optval);
+				return STATUS_ERR;
+			}
+			if (sctp_stream_value->stream_id != value) {
+				asprintf(error, "Bad getsockopt sctp_stream_value.stream_id: expected: %u actual: %u",
+					 value, sctp_stream_value->stream_id);
+				free(live_optval);
+				return STATUS_ERR;
+			}
+		}
+		if (stream_value->type != EXPR_ELLIPSIS) {
+			if (get_u16(stream_value, &value, error)) {
+				free(live_optval);
+				return STATUS_ERR;
+			}
+			if (sctp_stream_value->stream_value != value) {
+				asprintf(error, "Bad getsockopt sctp_stream_value.stream_value: expected: %u actual: %u",
+					 value, sctp_stream_value->stream_value);
+				free(live_optval);
+				return STATUS_ERR;
+			}
+		}
 #endif
 	} else {
 		if (*(int*)live_optval != script_optval) {
@@ -1807,7 +1871,12 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 	int script_fd, live_fd, level, optname, optval_s32, optlen, result;
 	void *optval = NULL;
 	struct expression *val_expression;
+#if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST) || defined(SCTP_INTERLEAVING_SUPPORTED)
 	struct sctp_assoc_value assoc_value;
+#endif
+#if defined(SCTP_SS_VALUE)
+	struct sctp_stream_value stream_value;
+#endif
 
 	if (check_arg_count(args, 5, error))
 		return STATUS_ERR;
@@ -1867,6 +1936,19 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 		}
 		optval = &assoc_value;
 #endif
+#ifdef SCTP_SS_VALUE
+	} else if (val_expression->type == EXPR_SCTP_STREAM_VALUE) {
+		stream_value.assoc_id = 0;
+		if (get_u16(val_expression->value.sctp_stream_value->stream_id,
+		            &stream_value.stream_id, error)) {
+			return STATUS_ERR;
+		}
+		if (get_u16(val_expression->value.sctp_stream_value->stream_value,
+		            &stream_value.stream_value, error)) {
+			return STATUS_ERR;
+		}
+		optval = &stream_value;
+#endif
 #ifdef SCTP_DELAYED_SACK
 	} else if (val_expression->type == EXPR_SCTP_SACKINFO) {
 		optval = &val_expression->value.sctp_sack_info;
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 3592bd39..b28360fd 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -79,6 +79,9 @@ struct expression_type_entry expression_type_table[] = {
 #endif
 #ifdef SCTP_STATUS
 	{ EXPR_SCTP_STATUS,           "sctp_status"},
+#endif
+#ifdef SCTP_SS_VALUE
+	{ EXPR_SCTP_STREAM_VALUE,     "sctp_stream_value"},
 #endif
 	{ NUM_EXPR_TYPES,            NULL}
 };
@@ -312,6 +315,12 @@ void free_expression(struct expression *expression)
 	case EXPR_SCTP_STATUS:
 #endif
 		break;
+#ifdef SCTP_SS_VALUE
+	case EXPR_SCTP_STREAM_VALUE:
+		free(expression->value.sctp_stream_value->stream_id);
+		free(expression->value.sctp_stream_value->stream_value);
+		break;
+#endif
 	case EXPR_WORD:
 		assert(expression->value.string);
 		free(expression->value.string);
@@ -521,6 +530,32 @@ static int evaluate_sctp_assoc_value_expression(struct expression *in,
 }
 #endif
 
+#ifdef SCTP_SS_VALUE
+static int evaluate_sctp_stream_value_expression(struct expression *in,
+						 struct expression *out,
+						 char **error)
+{
+	struct sctp_stream_value_expr *in_value;
+	struct sctp_stream_value_expr *out_value;
+
+	assert(in->type == EXPR_SCTP_STREAM_VALUE);
+	assert(in->value.sctp_stream_value);
+	assert(out->type == EXPR_SCTP_STREAM_VALUE);
+
+	out->value.sctp_stream_value = calloc(1, sizeof(struct sctp_stream_value_expr));
+
+	in_value = in->value.sctp_stream_value;
+	out_value = out->value.sctp_stream_value;
+
+	if (evaluate(in_value->stream_id, &out_value->stream_id, error))
+		return STATUS_ERR;
+	if (evaluate(in_value->stream_value, &out_value->stream_value, error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+#endif
+
 static int evaluate(struct expression *in,
 		    struct expression **out_ptr, char **error)
 {
@@ -572,6 +607,11 @@ static int evaluate(struct expression *in,
 		memcpy(&out->value.sctp_status, &in->value.sctp_status,
 		       sizeof(in->value.sctp_status));
 		break;
+#endif
+#ifdef SCTP_SS_VALUE
+	case EXPR_SCTP_STREAM_VALUE:
+		evaluate_sctp_stream_value_expression(in, out, error);
+		break;
 #endif
 	case EXPR_WORD:
 		out->type = EXPR_INTEGER;
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 3fc9ca95..a7fc4014 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -60,6 +60,9 @@ enum expression_t {
 #endif
 #ifdef SCTP_STATUS
 	EXPR_SCTP_STATUS,	  /* struct sctp_status for SCTP_STATUS */
+#endif
+#ifdef SCTP_SS_VALUE
+	EXPR_SCTP_STREAM_VALUE,	  /* struct sctp_stream_value for SCTP_SS_VALUE */
 #endif
 	NUM_EXPR_TYPES,
 };
@@ -94,6 +97,9 @@ struct expression {
 #endif
 #ifdef SCTP_STATUS
 		struct sctp_status sctp_status;
+#endif
+#ifdef SCTP_SS_VALUE
+		struct sctp_stream_value_expr *sctp_stream_value;
 #endif
 	} value;
 	const char *format;	/* the printf format for printing the value */
@@ -157,6 +163,14 @@ struct sctp_assoc_value_expr {
 };
 #endif
 
+#ifdef SCTP_SS_VALUE
+/* Parse tree for a sctp_stream_value struct in a [gs]etsockopt syscall. */
+struct sctp_stream_value_expr {
+	struct expression *stream_id;
+	struct expression *stream_value;
+};
+#endif
+
 /* 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 ffc7d6cb..ad5231e6 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -105,6 +105,7 @@ struct int_symbol platform_symbols_table[] = {
 	 * The old symbols currently being deployed are also provided.
 	 */
 	{ SCTP_PLUGGABLE_SS,                "SCTP_PLUGGABLE_SS"               },
+	{ SCTP_SS_VALUE,                    "SCTP_SS_VALUE"                   },
 	{ SCTP_SS_DEFAULT,                  "SCTP_SS_DEFAULT"                 },
 	{ SCTP_SS_ROUND_ROBIN,              "SCTP_SS_ROUND_ROBIN"             },
 	{ SCTP_SS_ROUND_ROBIN_PACKET,       "SCTP_SS_ROUND_ROBIN_PACKET"      },
-- 
GitLab