Add a test case for verifying the support for user message interleave implemented in RFC8260. This test case will not be built if the SCTP_FRAGMENT_INTERLEAVE declaration is not present and will skip if net.sctp.intl_enable is not set to 1. --- configure.ac | 3 + src/func_tests/.gitignore | 2 + src/func_tests/Makefile.am | 10 ++ src/func_tests/test_interleave.c | 254 +++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 src/func_tests/test_interleave.c diff --git a/configure.ac b/configure.ac index 28132bf..bd0144c 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,9 @@ LKSCTP_CHECK_MEMBER([struct sctp_paddrparams.spp_ipv6_flowlabel], LKSCTP_CHECK_MEMBER([struct sctp_paddrparams.spp_dscp], [HAVE_SCTP_SPP_DSCP]) +# RFC 8260 (Message Interleaving) +LKSCTP_CHECK_DECL([SCTP_FRAGMENT_INTERLEAVE], [HAVE_SCTP_FRAGMENT_INTERLEAVE]) + AC_CONFIG_HEADERS([src/include/netinet/sctp.h]) AC_CONFIG_FILES([lksctp-tools.spec Makefile diff --git a/src/func_tests/.gitignore b/src/func_tests/.gitignore index 720db65..4e51a74 100644 --- a/src/func_tests/.gitignore +++ b/src/func_tests/.gitignore @@ -28,6 +28,8 @@ test_getname test_getname_v6 test_inaddr_any test_inaddr_any_v6 +test_interleave +test_interleave_v6 test_peeloff test_peeloff_v6 test_recvmsg diff --git a/src/func_tests/Makefile.am b/src/func_tests/Makefile.am index e5bf454..07d9fa4 100644 --- a/src/func_tests/Makefile.am +++ b/src/func_tests/Makefile.am @@ -68,6 +68,13 @@ PASSING_V6_KERN_TESTS += \ test_sctp_sendvrecvv_v6 endif +if HAVE_SCTP_FRAGMENT_INTERLEAVE +PASSING_KERN_TESTS += \ + test_interleave +PASSING_V6_KERN_TESTS += \ + test_interleave_v6 +endif + noinst_PROGRAMS = ${PASSING_KERN_TESTS} ${PASSING_V6_KERN_TESTS} $(top_builddir)/src/lib/libsctp.la: @@ -177,3 +184,6 @@ test_getname_v6_CFLAGS = ${V6FLAGS} test_tcp_style_v6_SOURCES = test_tcp_style.c test_tcp_style_v6_CFLAGS = ${V6FLAGS} + +test_interleave_v6_SOURCES = test_interleave.c +test_interleave_v6_CFLAGS = ${V6FLAGS} diff --git a/src/func_tests/test_interleave.c b/src/func_tests/test_interleave.c new file mode 100644 index 0000000..d92e2b0 --- /dev/null +++ b/src/func_tests/test_interleave.c @@ -0,0 +1,254 @@ +/* This purpose of this test is to examine the + * support of the interleaving + * The following tests are done in sequence: + * - Verify SCTP_FRAGMENT_INTERLEAVE and SCTP_INTERLEAVING_SUPPORTED + * socket option by doing a setsockopt() followed by a getsockopt() + * - Verify the integrity of the data sent by client using a sendmsg() + */ + +#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 MSG_SIZE 1000 + +char *TCID = __FILE__; +int TST_TOTAL = 2; +int TST_CNT = 0; + +int +main(int argc, char *argv[]) +{ + int sk1, sk2; + int pf_class; + int error; + int fd; + int msg_len, bytes_sent; + int frag_interleave, get_result; + sockaddr_storage_t loop1; + sockaddr_storage_t loop2; + struct iovec iov; + struct iovec out_iov; + struct msghdr inmessage; + struct msghdr outmessage; + struct cmsghdr *cmsg; + struct sctp_sndrcvinfo *sinfo; + struct sctp_assoc_value assoc, get_assoc; + uint32_t ppid; + uint32_t stream; + char *buffer; + char setting[4]; + char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))]; + char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; + void *msg_buffer; + socklen_t optlen; + + /* Rather than fflush() throughout the code, set stdout to + * be unbuffered. + */ + setvbuf(stdout, NULL, _IONBF, 0); + + /* Set some basic values which depend on the address family. */ +#if TEST_V6 + pf_class = PF_INET6; + + 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; + + 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_SEQPACKET, IPPROTO_SCTP); + sk2 = test_socket(pf_class, SOCK_SEQPACKET, 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(loop1)); + test_bind(sk2, &loop2.sa, sizeof(loop2)); + + /* Let sk2 listen to new associations */ + test_listen(sk2, 1); + + /* TEST #1: verify SCTP_FRAGMENT_INTERLEAVE option + * followed by a getsockopt() + */ + frag_interleave = 1; + test_setsockopt(sk1, SCTP_FRAGMENT_INTERLEAVE, + &frag_interleave, sizeof(frag_interleave)); + test_setsockopt(sk2, SCTP_FRAGMENT_INTERLEAVE, + &frag_interleave, sizeof(frag_interleave)); + + tst_resm(TPASS, "setsockopt(SCTP_FRAGMENT_INTERLEAVE)"); + + get_result = 0; + optlen = sizeof(get_result); + error = test_getsockopt(sk1, SCTP_FRAGMENT_INTERLEAVE, + &get_result, &optlen); + if (get_result != frag_interleave) + tst_brkm(TBROK, tst_exit, "getsockopt(SCTP_FRAGMENT_INTERLEAVE) " + "error: %d errno: %d get_result: %d", + error, errno, get_result); + get_result = 0; + error = test_getsockopt(sk2, SCTP_FRAGMENT_INTERLEAVE, + &get_result, &optlen); + if (get_result != frag_interleave) + tst_brkm(TBROK, tst_exit, "getsockopt(SCTP_FRAGMENT_INTERLEAVE) " + "error: %d errno: %d get_result: %d", + error, errno, get_result); + + tst_resm(TPASS, "getsockopt(SCTP_FRAGMENT_INTERLEAVE)"); + + /* Check if net.sctp.intl_enable=1, exit if not */ + fd = open("/proc/sys/net/sctp/intl_enable", O_RDONLY); + if (fd < 0 || + read(fd, &setting, 4) < 0 || + strncmp("1", setting, 1) != 0) { + tst_resm(TINFO, "intl_enable is not set to 1, skip test\n"); + return 0; + } + close(fd); + + assoc.assoc_id = 0; + assoc.assoc_value = 1; + test_setsockopt(sk1, SCTP_INTERLEAVING_SUPPORTED, + &assoc, sizeof(assoc)); + test_setsockopt(sk2, SCTP_INTERLEAVING_SUPPORTED, + &assoc, sizeof(assoc)); + + tst_resm(TPASS, "setsockopt(SCTP_INTERLEAVING_SUPPORTED)"); + + + memset(&get_assoc, 0x00, sizeof(struct sctp_assoc_value)); + optlen = sizeof(get_assoc); + error = test_getsockopt(sk1, SCTP_INTERLEAVING_SUPPORTED, + &get_assoc, &optlen); + if (get_assoc.assoc_value != 1) + 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_INTERLEAVING_SUPPORTED, + &get_assoc, &optlen); + if (get_assoc.assoc_value != 1) + 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_INTERLEAVING_SUPPORTED)"); + /* End of TEST #1 */ + + /* Send the first message to create an association */ + 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); + + /* 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; + + /* Get the communication up message on sk2 */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(sk2, &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 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 first message which was sent. */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(sk2, &inmessage, MSG_WAITALL); + test_check_msg_data(&inmessage, error, msg_len, MSG_EOR, stream, ppid); + + /* TEST #2: Verify data integrity */ + msg_len = MSG_SIZE; + msg_buffer = test_build_msg(msg_len); + outmessage.msg_iov->iov_base = msg_buffer; + outmessage.msg_iov->iov_len = msg_len; + bytes_sent = test_sendmsg(sk1, &outmessage, 0, msg_len); + + tst_resm(TINFO, "Sent %d byte message", bytes_sent); + + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(sk2, &inmessage, MSG_WAITALL); + + tst_resm(TINFO, "Received %d byte message", error); + + test_check_msg_data(&inmessage, error, bytes_sent, + MSG_EOR, stream, ppid); + + tst_resm(TPASS, "Received same byte of message"); + /* End of TEST #2 */ + + /* Shut down the link */ + close(sk1); + + /* Get the shutdown complete notification */ + inmessage.msg_controllen = sizeof(incmsg); + error = test_recvmsg(sk2, &inmessage, MSG_WAITALL); + test_check_msg_notification(&inmessage, error, + sizeof(struct sctp_assoc_change), + SCTP_ASSOC_CHANGE, SCTP_SHUTDOWN_COMP); + close(sk2); + + free(buffer); + + /* Indicate successful completion */ + return 0; +} -- 2.37.1