[PATCH 1/1] selinux-testsuite: Update SCTP asconf client/server

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Reviewed the tests using kernel tree: Documentation/security/SCTP.rst, this
added one additional test.

The main changes have been to sctp_asconf_params_client.c and
sctp_asconf_params_server.c to support the tests, and also to make them
more reliable for running the client and server on different systems.

Updated common code in sctp_common.c for sctp event handling and updated
relevant programs to use handle_event()

Removed obsolete code/policy.

Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
---
 policy/test_sctp.te                    |  75 ++++-
 tests/sctp/.gitignore                  |   1 -
 tests/sctp/Makefile                    |   3 +-
 tests/sctp/sctp_asconf_params_client.c | 322 ++++++++-----------
 tests/sctp/sctp_asconf_params_server.c | 275 +++++++++-------
 tests/sctp/sctp_common.c               | 189 ++++++++++-
 tests/sctp/sctp_common.h               |  12 +-
 tests/sctp/sctp_peeloff_server.c       |  42 +--
 tests/sctp/sctp_server.c               |   4 +-
 tests/sctp/sctp_set_peer_addr.c        | 415 -------------------------
 tests/sctp/test                        |  70 ++++-
 11 files changed, 623 insertions(+), 785 deletions(-)
 delete mode 100644 tests/sctp/sctp_set_peer_addr.c

diff --git a/policy/test_sctp.te b/policy/test_sctp.te
index 5f6553f..793f451 100644
--- a/policy/test_sctp.te
+++ b/policy/test_sctp.te
@@ -168,19 +168,68 @@ corenet_sctp_bind_all_nodes(test_sctp_deny_bindx_t)
 corenet_inout_generic_node(test_sctp_deny_bindx_t)
 
 #
-########## SET_PRI_ADDR + SET_PEER ADDR for ASCONF process testing ##########
-#
-type test_sctp_set_peer_addr_t;
-domain_type(test_sctp_set_peer_addr_t)
-unconfined_runs_test(test_sctp_set_peer_addr_t)
-typeattribute test_sctp_set_peer_addr_t testdomain;
-typeattribute test_sctp_set_peer_addr_t sctpsocketdomain;
-allow test_sctp_set_peer_addr_t self:sctp_socket create_stream_socket_perms;
-allow test_sctp_server_t test_sctp_set_peer_addr_t:peer { recv };
-allow test_sctp_set_peer_addr_t test_sctp_server_t:peer { recv };
-corenet_sctp_bind_all_nodes(test_sctp_set_peer_addr_t)
-corenet_inout_generic_node(test_sctp_set_peer_addr_t)
-corenet_inout_generic_if(test_sctp_set_peer_addr_t)
+############################# ASCONF Server ##############################
+#
+type sctp_asconf_params_server_t;
+domain_type(sctp_asconf_params_server_t)
+unconfined_runs_test(sctp_asconf_params_server_t)
+typeattribute sctp_asconf_params_server_t testdomain;
+typeattribute sctp_asconf_params_server_t sctpsocketdomain;
+allow sctp_asconf_params_server_t self:sctp_socket { create listen bind ioctl read getattr write getopt setopt };
+corenet_sctp_bind_all_nodes(sctp_asconf_params_server_t)
+corenet_inout_generic_node(sctp_asconf_params_server_t)
+
+#
+############################# ASCONF Client ##############################
+#
+type sctp_asconf_params_client_t;
+domain_type(sctp_asconf_params_client_t)
+unconfined_runs_test(sctp_asconf_params_client_t)
+typeattribute sctp_asconf_params_client_t testdomain;
+typeattribute sctp_asconf_params_client_t sctpsocketdomain;
+allow sctp_asconf_params_client_t self:sctp_socket { create connect ioctl read getattr write getopt setopt };
+corenet_inout_generic_node(sctp_asconf_params_client_t)
+corenet_sctp_bind_generic_node(sctp_asconf_params_client_t)
+corenet_inout_generic_if(sctp_asconf_params_client_t)
+
+# When running locally need this rule, else Client error 'Dynamic Address Reconfiguration'
+allow sctp_asconf_params_server_t sctp_asconf_params_client_t:sctp_socket { connect };
+# net/sctp/socket.c sctp_setsockopt_peer_primary_addr(setsockopt(SCTP_PRIMARY_ADDR))
+allow sctp_asconf_params_client_t self:sctp_socket { bind };
+
+#
+################## ASCONF Client - Deny SCTP_PRIMARY_ADDR ####################
+#
+type sctp_asconf_deny_pri_addr_client_t;
+domain_type(sctp_asconf_deny_pri_addr_client_t)
+unconfined_runs_test(sctp_asconf_deny_pri_addr_client_t)
+typeattribute sctp_asconf_deny_pri_addr_client_t testdomain;
+typeattribute sctp_asconf_deny_pri_addr_client_t sctpsocketdomain;
+allow sctp_asconf_deny_pri_addr_client_t self:sctp_socket { create connect ioctl read getattr write getopt setopt };
+corenet_inout_generic_node(sctp_asconf_deny_pri_addr_client_t)
+corenet_sctp_bind_generic_node(sctp_asconf_deny_pri_addr_client_t)
+corenet_inout_generic_if(sctp_asconf_deny_pri_addr_client_t)
+
+# net/sctp/sm_make_chunk.c sctp_process_asconf_param() SCTP_PARAM_ADD_IP and SCTP_PARAM_SET_PRIMARY
+allow sctp_asconf_params_server_t sctp_asconf_deny_pri_addr_client_t:sctp_socket { connect };
+# net/sctp/socket.c sctp_setsockopt_primary_addr() SCTP_PRIMARY_ADDR
+# neverallow sctp_asconf_deny_pri_addr_client_t self:sctp_socket { bind };
+
+#
+### ASCONF Client - Deny Server SCTP_PARAM_ADD_IP / SCTP_PARAM_SET_PRIMARY ###
+#
+type sctp_asconf_deny_param_add_client_t;
+domain_type(sctp_asconf_deny_param_add_client_t)
+unconfined_runs_test(sctp_asconf_deny_param_add_client_t)
+typeattribute sctp_asconf_deny_param_add_client_t testdomain;
+typeattribute sctp_asconf_deny_param_add_client_t sctpsocketdomain;
+allow sctp_asconf_deny_param_add_client_t self:sctp_socket { create connect ioctl read getattr write getopt setopt };
+corenet_inout_generic_node(sctp_asconf_deny_param_add_client_t)
+corenet_sctp_bind_generic_node(sctp_asconf_deny_param_add_client_t)
+corenet_inout_generic_if(sctp_asconf_deny_param_add_client_t)
+
+# net/sctp/sm_make_chunk.c sctp_process_asconf_param() SCTP_PARAM_ADD_IP and SCTP_PARAM_SET_PRIMARY
+# neverallow sctp_asconf_params_server_t sctp_asconf_deny_param_add_client_t:sctp_socket { connect };
 
 #
 ######################### SECMARK-specific policy ############################
diff --git a/tests/sctp/.gitignore b/tests/sctp/.gitignore
index 8671c27..c022b11 100644
--- a/tests/sctp/.gitignore
+++ b/tests/sctp/.gitignore
@@ -6,4 +6,3 @@ sctp_client
 sctp_connectx
 sctp_peeloff_server
 sctp_server
-sctp_set_peer_addr
diff --git a/tests/sctp/Makefile b/tests/sctp/Makefile
index f5dfdae..fbe8fb0 100644
--- a/tests/sctp/Makefile
+++ b/tests/sctp/Makefile
@@ -1,4 +1,5 @@
-TARGETS = sctp_client sctp_server sctp_bind sctp_bindx sctp_connectx sctp_set_peer_addr sctp_asconf_params_client sctp_asconf_params_server sctp_peeloff_server
+TARGETS = sctp_client sctp_server sctp_bind sctp_bindx sctp_connectx \
+sctp_asconf_params_client sctp_asconf_params_server sctp_peeloff_server
 
 DEPS = sctp_common.c sctp_common.h
 CFLAGS ?= -Wall
diff --git a/tests/sctp/sctp_asconf_params_client.c b/tests/sctp/sctp_asconf_params_client.c
index 12522f3..ada5982 100644
--- a/tests/sctp/sctp_asconf_params_client.c
+++ b/tests/sctp/sctp_asconf_params_client.c
@@ -29,11 +29,9 @@
 static void usage(char *progname)
 {
 	fprintf(stderr,
-		"usage:  %s [-v] [-n] addr port\n"
+		"usage:  %s [-v] addr port\n"
 		"\nWhere:\n\t"
 		"-v     Print status information.\n\t"
-		"-n     No bindx_rem will be received from server. This happens\n\t"
-		"       when the client and server are on different systems.\n\t"
 		"addr   IPv4 or IPv6 address (MUST NOT be loopback address).\n\t"
 		"port   port.\n", progname);
 
@@ -46,75 +44,90 @@ static void usage(char *progname)
 	exit(1);
 }
 
