Add a test case for verifying the support for stream re_config implemented in RFC6525. This test case will not be built if the SCTP_RECONFIG_SUPPORTED declaration is not present and will abort if net.sctp.reconf_enable is not set to 1. --- src/func_tests/.gitignore | 2 + src/func_tests/Makefile.am | 10 ++ src/func_tests/test_re_conf.c | 251 ++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 src/func_tests/test_re_conf.c diff --git a/src/func_tests/.gitignore b/src/func_tests/.gitignore index 4e51a74..175e71b 100644 --- a/src/func_tests/.gitignore +++ b/src/func_tests/.gitignore @@ -32,6 +32,8 @@ test_interleave test_interleave_v6 test_peeloff test_peeloff_v6 +test_re_conf +test_re_conf_v6 test_recvmsg test_sctp_sendrecvmsg test_sctp_sendrecvmsg_v6 diff --git a/src/func_tests/Makefile.am b/src/func_tests/Makefile.am index 07d9fa4..54e1627 100644 --- a/src/func_tests/Makefile.am +++ b/src/func_tests/Makefile.am @@ -75,6 +75,13 @@ PASSING_V6_KERN_TESTS += \ test_interleave_v6 endif +if HAVE_SCTP_STREAM_RECONFIG +PASSING_KERN_TESTS += \ + test_re_conf +PASSING_V6_KERN_TESTS += \ + test_re_conf_v6 +endif + noinst_PROGRAMS = ${PASSING_KERN_TESTS} ${PASSING_V6_KERN_TESTS} $(top_builddir)/src/lib/libsctp.la: @@ -187,3 +194,6 @@ test_tcp_style_v6_CFLAGS = ${V6FLAGS} test_interleave_v6_SOURCES = test_interleave.c test_interleave_v6_CFLAGS = ${V6FLAGS} + +test_re_conf_v6_SOURCES = test_re_conf.c +test_re_conf_v6_CFLAGS = ${V6FLAGS} diff --git a/src/func_tests/test_re_conf.c b/src/func_tests/test_re_conf.c new file mode 100644 index 0000000..ea330a9 --- /dev/null +++ b/src/func_tests/test_re_conf.c @@ -0,0 +1,251 @@ +/* The purpose of this test is to examine the support for RE-CONFIG chunk + * The following tests are done in sequence: + * - Verify the SCTP_ENABLE_STREAM_RESET option by doing a setsockopt() + * followed by a getsockopt() + * - Verify the reset I/O stream functionality by setting SCTP_RESET_STREAMS + * - Verify the reset assoc functionality by setting SCTP_RESET_ASSOC + * - Verify the add streams functionality by setting SCTP_ADD_STREAMS + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/sctp.h> +#include <sctputil.h> + +#define STREAM_NUM 3 +#define ADD_STREAM 2 + +char *TCID = __FILE__; +int TST_TOTAL = 4; +int TST_CNT = 0; + +int +main(int argc, char *argv[]) +{ + int error; + int sk1, sk2, acpt_sk; + int pf_class; + int fd; + int msg_len; + void *msg_buffer; + sockaddr_storage_t loop1; + sockaddr_storage_t loop2; + sockaddr_storage_t acpt_addr; + sctp_assoc_t assoc_id; + socklen_t optlen, sock_len; + uint32_t ppid; + uint32_t stream; + struct msghdr inmessage; + struct msghdr outmessage; + struct iovec iov; + struct iovec out_iov; + struct cmsghdr *cmsg; + struct sctp_assoc_value assoc; + struct sctp_assoc_value get_assoc; + struct sctp_reset_streams *srs; + struct sctp_add_streams *sas; + struct sctp_sndrcvinfo *sinfo; + char setting[4]; + char * buffer; + char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))]; + char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; + + /* Rather than fflush() throughout the code, set stdout to + * be unbuffered. + */ + setvbuf(stdout, NULL, _IONBF, 0); + +#if TEST_V6 + pf_class = PF_INET6; + sock_len = sizeof(struct sockaddr_in6); + + loop1.v6.sin6_family = AF_INET6; + loop1.v6.sin6_addr = in6addr_loopback; + loop1.v6.sin6_port = htons(SCTP_TESTPORT_1); + + loop2.v6.sin6_family = AF_INET6; + loop2.v6.sin6_addr = in6addr_loopback; + loop2.v6.sin6_port = htons(SCTP_TESTPORT_2); +#else + pf_class = PF_INET; + sock_len = sizeof(struct sockaddr_in); + + loop1.v4.sin_family = AF_INET; + loop1.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK; + loop1.v4.sin_port = htons(SCTP_TESTPORT_1); + + loop2.v4.sin_family = AF_INET; + loop2.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK; + loop2.v4.sin_port = htons(SCTP_TESTPORT_2); +#endif /* TEST_V6 */ + + /* Create the two endpoints which will talk to each other */ + sk1 = test_socket(pf_class, SOCK_STREAM, IPPROTO_SCTP); + sk2 = test_socket(pf_class, SOCK_STREAM, IPPROTO_SCTP); + + /* Enable ASSOC_CHANGE and SNDRCVINFO notifications */ + test_enable_assoc_change(sk1); + test_enable_assoc_change(sk2); + + /* Bind these sockets to the test ports */ + test_bind(sk1, &loop1.sa, sizeof(loop2)); + test_bind(sk2, &loop2.sa, sizeof(loop2)); + + /* Let sk2 listen to new associations */ + test_listen(sk2, 1); + + /* Set up connection */ + test_connectx(sk1, &loop2.sa, 1); + acpt_sk = test_accept(sk2, &acpt_addr.sa, &sock_len); + test_enable_assoc_change(acpt_sk); + + /* Initialize inmessage for all receives */ + buffer = test_malloc(REALLY_BIG); + memset(&inmessage, 0x00, sizeof(inmessage)); + iov.iov_base = buffer; + iov.iov_len = REALLY_BIG; + inmessage.msg_iov = &iov; + inmessage.msg_iovlen = 1; + inmessage.msg_control = incmsg; + + /* Send the first message to create an association */ + memset(&outmessage, 0x00, sizeof(outmessage)); + outmessage.msg_name = &loop2; + outmessage.msg_namelen = sizeof(loop2); + outmessage.msg_iov = &out_iov; + outmessage.msg_iovlen = 1; + outmessage.msg_control = outcmsg; + outmessage.msg_controllen = sizeof(outcmsg); + outmessage.msg_flags = 0; + cmsg = CMSG_FIRSTHDR(&outmessage); + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDRCV; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); + memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); + ppid = rand(); /* Choose an arbitrary value */ + stream = 1; + sinfo->sinfo_ppid = ppid; + sinfo->sinfo_stream = stream; + msg_len = 10; + msg_buffer = test_build_msg(10); + outmessage.msg_iov->iov_base = msg_buffer; + outmessage.msg_iov->iov_len = msg_len; + test_sendmsg(sk1, &outmessage, 0, msg_len); + + /* Get the communication up message on sk1 */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(sk1, &inmessage, MSG_WAITALL); + test_check_msg_notification(&inmessage, error, + sizeof(struct sctp_assoc_change), + SCTP_ASSOC_CHANGE, SCTP_COMM_UP); + + /* Get the communication up message on acpt_sk */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(acpt_sk, &inmessage, MSG_WAITALL); + test_check_msg_notification(&inmessage, error, + sizeof(struct sctp_assoc_change), + SCTP_ASSOC_CHANGE, SCTP_COMM_UP); + + /* Get the first data message which was sent. */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(acpt_sk, &inmessage, MSG_WAITALL); + test_check_msg_data(&inmessage, error, 10, MSG_EOR, stream, ppid); + + /* TEST #1: verify SCTP_ENABLE_STREAM_RESET option */ + memset(&assoc, 0, sizeof(struct sctp_assoc_value)); + assoc.assoc_id = 0; + assoc.assoc_value = (SCTP_ENABLE_RESET_STREAM_REQ + | SCTP_ENABLE_RESET_ASSOC_REQ + | SCTP_ENABLE_CHANGE_ASSOC_REQ); + test_setsockopt(sk1, SCTP_ENABLE_STREAM_RESET, &assoc, sizeof(assoc)); + test_setsockopt(sk2, SCTP_ENABLE_STREAM_RESET, &assoc, sizeof(assoc)); + + tst_resm(TPASS, "setsockopt(SCTP_ENABLE_STREAM_RESET)"); + + optlen = sizeof(struct sctp_assoc_value); + memset(&get_assoc, 0x00, optlen); + assoc.assoc_id = 0; + error = test_getsockopt(sk1, SCTP_ENABLE_STREAM_RESET, + &get_assoc, &optlen); + if (get_assoc.assoc_value != 7) + tst_brkm(TBROK, tst_exit, + "getsockopt(SCTP_INTERLEAVING_SUPPORTED) " + "error: %d errno: %d get_result: %d", + error, errno, get_assoc.assoc_value); + error = test_getsockopt(sk2, SCTP_ENABLE_STREAM_RESET, + &get_assoc, &optlen); + if (get_assoc.assoc_value != 7) + tst_brkm(TBROK, tst_exit, + "getsockopt(SCTP_INTERLEAVING_SUPPORTED) " + "error: %d errno: %d get_result: %d", + error, errno, get_assoc.assoc_value); + + tst_resm(TPASS, "getsockopt(SCTP_ENABLE_STREAM_RESET)"); + + /* TEST #2: Reset I/O streams */ + /* Check if net.sctp.reconf_enable=1, exit if not */ + fd = open("/proc/sys/net/sctp/reconf_enable", O_RDONLY); + if (0 > fd || + 0 >= read(fd, &setting, 4) || + strncmp("1", setting, 1) != 0) { + fprintf(stderr, "reconf_enable is not set to 1, skip test\n"); + exit(0); + } + close(fd); + + optlen = sizeof(struct sctp_reset_streams) + + STREAM_NUM * sizeof(uint16_t); + srs = (struct sctp_reset_streams*) test_malloc(optlen); + srs->srs_assoc_id = 0; + srs->srs_number_streams = STREAM_NUM; + for (int i = 0; i < srs->srs_number_streams; ++i) + srs->srs_stream_list[i] = i; + srs->srs_flags = (SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING); + + test_setsockopt(sk1, SCTP_RESET_STREAMS, srs, optlen); + + tst_resm(TPASS, "setsockopt(SCTP_RESET_STREAMS)"); + + /* TEST #3: Reset assoc */ + assoc_id = 0; + test_setsockopt(sk1, SCTP_RESET_ASSOC, &assoc_id, sizeof(socklen_t)); + + tst_resm(TPASS, "setsockopt(SCTP_RESET_ASSOC)"); + + /* TEST #4: Add stream */ + sas = (struct sctp_add_streams *) test_malloc(sizeof(struct sctp_add_streams)); + sas->sas_assoc_id = 0; + sas->sas_instrms = ADD_STREAM; + sas->sas_outstrms= ADD_STREAM; + test_setsockopt(sk1, SCTP_ADD_STREAMS, sas, (socklen_t)sizeof(struct sctp_add_streams)); + + tst_resm(TPASS, "setsockopt(SCTP_ADD_STREAMS)"); + + /* Shut down the link */ + close(sk1); + + /* Get the shutdown complete notification */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(acpt_sk, &inmessage, MSG_WAITALL); + test_check_msg_notification(&inmessage, error, + sizeof(struct sctp_assoc_change), + SCTP_ASSOC_CHANGE, SCTP_SHUTDOWN_COMP); + close(acpt_sk); + close(sk2); + + free(buffer); + free(srs); + free(sas); + + /*Indicate successful completion*/ + exit(EXIT_SUCCESS); +} -- 2.37.1