Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108285986
D42525.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D42525.diff
View Options
diff --git a/tests/sys/netlink/Makefile b/tests/sys/netlink/Makefile
--- a/tests/sys/netlink/Makefile
+++ b/tests/sys/netlink/Makefile
@@ -4,7 +4,8 @@
TESTSDIR= ${TESTSBASE}/sys/netlink
-ATF_TESTS_C += test_snl test_snl_generic
+ATF_TESTS_C+= netlink_socket
+ATF_TESTS_C+= test_snl test_snl_generic
ATF_TESTS_PYTEST += test_nl_core.py
ATF_TESTS_PYTEST += test_rtnl_iface.py
ATF_TESTS_PYTEST += test_rtnl_ifaddr.py
diff --git a/tests/sys/netlink/netlink_socket.c b/tests/sys/netlink/netlink_socket.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/netlink/netlink_socket.c
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+
+#include <atf-c.h>
+
+static struct itimerval itv = {
+ .it_interval = { 0, 0 },
+ .it_value = { 1, 0 }, /* one second */
+};
+static sig_atomic_t timer_done = 0;
+static void
+sigalarm(int sig __unused)
+{
+
+ timer_done = 1;
+}
+
+static struct sigaction sigact = {
+ .sa_handler = sigalarm,
+};
+
+static struct nlmsghdr hdr = (struct nlmsghdr) {
+ .nlmsg_type = RTM_GETLINK,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .nlmsg_len = sizeof(struct nlmsghdr),
+};
+
+#define BUFLEN 1000
+
+static int
+fullsocket(void)
+{
+ char buf[BUFLEN];
+ socklen_t slen = sizeof(int);
+ int fd, sendspace, recvspace, sendavail, recvavail, rsize;
+ u_int cnt = 0;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace,
+ &slen) == 0);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace,
+ &slen) == 0);
+
+ /* Check the expected size of reply on a single RTM_GETLINK. */
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==
+ sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+
+
+ /*
+ * Flood the socket with requests, without reading out the replies.
+ * While we are flooding, the kernel tries to process the requests.
+ * Kernel takes off requests from the send buffer and puts replies
+ * on receive buffer. Once the receive buffer is full it stops working
+ * on queue in the send buffer. At this point we must get a solid
+ * failure. However, if we flood faster than kernel taskqueue runs,
+ * we may get intermittent failures.
+ */
+ do {
+ ssize_t rv;
+
+ rv = send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT);
+ if (__predict_true(rv == sizeof(hdr)))
+ cnt++;
+ else {
+ ATF_REQUIRE(errno == EAGAIN);
+ ATF_REQUIRE(sizeof(hdr) * cnt > sendspace);
+ }
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1);
+ ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendavail) != -1);
+ } while (recvavail <= recvspace - rsize ||
+ sendavail <= sendspace - sizeof(hdr));
+
+ return (fd);
+}
+
+ATF_TC_WITHOUT_HEAD(overflow);
+ATF_TC_BODY(overflow, tc)
+{
+ char buf[BUFLEN];
+ int fd;
+
+ fd = fullsocket();
+
+ /* Both buffers full: block. */
+ timer_done = 0;
+ ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1);
+ ATF_REQUIRE(errno == EINTR);
+ ATF_REQUIRE(timer_done == 1);
+
+ /*
+ * Now, reading something from the receive buffer should wake up the
+ * taskqueue and send buffer should start getting drained.
+ */
+ ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));
+ timer_done = 0;
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(timer_done == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(peek);
+ATF_TC_BODY(peek, tc)
+{
+ char *buf;
+ ssize_t ss, ss1;
+ int fd;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ss = recv(fd, buf, 0, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE((buf = malloc(ss)) != NULL);
+ ATF_REQUIRE(recv(fd, buf, ss, MSG_WAITALL) == ss);
+}
+
+struct nl_control {
+ struct nlattr nla;
+ uint32_t val;
+};
+
+static void
+cmsg_check(struct msghdr *msg)
+{
+ static pid_t pid = 0;
+ struct cmsghdr *cmsg;
+ struct nl_control *nlc;
+
+ ATF_REQUIRE((cmsg = CMSG_FIRSTHDR(msg)) != NULL);
+ ATF_REQUIRE(cmsg->cmsg_level == SOL_NETLINK);
+ ATF_REQUIRE(cmsg->cmsg_type == NETLINK_MSG_INFO);
+ nlc = (struct nl_control *)CMSG_DATA(cmsg);
+ ATF_REQUIRE(nlc[0].nla.nla_type == NLMSGINFO_ATTR_PROCESS_ID);
+ if (pid == 0)
+ pid = getpid();
+ ATF_REQUIRE(nlc[0].val == pid);
+ ATF_REQUIRE(nlc[1].nla.nla_type == NLMSGINFO_ATTR_PORT_ID);
+ /* XXX need another test to test port id */
+ ATF_REQUIRE(nlc[1].val == 0);
+ ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL);
+ ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(sizes);
+ATF_TC_BODY(sizes, tc)
+{
+#define NLMSG_LARGE 2048 /* XXX: match kernel nl_buf */
+ char buf[NLMSG_LARGE * 10];
+ char cbuf[CMSG_SPACE(sizeof(struct nl_control) * 2)];
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ ssize_t ss;
+ int fd, size, rsize;
+
+ fd = fullsocket();
+
+ /*
+ * Set NETLINK_MSG_INFO, so that later cmsg_check will check that any
+ * read is accompanied with control data.
+ */
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO,
+ &(int){1}, sizeof(int)) == 0);
+
+ iov = (struct iovec ){
+ .iov_base = &hdr,
+ .iov_len = sizeof(hdr),
+ };
+ /* Obtain size of the first message in the socket. */
+ ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE(ss == hdr.nlmsg_len);
+ /* And overall amount of data in the socket. */
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ cmsg_check(&msg);
+
+ /* Zero-sized read should not affect state of the socket buffer. */
+ ATF_REQUIRE(recv(fd, buf, 0, 0) == 0);
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1);
+ ATF_REQUIRE(size == rsize);
+
+ /*
+ * Undersized read should lose a message. This isn't exactly
+ * pronounced in the Netlink RFC, but it always says that Netlink
+ * socket is an analog of the BSD routing socket, and this is how
+ * a route(4) socket deals with undersized read.
+ */
+ iov = (struct iovec ){
+ .iov_base = buf,
+ .iov_len = sizeof(hdr),
+ };
+ ATF_REQUIRE(recvmsg(fd, &msg, 0) == sizeof(hdr));
+ ATF_REQUIRE(msg.msg_flags & MSG_TRUNC);
+ ATF_REQUIRE(hdr.nlmsg_len > sizeof(hdr));
+ size = rsize - hdr.nlmsg_len;
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ ATF_REQUIRE(size == rsize);
+ cmsg_check(&msg);
+
+ /*
+ * Large read should span several nl_bufs, seeing no boundaries.
+ */
+ iov = (struct iovec ){
+ .iov_base = buf,
+ .iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
+ };
+ ss = recvmsg(fd, &msg, 0);
+ ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
+ cmsg_check(&msg);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ if (modfind("netlink") == -1)
+ atf_tc_skip("netlink module not loaded");
+
+ ATF_TP_ADD_TC(tp, overflow);
+ ATF_TP_ADD_TC(tp, peek);
+ ATF_TP_ADD_TC(tp, sizes);
+
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 24, 12:49 PM (7 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16087345
Default Alt Text
D42525.diff (8 KB)
Attached To
Mode
D42525: tests/netlink: add netlink socket buffer test
Attached
Detach File
Event Timeline
Log In to Comment