-static int peer_count, peer_count_err;
-static void getpaddrs_alarm(int sig)
-{
-	fprintf(stderr,
-		"Get peer address count timer expired - carry on test\n");
-	peer_count += 1;
-	peer_count_err = true;
-}
-
-static void getprimaddr_alarm(int sig)
-{
-	fprintf(stderr, "Get primary address timer expired - end test.\n");
-	exit(1);
-}
-
-static void get_primaddr(char *addr_buf, int socket)
+static int get_set_primaddr(int socket, sctp_assoc_t id, bool verbose)
 {
 	int result;
-	struct sctp_prim prim;
-	struct sockaddr_in *in_addr;
-	struct sockaddr_in6 *in6_addr;
-	struct sockaddr *paddr;
+	struct sctp_prim prim;	/* Defined in linux/sctp.t */
 	socklen_t prim_len;
-	const char *addr_ptr = NULL;
+
+	/*
+	 * At this point the new primary address is already set. To test the
+	 * bind permission, just reset the address.
+	 */
 
 	memset(&prim, 0, sizeof(struct sctp_prim));
 	prim_len = sizeof(struct sctp_prim);
+	prim.ssp_assoc_id = id;
 
 	result = getsockopt(socket, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
 			    &prim, &prim_len);
 	if (result < 0) {
-		perror("getsockopt: SCTP_PRIMARY_ADDR");
-		exit(1);
+		perror("Client getsockopt: SCTP_PRIMARY_ADDR");
+		return 50;
 	}
 
-	paddr = (struct sockaddr *)&prim.ssp_addr;
-	if (paddr->sa_family == AF_INET) {
-		in_addr = (struct sockaddr_in *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET, &in_addr->sin_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
-	} else if (paddr->sa_family == AF_INET6) {
-		in6_addr = (struct sockaddr_in6 *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET6, &in6_addr->sin6_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
+	if (verbose)
+		print_addr_info((struct sockaddr *)&prim.ssp_addr,
+				"Client getsockopt - SCTP_PRIMARY_ADDR:");
+
+	/*
+	 * If the new primary address is an IPv6 local link address, it will
+	 * have been received by the DAR process with a scope id of '0'.
+	 * Therefore when the setsockopt is called it will error with EINVAL.
+	 * To resolve this set scope_id=1 (first link) before the call.
+	 */
+	struct sockaddr_in6 *addr6;
+	struct sockaddr *sin;
+
+	sin = (struct sockaddr *)&prim.ssp_addr;
+
+	if (sin->sa_family == AF_INET6) {
+		addr6 = (struct sockaddr_in6 *)sin;
+		if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr) &&
+		    ((struct sockaddr_in6 *)addr6)->sin6_scope_id == 0) {
+			((struct sockaddr_in6 *)addr6)->sin6_scope_id = 1;
+			if (verbose)
+				printf("Client set new Local Link primary address scope_id=1\n");
+		}
 	}
-	if (!addr_ptr) {
-		perror("inet_ntop");
-		exit(1);
+
+	/*
+	 * This tests the net/sctp/socket.c sctp_setsockopt_primary_addr()
+	 * SCTP_PRIMARY_ADDR function by setting policy to:
+	 *   allow sctp_asconf_params_client_t self:sctp_socket { bind };
+	 * or:
+	 *   neverallow sctp_asconf_params_client_t self:sctp_socket { bind };
+	 */
+	result = setsockopt(socket, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
+			    &prim, prim_len);
+	if (result < 0) {
+		perror("Client setsockopt: SCTP_PRIMARY_ADDR");
+		return 51;
 	}
+	if (verbose)
+		print_addr_info((struct sockaddr *)&prim.ssp_addr,
+				"Client set SCTP_PRIMARY_ADDR to:");
+	return 0;
 }
 
 int main(int argc, char **argv)
 {
-	int opt, client_sock, result, len;
+	int opt, client_sock, result, flags = 0, on = 1;
 	struct addrinfo client_hints, *client_res;
-	struct sockaddr *paddrs;
-	bool verbose = false, no_bindx_rem = false;
-	char client_prim_addr1[INET6_ADDRSTRLEN];
-	char client_prim_addr2[INET6_ADDRSTRLEN];
-	char buffer[1024];
-
-	while ((opt = getopt(argc, argv, "vn")) != -1) {
+	struct sctp_sndrcvinfo sinfo;
+	struct sockaddr_storage sin;
+	socklen_t sinlen = sizeof(sin);
+	struct timeval tm;
+	bool verbose = false;
+	char buffer[512];
+	char msg[] = "Send peer address";
+	char *rcv_new_addr_buf = NULL;
+
+	while ((opt = getopt(argc, argv, "v")) != -1) {
 		switch (opt) {
 		case 'v':
 			verbose = true;
 			break;
-		case 'n':
-			no_bindx_rem = true;
-			break;
 		default:
 			usage(argv[0]);
 		}
@@ -123,176 +136,117 @@ int main(int argc, char **argv)
 	if ((argc - optind) != 2)
 		usage(argv[0]);
 
-	/* Set up client side and connect */
 	memset(&client_hints, 0, sizeof(struct addrinfo));
-	client_hints.ai_socktype = SOCK_STREAM;
+	client_hints.ai_socktype = SOCK_SEQPACKET;
 	client_hints.ai_protocol = IPPROTO_SCTP;
 	result = getaddrinfo(argv[optind], argv[optind + 1],
 			     &client_hints, &client_res);
 	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - client: %s\n",
+		fprintf(stderr, "Client getaddrinfo err: %s\n",
 			gai_strerror(result));
 		exit(1);
 	}
 
-
-	/* printf("Client scopeID: %d\n",
-	 *        ((struct sockaddr_in6 *)client_res->ai_addr)->sin6_scope_id);
-	 */
-
 	client_sock = socket(client_res->ai_family, client_res->ai_socktype,
 			     client_res->ai_protocol);
 	if (client_sock < 0) {
-		perror("socket");
+		perror("Client socket");
+		freeaddrinfo(client_res);
 		exit(1);
 	}
 
-	result = connect(client_sock, client_res->ai_addr,
-			 client_res->ai_addrlen);
+	/* Need to set a timeout if no reply from server */
+	memset(&tm, 0, sizeof(struct timeval));
+	tm.tv_sec = 1;
+	result = setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm));
 	if (result < 0) {
-		if (errno != EINPROGRESS)
-			perror("connect");
-		else
-			fprintf(stderr, "connect timeout\n");
-
-		close(client_sock);
-		exit(1);
+		perror("Client setsockopt: SO_RCVTIMEO");
+		goto err1;
 	}
 
-	/* Get number of peer addresses on CLIENT (should be 1) for a check
-	 * later as sctp_bindx SERVER -> CLIENT is non-blocking.
-	 */
-	peer_count = sctp_getpaddrs(client_sock, 0, &paddrs);
-	sctp_freepaddrs(paddrs);
-	len = sprintf(buffer, "Client peer address count: %d", peer_count);
-	if (verbose)
-		printf("%s\n", buffer);
-
-
-	/* Get initial CLIENT primary address (that should be ADDR1). */
-	get_primaddr(client_prim_addr1, client_sock);
-
-	/* server waiting for write before sending BINDX_ADD */
-	result = write(client_sock, buffer, len);
+	result = setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm));
 	if (result < 0) {
-		perror("write");
-		close(client_sock);
-		exit(1);
+		perror("Client setsockopt: SO_SNDVTIMEO");
+		goto err1;
 	}
 
