<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<pre style="color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; overflow-wrap: break-word; white-space: pre-wrap;">From 59d293fed8db662b3c252e1a063300a4a25bf969 Mon Sep 17 00:00:00 2001
From: Ramon Pantin <a class="moz-txt-link-rfc2396E" href="mailto:pantin@google.com"><pantin@google.com></a>
Date: Thu, 14 Feb 2019 22:36:04 -0800
Subject: [PATCH] Tests for sendmmsg/recvmmsg, it also contains a bunch of
tests not covered by the existing test sendmsg/recvmsg tests cases. Not going
to separate those into independent tests because they are used in the
multiple-message syscall tests to form a variety of multi-message test cases.
Signed-off-by: Ramon Pantin <a class="moz-txt-link-rfc2396E" href="mailto:pantin@google.com"><pantin@google.com></a>
---
testcases/kernel/syscalls/recvmmsg/Makefile | 10 +
.../kernel/syscalls/recvmmsg/recvmmsg01.c | 2164 +++++++++++++++++
2 files changed, 2174 insertions(+)
create mode 100644 testcases/kernel/syscalls/recvmmsg/Makefile
create mode 100644 testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
diff --git a/testcases/kernel/syscalls/recvmmsg/Makefile b/testcases/kernel/syscalls/recvmmsg/Makefile
new file mode 100644
index 000000000..c6f94c9e2
--- /dev/null
+++ b/testcases/kernel/syscalls/recvmmsg/Makefile
@@ -0,0 +1,10 @@
+# Copyright (c) Google LLC, 2018
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+CFLAGS = -pthread
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
new file mode 100644
index 000000000..9cc09adc6
--- /dev/null
+++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
@@ -0,0 +1,2164 @@
+/*
+ * Copyright (c) Google LLC, 2018
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+
+#ifdef HAVE_NETINET_SCTP_H
+#define RUN_SCTP_TESTS 1
+#include <netinet/sctp.h>
+#else
+#define RUN_SCTP_TESTS 0
+#endif
+
+/*
+ * Contants.
+ */
+
+#define GUARD_CHAR '+' /* guard character to check overwrites*/
+#define INVALID_SYSCALL_VALUE (-2) /* syscalls values are all >= -1 */
+#define TEST_IP 0x7F000001
+#define TEST_PORT 6789
+#define SENDMSG_SLEEP_MS 10
+
+/*
+ * Must be disjoint with errno values and non-zero, value returned by tests
+ * that do multiple recvmsg() calls internally, or that have a non-zero flag,
+ * or that require special treatment (sender needs to sleep). Those tests
+ * can not be bundled with other tests into a larger recvmmsg(2) multi-
+ * receive message test, some can only be bundled with their own kind.
+ */
+
+#define KIND_HAS_MULTIPLE_RECVMSG (-1) /* can't be bundled at all */
+#define KIND_CLOEXEC_FLAG (-2) /* bundle these together */
+#define KIND_NEEDS_SLEEP_IN_SENDER (-3) /* bundle these together */
+
+struct ctlmsgfd {
+ struct cmsghdr cmf_cmsg;
+ int cmf_fdspace; /* padding in cmf_cmsg implies fd might */
+ /* not be here, might be in cmf_cmsg! */
+};
+
+/*
+ * sendmsg(2) call to be sent individually or bundled up with other such
+ * calls with sendmmsg(2) by copying them into an struct mmsghdr array, the
+ * sending is done by a pthread_create(3) created thread
+ */
+
+struct sendmsg_call {
+ pthread_t smc_pthr; /* must be first */
+ int smc_sockfd;
+ struct msghdr *smc_msg;
+ int smc_flags;
+ ssize_t smc_value;
+ int smc_errno;
+};
+
+/*
+ * sendmmsg(2) call, will be done as a single sendmmsg(2) call or vlen
+ * individual sendmsg(2) calls
+ */
+
+struct sendmmsg_call {
+ pthread_t smmc_pthr; /* must be first */
+ int smmc_sockfd;
+ unsigned smmc_vlen;
+ struct mmsghdr *smmc_smm;
+ int smmc_flags;
+ bool smmc_call_sendmmsg;
+ bool smmc_sender_sleeps;
+ int smmc_value;
+ int smmc_errno;
+};
+
+/*
+ * recvmsg(2) call to be received individually or bundled up with other
+ * such calls with recvmmsg(2) by copying them into an struct mmsghdr array
+ */
+
+struct recvmsg_call {
+ ssize_t rmc_value;
+ int rmc_errno;
+ struct msghdr *rmc_msg;
+ int rmc_flags;
+};
+
+/*
+ * client server connection
+ *
+ * A struct con abstracts a connection between a client and a server, it is used
+ * two different ways:
+ *
+ * con_call_vec == NULL && con_call_n == 0 && con_call_sendmmsg == false
+ *
+ * - Individual tests encapsulated in a single test function, these are
+ * usually just one sendmsg(2) and one or maybe two sequential recvmsg(2)
+ * calls, for example to peek then get the message or to test non-blocking
+ * then blocking recvmsg, etc.
+ *
+ * con_call_vec != NULL && con_call_n >= 1
+ *
+ * - Bundled unrelated tests each encapsulated in their own test function,
+ * all of these tests have to be implemented with a 1 send and 1 recveive,
+ * so that they can all run with a multi-receive message, recvmmsg(2), call.
+ * In that case the struct con is used to hide the bundling of the tests from
+ * each other and the tests, unsupectingly, pass the test vector via the
+ * con_call_vec and con_call_n members. Note that con_call_n == 1 is used
+ * as a degenerate case to test recvmmsg(2) with a single message. Note
+ * when recvmmsg(2) is used the messages can be sent individually or in
+ * a single sendmmsg(2) for testing multi-message sending. Both ways of
+ * sending are done controlled by con_call_sendmmsg == true to indicate
+ * used of sendmmsg(2), otherwise multiple sendmsg(2) are used.
+ */
+
+struct call;
+
+struct con {
+ int con_client;
+ int con_server;
+ int con_type;
+ struct sockaddr_in con_server_sin;
+ bool con_call_sendmmsg;
+ bool con_sender_sleeps;
+ bool con_waitforone;
+ bool con_timeout;
+ size_t con_call_end; /* index of last+1 filled */
+ size_t con_call_n;
+ struct call **con_call_vec;/* points to array of con_call_n */
+ /* pointers to be used in a */
+ /* recvmmsg(2) multi-message test */
+ struct sendmsg_call **con_smc_vec;
+ struct recvmsg_call **con_rmc_vec;
+};
+
+struct call {
+ int (*call_test)(struct call *, struct con *, int);
+ void (*call_coninit)(struct con *);
+ int call_errno; /* set on first run */
+ size_t call_niov;
+ bool call_iov_max_relative;
+ int call_cloexec_flag;
+ bool call_some_data;
+ ssize_t call_controllen_delta;
+};
+
+/*
+ * Prototypes.
+ */
+
+static void tests_setup(void);
+static int tests_select(void (*coninit)(struct con *),
+ struct call *chosen[], int error);
+
+static void tests_run(void);
+static void tests_run_individually(void);
+static void tests_run_all_socketpair(void);
+static void tests_run_all_ocloexec_flag(void);
+static void tests_run_all_sock_sepacket(void);
+static void tests_run_all_sock_sepacket_with_n_repeat(void);
+
+static void sleep_ms(long ms);
+
+static void thread_call(pthread_t *pthr, void *(*func)(void *));
+static void thread_join(pthread_t pthr);
+
+static int test_number_get(void);
+
+static int subtest_number(void);
+static void subtest_nest(void);
+static void subtest_unnest(void);
+static bool subtest_show(void);
+
+static bool mem_is_zero(void *a, size_t size);
+
+static void sockaddr_in_init(struct sockaddr_in *sinp, sa_family_t family,
+ in_port_t port, uint32_t ip);
+
+static size_t iovec_max(void);
+static void iovec_init(struct iovec *iovecp, void *base, size_t len);
+
+static void msghdr_init(struct msghdr *msgp, struct sockaddr_in *sinp,
+ struct iovec *iovecp, size_t iovlen, int flags);
+static void msghdr_init_with_control(struct msghdr *msgp,
+ struct sockaddr_in *sinp,
+ struct iovec *iovecp, size_t iovlen,
+ void *control, size_t controllen,
+ int flags);
+
+static void mmsghdr_init(struct mmsghdr *mmsgp, struct msghdr *msgp);
+
+static void recvmsg_call_init(struct recvmsg_call *rmcp,
+ struct msghdr *msgp, int flags);
+
+static void sendmsg_call_init(struct sendmsg_call *smcp, int sockfd,
+ struct msghdr *msgp, int flags);
+static void *sendmsg_call(struct sendmsg_call *smcp);
+static void *sendmsg_call_after_sleep(struct sendmsg_call *smcp);
+
+static void sendmmsg_init(struct sendmmsg_call *smmcp, int sockfd,
+ unsigned vlen, struct mmsghdr *smm, int flags,
+ bool call_sendmmsg, bool sender_sleeps);
+
+static void ctlmsgfd_init(struct ctlmsgfd *cmfp);
+static void ctlmsgfd_init_fd(struct ctlmsgfd *cmfp, int fd);
+static int ctlmsgfd_get_fd(struct ctlmsgfd *cmfp);
+
+static void con_init(struct con *conp, int client, int server,
+ int type, struct sockaddr_in *sinp);
+static void con_init_base(struct con *conp, int client, int server,
+ int type, struct sockaddr_in *sinp);
+static void con_make_vectored(struct con *conp, bool sendmulti,
+ size_t n, struct call **call_vec,
+ struct sendmsg_call **smc_vec,
+ struct recvmsg_call **rmc_vec);
+static void con_make_waitforone_tests(struct con *conp);
+static void con_make_timeout_tests(struct con *conp);
+static void con_init_socketpair(struct con *conp);
+static void con_init_seqpacket(struct con *conp);
+static void con_init_dgram(struct con *conp);
+static void con_deinit(struct con *conp);
+static void con_sendmmsg_recvmmsg(struct con *conp, size_t n,
+ int sflags, struct mmsghdr *smm,
+ int rflags, struct mmsghdr *rmm);
+static void con_do_multi_send_recv(struct con *conp);
+static void con_add_send_recv_calls_vec(struct con *conp,
+ struct sendmsg_call *smcp,
+ struct recvmsg_call *rmcp,
+ bool sender_sleeps);
+static void con_add_send_recv_calls(struct con *conp,
+ struct sendmsg_call *smcp,
+ struct recvmsg_call *rmcp,
+ bool sender_sleeps);
+
+static int call_go(struct call *callp, struct con *conp);
+
+static int call_receive_iovec_boundary_checks(struct call *callp,
+ struct con *conp, int tn);
+static int call_receive_iovec_boundary_checks_peeking(struct call *callp,
+ struct con *conp, int tn);
+static int call_receive_file_descriptor(struct call *callp,
+ struct con *conp, int tn);
+static int call_message_too_long_to_fit_discards_bytes(struct call *callp,
+ struct con *conp,
+ int tn);
+static int call_message_too_long_to_fit_doesnt_discard_bytes(struct call *callp,
+ struct con *conp,
+ int tn);
+static int call_receive_waits_for_message(struct call *callp,
+ struct con *conp, int tn);
+static int call_receiver_doesnt_wait_for_messages(struct call *callp,
+ struct con *conp, int tn);
+static int call_receive_returns_what_is_available(struct call *callp,
+ struct con *conp, int tn);
+static int call_empty_datagram_received_as_empty_datagram(struct call *callp,
+ struct con *conp,
+ int tn);
+
+static int con_receive_iovec_boundary_checks(struct con *conp,
+ int tn, size_t niov);
+static int con_receive_iovec_boundary_checks_peeking(struct con *conp, int tn,
+ size_t niov);
+static int con_receive_file_descriptor(struct con *conp, int tn,
+ int cloexec_flag,
+ bool some_data,
+ ssize_t controllen_delta);
+static int con_message_too_long_to_fit_discards_bytes(struct con *conp, int tn);
+static int con_message_too_long_to_fit_doesnt_discard_bytes(struct con *conp,
+ int tn);
+static int con_receive_waits_for_message(struct con *conp, int tn);
+static int con_receiver_doesnt_wait_for_messages(struct con *conp, int tn);
+static int con_receive_returns_what_is_available(struct con *conp, int tn);
+static int con_empty_datagram_received_as_empty_datagram(struct con *conp,
+ int tn);
+
+/*
+ * test_assert() checks something that should not fail in the test,
+ * if it fails the test code is broken, not the system call being tested.
+ */
+
+#define test_assert(expr) \
+ ((expr) ? (void)0: tst_brk(TBROK, "%s", #expr))
+
+/*
+ * ensure() tests something that shouldn't fail
+ *
+ * Test cases use ensure(expr) to indicate that an expression is expected
+ * to be true. When the test cases are run in a verbose manner, the ensure
+ * uses produce a trace of the conditions being tested, making it easier
+ * to understand what the test cases are verifying.
+ *
+ * Messages are not shown unless an expected condition is not true, but if
+ * the level of verbosity is high enough, then the non-failing ensure()
+ * expressions are shown too, this is just for debug-ability of the tests,
+ * in this case the ensure() serve as a trace.
+ *
+ * A test, made out of multiple ensure() calls is expected not to change the
+ * value of ensure_failures.
+ */
+
+int ensure_failures;
+
+#define ensure(test_number, expr) \
+ ((expr) ? (test_verbose < 2 ? (void)0 : \
+ tst_res(TINFO, "%s(): true: %s", __FUNCTION__, #expr)) \
+ : (++ensure_failures, \
+ tst_res(TINFO, "test_number = %d: %s(): false: %s", \
+ test_number, __FUNCTION__, #expr)))
+
+/*
+ * Leaf test variations, combined into multi-message tests at run time.
+ */
+
+struct call call_tests[] = {
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 1},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 2},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 3},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = -2,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = -1,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 0,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 1,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks_peeking,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 2,
+ .call_iov_max_relative = true},
+
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 1},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 2},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 3},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = -2,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = -1,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 0,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 1,
+ .call_iov_max_relative = true},
+ {.call_test = call_receive_iovec_boundary_checks,
+ .call_coninit = con_init_socketpair,
+ .call_niov = 2,
+ .call_iov_max_relative = true},
+
+ {.call_test = call_empty_datagram_received_as_empty_datagram,
+ .call_coninit = con_init_dgram},
+ {.call_test = call_message_too_long_to_fit_discards_bytes,
+ .call_coninit = con_init_dgram},
+
+ {.call_test = call_message_too_long_to_fit_doesnt_discard_bytes,
+ .call_coninit = con_init_seqpacket},
+ {.call_test = call_receive_waits_for_message,
+ .call_coninit = con_init_seqpacket},
+ {.call_test = call_receiver_doesnt_wait_for_messages,
+ .call_coninit = con_init_seqpacket},
+ {.call_test = call_receive_returns_what_is_available,
+ .call_coninit = con_init_seqpacket},
+
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = true,
+ .call_controllen_delta = 0},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = false,
+ .call_controllen_delta = 0},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = true,
+ .call_controllen_delta = 0},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = false,
+ .call_controllen_delta = 0},
+
+ /*
+ * struct ctlmsgfd (with 64 bit size_t) gets an extra int of padding
+ * missing bytes then has to be sizeof(size_t) not sizeof(int),
+ * otherwise there is still space for a sizeof(int) sized fd.
+ */
+
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = true,
+ .call_controllen_delta = -sizeof(size_t)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = false,
+ .call_controllen_delta = -sizeof(size_t)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = true,
+ .call_controllen_delta = -sizeof(size_t)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = false,
+ .call_controllen_delta = -sizeof(size_t)},
+
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = true,
+ .call_controllen_delta = -(ssize_t) (2 * sizeof(size_t))},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = false,
+ .call_controllen_delta = -(ssize_t) (2 * sizeof(size_t))},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = true,
+ .call_controllen_delta = -(ssize_t) (2 * sizeof(size_t))},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = false,
+ .call_controllen_delta = -(ssize_t) (2 * sizeof(size_t))},
+
+ /*
+ * Just one byte in the underlying struct cmsghdr.
+ */
+
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = true,
+ .call_controllen_delta = -(ssize_t) (sizeof(struct ctlmsgfd) - 1)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = 0,
+ .call_some_data = false,
+ .call_controllen_delta = -(ssize_t) (sizeof(struct ctlmsgfd) - 1)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = true,
+ .call_controllen_delta = -(ssize_t) (sizeof(struct ctlmsgfd) - 1)},
+ {.call_test = call_receive_file_descriptor,
+ .call_coninit = con_init_socketpair,
+ .call_cloexec_flag = MSG_CMSG_CLOEXEC,
+ .call_some_data = false,
+ .call_controllen_delta = -(ssize_t) (sizeof(struct ctlmsgfd) - 1)},
+};
+
+#define N_TESTS ARRAY_SIZE(call_tests)
+#define call_tests_end (&call_tests[N_TESTS])
+
+int test_verbose = 0; /* 1 a bit verbose, 2 more verbose */
+char *prog_name;
+bool run_sctp_tests = RUN_SCTP_TESTS;
+
+/*
+ * Test functions.
+ *
+ * The lowest level test functions are organized as standalone test
+ * functions that can check some aspect of sending and receiving messages
+ * works per the system call documentation.
+ *
+ * These lowest level test functions are written as straight line code
+ * that sets up a message to be send, hands the message to a thread,
+ * to send it and the receiver proceeds to receive the message.
+ *
+ * Some of these test cases involve multiple sends and receives to test
+ * various conditions, but most involve a single send and receive.
+ *
+ * To be able to test multi-send and multi-receive sequences, the lowest
+ * level test functions are combined into sendmmsg(2) and recvmmsg(2)
+ * system calls. The underlying lowest level test functions don't know
+ * about this, but each one of them does the setup logical sending (which
+ * in this case is postponed to be combined into a multi-message send)
+ * and the logical receiving (which in this case works on the proper
+ * message received from a multi-message receive operation).
+ *
+ * Some lowest level test functions can be combined with others, some
+ * can't, for example because they use a different type of connection,
+ * for example a socketpair() vs an SCTP/IP connection. Or because they
+ * require different flags, and the send message flag is shared by all
+ * the messages being sent, it is not per message, so they can only
+ * be aggregated into a multi-message operation if their flags don't
+ * conflict.
+ *
+ * The test selection and combination functions can be found by
+ * searching for the callers of: tests_select()
+ */
+
+/*
+ * Various boundary tests related to use of more than one iovec
+ */
+
+static int con_receive_iovec_boundary_checks(struct con *conp,
+ int tn, size_t niov)
+{
+ size_t iov_max = iovec_max();
+ int salt = tn + subtest_number(); /* salt test data */
+
+ int client_data[niov];
+ struct iovec client_iov[niov];
+ size_t i;
+ for (i = 0; i < niov; ++i) {
+ client_data[i] = i + salt;
+ iovec_init(&client_iov[i], &client_data[i],
+ sizeof(client_data[0]));
+ }
+
+ int server_data[niov];
+ struct iovec server_iov[niov];
+ for (i = 0; i < niov; ++i) {
+ server_data[i] = 0;
+ iovec_init(&server_iov[i], &server_data[i],
+ sizeof(server_data[0]));
+ }
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, NULL, client_iov, niov, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, NULL, server_iov, niov, 0);
+
+ struct recvmsg_call rmc;
+ recvmsg_call_init(&rmc, &server_recv, 0);
+
+ con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+ ssize_t received = rmc.rmc_value;
+ if (niov > iov_max) {
+ ensure(tn, smc.smc_value == -1);
+ ensure(tn, smc.smc_errno == EMSGSIZE);
+ ensure(tn, received == -1);
+ ensure(tn, rmc.rmc_errno == EMSGSIZE);
+ ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+ return errno;
+ }
+
+ if (smc.smc_value < 0)
+ tst_res(TBROK, "sendmsg");
+ if (received < 0)
+ tst_res(TBROK, "recmsg or recvmmsg");
+ ensure(tn, (size_t) received == sizeof(client_data));
+ ensure(tn, !memcmp(client_data, server_data, received));
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receive_iovec_boundary_checks(niov = %d)"
+ " // iov_max = %d",
+ (int) niov, (int) iovec_max());
+ return 0;
+}
+
+/*
+ * Various boundary tests related to use of more than one iovec,
+ * peek at the data first, then receive it
+ */
+
+static int con_receive_iovec_boundary_checks_peeking(struct con *conp,
+ int tn, size_t niov)
+{
+ size_t iov_max = iovec_max();
+
+ int client_data[niov];
+ struct iovec client_iov[niov];
+ size_t i;
+ for (i = 0; i < niov; ++i) {
+ client_data[i] = i;
+ iovec_init(&client_iov[i], &client_data[i],
+ sizeof(client_data[0]));
+ }
+
+ int server_data[niov];
+ struct iovec server_iov[niov];
+ for (i = 0; i < niov; ++i) {
+ server_data[i] = 0;
+ iovec_init(&server_iov[i], &server_data[i],
+ sizeof(server_data[0]));
+ }
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, NULL, client_iov, niov, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, NULL, server_iov, niov, 0);
+
+ thread_call(&smc.smc_pthr, (void *(*)(void *)) sendmsg_call);
+ ssize_t received = recvmsg(conp->con_server, &server_recv, MSG_PEEK);
+ thread_join(smc.smc_pthr);
+
+ if (niov > iov_max) {
+ ensure(tn, smc.smc_value == -1);
+ ensure(tn, smc.smc_errno == EMSGSIZE);
+ ensure(tn, received == -1);
+ ensure(tn, errno == EMSGSIZE);
+ ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+ } else {
+ if (smc.smc_value < 0)
+ tst_res(TBROK, "sendmsg");
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+ ensure(tn, (size_t) received == sizeof(client_data));
+ ensure(tn, !memcmp(client_data, server_data, received));
+ }
+
+ received = recvmsg(conp->con_server, &server_recv, 0);
+ if (niov > iov_max) {
+ ensure(tn, smc.smc_value == -1);
+ ensure(tn, smc.smc_errno == EMSGSIZE);
+ ensure(tn, received == -1);
+ ensure(tn, errno == EMSGSIZE);
+ ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+ } else {
+ if (smc.smc_value < 0)
+ tst_res(TBROK, "sendmsg");
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+ ensure(tn, (size_t) received == sizeof(client_data));
+ ensure(tn, !memcmp(client_data, server_data, received));
+ errno = KIND_HAS_MULTIPLE_RECVMSG;
+ }
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receive_iovec_boundary_checks_peeking(niov"
+ " = %d) // iov_max = %d",
+ (int) niov, (int) iovec_max());
+ return errno;;
+}
+
+/*
+ * Test that file descriptor is received over a unix domain socket.
+ * Variations to test the close on exec flag, whether some amount of
+ * data is to be sent as regular data, and missing bytes of space for
+ * the control message to receive the file descriptor. The missing
+ * bytes are expressed as a (signed) ssize_t controllen_delta. Passing
+ * in struct cmsghdr requires a negative delta to be small enough so as to
+ * actually cause trancation of the space for an int sized file descriptor,
+ * a negative controllen_delta must be <= -(ssize_t)(sizeof(size_t)).
+ */
+
+static int con_receive_file_descriptor(struct con *conp, int tn,
+ int cloexec_flag, bool some_data,
+ ssize_t controllen_delta)
+{
+ test_assert(controllen_delta >= 0 ||
+ controllen_delta <= -(ssize_t) (sizeof(size_t)));
+ test_assert(cloexec_flag == 0 || cloexec_flag == MSG_CMSG_CLOEXEC);
+
+ int pipefd[2];
+ SAFE_PIPE(pipefd); /* something to send that's easy to check */
+ int pipe_read = pipefd[0];
+ int pipe_write = pipefd[1];
+
+ struct ctlmsgfd client_ctlmsgfd;
+ ctlmsgfd_init_fd(&client_ctlmsgfd, pipe_write);
+
+ struct iovec client_iov;
+ char server_data = 0;
+ iovec_init(&client_iov, "m", 1);
+ struct iovec server_iov;
+ iovec_init(&server_iov, &server_data, 1);
+
+ struct iovec *client_iovp = NULL;
+ struct iovec *server_iovp = NULL;
+ size_t client_iovlen = 0;
+ size_t server_iovlen = 0;
+ size_t expected = 0;
+ if (some_data) {
+ client_iovp = &client_iov;
+ server_iovp = &server_iov;
+ client_iovlen = 1;
+ server_iovlen = 1;
+ expected = 1;
+ }
+
+ struct msghdr client_send;
+ msghdr_init_with_control(&client_send, NULL, client_iovp, client_iovlen,
+ &client_ctlmsgfd, sizeof(client_ctlmsgfd), 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ struct ctlmsgfd server_ctlmsgfd;
+ ctlmsgfd_init(&server_ctlmsgfd);
+
+ struct msghdr server_recv;
+ msghdr_init_with_control(&server_recv, NULL, server_iovp, server_iovlen,
+ &server_ctlmsgfd,
+ (size_t) ((ssize_t) sizeof(server_ctlmsgfd)
+ + controllen_delta),
+ 0);
+
+ struct recvmsg_call rmc;
+ recvmsg_call_init(&rmc, &server_recv, cloexec_flag);
+
+ con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+ ssize_t received = rmc.rmc_value;
+ if (received < 0)
+ tst_res(TBROK, "recvmsg or recvmmsg");
+
+ ensure(tn, (size_t) received == expected);
+ if (some_data)
+ ensure(tn, server_data == 'm');
+
+ if (controllen_delta < 0)
+ ensure(tn, server_recv.msg_flags & MSG_CTRUNC);
+ else {
+ ensure(tn, ! (server_recv.msg_flags & MSG_CTRUNC));
+ ensure(tn, server_ctlmsgfd.cmf_cmsg.cmsg_len >=
+ sizeof(struct ctlmsgfd));
+ int received_fd = ctlmsgfd_get_fd(&server_ctlmsgfd);
+ ensure(tn, received_fd > 0);
+
+ ssize_t nwrote = SAFE_WRITE(0, received_fd, "p", 1);
+ ensure(tn, nwrote == 1);
+
+ char c;
+ ssize_t nread = SAFE_READ(0, pipe_read, &c, 1);
+ ensure(tn, nread == 1);
+ ensure(tn, c == 'p');
+
+ int fdflags = SAFE_FCNTL(received_fd, F_GETFD, 0);
+ if (cloexec_flag)
+ ensure(tn, fdflags & FD_CLOEXEC);
+ else
+ ensure(tn, ! (fdflags & FD_CLOEXEC));
+ close(received_fd);
+ }
+
+ close(pipe_read);
+ close(pipe_write);
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receive_file_descriptor("
+ "cloexec_flag = %s, some_data = %d, "
+ "controllen_delta = %ld)",
+ (cloexec_flag & MSG_CMSG_CLOEXEC) ?
+ "MSG_CMSG_CLOEXEC" : "0",
+ some_data,
+ (long) controllen_delta);
+ if (cloexec_flag)
+ return KIND_CLOEXEC_FLAG; /* can not mix flags */
+ return 0;
+}
+
+/*
+ * To be able to test this specification in the recvmsg(2) man page:
+ *
+ * "If a message is too long to fit in the supplied buffer,
+ * excess bytes may be discarded depending on the type of
+ * socket the message is received from."
+ *
+ * The udp(7) protocol discards the excess bytes.
+ *
+ * The test sends a 16 byte message, but receives it with an 8 byte buffer,
+ * a second recvmsg(2) is done to see if the next 8 bytes are of a 2nd
+ * identical 16 byte packet sent or if they belong to the first packet.
+ */
+
+static int con_message_too_long_to_fit_discards_bytes(struct con *conp, int tn)
+{
+ size_t total = 16;
+ test_assert(total % 2 == 0); /* must be even */
+ size_t half = total / 2;
+
+ struct iovec client_iov;
+ iovec_init(&client_iov, "0123456789ABCDEF", total);
+ test_assert(strlen(client_iov.iov_base) == total);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ char server_data[half + 1]; /* client sends total, receive half */
+ memset(&server_data, 0, half); /* 1 byte of space for GUARD_CHAR */
+ server_data[half] = GUARD_CHAR;
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, half);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ thread_call(&smc.smc_pthr, (void *(*)(void *)) sendmsg_call);
+ ssize_t received = recvmsg(conp->con_server, &server_recv, 0);
+ thread_join(smc.smc_pthr);
+
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+
+ ensure(tn, (size_t) received == half);
+ ensure(tn, server_data[half] == GUARD_CHAR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, half));
+ ensure(tn, server_recv.msg_flags & MSG_TRUNC);
+
+ /*
+ * Send the same data in a second message from client, should
+ * receive the same data, not the second half of the first
+ * message, meaning then the excess bytes are being discarded.
+ */
+
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ thread_call(&smc.smc_pthr, (void *(*)(void *)) sendmsg_call);
+ received = recvmsg(conp->con_server, &server_recv, MSG_TRUNC);
+ thread_join(smc.smc_pthr);
+
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+
+ /*
+ * MSG_TRUNC in this recvmsg() asks for real size of the datagram,
+ * not the read size, for SOCK_DGRAM this should be total not half
+ * the GUARD_CHAR check below ensures that no more than half were
+ * read, and the check on the read bytes ensures half were returned
+ *
+ * "MSG_TRUNC"
+ * "For raw (AF_PACKET), Internet datagram, netlink, and UNIX
+ * datagram sockets: return the real length of the packet or
+ * datagram, even when it was longer than the passed buffer."
+ * -- recvmsg(2)
+ */
+
+ ensure(tn, (size_t) received == total);
+ ensure(tn, server_data[half] == GUARD_CHAR);
+ ensure(tn, ! (server_recv.msg_flags & MSG_EOR));
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, half));
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_message_too_long_to_fit_discards_bytes");
+ return KIND_HAS_MULTIPLE_RECVMSG;
+}
+
+/*
+ * To be able to test this specification in the recvmsg(2) man page:
+ *
+ * "If a message is too long to fit in the supplied buffer,
+ * excess bytes may be discarded depending on the type of
+ * socket the message is received from."
+ *
+ * The sctp(7) protocol does not discard the excess bytes.
+ *
+ * Tests for MSG_EOR being set together with the non-discarded bytes.
+ * The test sends a 16 byte message, but receives it with an 8 byte buffer,
+ * a second recvmsg(2) is done to see if the next 8 bytes are of a 2nd
+ * identical 16 byte packet sent or if they belong to the first packet.
+ */
+
+static int con_message_too_long_to_fit_doesnt_discard_bytes(struct con *conp,
+ int tn)
+{
+ size_t total = 16;
+ test_assert(total % 2 == 0); /* must be even */
+ size_t half = total / 2;
+
+ struct iovec client_iov;
+ iovec_init(&client_iov, "0123456789ABCDEF", total);
+ test_assert(strlen(client_iov.iov_base) == total);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ char server_data[half + 1]; /* client sends total, receive half */
+ memset(&server_data, 0, half); /* 1 byte of space for GUARD_CHAR */
+ server_data[half] = GUARD_CHAR;
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, half);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ thread_call(&smc.smc_pthr, (void *(*)(void *)) sendmsg_call);
+ ssize_t received = recvmsg(conp->con_server, &server_recv, 0);
+ thread_join(smc.smc_pthr);
+
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+
+ ensure(tn, (size_t) received == half);
+ ensure(tn, server_data[half] == GUARD_CHAR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, half));
+ ensure(tn, server_recv.msg_flags == 0);
+
+ /*
+ * Send the same data in a second message from client, if it
+ * receives the second half of the first message, then they are not
+ * being discarded and MSG_EOR indicates the end of the first record.
+ */
+
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ thread_call(&smc.smc_pthr, (void *(*)(void *)) sendmsg_call);
+ received = recvmsg(conp->con_server, &server_recv, MSG_TRUNC);
+ thread_join(smc.smc_pthr);
+
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+
+ /*
+ * MSG_TRUNC in this recvmsg() asks for real size of the datagram,
+ * not the read size, for SOCK_DGRAM this should be total not half
+ * the GUARD_CHAR check below ensures that no more than half were
+ * read, and the check on the read bytes ensures half were returned
+ *
+ * "MSG_TRUNC"
+ * "For raw (AF_PACKET), Internet datagram, netlink, and UNIX
+ * datagram sockets: return the real length of the packet or
+ * datagram, even when it was longer than the passed buffer."
+ * -- recvmsg(2)
+ */
+
+ ensure(tn, (size_t) received == half);
+ ensure(tn, server_data[half] == GUARD_CHAR);
+ ensure(tn, server_recv.msg_flags & MSG_EOR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base + half, half));
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO,
+ "con_message_too_long_to_fit_doesnt_discard_bytes");
+ return KIND_HAS_MULTIPLE_RECVMSG;
+}
+
+/*
+ * recvmsg(2):
+ *
+ * "If no messages are available at the socket, the
+ * receive calls wait for a message to arrive"
+ *
+ * The sending thread waits a second prior to sending to see if the
+ * recvmsg(2) indeed waits for the message to arrive.
+ */
+
+static int con_receive_waits_for_message(struct con *conp, int tn)
+{
+ size_t total = 16;
+ struct iovec client_iov;
+ iovec_init(&client_iov, "0123456789ABCDEF", total);
+ test_assert(strlen(client_iov.iov_base) == total);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ char server_data[total + 1];
+ memset(&server_data, 0, total); /* 1 byte of space for GUARD_CHAR */
+ server_data[total] = GUARD_CHAR;
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, total);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ struct recvmsg_call rmc;
+ recvmsg_call_init(&rmc, &server_recv, 0);
+
+ con_add_send_recv_calls(conp, &smc, &rmc, true);
+
+ ssize_t received = rmc.rmc_value;
+ if (received < 0)
+ tst_res(TBROK, "recvmsg or recvmmsg");
+
+ ensure(tn, (size_t) received == total);
+ ensure(tn, server_data[total] == GUARD_CHAR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, total));
+ ensure(tn, server_recv.msg_flags & MSG_EOR);
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receive_waits_for_message()");
+ return KIND_NEEDS_SLEEP_IN_SENDER;
+}
+
+/*
+ * recvmsg(2):
+ *
+ * "If no messages are available at the socket, the
+ * receive calls wait for a message to arrive, unless
+ * the socket is nonblocking (see fcntl(2)), in which
+ * case the value -1 is returned and the external variable
+ * errno is set to EAGAIN or EWOULDBLOCK."
+ *
+ * If a message is never sent, the receiver should not block.
+ */
+
+static int con_receiver_doesnt_wait_for_messages(struct con *conp, int tn)
+{
+ size_t total = 16;
+ struct iovec client_iov;
+ iovec_init(&client_iov, "0123456789ABCDEF", total);
+ test_assert(strlen(client_iov.iov_base) == total);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ char server_data[total + 1];
+ memset(&server_data, 0, total); /* 1 byte of space for GUARD_CHAR */
+ server_data[total] = GUARD_CHAR;
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, total);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ memset(&server_recv, 0, sizeof(server_recv));
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ thread_call(&smc.smc_pthr,
+ (void *(*)(void *)) sendmsg_call_after_sleep);
+ ssize_t received = recvmsg(conp->con_server,
+ &server_recv, MSG_DONTWAIT);
+ ensure(tn, received == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
+ received = recvmsg(conp->con_server, &server_recv, 0); /* now wait */
+ thread_join(smc.smc_pthr);
+
+ if (received < 0)
+ tst_res(TBROK, "recvmsg");
+
+ ensure(tn, (size_t) received == total);
+ ensure(tn, server_data[total] == GUARD_CHAR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, total));
+ ensure(tn, server_recv.msg_flags & MSG_EOR);
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receiver_doesnt_wait_for_messages()");
+ return KIND_HAS_MULTIPLE_RECVMSG;
+}
+
+/*
+ * recvmsg(2):
+ *
+ * "The receive calls normally return any data available,
+ * up to the requested amount, rather than waiting for
+ * receipt of the full amount requested."
+ */
+
+static int con_receive_returns_what_is_available(struct con *conp, int tn)
+{
+ size_t total = 8;
+ struct iovec client_iov;
+ iovec_init(&client_iov, "01234567", total);
+ test_assert(strlen(client_iov.iov_base) == total);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ size_t twice = 2 * total;
+ char server_data[twice + 1]; /* client sends total, want twice */
+ memset(&server_data, 0, twice); /* 1 byte of space for GUARD_CHAR */
+ server_data[total] = GUARD_CHAR;
+ server_data[twice] = GUARD_CHAR; /* set two guards */
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, twice);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ struct recvmsg_call rmc;
+ recvmsg_call_init(&rmc, &server_recv, 0);
+
+ con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+ ssize_t received = rmc.rmc_value;
+ if (received < 0)
+ tst_res(TBROK, "recvmsg or recvmmsg");
+
+ ensure(tn, (size_t) received == total);
+ ensure(tn, server_data[total] == GUARD_CHAR);
+ ensure(tn, server_data[twice] == GUARD_CHAR);
+ ensure(tn, !memcmp(server_data, client_iov.iov_base, total));
+ ensure(tn, server_recv.msg_flags & MSG_EOR);
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO, "con_receive_returns_what_is_available()");
+ return 0;
+}
+
+/*
+ * recvmsg(2):
+ *
+ * "Datagram sockets in various domains (e.g., the UNIX and Internet
+ * domains) permit zero-length datagrams. When such a datagram is
+ * received, the return value is 0."
+ */
+
+static int con_empty_datagram_received_as_empty_datagram(struct con *conp,
+ int tn)
+{
+ struct iovec client_iov;
+ iovec_init(&client_iov, "", 0);
+
+ struct msghdr client_send;
+ msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+ struct sendmsg_call smc;
+ sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+ char server_data[1]; /* client sends nothing */
+ server_data[0] = GUARD_CHAR; /* set guard */
+ struct iovec server_iov;
+ iovec_init(&server_iov, server_data, 1);
+
+ struct sockaddr_in client_sin;
+ memset(&client_sin, 0, sizeof(client_sin));
+ struct msghdr server_recv;
+ msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+ struct recvmsg_call rmc;
+ recvmsg_call_init(&rmc, &server_recv, 0);
+
+ con_add_send_recv_calls(conp, &smc, &rmc, true);
+
+ ssize_t received = rmc.rmc_value;
+ if (received < 0)
+ tst_res(TBROK, "recvmsg or recvmmsg");
+
+ ensure(tn, received == 0);
+ ensure(tn, server_data[0] == GUARD_CHAR);
+
+ if (test_verbose || subtest_show())
+ tst_res(TINFO,
+ "con_empty_datagram_received_as_empty_datagram()");
+ return KIND_NEEDS_SLEEP_IN_SENDER;
+}
+
+/*
+ * The test functions above have variations based on their arguments.
+ *
+ * The variations are wrapped with specific argument values into objects
+ * that derive from struct call, the wrapped objects don't change so they
+ * can be used to repeat a test multiple times for a multi-message call.
+ *
+ */
+
+static void tests_run(void)
+{
+ tests_run_individually();
+ tests_run_all_socketpair();
+ if (run_sctp_tests) {
+ tests_run_all_sock_sepacket();
+ tests_run_all_sock_sepacket_with_n_repeat();
+ }
+ tests_run_all_ocloexec_flag();
+}
+
+/*
+ * Test selection and combination into multi-message tests.
+ */
+
+static void tests_setup(void)
+{
+ size_t iov_max = iovec_max();
+ struct call *c;
+ for (c = call_tests; c < call_tests_end; ++c)
+ if (c->call_iov_max_relative)
+ c->call_niov += iov_max;
+}
+
+static int tests_select(void (*coninit)(struct con *),
+ struct call *chosen[], int error)
+{
+ int n = 0;
+ struct call *c;
+ for (c = call_tests; c < call_tests_end; ++c)
+ if (c->call_coninit == coninit && c->call_errno == error) {
+ if (!run_sctp_tests &&
+ c->call_coninit == con_init_seqpacket)
+ continue;
+ *chosen++ = c;
+ ++n;
+ }
+ return n;
+}
+
+static void tests_run_individually(void)
+{
+ tst_res(TINFO, "Running %d individual sendmsg(2) tests:\n",
+ (int) N_TESTS);
+ struct call *c;
+ for (c = call_tests; c < call_tests_end; ++c) {
+ if (!run_sctp_tests && c->call_coninit == con_init_seqpacket)
+ continue;
+ struct con con;
+ c->call_coninit(&con);
+ c->call_errno = call_go(c, &con);
+ con_deinit(&con);
+ }
+}
+
+static void tests_run_all_socketpair(void)
+{
+ struct con con;
+ int error;
+ struct call *callp;
+ size_t max_tests = iovec_max();
+ test_assert(max_tests > N_TESTS);
+ struct call *call_tests_selected[max_tests + 1];
+ struct sendmsg_call *smc_vec[max_tests];
+ struct recvmsg_call *rmc_vec[max_tests];
+
+ /*
+ * Select all the socketpair() based test cases a test array so
+ * they can be used together in single recvmmsg() multi-message test
+ * that uses the same connection.
+ */
+
+ int n_selected = tests_select(con_init_socketpair,
+ call_tests_selected, 0);
+
+ /*
+ * Call them individually sharing the connection one after the
+ * other to ensure there are no dependencies, for example left-
+ * over data leakage left in the socket between individual tests
+ * that might break the tests
+ */
+
+ tst_res(TINFO, "Running %d non-error socketpair() "
+ "tests sharing a connection:\n", (int) n_selected);
+ con_init_socketpair(&con);
+ int i;
+ for (i = 0; i < n_selected; ++i) {
+ callp = call_tests_selected[i];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ }
+ con_deinit(&con);
+
+ /*
+ * We have n_selected tests that can work over the same type of
+ * connection and don't have any failures. Use them for mult-receive
+ * recvmmsg(2) testing.
+ *
+ * Calling the first test causes it to call in the middle of it
+ * (unknown to it) the next one in a nested recursive call, the
+ * same occurs for each one of them, when there are no more to
+ * recurse all the struct sendmsg_call and struct recvmsg_call have been
+ * gathered and they can be used to craft the single recvmmsg(2)
+ * call which is tested both with n_selected sendmsg(2) calls or
+ * a single call to sendmmsg(2).
+ *
+ * The recursion occurs, unknown by the tests, in their call to:
+ * con_add_send_recv_calls()
+ */
+
+ int sendmulti;
+ for (sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+ con_init_socketpair(&con);
+ tst_res(TINFO, "Running %d non-error socketpair() tests"
+ " with a single recvmmsg(2) call and %s:\n",
+ n_selected,
+ sendmulti ? "a single sendmmsg(2) call"
+ : "multiple sendmsg(2) calls");
+ con_make_vectored(&con, sendmulti > 0, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+ }
+
+ /*
+ * Run the recvmmsg(2) test from above with sleeps in the
+ * sender and MSG_WAITFORONE in the receiver, the receiver
+ * keeps on on re-trying until they are all received.
+ */
+
+ tst_res(TINFO, "Running %d non-error socketpair() tests with as"
+ " many MSG_WAITFORONE recvmmsg(2) calls as needed and"
+ " individual sleeping sendmsg(2) calls:\n", n_selected);
+ con_init_socketpair(&con);
+ con_make_vectored(&con, false, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ con_make_waitforone_tests(&con);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+
+ /*
+ * Run the recvmmsg(2) test from above with sleeps in the
+ * sender and timeouts in the receiver, the receiver keeps
+ * on on re-trying until they are all received.
+ */
+
+ tst_res(TINFO, "Running %d non-error socketpair() tests with as"
+ " many recvmmsg(2) calls with a timeout as needed and"
+ " individual sleeping sendmsg(2) calls:\n", n_selected);
+ con_init_socketpair(&con);
+ con_make_vectored(&con, false, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ con_make_timeout_tests(&con);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+}
+
+static void tests_run_all_ocloexec_flag(void)
+{
+ struct con con;
+ int error;
+ size_t max_tests = iovec_max();
+ test_assert(max_tests > N_TESTS);
+ struct call *call_tests_selected[max_tests + 1];
+ struct sendmsg_call *smc_vec[max_tests];
+ struct recvmsg_call *rmc_vec[max_tests];
+
+ /*
+ * Get the socketpair tests that returned KIND_CLOEXEC_FLAG
+ * and run them sequentially to ensure there are no issues with
+ * the connection sharing.
+ */
+
+ int n_selected = tests_select(con_init_socketpair,
+ call_tests_selected,
+ KIND_CLOEXEC_FLAG);
+ tst_res(TINFO, "Running %d KIND_CLOEXEC_FLAG socketpair() "
+ "tests sharing a connection:\n", n_selected);
+ con_init_socketpair(&con);
+ int i;
+ struct call *callp;
+ for (i = 0; i < n_selected; ++i) {
+ callp = call_tests_selected[i];
+ int error = call_go(callp, &con);
+ test_assert(error == KIND_CLOEXEC_FLAG);
+ }
+ con_deinit(&con);
+
+ /*
+ * Now run them with recvmmsg(2)
+ */
+
+ int sendmulti;
+ for (sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+ tst_res(TINFO, "Running %d KIND_CLOEXEC_FLAG "
+ "socketpair() tests with a single recvmmsg(2) "
+ "call and %s:\n", n_selected,
+ sendmulti ? "a single sendmmsg(2) call"
+ : "multiple sendmsg(2) calls");
+ con_init_socketpair(&con);
+ con_make_vectored(&con, sendmulti > 0, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(error == KIND_CLOEXEC_FLAG);
+ con_deinit(&con);
+ }
+}
+
+static void tests_run_all_sock_sepacket(void)
+{
+ struct con con;
+ int error;
+ size_t max_tests = iovec_max();
+ test_assert(max_tests > N_TESTS);
+ struct call *call_tests_selected[max_tests + 1];
+ struct sendmsg_call *smc_vec[max_tests];
+ struct recvmsg_call *rmc_vec[max_tests];
+
+ /*
+ * Select the SOCK_SEQPACKET tests, run them sequentially sharing
+ * the connection to ensure there are no issues sharing it.
+ */
+
+ int n_selected = tests_select(con_init_seqpacket,
+ call_tests_selected, 0);
+ tst_res(TINFO, "Running %d non-error SOCK_SEQPACKET "
+ "tests sharing a connection:\n", n_selected);
+ con_init_seqpacket(&con);
+ int i;
+ struct call *callp;
+ for (i = 0; i < n_selected; ++i) {
+ callp = call_tests_selected[i];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ }
+ con_deinit(&con);
+
+ /*
+ * Run the SOCK_SEQPACKET tests with multiple sendmsg(2) calls
+ * (or a single sendmmsg(2) call) and recvmmsg(2).
+ */
+
+ int sendmulti;
+ for (sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+ tst_res(TINFO, "Running %d non-error SOCK_SEQPACKET "
+ "tests with a single recvmmsg(2) call "
+ "and %s:\n", n_selected,
+ sendmulti ? "a single sendmmsg(2) call"
+ : "multiple sendmsg(2) calls");
+ con_init_seqpacket(&con);
+ con_make_vectored(&con, sendmulti > 0, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+ }
+}
+
+#define N_REPEAT 4
+
+static void tests_run_all_sock_sepacket_with_n_repeat(void)
+{
+ struct con con;
+ int error;
+ size_t iov_max = iovec_max();
+ size_t max_tests = iov_max;
+ test_assert(max_tests > N_TESTS);
+ struct call *call_tests_selected[max_tests + 1];
+ struct sendmsg_call *smc_vec[max_tests];
+ struct recvmsg_call *rmc_vec[max_tests];
+
+ /*
+ * Get the SOCK_SEQPACKET tests again, repeat them N_REPEAT times
+ * and run them sequentially to ensure there are no issues with the
+ * connection sharing.
+ */
+
+ int n_selected = tests_select(con_init_seqpacket,
+ call_tests_selected, 0);
+ test_assert(N_REPEAT * n_selected < (int) N_TESTS);
+ int i;
+ struct call *callp;
+ for (i = 0; i < n_selected; ++i) {
+ callp = call_tests_selected[i];
+ int skip, r;
+ for (skip = n_selected, r = 0; r < N_REPEAT;
+ ++r, skip += n_selected)
+ call_tests_selected[i + skip] = callp;
+ }
+
+ n_selected *= N_REPEAT;
+ tst_res(TINFO, "Running %d non-error SOCK_SEQPACKET "
+ "tests sharing a connection (N_REPEAT = %d):\n",
+ n_selected, N_REPEAT);
+ con_init_seqpacket(&con);
+ for (i = 0; i < n_selected; ++i) {
+ callp = call_tests_selected[i];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ }
+ con_deinit(&con);
+
+ /*
+ * Now run them with recvmmsg(2)
+ */
+
+ int sendmulti;
+ for (sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+ tst_res(TINFO, "Running %d non-error SOCK_SEQPACKET "
+ "tests with a single recvmmsg(2) call "
+ "(N_REPEAT = %d) and %s:\n",
+ n_selected, N_REPEAT,
+ sendmulti ? "a single sendmmsg(2) call"
+ : "multiple sendmsg(2) calls");
+ con_init_seqpacket(&con);
+ con_make_vectored(&con, sendmulti > 0, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+ }
+
+ /*
+ * Repeat the first test iov_max with recvmmsg(2)
+ */
+
+ n_selected = iov_max;
+ callp = call_tests_selected[0];
+ for (i = 1; i < n_selected; ++i)
+ call_tests_selected[i] = callp;
+
+ for (sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+ tst_res(TINFO, "Running %d non-error SOCK_SEQPACKET "
+ "tests with a single recvmmsg(2) call "
+ "and %s:\n", n_selected,
+ sendmulti ? "a single sendmmsg(2) call"
+ : "multiple sendmsg(2) calls");
+ con_init_seqpacket(&con);
+ con_make_vectored(&con, sendmulti > 0, n_selected,
+ call_tests_selected, smc_vec, rmc_vec);
+ callp = call_tests_selected[0];
+ error = call_go(callp, &con);
+ test_assert(!error);
+ con_deinit(&con);
+ }
+}
+
+static struct tst_test test = {
+ .setup = tests_setup,
+ .cleanup = NULL,
+ .test_all = tests_run,
+};
+
+/*
+ * Utility functions and mechanism for test combinations and for
+ * turning individual sendmsg(2) / recvmsg(2) based tests into
+ * multi-message sendmmsg(2) / recvmmsg(2) calls are in this code.
+ *
+ * Test functions that use a single sendmsg() and a single recvmsg() use:
+ * con_add_send_recv_calls()
+ *
+ * To either run the test with an individual sendmsg() and recvmsg(),
+ * or to have those combined with the send and receive of unrelated
+ * tests into a multi-message test.
+ *
+ * The combination happens in:
+ * con_do_multi_send_recv()
+ */
+
+static void con_init_base(struct con *conp, int client, int server,
+ int type, struct sockaddr_in *sinp)
+{
+ conp->con_type = type;
+ conp->con_client = client;
+ conp->con_server = server;
+ if (!sinp)
+ memset(&conp->con_server_sin, 0 , sizeof(conp->con_server_sin));
+ else
+ conp->con_server_sin = *sinp;
+}
+
+static void con_init(struct con *conp, int client, int server,
+ int type, struct sockaddr_in *sinp)
+{
+ con_init_base(conp, client, server, type, sinp);
+ conp->con_call_sendmmsg = false;
+ conp->con_sender_sleeps = false; /* determined later */
+ conp->con_waitforone = false;
+ conp->con_timeout = false;
+ conp->con_call_end = 0;
+ conp->con_call_n = 0;
+ conp->con_call_vec = NULL;
+ conp->con_smc_vec = NULL;
+ conp->con_rmc_vec = NULL;
+}
+
+static void con_make_vectored(struct con *conp, bool sendmulti,
+ size_t n, struct call **call_vec,
+ struct sendmsg_call **smc_vec,
+ struct recvmsg_call **rmc_vec)
+{
+ test_assert(n >= 1 && call_vec && smc_vec && rmc_vec);
+ test_assert(conp->con_client >= 0 &&
+ conp->con_server >= 0 &&
+ !conp->con_call_sendmmsg &&
+ !conp->con_call_end &&
+ !conp->con_call_n &&
+ !conp->con_call_vec &&
+ !conp->con_smc_vec &&
+ !conp->con_rmc_vec);
+ conp->con_call_sendmmsg = sendmulti;
+ conp->con_call_end = 0;
+ conp->con_call_n = n;
+ conp->con_call_vec = call_vec;
+ conp->con_smc_vec = smc_vec;
+ conp->con_rmc_vec = rmc_vec;
+}
+
+static void con_make_waitforone_tests(struct con *conp)
+{
+ test_assert(conp->con_call_vec);
+ test_assert(!conp->con_call_sendmmsg);
+ conp->con_timeout = true;
+}
+
+static void con_make_timeout_tests(struct con *conp)
+{
+ test_assert(conp->con_call_vec);
+ test_assert(!conp->con_call_sendmmsg);
+ conp->con_waitforone = true;
+}
+
+static void con_add_send_recv_calls_vec(struct con *conp,
+ struct sendmsg_call *smcp,
+ struct recvmsg_call *rmcp,
+ bool sender_sleeps)
+{
+ test_assert(conp->con_call_end < conp->con_call_n);
+ size_t i = conp->con_call_end;
+
+ /*
+ * All senders must either: need to sleep or must not sleep.
+ */
+
+ if (i == 0)
+ conp->con_sender_sleeps = sender_sleeps;
+ else
+ test_assert(conp->con_sender_sleeps == sender_sleeps);
+ conp->con_smc_vec[i] = smcp;
+ conp->con_rmc_vec[i] = rmcp;
+
+ ++i;
+ conp->con_call_end = i;
+ if (i == conp->con_call_n)
+ con_do_multi_send_recv(conp);
+ else {
+ subtest_nest();
+ struct call *callp = conp->con_call_vec[i];
+ int error = call_go(callp, conp);
+ test_assert(error <= 0); /* not an error, see KIND_* defines */
+ subtest_unnest();
+ }
+}
+
+static void *sendmmsg_call(struct sendmmsg_call *smmcp)
+{
+ errno = 0;
+
+ int sockfd = smmcp->smmc_sockfd;
+ int vlen = smmcp->smmc_vlen;
+ struct mmsghdr *smm = smmcp->smmc_smm;
+ int flags = smmcp->smmc_flags;
+ bool call_sendmmsg = smmcp->smmc_call_sendmmsg;
+ bool sender_sleeps = smmcp->smmc_sender_sleeps;
+
+ int nsent;
+ if (call_sendmmsg)
+ nsent = sendmmsg(sockfd, smm, vlen, flags);
+ else {
+ for (nsent = 0; nsent < vlen; ++nsent) {
+ if (sender_sleeps)
+ sleep_ms(SENDMSG_SLEEP_MS);
+ int value = sendmsg(sockfd, &smm[nsent].msg_hdr, flags);
+ test_assert(value >= 0);
+ smm[nsent].msg_len = value;
+ }
+ }
+
+ smmcp->smmc_value = nsent;
+ smmcp->smmc_errno = errno;
+ return NULL;
+}
+
+static void con_sendmmsg_recvmmsg(struct con *conp, size_t n,
+ int sflags, struct mmsghdr *smm,
+ int rflags, struct mmsghdr *rmm)
+{
+ bool waitforone = conp->con_waitforone;
+ bool timeout = conp->con_timeout;
+ test_assert(!waitforone || !timeout);
+
+ struct sendmmsg_call smmc;
+ sendmmsg_init(&smmc, conp->con_client, (unsigned) n,
+ smm, sflags, conp->con_call_sendmmsg,
+ waitforone || timeout || conp->con_sender_sleeps);
+
+ thread_call(&smmc.smmc_pthr, (void *(*)(void *)) sendmmsg_call);
+
+ int nrecv;
+ if (!waitforone && !timeout) {
+ nrecv = recvmmsg(conp->con_server, rmm, n, rflags, NULL);
+ test_assert((size_t) nrecv == n);
+ } else {
+ struct timespec ts, *tsp = NULL;
+ if (timeout) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100 * 1000 * 1000;
+ tsp = &ts;
+ }
+ if (waitforone)
+ rflags |= MSG_WAITFORONE;
+ int nleft = n;
+ do {
+ nrecv = recvmmsg(conp->con_server, rmm,
+ nleft, rflags, tsp);
+ if (waitforone)
+ test_assert(nrecv > 0);
+ if (timeout && nrecv == -1) {
+ nrecv = 0;
+ test_assert(errno == ETIMEDOUT ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK);
+ }
+ nleft -= nrecv;
+ rmm += nrecv;
+ } while (nleft > 0);
+ }
+
+ thread_join(smmc.smmc_pthr);
+
+ int nsent = smmc.smmc_value;
+ test_assert((size_t) nsent == n);
+}
+
+static void con_do_multi_send_recv(struct con *conp)
+{
+ test_assert(conp->con_call_n >= 1 && conp->con_call_n <= iovec_max());
+ size_t n = conp->con_call_n;
+ struct mmsghdr smm[n];
+ struct mmsghdr rmm[n];
+
+ /*
+ * Gather the arguments from all the struct sendmsg_call and the
+ * struct recvmsg_call into the smm[] and rmm[] vectors for the
+ * multi-message send and receive syscalls.
+ *
+ * All the send and recv flags must be the same, there is
+ * a single argument for the flags in sendmmsg(2) and in
+ * recvmmsg(2), there is not an argument per message.
+ */
+
+ int sflags = conp->con_smc_vec[0]->smc_flags;
+ int rflags = conp->con_rmc_vec[0]->rmc_flags;
+ size_t i;
+ for (i = 0; i < n; i++) {
+ test_assert(conp->con_smc_vec[i]->smc_flags == sflags);
+ test_assert(conp->con_rmc_vec[i]->rmc_flags == rflags);
+ mmsghdr_init(&smm[i], conp->con_smc_vec[i]->smc_msg);
+ mmsghdr_init(&rmm[i], conp->con_rmc_vec[i]->rmc_msg);
+ }
+
+ con_sendmmsg_recvmmsg(conp, n, sflags, smm, rflags, rmm);
+
+ /*
+ * Scatter the results from smm[] and rmm[].
+ */
+
+ for (i = 0; i < n; i++) {
+ conp->con_smc_vec[i]->smc_value = smm[i].msg_len;
+ conp->con_smc_vec[i]->smc_msg->msg_flags =
+ smm[i].msg_hdr.msg_flags;
+ conp->con_rmc_vec[i]->rmc_value = rmm[i].msg_len;
+ conp->con_rmc_vec[i]->rmc_msg->msg_flags =
+ rmm[i].msg_hdr.msg_flags;
+ }
+}
+
+static void con_init_socketpair(struct con *conp)
+{
+ int pair[2];
+ SAFE_SOCKETPAIR(AF_UNIX, SOCK_SEQPACKET, 0, pair);
+ con_init(conp, pair[0], pair[1], SOCK_SEQPACKET, NULL);
+}
+
+static void con_init_seqpacket(struct con *conp)
+{
+ int server = SAFE_SOCKET(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+ struct sockaddr_in sin;
+ sockaddr_in_init(&sin, AF_INET, TEST_PORT, TEST_IP);
+ SAFE_BIND(server, (struct sockaddr *) &sin, sizeof(sin));
+ int client = SAFE_SOCKET(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+ SAFE_LISTEN(server, 1);
+ con_init(conp, client, server, SOCK_SEQPACKET, &sin);
+}
+
+static void con_init_dgram(struct con *conp)
+{
+ int server = SAFE_SOCKET(PF_INET, SOCK_DGRAM, 0);
+ struct sockaddr_in sin;
+ sockaddr_in_init(&sin, AF_INET, TEST_PORT, TEST_IP);
+ SAFE_BIND(server, (struct sockaddr *) &sin, sizeof(sin));
+ int client = SAFE_SOCKET(PF_INET, SOCK_DGRAM, 0);
+ con_init(conp, client, server, SOCK_DGRAM, &sin);
+}
+
+static void con_deinit(struct con *conp)
+{
+ close(conp->con_client);
+ close(conp->con_server);
+}
+
+static void con_add_send_recv_calls(struct con *conp, struct sendmsg_call *smcp,
+ struct recvmsg_call *rmcp,
+ bool sender_sleeps)
+{
+ if (conp->con_call_n > 0) {
+ con_add_send_recv_calls_vec(conp, smcp, rmcp, sender_sleeps);
+ return;
+ }
+
+ thread_call(&smcp->smc_pthr, (void *(*)(void *))
+ (sender_sleeps ? sendmsg_call_after_sleep : sendmsg_call));
+ struct timespec pretv, postv;
+ if (sender_sleeps)
+ clock_gettime(CLOCK_MONOTONIC, &pretv);
+ ssize_t val = recvmsg(conp->con_server, rmcp->rmc_msg, rmcp->rmc_flags);
+ if (val < 0)
+ rmcp->rmc_errno = errno;
+
+ if (sender_sleeps) {
+ /*
+ * If the sender is supposed to sleep prior to sending then
+ * the receiver is supposed to sleep too because its receive
+ * should block for the sender, testing that sender blocked
+ * for at least half as long as the sender sleeps is good
+ * enough to ensure that scheduling noise doesn't affect test.
+ */
+ clock_gettime(CLOCK_MONOTONIC, &postv);
+ unsigned long long pre = pretv.tv_sec * 1000 * 1000 * 1000;
+ pre += pretv.tv_nsec;
+ unsigned long long pos = postv.tv_sec * 1000 * 1000 * 1000;
+ pos += postv.tv_nsec;
+ test_assert((pos - pre) > (SENDMSG_SLEEP_MS * 1000) / 2);
+ }
+
+ rmcp->rmc_value = val;
+ thread_join(smcp->smc_pthr);
+}
+
+/*
+ * Miscellaneous supporting code is in this section.
+ */
+
+static void sleep_ms(long ms)
+{
+ test_assert(ms < 1000);
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = ms * 1000 * 1000;
+ nanosleep(&ts, NULL);
+}
+
+static int test_number_get(void)
+{
+ static int test_number_gen;
+
+ /*
+ * Nested subtests share the same test number, these are multi
+ * message tests, i.e. recvmmsg() and sendmmsg(), where each message
+ * is treated as a subtest
+ */
+
+ if (subtest_number() > 1)
+ return test_number_gen;
+ return ++test_number_gen;
+}
+
+/*
+ * subtests numbers
+ */
+
+static int subtest_level = 1;
+
+static int subtest_number(void)
+{
+ return subtest_level;
+}
+
+static void subtest_nest(void)
+{
+ ++subtest_level;
+}
+
+static void subtest_unnest(void)
+{
+ --subtest_level;
+}
+
+static bool subtest_show(void)
+{
+ /*
+ * Some tests involve too many subtests, this limits the output.
+ */
+ return subtest_level <= 20;
+}
+
+/*
+ * compare that memory is equal, makes the output of various ensure() nicer
+ */
+
+static bool mem_is_zero(void *a, size_t size)
+{
+ unsigned char set = 0;
+ unsigned char *p = a;
+ unsigned char *endp = p + size;
+ while (p < endp) set |= *p++;
+ return !set;
+}
+
+static void thread_call(pthread_t *pthr, void *(*func)(void *))
+{
+ SAFE_PTHREAD_CREATE(pthr, NULL, func, pthr);
+}
+
+static void thread_join(pthread_t pthr)
+{
+ SAFE_PTHREAD_JOIN(pthr, NULL);
+}
+
+static void sendmmsg_init(struct sendmmsg_call *smmcp, int sockfd,
+ unsigned vlen, struct mmsghdr *smm, int flags,
+ bool call_sendmmsg, bool sender_sleeps)
+{
+ smmcp->smmc_sockfd = sockfd;
+ smmcp->smmc_vlen = vlen;
+ smmcp->smmc_smm = smm;
+ smmcp->smmc_flags = flags;
+ smmcp->smmc_call_sendmmsg = call_sendmmsg;
+ smmcp->smmc_sender_sleeps = sender_sleeps;
+ smmcp->smmc_value = INVALID_SYSCALL_VALUE;
+ smmcp->smmc_errno = 0;
+}
+
+/*
+ * various init and member functions for various types
+ */
+
+static void sockaddr_in_init(struct sockaddr_in *sinp, sa_family_t family,
+ in_port_t port, uint32_t ip)
+{
+ memset(sinp, 0, sizeof(struct sockaddr));
+ sinp->sin_family = family;
+ sinp->sin_port = htons(port);
+ sinp->sin_addr.s_addr = htonl(ip);
+}
+
+static size_t iovec_max(void)
+{
+ long iov_max = SAFE_SYSCONF(_SC_IOV_MAX);
+ test_assert(iov_max > 2);
+ return (size_t) iov_max;
+}
+
+static void iovec_init(struct iovec *iovecp, void *base, size_t len)
+{
+ iovecp->iov_base = base;
+ iovecp->iov_len = len;
+}
+
+static void msghdr_init(struct msghdr *msgp, struct sockaddr_in *sinp,
+ struct iovec *iovecp, size_t iovlen, int flags)
+{
+ msghdr_init_with_control(msgp, sinp, iovecp, iovlen, NULL, 0, flags);
+}
+
+static void msghdr_init_with_control(struct msghdr *msgp,
+ struct sockaddr_in *sinp,
+ struct iovec *iovecp, size_t iovlen,
+ void *control, size_t controllen,
+ int flags)
+{
+ memset(msgp, 0, sizeof(*msgp));
+ msgp->msg_name = sinp;
+ msgp->msg_namelen = sinp ? sizeof(*sinp) : 0;
+ msgp->msg_iov =iovecp;
+ msgp->msg_iovlen = iovlen;
+ msgp->msg_control = control;
+ msgp->msg_controllen = controllen;
+ msgp->msg_flags = flags;
+}
+
+static void mmsghdr_init(struct mmsghdr *mmsgp, struct msghdr *msgp)
+{
+ mmsgp->msg_hdr = *msgp;
+ mmsgp->msg_len = 0;
+}
+
+static void recvmsg_call_init(struct recvmsg_call *rmcp,
+ struct msghdr *msgp, int flags)
+{
+ rmcp->rmc_value = INVALID_SYSCALL_VALUE;
+ rmcp->rmc_errno = 0;
+ rmcp->rmc_msg = msgp;
+ rmcp->rmc_flags = flags;
+}
+
+static void sendmsg_call_init(struct sendmsg_call *smcp, int sockfd,
+ struct msghdr *msgp, int flags)
+{
+ smcp->smc_value = INVALID_SYSCALL_VALUE;
+ smcp->smc_errno = 0;
+ smcp->smc_sockfd = sockfd;
+ smcp->smc_msg = msgp;
+ smcp->smc_flags = flags;
+}
+
+/*
+ * functions that can be called directly through pthread_create(3)
+ */
+
+static void *sendmsg_call(struct sendmsg_call *smcp)
+{
+ errno = 0;
+ smcp->smc_value = sendmsg(smcp->smc_sockfd, smcp->smc_msg,
+ smcp->smc_flags);
+ smcp->smc_errno = errno;
+ return NULL;
+}
+
+static void *sendmsg_call_after_sleep(struct sendmsg_call *smcp)
+{
+ sleep_ms(SENDMSG_SLEEP_MS);
+ return sendmsg_call(smcp);
+}
+
+static void ctlmsgfd_init(struct ctlmsgfd *cmfp)
+{
+ memset(cmfp, 0, sizeof(*cmfp));
+}
+
+static void ctlmsgfd_init_fd(struct ctlmsgfd *cmfp, int fd)
+{
+ ctlmsgfd_init(cmfp);
+ cmfp->cmf_cmsg.cmsg_len = sizeof(*cmfp);
+ cmfp->cmf_cmsg.cmsg_level = SOL_SOCKET;
+ cmfp->cmf_cmsg.cmsg_type = SCM_RIGHTS;
+ *(int *) CMSG_DATA(&cmfp->cmf_cmsg) = fd;
+}
+
+static int ctlmsgfd_get_fd(struct ctlmsgfd *cmfp)
+{
+ return *(int *) CMSG_DATA(&cmfp->cmf_cmsg);
+}
+
+static int call_go(struct call *callp, struct con *conp)
+{
+ static int max_subtest_level = 1;
+
+ int failures = ensure_failures;
+ int tn = test_number_get();
+ int error = callp->call_test(callp, conp, tn);
+ if (subtest_number() > 1) {
+ if (subtest_number() > max_subtest_level)
+ max_subtest_level = subtest_number();
+ } else {
+ bool pass = failures == ensure_failures;
+ if (max_subtest_level == 1)
+ tst_res(pass ? TPASS : TFAIL, "[single test]\n");
+ else
+ tst_res(pass ? TPASS : TFAIL,
+ "[%d tests combined into multi message test]\n",
+ max_subtest_level);
+ max_subtest_level = 1;
+ }
+ return error;
+}
+
+static int call_receive_iovec_boundary_checks(struct call *callp,
+ struct con *conp, int tn)
+{
+ return con_receive_iovec_boundary_checks(conp, tn, callp->call_niov);
+}
+static int call_receive_iovec_boundary_checks_peeking(struct call *callp,
+ struct con *conp, int tn)
+{
+ return con_receive_iovec_boundary_checks_peeking(conp, tn,
+ callp->call_niov);
+}
+static int call_receive_file_descriptor(struct call *callp,
+ struct con *conp, int tn)
+{
+ return con_receive_file_descriptor(conp, tn,
+ callp->call_cloexec_flag,
+ callp->call_some_data,
+ callp->call_controllen_delta);
+}
+static int call_message_too_long_to_fit_discards_bytes(struct call *callp,
+ struct con *conp,
+ int tn)
+{
+ (void)(callp);
+ return con_message_too_long_to_fit_discards_bytes(conp, tn);
+}
+static int call_message_too_long_to_fit_doesnt_discard_bytes(struct call *callp,
+ struct con *conp,
+ int tn)
+{
+ (void)(callp);
+ return con_message_too_long_to_fit_doesnt_discard_bytes(conp, tn);
+}
+static int call_receive_waits_for_message(struct call *callp,
+ struct con *conp, int tn)
+{
+ (void)(callp);
+ return con_receive_waits_for_message(conp, tn);
+}
+static int call_receiver_doesnt_wait_for_messages(struct call *callp,
+ struct con *conp, int tn)
+{
+ (void)(callp);
+ return con_receiver_doesnt_wait_for_messages(conp, tn);
+}
+static int call_receive_returns_what_is_available(struct call *callp,
+ struct con *conp, int tn)
+{
+ (void)(callp);
+ return con_receive_returns_what_is_available(conp, tn);
+}
+static int call_empty_datagram_received_as_empty_datagram(struct call *callp,
+ struct con *conp,
+ int tn)
+{
+ (void)(callp);
+ return con_empty_datagram_received_as_empty_datagram(conp, tn);
+}
--
2.21.0.rc0.258.g878e2cd30e-goog
</pre>
<br class="Apple-interchange-newline">
</body>
</html>