@@ -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 ############################
@@ -6,4 +6,3 @@ sctp_client
sctp_connectx
sctp_peeloff_server
sctp_server
-sctp_set_peer_addr
@@ -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
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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);
@@ -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);
deleted file mode 100644
@@ -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);
-}
@@ -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");
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@btinternet.com> --- 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