-	/* Sleep a while as server pings us the new address */
-	sleep(1);
-	/* then set an alarm and check number of peer addresses for CLIENT */
-	signal(SIGALRM, getpaddrs_alarm);
-	alarm(2);
-	peer_count_err = false;
-	result = 0;
-
-	while (result != peer_count + 1) {
-		result = sctp_getpaddrs(client_sock, 0, &paddrs);
-		if (result > 0)
-			sctp_freepaddrs(paddrs);
-
-		if (peer_count_err)
-			break;
+	result = connect(client_sock, client_res->ai_addr,
+			 client_res->ai_addrlen);
+	if (result < 0) {
+		perror("Client connect");
+		goto err1;
 	}
-	alarm(0);
-	peer_count = result;
-
-	len = sprintf(buffer, "Client peer address count: %d", result);
-	if (verbose)
-		printf("%s\n", buffer);
 
-	/* server waiting for write before send SCTP_SET_PEER_PRIMARY_ADDR */
-	result = write(client_sock, buffer, len);
+	result = set_subscr_events(client_sock, on, on, on, on);
 	if (result < 0) {
-		perror("write");
-		close(client_sock);
-		exit(1);
+		perror("Client setsockopt SCTP_EVENTS");
+		goto err1;
 	}
 
-	/* Now get the new primary address from the client */
-	signal(SIGALRM, getprimaddr_alarm);
-	alarm(2);
-	memcpy(client_prim_addr2, client_prim_addr1, INET6_ADDRSTRLEN);
-
-	while (!strcmp(client_prim_addr1, client_prim_addr2))
-		get_primaddr(client_prim_addr2, client_sock);
-
-	alarm(0);
-	len = sprintf(buffer,
-		      "Client initial SCTP_PRIMARY_ADDR: %s\nClient current SCTP_PRIMARY_ADDR: %s",
-		      client_prim_addr1, client_prim_addr2);
-	if (verbose)
-		printf("%s\n", buffer);
+	/* Send msg to form an association with server */
+	result = sctp_sendmsg(client_sock, msg, sizeof(msg),
+			      client_res->ai_addr,
+			      client_res->ai_addrlen,
+			      0, 0, 0, 0, 0);
+	if (result < 0) {
+		perror("Client sctp_sendmsg-1");
+		goto err1;
+	}
 
-	if (!no_bindx_rem) {
-		/* Let server send bindx_rem */
-		result = write(client_sock, buffer, len);
-		if (result < 0) {
-			perror("write");
-			close(client_sock);
-			exit(1);
+	rcv_new_addr_buf = NULL;
+	memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+	/*
+	 * Should receive notifications for initial addr change, then
+	 * the address to match against from the server, then change to new
+	 * peer addr and finally exit.
+	 */
+	while (1) {
+		memset(buffer, 0, sizeof(buffer));
+
+		result = sctp_recvmsg(client_sock, buffer,
+				      sizeof(buffer),
+				      (struct sockaddr *)&sin,
+				      &sinlen, &sinfo, &flags);
+		if (result < 0 && errno == EAGAIN) {
+			result = EAGAIN;
+			fprintf(stderr, "Client error 'Dynamic Address Reconfiguration'\n");
+			goto end;
+		} else if (result < 0) {
+			perror("Client sctp_recvmsg-1");
+			goto err1;
 		}
 
-		/* Then delete addr that checks ASCONF - SCTP_PARAM_DEL_IP */
-		if (!peer_count_err) {
-			signal(SIGALRM, getprimaddr_alarm);
-			alarm(2);
-			result = 0;
-			while (result != peer_count - 1) {
-				result = sctp_getpaddrs(client_sock,
-							0, &paddrs);
-				if (result > 0)
-					sctp_freepaddrs(paddrs);
-
-				if (peer_count_err)
-					break;
-			}
-			alarm(0);
-			sprintf(buffer, "Client peer address count: %d",
-				result);
+		if (sinfo.sinfo_assoc_id) {
 			if (verbose)
-				printf("%s\n", buffer);
+				printf("Client assoc_id: %d\n",
+				       sinfo.sinfo_assoc_id);
 		}
-	}
-
-	/* server waiting for client peer address count */
-	result = write(client_sock, buffer, len);
-	if (result < 0) {
-		perror("write");
-		close(client_sock);
-		exit(1);
-	}
-
-	/* Compare the client primary addresses, they should be different. */
-	if (!strcmp(client_prim_addr1, client_prim_addr2)) {
-		len = sprintf(buffer,
-			      "Client ADDR1: %s same as ADDR2: %s - SCTP_SET_PEER_PRIMARY_ADDR failed",
-			      client_prim_addr1, client_prim_addr2);
-		fprintf(stderr, "%s\n", buffer);
+		if (flags & MSG_NOTIFICATION && flags & MSG_EOR) {
+			result = handle_event(buffer, rcv_new_addr_buf,
+					      NULL, verbose, "Client");
+			if (result == EVENT_ADDR_MATCH) /* Have new primary addr */
+				break;
+		} else { /* Should receive only one buffer from server */
+			if (verbose)
+				printf("Client received new pri addr: %s\n",
+				       buffer);
 
-		/* server waiting for write to finish */
-		result = write(client_sock, buffer, len);
-		if (result < 0) {
-			perror("write");
-			close(client_sock);
+			rcv_new_addr_buf = strdup(buffer);
 		}
-		exit(1);
 	}
 
-	len = sprintf(buffer, "Client primary address changed successfully\n");
-	if (verbose)
-		printf("%s\n", buffer);
-
-	/* server waiting for write to finish */
-	result = write(client_sock, buffer, len);
-	if (result < 0) {
-		perror("write");
-		close(client_sock);
-		exit(1);
-	}
+	/* Get new CLIENT primary address */
+	result = get_set_primaddr(client_sock, sinfo.sinfo_assoc_id, verbose);
+	if (result > 0)
+		goto end;
 
+	result = 0;
+end:
 	close(client_sock);
-	exit(0);
+	freeaddrinfo(client_res);
+	free(rcv_new_addr_buf);
+	return result;
+err1:
+	result = -1;
+	goto end;
 }
diff --git a/tests/sctp/sctp_asconf_params_server.c b/tests/sctp/sctp_asconf_params_server.c
index ff7473b..18e2cb2 100644
--- a/tests/sctp/sctp_asconf_params_server.c
+++ b/tests/sctp/sctp_asconf_params_server.c
@@ -22,16 +22,18 @@ static void usage(char *progname)
 
 int main(int argc, char **argv)
 {
-	int opt, srv_sock, new_sock, result, on = 1;
+	int opt, srv_sock, result, on = 1, flags = 0, if_index = 0;
+	size_t new_len;
 	struct addrinfo srv_hints, *srv_res;
 	struct addrinfo *new_pri_addr_res;
 	struct sockaddr *sa_ptr;
-	socklen_t sinlen;
 	struct sockaddr_storage sin;
+	socklen_t sinlen = sizeof(sin);
 	struct sctp_setpeerprim setpeerprim;
+	struct sctp_sndrcvinfo sinfo;
 	bool verbose = false, is_ipv6 = false;
-	char buffer[128];
-	char *flag_file = NULL;
+	char buffer[512];
+	char *flag_file = NULL, *ptr, if_name[30], *new_pri_addr = NULL;
 
 	while ((opt = getopt(argc, argv, "f:v")) != -1) {
 		switch (opt) {
@@ -62,191 +64,230 @@ int main(int argc, char **argv)
 
 	memset(&srv_hints, 0, sizeof(struct addrinfo));
 	srv_hints.ai_flags = AI_PASSIVE;
-	srv_hints.ai_socktype = SOCK_STREAM;
+	srv_hints.ai_socktype = SOCK_SEQPACKET;
 	srv_hints.ai_protocol = IPPROTO_SCTP;
 
-	/* Set up server side */
+	/*
+	 * Setup the 2nd address for sending to client.
+	 */
+	/* If local link, get if_name & if_index */
+	if (is_ipv6) {
+		ptr = strpbrk(argv[optind], "%");
+		if (ptr) {
+			strcpy(if_name, ptr + 1);
+			if_index = if_nametoindex(if_name);
+			if (!if_index) {
+				perror("Server if_nametoindex");
+				exit(1);
+			}
+			if (verbose)
+				printf("if_name: %s if_index: %d\n",
+				       if_name, if_index);
+		}
+	}
+
+	/* Now remove % if on new peer */
+	new_len = strcspn(argv[optind + 1], "%");
+	new_pri_addr = strndup(argv[optind + 1], new_len);
+
+	/*
+	 * Use the 1st address for server side setup.
+	 */
 	result = getaddrinfo(argv[optind], argv[optind + 2],
 			     &srv_hints, &srv_res);
 	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - server: %s\n",
+		fprintf(stderr, "Server getaddrinfo err: %s\n",
 			gai_strerror(result));
 		exit(1);
 	}
 	if (is_ipv6 && verbose)
-		printf("Server scopeID: %d\n",
+		printf("Server address: %s has scopeID: %d\n", argv[optind],
 		       ((struct sockaddr_in6 *)
 			srv_res->ai_addr)->sin6_scope_id);
 
 	srv_sock = socket(srv_res->ai_family, srv_res->ai_socktype,
 			  srv_res->ai_protocol);
 	if (srv_sock < 0) {
-		perror("socket - server");
+		perror("Server socket");
+		freeaddrinfo(srv_res);
 		exit(1);
 	}
 
 	result = setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &on,
 			    sizeof(on));
 	if (result < 0) {
-		perror("setsockopt: SO_REUSEADDR");
-		close(srv_sock);
-		exit(1);
+		perror("Server setsockopt: SO_REUSEADDR");
+		goto err1;
 	}
 
-	result = bind(srv_sock, srv_res->ai_addr, srv_res->ai_addrlen);
+	result = sctp_bindx(srv_sock, srv_res->ai_addr, 1, SCTP_BINDX_ADD_ADDR);
 	if (result < 0) {
-		perror("bind");
-		close(srv_sock);
-		exit(1);
+		perror("Server bind");
+		goto err1;
 	}
 
-	listen(srv_sock, 1);
+	listen(srv_sock, SOMAXCONN);
 
 	if (flag_file) {
 		FILE *f = fopen(flag_file, "w");
 		if (!f) {
-			perror("Flag file open");
-			exit(1);
+			perror("Server Flag file open");
+			goto err1;
 		}
 		fprintf(f, "listening\n");
 		fclose(f);
 	}
 
-	sinlen = sizeof(sin);
-	new_sock = accept(srv_sock, (struct sockaddr *)&sin, &sinlen);
-	if (new_sock < 0) {
-		perror("accept");
-		result = 1;
-		goto err2;
-	}
-
-	/* This waits for a client message before continuing. */
-	result = read(new_sock, &buffer, sizeof(buffer));
-	if (result < 0) {
-		perror("read");
-		exit(1);
-	}
-	buffer[result] = 0;
-	if (verbose)
-		printf("%s\n", buffer);
-
-	/* Obtain address info for the BINDX_ADD and new SCTP_PRIMARY_ADDR. */
-	result = getaddrinfo(argv[optind + 1], argv[optind + 2],
-			     &srv_hints, &new_pri_addr_res);
+	result = set_subscr_events(srv_sock, on, on, on, on);
 	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - new SCTP_PRIMARY_ADDR: %s\n",
-			gai_strerror(result));
-		close(srv_sock);
-		exit(1);
+		perror("Server setsockopt SCTP_EVENTS");
+		goto err1;
 	}
-	if (is_ipv6 && verbose)
-		printf("new_pri_addr scopeID: %d\n",
-		       ((struct sockaddr_in6 *)
-			new_pri_addr_res->ai_addr)->sin6_scope_id);
-
 
-	/* Now call sctp_bindx to add ADDR2, this will cause an
-	 * ASCONF - SCTP_PARAM_ADD_IP chunk to be sent to the CLIENT.
-	 * This is non-blocking so there maybe a delay before the CLIENT
-	 * receives the asconf chunk.
+	/*
+	 * Receive notifications for initial addr changes, then a request for
+	 * the 'new_pri_addr' from the client.
 	 */
-	if (verbose)
-		printf("Calling sctp_bindx ADD: %s\n", argv[optind + 1]);
-
-	result = sctp_bindx(new_sock,
-			    (struct sockaddr *)new_pri_addr_res->ai_addr,
-			    1, SCTP_BINDX_ADD_ADDR);
-	if (result < 0) {
-		if (errno == EACCES) {
-			perror("sctp_bindx ADD");
-		} else {
-			perror("sctp_bindx ADD");
-			result = 1;
+	memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+	while (1) {
+		result = sctp_recvmsg(srv_sock, buffer, sizeof(buffer),
+				      (struct sockaddr *)&sin, &sinlen,
+				      &sinfo, &flags);
+		if (result < 0) {
+			perror("Server sctp_recvmsg-1");
 			goto err1;
 		}
-	}
 
-	/* This waits for a client message before continuing. */
-	result = read(new_sock, &buffer, sizeof(buffer));
-	if (result < 0) {
-		perror("read");
-		exit(1);
+		if (flags & MSG_NOTIFICATION && flags & MSG_EOR) {
+			result = handle_event(buffer, NULL, NULL, verbose,
+					      "Server");
+			if (result == EVENT_SHUTDOWN)
+				goto err1;
+		} else {
+			if (verbose)
+				printf("Server received: %s\n", buffer);
+			break;
+		}
 	}
-	buffer[result] = 0;
-	if (verbose)
-		printf("%s\n", buffer);
 
-	/* Now that the CLIENT has the new primary address ensure they use
-	 * it by SCTP_SET_PEER_PRIMARY_ADDR.
+	/*
+	 * Request the peer sets the 1st cmd line address as peer primary.
+	 * This uses Dynamic Address Reconfiguration by sending an asconf
+	 * chunk with SCTP_PARAM_SET_PRIMARY set to the client.
 	 */
 	memset(&setpeerprim, 0, sizeof(struct sctp_setpeerprim));
+	setpeerprim.sspp_assoc_id = sinfo.sinfo_assoc_id;
 	sa_ptr = (struct sockaddr *)&setpeerprim.sspp_addr;
 	if (is_ipv6)
-		memcpy(sa_ptr, new_pri_addr_res->ai_addr,
+		memcpy(sa_ptr, srv_res->ai_addr,
 		       sizeof(struct sockaddr_in6));
 	else
-		memcpy(sa_ptr, new_pri_addr_res->ai_addr,
+		memcpy(sa_ptr, srv_res->ai_addr,
 		       sizeof(struct sockaddr_in));
 
+	result = setsockopt(srv_sock, IPPROTO_SCTP,
+			    SCTP_SET_PEER_PRIMARY_ADDR,
+			    &setpeerprim,
+			    sizeof(struct sctp_setpeerprim));
+	if (result < 0) {
+		perror("Server setsockopt: SCTP_SET_PEER_PRIMARY_ADDR");
+		goto err1;
+	}
 	if (verbose)
-		printf("Calling setsockopt SCTP_SET_PEER_PRIMARY_ADDR: %s\n",
+		printf("Server setsockopt: SCTP_SET_PEER_PRIMARY_ADDR with:\n\t%s\n",
 		       argv[optind + 1]);
 
-	result = setsockopt(new_sock, IPPROTO_SCTP,
-			    SCTP_SET_PEER_PRIMARY_ADDR,
-			    &setpeerprim, sizeof(struct sctp_setpeerprim));
+	if (sin.ss_family == AF_INET6) /* Set scope_id for local link */
+		((struct sockaddr_in6 *)&sin)->sin6_scope_id = if_index;
+
+	/* Send client the new primary address */
+	result = sctp_sendmsg(srv_sock, new_pri_addr, new_len,
+			      (struct sockaddr *)&sin,
+			      sinlen, 0, 0, 0, 0, 0);
+	free(new_pri_addr);
 	if (result < 0) {
-		perror("setsockopt: SCTP_SET_PEER_PRIMARY_ADDR");
-		result = 1;
+		perror("Server sctp_sendmsg");
 		goto err1;
 	}
-	/* Sleep a sec to ensure client get info. */
-	result = read(new_sock, &buffer, sizeof(buffer));
+
+	/* Ready the 2nd cmd line address for BINDX_ADD */
+	result = getaddrinfo(argv[optind + 1], argv[optind + 2],
+			     &srv_hints, &new_pri_addr_res);
 	if (result < 0) {
-		perror("read");
-		exit(1);
+		fprintf(stderr, "Server getaddrinfo - new SCTP_PRIMARY_ADDR: %s\n",
+			gai_strerror(result));
+		goto err1;
 	}
-	buffer[result] = 0;
-	if (verbose)
-		printf("%s\n", buffer);
 
-	/* Then delete addr that checks ASCONF - SCTP_PARAM_DEL_IP. */
-	if (verbose)
-		printf("Calling sctp_bindx REM: %s\n", argv[optind]);
+	if (is_ipv6 && verbose)
+		printf("Server new_pri_addr: %s has scopeID: %d\n",
+		       argv[optind + 1],
+		       ((struct sockaddr_in6 *)
+			new_pri_addr_res->ai_addr)->sin6_scope_id);
 
-	result = sctp_bindx(new_sock, (struct sockaddr *)srv_res->ai_addr,
-			    1, SCTP_BINDX_REM_ADDR);
+	/*
+	 * Now call sctp_bindx(3) to add 'new_pri_addr'. This uses Dynamic
+	 * Address Reconfiguration by sending an asconf chunk with
+	 * SCTP_PARAM_ADD_IP set to the client.
+	 */
+	result = sctp_bindx(srv_sock,
+			    (struct sockaddr *)new_pri_addr_res->ai_addr,
+			    1, SCTP_BINDX_ADD_ADDR);
 	if (result < 0) {
-		perror("sctp_bindx - REM");
-		result = 1;
+		perror("Server sctp_bindx ADD");
 		goto err1;
 	}
+	if (verbose)
+		printf("Server sctp_bindx(3) SCTP_BINDX_ADD_ADDR:\n\t%s\n",
+		       argv[optind + 1]);
 
-	result = read(new_sock, &buffer, sizeof(buffer));
-	if (result <= 0) {
-		if (errno != 0)
-			perror("read");
-		result = 1;
+	/* Then delete 'addr' that DAR's - SCTP_PARAM_DEL_IP. */
+	result = sctp_bindx(srv_sock, (struct sockaddr *)srv_res->ai_addr,
+			    1, SCTP_BINDX_REM_ADDR);
+	if (result < 0) {
+		perror("Server sctp_bindx - REM");
 		goto err1;
 	}
-	buffer[result] = 0;
 	if (verbose)
-		printf("%s\n", buffer);
+		printf("Server sctp_bindx(3) SCTP_BINDX_REM_ADDR:\n\t%s\n",
+		       argv[optind]);
 
-	result = read(new_sock, &buffer, sizeof(buffer));
-	if (result < 0) {
-		perror("read");
-		exit(1);
+	/*
+	 * End of test - The 'new_pri_addr' is not used for any sessions as
+	 * the objective was to only exercise Dynamic Address Reconfiguration
+	 * so wait for client to complete before closing otherwise it
+	 * errors with ENOTCONN
+	 */
+	while (1) {
+		result = sctp_recvmsg(srv_sock, buffer, sizeof(buffer),
+				      (struct sockaddr *)&sin, &sinlen,
+				      &sinfo, &flags);
+		if (result < 0) {
+			perror("Server sctp_recvmsg-2");
+			goto err1;
+		}
+
+		if (verbose)
+			printf("Client assoc_id: %d\n", sinfo.sinfo_assoc_id);
+
+		if (flags & MSG_NOTIFICATION) {
+			result = handle_event(buffer, NULL, NULL, verbose, "Server");
+			if (result == EVENT_SHUTDOWN)
+				break;
+		} else {
+			if (verbose)
+				printf("Server received: %s\n", buffer);
+			break;
+		}
 	}
-	buffer[result] = 0;
-	if (verbose)
-		printf("%s\n", buffer);
 
 	result = 0;
-
-err1:
-	close(new_sock);
-err2:
+end:
 	close(srv_sock);
-	exit(result);
+	freeaddrinfo(srv_res);
+	freeaddrinfo(new_pri_addr_res);
+	return result;
+err1:
+	result = -1;
+	goto end;
 }
diff --git a/tests/sctp/sctp_common.c b/tests/sctp/sctp_common.c
index 089af2a..8b65870 100644
--- a/tests/sctp/sctp_common.c
+++ b/tests/sctp/sctp_common.c
@@ -40,6 +40,13 @@ void print_addr_info(struct sockaddr *sin, char *text)
 				  addr_str, INET6_ADDRSTRLEN + 1);
 			printf("%s IPv6->IPv4 MAPPED addr %s\n",
 			       text, addr_str);
+		} else if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) {
+			inet_ntop(sin->sa_family,
+				  (void *)&addr6->sin6_addr,
+				  addr_str, INET6_ADDRSTRLEN + 1);
+			printf("%s IPv6 local link addr %s scope_id %d\n",
+			       text, addr_str,
+			       ((struct sockaddr_in6 *)addr6)->sin6_scope_id);
 		} else {
 			inet_ntop(sin->sa_family,
 				  (void *)&addr6->sin6_addr,
@@ -48,6 +55,9 @@ void print_addr_info(struct sockaddr *sin, char *text)
 			       addr_str);
 		}
 		break;
+	default:
+		printf("%s Unknown IP family %d\n", text, sin->sa_family);
+		break;
 	}
 }
 
@@ -103,13 +113,15 @@ void print_ip_option(int fd, bool ipv4, char *text)
 	}
 }
 
-int set_subscr_events(int fd, int data_io, int association)
+int set_subscr_events(int fd, int data_io, int assoc, int addr, int shutd)
 {
 	struct sctp_event_subscribe subscr_events;
 
 	memset(&subscr_events, 0, sizeof(subscr_events));
 	subscr_events.sctp_data_io_event = data_io;
-	subscr_events.sctp_association_event = association;
+	subscr_events.sctp_association_event = assoc;
+	subscr_events.sctp_address_event = addr;
+	subscr_events.sctp_shutdown_event = shutd;
 
 	/*
 	 * Truncate optlen to just the fields we touch to avoid errors when
@@ -117,5 +129,176 @@ int set_subscr_events(int fd, int data_io, int association)
 	 */
 	return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &subscr_events,
 			  sizeof_up_to(struct sctp_event_subscribe,
-				       sctp_association_event));
+				       sctp_shutdown_event));
+}
+
+/*
+ * Currently only SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE and
+ * SCTP_SHUTDOWN_EVENT are enabled via set_subscr_events().
+ */
+int handle_event(void *buf, char *cmp_addr, sctp_assoc_t *assoc_id,
+		 bool verbose, char *text)
+{
+	union sctp_notification *snp = buf;
+	char addrbuf[INET6_ADDRSTRLEN];
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	const char *ap;
+	struct sctp_paddr_change *spc;
+	struct sctp_assoc_change *sac;
+	struct sctp_remote_error *sre;
+	struct sctp_send_failed *ssf;
+	struct sctp_authkey_event *auth_event;
+
+	switch (snp->sn_header.sn_type) {
+	case SCTP_ASSOC_CHANGE:
+		sac = &snp->sn_assoc_change;
+
+		if (verbose)
+			printf("%s SCTP_ASSOC_CHANGE event for assoc_id: %d ERR: 0x%x\n",
+			       text, sac->sac_assoc_id, sac->sac_error);
+
+		if (assoc_id)
+			*assoc_id = sac->sac_assoc_id;
+		break;
+	case SCTP_PEER_ADDR_CHANGE:
+		spc = &snp->sn_paddr_change;
+
+		if (verbose)
+			/*
+			 * Not all spc_error codes are errors - linux/sctp.h
+			 * (e.g. SCTP_HEARTBEAT_SUCCESS = 0x02)
+			 */
+			printf("%s SCTP_PEER_ADDR_CHANGE event for assoc_id: %d ERR: 0x%x\n",
+			       text, spc->spc_assoc_id, spc->spc_error);
+
+		if (spc->spc_aaddr.ss_family == AF_INET) {
+			sin = (struct sockaddr_in *) &spc->spc_aaddr;
+			ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,
+				       INET6_ADDRSTRLEN);
+		} else {
+			sin6 = (struct sockaddr_in6 *) &spc->spc_aaddr;
+			ap = inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf,
+				       INET6_ADDRSTRLEN);
+		}
+		if (verbose) /* Print additional address details */
+			print_addr_info((struct sockaddr *)&spc->spc_aaddr,
+					"Peer Address change:\n\t");
+
+		switch (spc->spc_state) {
+		case SCTP_ADDR_AVAILABLE:
+			if (verbose)
+				printf("\t%s is available\n", text);
+			break;
+		case SCTP_ADDR_UNREACHABLE:
+			if (verbose)
+				printf("\t%s is not available - Error: 0x%x\n",
+				       text, spc->spc_error);
+			break;
+		case SCTP_ADDR_REMOVED:
+			if (verbose)
+				printf("\t%s was removed\n", text);
+			break;
+		case SCTP_ADDR_ADDED:
+			if (verbose)
+				printf("\t%s was added\n", text);
+			break;
+		case SCTP_ADDR_MADE_PRIM:
+			if (verbose)
+				printf("\t%s is primary\n", text);
+			if (cmp_addr) {
+				if (!strcmp(ap, cmp_addr)) {
+					if (verbose)
+						printf("\t%s and is now the new primary\n", text);
+
+					return EVENT_ADDR_MATCH;
+				}
+			}
+			break;
+		case SCTP_ADDR_CONFIRMED:
+			if (verbose)
+				printf("\t%s is confirmed\n", text);
+			break;
+		default:
+			if (verbose)
+				printf("%s unknown state: %d\n", text,
+				       spc->spc_state);
+			break;
+		}
+		break;
+	case SCTP_SEND_FAILED:
+		ssf = &snp->sn_send_failed;
+
+		if (verbose)
+			printf("%s SCTP_SEND_FAILED event assoc_id: %d ERR: 0x%x\n",
+			       text, ssf->ssf_assoc_id, ssf->ssf_error);
+		break;
+	case SCTP_REMOTE_ERROR:
+		sre = &snp->sn_remote_error;
+		if (verbose) /* Error in network byte order - linux/sctp.h */
+			printf("%s SCTP_REMOTE_ERROR event ERR: 0x%x\n",
+			       text, ntohs(sre->sre_error));
+		break;
+	case SCTP_SHUTDOWN_EVENT:
+		if (verbose)
+			printf("%s SCTP_SHUTDOWN_EVENT\n", text);
+
+		return EVENT_SHUTDOWN;
+	case SCTP_PARTIAL_DELIVERY_EVENT:
+		if (verbose)
+			printf("%s SCTP_PARTIAL_DELIVERY_EVENT\n", text);
+		break;
+	case SCTP_ADAPTATION_INDICATION:
+		if (verbose)
+			printf("%s SCTP_ADAPTATION_INDICATION event\n", text);
+		break;
+	case SCTP_AUTHENTICATION_INDICATION:
+		auth_event = &snp->sn_authkey_event;
+
+		if (verbose) {
+			printf("%s SCTP_AUTHENTICATION_INDICATION event\n"
+			       "\tauth_event->auth_type:       0x%x\n"
+			       "\tauth_event->auth_flags:      0x%x\n"
+			       "\tauth_event->auth_length:     0x%x\n"
+			       "\tauth_event->auth_keynumber:  0x%x\n"
+			       "\tauth_event->auth_indication: 0x%x\n"
+			       "\tauth_event->auth_assoc_id:   %d\n",
+			       text, auth_event->auth_type,
+			       auth_event->auth_flags,
+			       auth_event->auth_length,
+			       auth_event->auth_keynumber,
+			       auth_event->auth_indication,
+			       auth_event->auth_assoc_id);
+		}
+		/* SCTP_AUTH_NO_AUTH defined in linux/sctp.h */
+		if (auth_event->auth_indication == SCTP_AUTH_NO_AUTH)
+			return EVENT_NO_AUTH;
+		break;
+	case SCTP_SENDER_DRY_EVENT:
+		if (verbose)
+			printf("%s SCTP_SENDER_DRY_EVENT\n", text);
+		break;
+	case SCTP_STREAM_RESET_EVENT:
+		if (verbose)
+			printf("%s SCTP_STREAM_RESET_EVENT\n", text);
+		break;
+	case SCTP_ASSOC_RESET_EVENT:
+		if (verbose)
+			printf("%s SCTP_ASSOC_RESET_EVENT\n", text);
+		break;
+	case SCTP_STREAM_CHANGE_EVENT:
+		if (verbose)
+			printf("%s SCTP_STREAM_CHANGE_EVENT\n", text);
+		break;
+	case SCTP_SEND_FAILED_EVENT:
+		if (verbose)
+			printf("%s SCTP_SEND_FAILED_EVENT\n", text);
+		break;
+	default:
+		fprintf(stderr, "%s unknown event: 0x%x\n", text,
+			snp->sn_header.sn_type);
+		break;
+	}
+
+	return EVENT_OK;
 }
