From b3e96682ba0153ad04c3cd9f3b3842951a315c69 Mon Sep 17 00:00:00 2001
From: hoelscher <jens.hoelscher@fh-muenster.de>
Date: Tue, 27 Oct 2015 13:44:51 +0100
Subject: [PATCH] add support for sctp_peer_addr_change notification

---
 gtests/net/packetdrill/lexer.l                |  7 ++
 gtests/net/packetdrill/parser.y               | 94 +++++++++++++++++++
 gtests/net/packetdrill/run_system_call.c      | 37 ++++++++
 gtests/net/packetdrill/script.c               | 61 ++++++++++++
 gtests/net/packetdrill/script.h               | 15 ++-
 gtests/net/packetdrill/symbols_freebsd.c      |  5 +-
 .../sctp_partial_delivery_event.pkt           | 13 ++-
 .../notifications/sctp_peer_addr_change.pkt   | 15 +++
 8 files changed, 238 insertions(+), 9 deletions(-)
 create mode 100644 gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_peer_addr_change.pkt

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 26d04437..8f9aaa8c 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -329,6 +329,13 @@ pdapi_indication                return PDAPI_INDICATION;
 pdapi_stream                    return PDAPI_STREAM;
 pdapi_seq                       return PDAPI_SEQ;
 pdapi_assoc_id                  return PDAPI_ASSOC_ID;
+spc_type                        return SPC_TYPE;
+spc_length                      return SPC_LENGTH;
+spc_flags                       return SPC_FLAGS;
+spc_aaddr                       return SPC_AADDR;
+spc_state                       return SPC_STATE;
+spc_error                       return SPC_ERROR;
+spc_assoc_id                    return SPC_ASSOC_ID;
 CHUNK				return CHUNK;
 DATA				return DATA;
 INIT				return INIT;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 448638be..6134be3a 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -559,6 +559,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> AUTH_TYPE AUTH_FLAGS AUTH_LENGTH AUTH_INDICATION AUTH_ASSOC_ID
 %token <reserved> SRE_TYPE SRE_FLAGS SRE_LENGTH SRE_ERROR SRE_ASSOC_ID SRE_DATA PDAPI_ASSOC_ID
 %token <reserved> PDAPI_TYPE PDAPI_FLAGS PDAPI_LENGTH PDAPI_INDICATION PDAPI_STREAM PDAPI_SEQ
+%token <reserved> SPC_TYPE SPC_FLAGS SPC_LENGTH SPC_AADDR SPC_STATE SPC_ERROR SPC_ASSOC_ID
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -623,6 +624,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> sctp_authkey_event auth_type auth_flags auth_length auth_keynumber auth_indication auth_assoc_id
 %type <expression> sctp_remote_error sre_type sre_flags sre_length sre_error sre_assoc_id sre_data
 %type <expression> sctp_pdapi_event pdapi_type pdapi_flags pdapi_length pdapi_indication pdapi_stream pdapi_seq pdapi_assoc_id
+%type <expression> sctp_paddr_change spc_type spc_flags spc_length spc_aaddr spc_error spc_state spc_assoc_id
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
@@ -2598,6 +2600,7 @@ sockaddr
 data
 : ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
 | sctp_assoc_change         { $$ = $1; }
+| sctp_paddr_change         { $$ = $1; }
 | sctp_remote_error         { $$ = $1; }
 | sctp_shutdown_event       { $$ = $1; }
 | sctp_pdapi_event          { $$ = $1; }
@@ -4073,6 +4076,97 @@ sctp_remote_error
 }
 ;
 