diff --git a/tests/sctp/sctp_common.h b/tests/sctp/sctp_common.h
index 351ee37..cb69f70 100644
--- a/tests/sctp/sctp_common.h
+++ b/tests/sctp/sctp_common.h
@@ -18,11 +18,19 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <errno.h>
-#include <signal.h>
 #include <selinux/selinux.h>
 
+enum event_ret {
+	EVENT_OK,
+	EVENT_ADDR_MATCH,
+	EVENT_SHUTDOWN,
+	EVENT_NO_AUTH
+};
+
 void print_context(int fd, char *text);
 void print_addr_info(struct sockaddr *sin, char *text);
 char *get_ip_option(int fd, bool ipv4, socklen_t *opt_len);
 void print_ip_option(int fd, bool ipv4, char *text);
-int set_subscr_events(int fd, int data_io, int association);
+int set_subscr_events(int fd, int data_io, int assoc, int addr, int shutd);
+int handle_event(void *buf, char *cmp_addr, sctp_assoc_t *assoc_id,
+		 bool verbose, char *text);
diff --git a/tests/sctp/sctp_peeloff_server.c b/tests/sctp/sctp_peeloff_server.c
index 8350cb4..bd797f2 100644
--- a/tests/sctp/sctp_peeloff_server.c
+++ b/tests/sctp/sctp_peeloff_server.c
@@ -16,36 +16,10 @@ static void usage(char *progname)
 	exit(1);
 }
 
-static sctp_assoc_t handle_event(void *buf)
-{
-	union sctp_notification *snp = buf;
-	struct sctp_assoc_change *sac;
-
-	switch (snp->sn_header.sn_type) {
-	case SCTP_ASSOC_CHANGE:
-		sac = &snp->sn_assoc_change;
-		return sac->sac_assoc_id;
-	case SCTP_PEER_ADDR_CHANGE:
-	case SCTP_SEND_FAILED:
-	case SCTP_REMOTE_ERROR:
-	case SCTP_SHUTDOWN_EVENT:
-	case SCTP_PARTIAL_DELIVERY_EVENT:
-	case SCTP_ADAPTATION_INDICATION:
-	case SCTP_AUTHENTICATION_INDICATION:
-	case SCTP_SENDER_DRY_EVENT:
-		printf("Unrequested event: %x\n", snp->sn_header.sn_type);
-		break;
-	default:
-		printf("Unknown event: %x\n", snp->sn_header.sn_type);
-		break;
-	}
-	return -1;
-}
-
 int main(int argc, char **argv)
 {
-	int opt, sock, result, peeloff_sk = 0, flags, on = 1;
-	sctp_assoc_t assoc_id;
+	int opt, sock, result, peeloff_sk = 0, flags, on = 1, off = 0;
+	sctp_assoc_t assoc_id = 0;
 	socklen_t sinlen, opt_len;
 	struct sockaddr_storage sin;
 	struct addrinfo hints, *res;
@@ -149,7 +123,7 @@ int main(int argc, char **argv)
 
 	do {
 		/* Get assoc_id for sctp_peeloff() */
-		result = set_subscr_events(sock, 0, 1);
+		result = set_subscr_events(sock, off, on, off, off);
 		if (result < 0) {
 			perror("Server setsockopt: SCTP_EVENTS");
 			close(sock);
@@ -172,7 +146,8 @@ int main(int argc, char **argv)
 					"Server SEQPACKET recvmsg");
 
 		if (flags & MSG_NOTIFICATION && flags & MSG_EOR) {
-			assoc_id = handle_event(msglabel);
+			handle_event(msglabel, NULL, &assoc_id,
+				     verbose, "Peeloff Server");
 			if (assoc_id <= 0) {
 				printf("Server Invalid association ID: %d\n",
 				       assoc_id);
@@ -180,7 +155,7 @@ int main(int argc, char **argv)
 				exit(1);
 			}
 			/* No more notifications */
-			result = set_subscr_events(sock, 0, 0);
+			result = set_subscr_events(sock, off, off, off, off);
 			if (result < 0) {
 				perror("Server setsockopt: SCTP_EVENTS");
 				close(sock);
@@ -210,9 +185,12 @@ int main(int argc, char **argv)
 				exit(1);
 			}
 
-			if (verbose)
+			if (verbose) {
 				print_addr_info((struct sockaddr *)&sin,
 						"Server SEQPACKET peeloff recvmsg");
+				printf("peeloff association ID: %d\n",
+				       assoc_id);
+			}
 		} else {
 			printf("Invalid sctp_recvmsg response FLAGS: %x\n",
 			       flags);
diff --git a/tests/sctp/sctp_server.c b/tests/sctp/sctp_server.c
index 7f2cd20..c53f46f 100644
--- a/tests/sctp/sctp_server.c
+++ b/tests/sctp/sctp_server.c
@@ -23,7 +23,7 @@ static void usage(char *progname)
 
 int main(int argc, char **argv)
 {
-	int opt, sock, newsock, result, if_index = 0, on = 1;
+	int opt, sock, newsock, result, if_index = 0, on = 1, off = 0;
 	socklen_t sinlen, opt_len;
 	struct sockaddr_storage sin;
 	struct addrinfo hints, *res;
@@ -134,7 +134,7 @@ int main(int argc, char **argv)
 	}
 
 	/* Enables sctp_data_io_events for sctp_recvmsg(3) for assoc_id. */
-	result = set_subscr_events(sock, on, 0);
+	result = set_subscr_events(sock, on, off, off, off);
 	if (result < 0) {
 		perror("Server setsockopt: SCTP_EVENTS");
 		close(sock);
diff --git a/tests/sctp/sctp_set_peer_addr.c b/tests/sctp/sctp_set_peer_addr.c
deleted file mode 100644
index c35b518..0000000
--- a/tests/sctp/sctp_set_peer_addr.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * This test will allow the server side to add/remove bindx addresses and
- * inform the client side via ASCONF chunks. It will also allow the server
- * side to inform the client that the peer primary address is being updated.
- * The code for checking these parameters are in net/sctp/sm_make_chunk.c
- * sctp_process_asconf_param().
- *
- * To enable the processing of these incoming ASCONF parameters for:
- *      SCTP_PARAM_SET_PRIMARY, SCTP_PARAM_ADD_IP and SCTP_PARAM_DEL_IP
- * the following options must be enabled:
- *	echo 1 > /proc/sys/net/sctp/addip_enable
- *	echo 1 > /proc/sys/net/sctp/addip_noauth_enable
- *
- * If these are not enabled the SCTP_SET_PEER_PRIMARY_ADDR setsockopt
- * fails with EPERM "Operation not permitted", however the bindx calls
- * will complete but the client side will not be informed.
- *
- * NOTES:
- *   1) SCTP_SET_PEER_PRIMARY_ADDR requires a non-loopback IP address.
- *   2) Both addresses MUST be the same type (i.e. IPv4 or IPv6).
- */
-
-#include "sctp_common.h"
-
-static void usage(char *progname)
-{
-	fprintf(stderr,
-		"usage:  %s -v addr new_pri_addr port\n"
-		"\nWhere:\n\t"
-		"-v           Print status information.\n\t"
-		"addr         IPv4/IPv6 address for initial connection.\n\t"
-		"new_pri_addr IPv4/IPv6 address that the server will bindx\n\t"
-		"             then set to the new SCTP_PRIMARY_ADDR.\n\t"
-		"port         port.\n", progname);
-	fprintf(stderr,
-		"Notes:\n\t"
-		"1) addr and new_pri_addr MUST NOT be loopback addresses.\n\t"
-		"2) addr and new_pri_addr MUST be same type (IPv4 or IPv6).\n\t"
-		"3) IPv6 link-local addresses require the %%<if_name> to\n\t"
-		"   obtain scopeid. e.g. fe80::7629:afff:fe0f:8e5d%%wlp6s0\n");
-	exit(1);
-}
-
-static int peer_count, peer_count_err;
-
-static void getpaddrs_alarm(int sig)
-{
-	fprintf(stderr, "Get peer address count timer expired - carry on test\n");
-	peer_count += 1;
-	peer_count_err = true;
-}
-
-static void getprimaddr_alarm(int sig)
-{
-	fprintf(stderr, "Get primary address timer expired - end test.\n");
-	exit(1);
-}
-
-static void print_primaddr(char *msg, int socket)
-{
-	int result;
-	struct sctp_prim prim;
-	struct sockaddr_in *in_addr;
-	struct sockaddr_in6 *in6_addr;
-	struct sockaddr *paddr;
-	socklen_t prim_len;
-	char addr_buf[INET6_ADDRSTRLEN];
-	const char *addr_ptr = NULL;
-
-	memset(&prim, 0, sizeof(struct sctp_prim));
-
-	prim_len = sizeof(struct sctp_prim);
-	result = getsockopt(socket, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
-			    &prim, &prim_len);
-	if (result < 0) {
-		perror("getsockopt: SCTP_PRIMARY_ADDR");
-		exit(1);
-	}
-
-	paddr = (struct sockaddr *)&prim.ssp_addr;
-	if (paddr->sa_family == AF_INET) {
-		in_addr = (struct sockaddr_in *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET, &in_addr->sin_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
-	} else if (paddr->sa_family == AF_INET6) {
-		in6_addr = (struct sockaddr_in6 *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET6, &in6_addr->sin6_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
-	}
-
-	if (!addr_ptr) {
-		perror("inet_ntop");
-		exit(1);
-	}
-
-	printf("%s SCTP_PRIMARY_ADDR: %s\n", msg, addr_ptr);
-}
-
-static void get_primaddr(char *addr_buf, int socket)
-{
-	int result;
-	struct sctp_prim prim;
-	struct sockaddr_in *in_addr;
-	struct sockaddr_in6 *in6_addr;
-	struct sockaddr *paddr;
-	socklen_t prim_len;
-	const char *addr_ptr = NULL;
-
-	memset(&prim, 0, sizeof(struct sctp_prim));
-	prim_len = sizeof(struct sctp_prim);
-	result = getsockopt(socket, IPPROTO_SCTP, SCTP_PRIMARY_ADDR,
-			    &prim, &prim_len);
-	if (result < 0) {
-		perror("getsockopt: SCTP_PRIMARY_ADDR");
-		exit(1);
-	}
-
-	paddr = (struct sockaddr *)&prim.ssp_addr;
-	if (paddr->sa_family == AF_INET) {
-		in_addr = (struct sockaddr_in *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET, &in_addr->sin_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
-	} else if (paddr->sa_family == AF_INET6) {
-		in6_addr = (struct sockaddr_in6 *)&prim.ssp_addr;
-		addr_ptr = inet_ntop(AF_INET6, &in6_addr->sin6_addr, addr_buf,
-				     INET6_ADDRSTRLEN);
-	}
-	if (!addr_ptr) {
-		perror("inet_ntop");
-		exit(1);
-	}
-}
-
-int main(int argc, char **argv)
-{
-	int opt, srv_sock, client_sock, new_sock, result, on = 1;
-	struct addrinfo srv_hints, client_hints, *srv_res, *client_res;
-	struct addrinfo *new_pri_addr_res;
-	struct sockaddr *sa_ptr, *paddrs;
-	socklen_t sinlen;
-	struct sockaddr_storage sin;
-	struct sctp_setpeerprim setpeerprim;
-	bool verbose = false, is_ipv6 = false;
-	char client_prim_addr[INET6_ADDRSTRLEN];
-	char client_prim_new_pri_addr[INET6_ADDRSTRLEN];
-
-	while ((opt = getopt(argc, argv, "v")) != -1) {
-		switch (opt) {
-		case 'v':
-			verbose = true;
-			break;
-		default:
-			usage(argv[0]);
-		}
-	}
-
-	if ((argc - optind) != 3)
-		usage(argv[0]);
-
-	if (strchr(argv[optind], ':') && strchr(argv[optind + 1], ':')) {
-		is_ipv6 = true;
-		srv_hints.ai_family = AF_INET6;
-	} else if (strchr(argv[optind], '.') &&
-		   strchr(argv[optind + 1], '.')) {
-		is_ipv6 = false;
-		srv_hints.ai_family = AF_INET;
-	} else {
-		usage(argv[0]);
-	}
-
-	memset(&srv_hints, 0, sizeof(struct addrinfo));
-	srv_hints.ai_flags = AI_PASSIVE;
-	srv_hints.ai_socktype = SOCK_STREAM;
-	srv_hints.ai_protocol = IPPROTO_SCTP;
-
-	/* Set up server side */
-	result = getaddrinfo(argv[optind], argv[optind + 2],
-			     &srv_hints, &srv_res);
-	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - server: %s\n",
-			gai_strerror(result));
-		exit(1);
-	}
-
-	result = getaddrinfo(argv[optind], argv[optind + 2],
-			     &srv_hints, &srv_res);
-	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - server: %s\n",
-			gai_strerror(result));
-		exit(1);
-	}
-	if (is_ipv6 && verbose)
-		printf("Server scopeID: %d\n",
-		       ((struct sockaddr_in6 *)
-			srv_res->ai_addr)->sin6_scope_id);
-
-	srv_sock = socket(srv_res->ai_family, srv_res->ai_socktype,
-			  srv_res->ai_protocol);
-	if (srv_sock < 0) {
-		perror("socket - server");
-		exit(1);
-	}
-
-	result = setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR,
-			    &on, sizeof(on));
-	if (result < 0) {
-		perror("setsockopt: SO_REUSEADDR");
-		close(srv_sock);
-		exit(1);
-	}
-
-	result = bind(srv_sock, srv_res->ai_addr, srv_res->ai_addrlen);
-	if (result < 0) {
-		perror("bind");
-		close(srv_sock);
-		exit(1);
-	}
-
-	listen(srv_sock, 1);
-
-	/* Set up client side and connect */
-	memset(&client_hints, 0, sizeof(struct addrinfo));
-	client_hints.ai_socktype = SOCK_STREAM;
-	client_hints.ai_protocol = IPPROTO_SCTP;
-	result = getaddrinfo(argv[optind], argv[optind + 2],
-			     &client_hints, &client_res);
-	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - client: %s\n",
-			gai_strerror(result));
-		close(srv_sock);
-		exit(1);
-	}
-	if (is_ipv6 && verbose)
-		printf("Client scopeID: %d\n",
-		       ((struct sockaddr_in6 *)
-			client_res->ai_addr)->sin6_scope_id);
-
-	client_sock = socket(client_res->ai_family, client_res->ai_socktype,
-			     client_res->ai_protocol);
-	if (client_sock < 0) {
-		perror("socket - client");
-		close(srv_sock);
-		exit(1);
-	}
-
-	result = connect(client_sock, client_res->ai_addr,
-			 client_res->ai_addrlen);
-	if (result < 0) {
-		if (errno != EINPROGRESS)
-			perror("connect");
-		else
-			fprintf(stderr, "connect timeout\n");
-		result = 1;
-		goto err2;
-	}
-
-	/* Obtain address info for the BINDX_ADD and new SCTP_PRIMARY_ADDR. */
-	result = getaddrinfo(argv[optind + 1], argv[optind + 2],
-			     &client_hints, &new_pri_addr_res);
-	if (result < 0) {
-		fprintf(stderr, "getaddrinfo - new SCTP_PRIMARY_ADDR: %s\n",
-			gai_strerror(result));
-		close(srv_sock);
-		exit(1);
-	}
-	if (is_ipv6 && verbose)
-		printf("new_pri_addr scopeID: %d\n",
-		       ((struct sockaddr_in6 *)
-			new_pri_addr_res->ai_addr)->sin6_scope_id);
-
-	/* Get number of peer addresses on CLIENT (should be 1) for a check
-	 * later as sctp_bindx SERVER -> CLIENT is non-blocking.
-	 */
-	peer_count = sctp_getpaddrs(client_sock, 0, &paddrs);
-	sctp_freepaddrs(paddrs);
-	if (verbose)
-		printf("Client peer address count: %d\n", peer_count);
-
-	/* Client and server now set so accept new socket on server side. */
-	sinlen = sizeof(sin);
-	new_sock = accept(srv_sock, (struct sockaddr *)&sin, &sinlen);
-	if (new_sock < 0) {
-		perror("accept");
-		result = 1;
-		goto err2;
-	}
-
-	/* Get initial CLIENT primary address (that should be ADDR1). */
-	get_primaddr(client_prim_addr, client_sock);
-
-	/* Now call sctp_bindx to add new_pri_addr, this will cause an
-	 * ASCONF - SCTP_PARAM_ADD_IP chunk to be sent to the CLIENT.
-	 * This is non-blocking so there maybe a delay before the CLIENT
-	 * receives the asconf chunk.
-	 */
-	if (verbose)
-		printf("Calling sctp_bindx ADD: %s\n", argv[optind + 1]);
-
-	result = sctp_bindx(new_sock,
-			    (struct sockaddr *)new_pri_addr_res->ai_addr,
-			    1, SCTP_BINDX_ADD_ADDR);
-	if (result < 0) {
-		if (errno == EACCES) {
-			perror("sctp_bindx ADD");
-		} else {
-			perror("sctp_bindx ADD");
-			result = 1;
-			goto err1;
-		}
-	}
-	/* so set an alarm and check number of peer addresses for CLIENT. */
-	signal(SIGALRM, getpaddrs_alarm);
-	alarm(2);
-	peer_count_err = false;
-	result = 0;
-
-	while (result != peer_count + 1) {
-		result = sctp_getpaddrs(client_sock, 0, &paddrs);
-		sctp_freepaddrs(paddrs);
-
-		if (peer_count_err)
-			break;
-	}
-	peer_count = result;
-
-	if (verbose)
-		printf("Client peer address count: %d\n", result);
-
-	/* Now that the CLIENT has the new primary address ensure they use
-	 * it by SCTP_SET_PEER_PRIMARY_ADDR.
-	 */
-	memset(&setpeerprim, 0, sizeof(struct sctp_setpeerprim));
-	sa_ptr = (struct sockaddr *)&setpeerprim.sspp_addr;
-	if (is_ipv6)
-		memcpy(sa_ptr, new_pri_addr_res->ai_addr,
-		       sizeof(struct sockaddr_in6));
-	else
-		memcpy(sa_ptr, new_pri_addr_res->ai_addr,
-		       sizeof(struct sockaddr_in));
-
-	if (verbose)
-		printf("Calling setsockopt SCTP_SET_PEER_PRIMARY_ADDR: %s\n",
-		       argv[optind + 1]);
-
-	result = setsockopt(new_sock, IPPROTO_SCTP,
-			    SCTP_SET_PEER_PRIMARY_ADDR,
-			    &setpeerprim, sizeof(struct sctp_setpeerprim));
-	if (result < 0) {
-		perror("setsockopt: SCTP_SET_PEER_PRIMARY_ADDR");
-		result = 1;
-		goto err1;
-	}
-
-	/* Now get the new primary address from the client */
-	signal(SIGALRM, getprimaddr_alarm);
-	alarm(2);
-	memcpy(client_prim_new_pri_addr, client_prim_addr, INET6_ADDRSTRLEN);
-
-	while (!strcmp(client_prim_addr, client_prim_new_pri_addr))
-		get_primaddr(client_prim_new_pri_addr, client_sock);
-
-	if (verbose) {
-		printf("Client initial SCTP_PRIMARY_ADDR: %s\n",
-		       client_prim_addr);
-		print_primaddr("Server", new_sock);
-		printf("Client current SCTP_PRIMARY_ADDR: %s\n",
-		       client_prim_new_pri_addr);
-	}
-
-	/* Then delete addr1 that checks ASCONF - SCTP_PARAM_DEL_IP. */
-	if (verbose)
-		printf("Calling sctp_bindx REM: %s\n", argv[optind]);
-
-	result = sctp_bindx(new_sock, (struct sockaddr *)client_res->ai_addr,
-			    1, SCTP_BINDX_REM_ADDR);
-	if (result < 0) {
-		perror("sctp_bindx - REM");
-		result = 1;
-		goto err1;
-	}
-
-	if (!peer_count_err) {
-		alarm(2);
-		result = 0;
-
-		while (result != peer_count - 1) {
-			result = sctp_getpaddrs(client_sock, 0, &paddrs);
-			sctp_freepaddrs(paddrs);
-		}
-
-		if (verbose)
-			printf("Client peer address count: %d\n", result);
-	}
-
-	/* Compare the client primary addresses, they should be different. */
-	if (!strcmp(client_prim_addr, client_prim_new_pri_addr)) {
-		fprintf(stderr,
-			"Client addr: %s same as new_pri_addr: %s - SCTP_SET_PEER_PRIMARY_ADDR failed\n",
-			client_prim_addr, client_prim_new_pri_addr);
-		result = 1;
-		goto err1;
-	}
-
-	if (verbose)
-		printf("Client primary address changed successfully.\n");
-
-	result = 0;
-
-err1:
-	close(new_sock);
-err2:
-	close(srv_sock);
-	close(client_sock);
-	exit(result);
-}
diff --git a/tests/sctp/test b/tests/sctp/test
index b4462c9..eede42f 100755
--- a/tests/sctp/test
+++ b/tests/sctp/test
@@ -50,7 +50,7 @@ BEGIN {
         }
 
         if ( $ipaddress[1] ne 0 and $ipaddress[0] ne $ipaddress[1] ) {
-            $test_count += 2;
+            $test_count += 3;
             $test_asconf = 1;
         }
 