+spc_type
+: SPC_TYPE '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("spc_type out of range");
+	}
+	$$ = new_integer_expression($3, "%hu");
+}
+| SPC_TYPE '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| SPC_TYPE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+spc_flags
+: SPC_FLAGS '=' INTEGER {
+	if (!is_valid_u16($3)) {
+		semantic_error("spc_flags out of range");
+	}
+	$$ = new_integer_expression($3, "%hu");
+}
+| SPC_FLAGS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+spc_length
+: SPC_LENGTH '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("spc_length out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SPC_LENGTH '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+spc_aaddr
+: SPC_AADDR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+| SPC_AADDR '=' sockaddr { $$ = $3; }
+;
+
+spc_state
+: SPC_STATE '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("spc_state out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SPC_STATE '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| SPC_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+spc_error
+: SPC_ERROR '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("spc_error out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SPC_ERROR '=' WORD {
+	$$ = new_expression(EXPR_WORD);
+	$$->value.string = $3;
+}
+| SPC_ERROR '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+spc_assoc_id
+: SPC_ASSOC_ID '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("spc_assoc_id out of range");
+	}
+	$$ = new_integer_expression($3, "%u");
+}
+| SPC_ASSOC_ID '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+;
+
+sctp_paddr_change
+: '{' spc_type ',' spc_flags ',' spc_length ',' spc_aaddr ',' spc_state ',' spc_error ',' spc_assoc_id '}' {
+	$$ = new_expression(EXPR_SCTP_PADDR_CHANGE);
+	$$->value.sctp_paddr_change = calloc(1, sizeof(struct sctp_paddr_change_expr));
+	$$->value.sctp_paddr_change->spc_type = $2;
+	$$->value.sctp_paddr_change->spc_length = $4;
+	$$->value.sctp_paddr_change->spc_flags = $6;
+	$$->value.sctp_paddr_change->spc_aaddr = $8;
+	$$->value.sctp_paddr_change->spc_state = $10;
+	$$->value.sctp_paddr_change->spc_error = $12;
+	$$->value.sctp_paddr_change->spc_assoc_id = $14;
+}
+;
+
 opt_errno
 :                   { $$ = NULL; }
 | WORD note         {
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index b5fc2103..7a0a6613 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -569,6 +569,7 @@ static int iovec_new(struct expression *expression,
 
 		assert(iov_expr->iov_base->type == EXPR_ELLIPSIS ||
 		       iov_expr->iov_base->type == EXPR_SCTP_ASSOC_CHANGE ||
+		       iov_expr->iov_base->type == EXPR_SCTP_PADDR_CHANGE ||
 		       iov_expr->iov_base->type == EXPR_SCTP_REMOTE_ERROR ||
 		       iov_expr->iov_base->type == EXPR_SCTP_SHUTDOWN_EVENT ||
 		       iov_expr->iov_base->type == EXPR_SCTP_PDAPI_EVENT ||
@@ -3419,6 +3420,36 @@ static int check_sctp_assoc_change(struct sctp_assoc_change_expr *expr,
 }
 #endif
 
+#if defined(__FreeBSD__) || defined(linux)
+static int check_sctp_paddr_change(struct sctp_paddr_change_expr *expr,
+				   struct sctp_paddr_change *sctp_event,
+				   char **error) {
+	if (check_u16_expr(expr->spc_type, sctp_event->spc_type,
+			   "sctp_paddr_change.spc_type", error))
+		return STATUS_ERR;
+	if (check_u16_expr(expr->spc_flags, sctp_event->spc_flags,
+			   "sctp_paddr_change.spc_flags", error))
+		return STATUS_ERR;
+	if (check_u32_expr(expr->spc_length, sctp_event->spc_length,
+			   "sctp_paddr_change.spc_length", error))
+		return STATUS_ERR;
+	if (check_sockaddr(expr->spc_aaddr,
+			   (struct sockaddr *)&sctp_event->spc_aaddr, error))
+		return STATUS_ERR;
+	if (check_u32_expr(expr->spc_state, sctp_event->spc_state,
+			   "sctp_paddr_change.spc_state", error))
+		return STATUS_ERR;
+	if (check_u32_expr(expr->spc_error, sctp_event->spc_error,
+			   "sctp_paddr_change.spc_error", error))
+		return STATUS_ERR;
+	if (check_u32_expr(expr->spc_assoc_id, sctp_event->spc_assoc_id,
+			   "sctp_paddr_change.spc_assoc_id", error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+#endif
+
 #if defined(__FreeBSD__) || defined(linux)
 static int check_sctp_remote_error(struct sctp_remote_error_expr *expr,
 				   struct sctp_remote_error *sctp_event,
@@ -3602,6 +3633,12 @@ static int check_sctp_notification(struct iovec *iov,
 						    error))
 				return STATUS_ERR;
 			break;
+		case EXPR_SCTP_PADDR_CHANGE:
+			if (check_sctp_paddr_change(script_iov_base->value.sctp_paddr_change,
+						    (struct sctp_paddr_change *) iov[i].iov_base,
+						    error))
+				return STATUS_ERR;
+			break;
 		case EXPR_SCTP_REMOTE_ERROR:
 			if (check_sctp_remote_error(script_iov_base->value.sctp_remote_error,
 						    (struct sctp_remote_error *) iov[i].iov_base,
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index a48423c5..35979f33 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -87,6 +87,7 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_SCTP_NXTINFO,         "sctp_nxtinfo"    },
 	{ EXPR_SCTP_RECVV_RN,        "sctp_recvv_rn "  },
 	{ EXPR_SCTP_ASSOC_CHANGE,    "sctp_assoc_change"},
+	{ EXPR_SCTP_PADDR_CHANGE,    "sctp_paddr_change"},
 	{ EXPR_SCTP_REMOTE_ERROR,    "sctp_remote_error"},
 	{ EXPR_SCTP_SHUTDOWN_EVENT,  "sctp_shutdown_event"},
 	{ EXPR_SCTP_PDAPI_EVENT,     "sctp_pdapi_event"},
@@ -447,6 +448,15 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_assoc_change->sac_assoc_id);
 		free_expression(expression->value.sctp_assoc_change->sac_info);
 		break;
+	case EXPR_SCTP_PADDR_CHANGE:
+		free_expression(expression->value.sctp_paddr_change->spc_type);
+		free_expression(expression->value.sctp_paddr_change->spc_flags);
+		free_expression(expression->value.sctp_paddr_change->spc_length);
+		free_expression(expression->value.sctp_paddr_change->spc_aaddr);
+		free_expression(expression->value.sctp_paddr_change->spc_state);
+		free_expression(expression->value.sctp_paddr_change->spc_error);
+		free_expression(expression->value.sctp_paddr_change->spc_assoc_id);
+		break;
 	case EXPR_SCTP_REMOTE_ERROR:
 		free_expression(expression->value.sctp_remote_error->sre_type);
 		free_expression(expression->value.sctp_remote_error->sre_flags);
@@ -1458,6 +1468,54 @@ static int evaluate_sctp_assoc_change_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_paddr_change_expression(struct expression *in,
+					         struct expression *out,
+						 char **error)
+{
+	struct sctp_paddr_change_expr *in_event;
+	struct sctp_paddr_change_expr *out_event;
+
+	assert(in->type == EXPR_SCTP_PADDR_CHANGE);
+	assert(in->value.sctp_paddr_change);
+	assert(out->type == EXPR_SCTP_PADDR_CHANGE);
+
+	out->value.sctp_paddr_change = calloc(1, sizeof(struct sctp_paddr_change_expr));
+
+	in_event = in->value.sctp_paddr_change;
+	out_event = out->value.sctp_paddr_change;
+
+	if (evaluate(in_event->spc_type,
+		     &out_event->spc_type,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_flags,
+		     &out_event->spc_flags,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_length,
+		     &out_event->spc_length,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_aaddr,
+		     &out_event->spc_aaddr,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_state,
+		     &out_event->spc_state,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_error,
+		     &out_event->spc_error,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_event->spc_assoc_id,
+		     &out_event->spc_assoc_id,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
 static int evaluate_sctp_remote_error_expression(struct expression *in,
 					         struct expression *out,
 						 char **error)
@@ -1798,6 +1856,9 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_ASSOC_CHANGE:
 		result = evaluate_sctp_assoc_change_expression(in, out, error);
 		break;
+	case EXPR_SCTP_PADDR_CHANGE:
+		result = evaluate_sctp_paddr_change_expression(in, out, error);
+		break;
 	case EXPR_SCTP_REMOTE_ERROR:
 		result = evaluate_sctp_remote_error_expression(in, out, error);
 		break;
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index c88287ef..f6f444df 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -67,6 +67,7 @@ enum expression_t {
 	EXPR_SCTP_NXTINFO,        /* struct sctp_nxtinfo for syscall sctp_recvv */
 	EXPR_SCTP_RECVV_RN,       /* struct sctp_recvv_rn for syscall sctp_recvv */
 	EXPR_SCTP_ASSOC_CHANGE,   /* expression tree for sctp_assoc_change_event */
+	EXPR_SCTP_PADDR_CHANGE,   /* expression tree for sctp_peer_addr_change */
 	EXPR_SCTP_REMOTE_ERROR,   /* expression tree for sctp_remote_error_event */
 	EXPR_SCTP_SHUTDOWN_EVENT, /* expression tree for sctp_shutdown_event */
 	EXPR_SCTP_PDAPI_EVENT,    /* expression tree for sctp_partial_delivery_event */
@@ -113,6 +114,7 @@ struct expression {
 		struct sctp_nxtinfo_expr *sctp_nxtinfo;
 		struct sctp_recvv_rn_expr *sctp_recvv_rn;
 		struct sctp_assoc_change_expr *sctp_assoc_change;
+		struct sctp_paddr_change_expr *sctp_paddr_change;
 		struct sctp_remote_error_expr *sctp_remote_error;
 		struct sctp_shutdown_event_expr *sctp_shutdown_event;
 		struct sctp_pdapi_event_expr *sctp_pdapi_event;
@@ -342,7 +344,18 @@ struct sctp_assoc_change_expr {
 	struct expression *sac_info;
 };
 
-/* Parse tree for sctp_assoc_change for notifications. */
+/* Parse tree for sctp_paddr_change for notifications. */
+struct sctp_paddr_change_expr {
+	struct expression *spc_type;
+	struct expression *spc_flags;
+	struct expression *spc_length;
+	struct expression *spc_aaddr;
+	struct expression *spc_state;
+	struct expression *spc_error;
+	struct expression *spc_assoc_id;
+};
+
+/* Parse tree for sctp_remote_error_event for notifications. */
 struct sctp_remote_error_expr {
 	struct expression *sre_type;
 	struct expression *sre_flags;
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index 7437cd12..9b0bc90b 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -217,7 +217,10 @@ struct int_symbol platform_symbols_table[] = {
 	{ SCTP_ASSOC_SUPPORTS_ASCONF,       "SCTP_ASSOC_SUPPORTS_ASCONF"      },
 	{ SCTP_ASSOC_SUPPORTS_MULTIBUF,     "SCTP_ASSOC_SUPPORTS_MULTIBUF"    },
 	{ SCTP_PARTIAL_DELIVERY_ABORTED,    "SCTP_PARTIAL_DELIVERY_ABORTED"   },
-
+	{ SCTP_ADDR_AVAILABLE,              "SCTP_ADDR_AVAILABLE"             },
+	{ SCTP_ADDR_UNREACHABLE,            "SCTP_ADDR_UNREACHABLE"           },
+	{ SCTP_ADDR_REMOVED,                "SCTP_ADDR_REMOVED"               },
+	{ SCTP_ADDR_MADE_PRIM,              "SCTP_ADDR_MADE_PRIM"             },
 	/* /usr/include/netinet/tcp.h */
 	{ TCP_NODELAY,                      "TCP_NODELAY"                     },
 	{ TCP_MAXSEG,                       "TCP_MAXSEG"                      },
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_partial_delivery_event.pkt b/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_partial_delivery_event.pkt
index 566aeeca..b1c619e8 100644
--- a/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_partial_delivery_event.pkt
+++ b/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_partial_delivery_event.pkt
@@ -9,13 +9,12 @@
 +0.1 < sctp: INIT_ACK[flgs=0, tag=2, a_rwnd=1500, os=1, is=1, 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 write(3, ..., 3000) = 3000
-*    > sctp: DATA[flgs=B, len=1468, tsn=1, sid=0, ssn=0, ppid=0]
-+0.0 < sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=1500, gaps=[], dups=[]]
 
-*    > sctp: DATA[flgs=0, len=1468, tsn=2, sid=0, ssn=0, ppid=0]
-//+0.0 < sctp: ABORT[flgs=0, USER_INITIATED_ABORT[info="Testing"]]
-
-+0.0 sctp_recvv(3, [{iov_base=..., iov_len=1000}, {iov_base={pdapi_type=SCTP_PARTIAL_DELIVERY_EVENT, pdapi_flags=0,pdapi_length=24,
++0.0 < sctp: DATA[flgs=B, len=300, tsn=1, sid=0, ssn=0, ppid=0]
++0.0 > sctp: SACK[flgs=0, cum_tsn=1, a_rwnd=..., gaps=[], dups=[]]
++0.0 < sctp: DATA[flgs=0, len=300, tsn=2, sid=0, ssn=0, ppid=0]
+*    > sctp: SACK[flgs=0, cum_tsn=2, a_rwnd=..., gaps=[], dups=[]]
++0.0 < sctp: ABORT[flgs=0]
++1.5 sctp_recvv(3, [{iov_base=..., iov_len=1000}, {iov_base={pdapi_type=SCTP_PARTIAL_DELIVERY_EVENT, pdapi_flags=0,pdapi_length=24,
 pdapi_indication=SCTP_PARTIAL_DELIVERY_ABORTED, pdapi_stream=0, pdapi_seq=0, pdapi_assoc_id=3 }, iov_len=1000}], 2, ..., 20, NULL, [0],
 [SCTP_RECVV_NOINFO], [MSG_NOTIFICATION|MSG_EOR]) = 21
diff --git a/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_peer_addr_change.pkt b/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_peer_addr_change.pkt
new file mode 100644
index 00000000..0c7069b8
--- /dev/null
+++ b/gtests/net/packetdrill/tests/bsd/sctp/notifications/sctp_peer_addr_change.pkt
@@ -0,0 +1,15 @@
++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
+// Check the handshake with an empty(!) cookie
++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
++0.0 setsockopt(3, IPPROTO_SCTP, SCTP_EVENT, {se_type=SCTP_PEER_ADDR_CHANGE, se_on=1}, 8) = 0
++0.0 getsockopt(3, IPPROTO_SCTP, SCTP_EVENT, {se_type=SCTP_PEER_ADDR_CHANGE, se_on=1}, [8]) = 0
++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=1, is=1, 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 sctp_recvv(3, [{iov_base={spc_type=SCTP_PEER_ADDR_CHANGE, spc_flags=0, spc_length=21, spc_aaddr=..., spc_state=SCTP_ADDR_MADE_PRIM, spc_error=0,
+ spc_assoc_id=3}, iov_len=1000}], 1, ..., 20, NULL, [0], [SCTP_RECVV_NOINFO],
+[MSG_NOTIFICATION|MSG_EOR]) = 21
-- 
GitLab