@@ -208,37 +208,77 @@ $result =
 ok( $result >> 8 eq 2 );
 
 #
-######################### SET_PRI_ADDR SET_PEER_ADDR ########################
+##################### Dynamic Address Reconfiguration #####################
+#
+# These tests require two non-loopback addresses.
+#
+# Server - setsockopt(SCTP_SET_PEER_PRIMARY_ADDR, $ipaddress[0]);
+# net/sctp/sm_make_chunk.c sctp_process_asconf_param() SCTP_PARAM_SET_PRIMARY
+# Server -> Client (Set $ipaddress[0] as primary - client acks)
+#
+# Server - sctp_bindx(SCTP_BINDX_ADD_ADDR, $ipaddress[1]);
+# net/sctp/sm_make_chunk.c sctp_process_asconf_param() SCTP_PARAM_ADD_IP
+# Server -> Client (Set $ipaddress[1] as primary - client acks)
+#
+# These are sent by the server and require bind permission. They are
+# received by the client and the SCTP_PARAM_ADD_IP is validated when
+# $ipaddress[1] is set for use via:
+#    net/sctp/socket.c sctp_setsockopt_peer_primary_addr(setsockopt(SCTP_PRIMARY_ADDR))
+# This requires the 'bind' permission, if not granted client exits with 51.
 #
 
-# These tests require two local non-loopback addresses.
 if ($test_asconf) {
-    print "# Testing asconf parameter chunk processing.\n";
 
-    # To enable processing of incoming ASCONF parameters:
-    # SCTP_PARAM_SET_PRIMARY, SCTP_PARAM_ADD_IP and SCTP_PARAM_DEL_IP,
-    # need to set:
+    # To enable processing of ASCONF parameters SCTP_PARAM_SET_PRIMARY
+    # and SCTP_PARAM_ADD_IP need to set:
     system("echo 1 > /proc/sys/net/sctp/addip_enable");
     system("echo 1 > /proc/sys/net/sctp/addip_noauth_enable");
 
-    # Verify ASCONF params.
+    print "Testing Dynamic Address Reconfiguration\n";
+
+    # Server should automatically exit after each test
+    $pid = server_start(
+        "-t sctp_asconf_params_server_t",
+        "sctp_asconf_params_server",
+        "$v $ipaddress[0] $ipaddress[1] 1035"
+    );
+
     $result = system
-"runcon -t test_sctp_set_peer_addr_t $basedir/sctp_set_peer_addr $v $ipaddress[0] $ipaddress[1] 1035";
+"runcon -t sctp_asconf_params_client_t $basedir/sctp_asconf_params_client $v $ipaddress[0] 1035";
     ok( $result eq 0 );
 
-    # Start the asconf server.
+    server_end($pid);
+
+    $pid = server_start(
+        "-t sctp_asconf_params_server_t",
+        "sctp_asconf_params_server",
+        "$v $ipaddress[0] $ipaddress[1] 1035"
+    );
+
+    print "Testing deny SCTP_PRIMARY_ADDR\n";
+    $result = system
+"runcon -t sctp_asconf_deny_pri_addr_client_t $basedir/sctp_asconf_params_client $v $ipaddress[0] 1035 2>&1";
+    ok( $result >> 8 eq 51 );    # setsockopt(2) failed
+
+    server_end($pid);
+
+    #
+    # This is a local only test as it's the neverallow rule that stops:
+    #    server -> client sctp_socket { connect };
+    #
+    # Srv sends SCTP_PARAM_ADD_IP and SCTP_PARAM_SET_PRIMARY in ASCONF's
+    # Client returns ASCONF_ACK's with 'Request refused - no authorization'
     $pid = server_start(
-        "-t test_sctp_set_peer_addr_t",
+        "-t sctp_asconf_params_server_t",
         "sctp_asconf_params_server",
         "$v $ipaddress[0] $ipaddress[1] 1035"
     );
 
-# This should fail connect permission attempting to send SCTP_PARAM_ADD_IP to client.
+    print "Testing deny SCTP_PARAM_ADD_IP/SCTP_PARAM_SET_PRIMARY\n";
     $result = system
-"runcon -t test_sctp_client_t -- $basedir/sctp_asconf_params_client $v $ipaddress[0] 1035 2>&1";
-    ok($result);
+"runcon -t sctp_asconf_deny_param_add_client_t $basedir/sctp_asconf_params_client $v $ipaddress[0] 1035 2>&1";
+    ok( $result >> 8 eq 11 );   # Client error 'Dynamic Address Reconfiguration'
 
-    # The server should automatically exit.
     server_end($pid);
 
     system("echo 0 > /proc/sys/net/sctp/addip_enable");
-- 
2.26.2





[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux