1*0ad011ecSGleb Smirnoff /*- 2*0ad011ecSGleb Smirnoff * SPDX-License-Identifier: BSD-2-Clause 3*0ad011ecSGleb Smirnoff * 4*0ad011ecSGleb Smirnoff * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org> 5*0ad011ecSGleb Smirnoff * 6*0ad011ecSGleb Smirnoff * Redistribution and use in source and binary forms, with or without 7*0ad011ecSGleb Smirnoff * modification, are permitted provided that the following conditions 8*0ad011ecSGleb Smirnoff * are met: 9*0ad011ecSGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 10*0ad011ecSGleb Smirnoff * notice, this list of conditions and the following disclaimer. 11*0ad011ecSGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 12*0ad011ecSGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 13*0ad011ecSGleb Smirnoff * documentation and/or other materials provided with the distribution. 14*0ad011ecSGleb Smirnoff * 15*0ad011ecSGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*0ad011ecSGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*0ad011ecSGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*0ad011ecSGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*0ad011ecSGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*0ad011ecSGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*0ad011ecSGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*0ad011ecSGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*0ad011ecSGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*0ad011ecSGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*0ad011ecSGleb Smirnoff * SUCH DAMAGE. 26*0ad011ecSGleb Smirnoff */ 27*0ad011ecSGleb Smirnoff 28*0ad011ecSGleb Smirnoff #include <sys/param.h> 29*0ad011ecSGleb Smirnoff #include <sys/ioctl.h> 30*0ad011ecSGleb Smirnoff #include <sys/time.h> 31*0ad011ecSGleb Smirnoff #include <sys/socket.h> 32*0ad011ecSGleb Smirnoff #include <sys/module.h> 33*0ad011ecSGleb Smirnoff #include <errno.h> 34*0ad011ecSGleb Smirnoff #include <fcntl.h> 35*0ad011ecSGleb Smirnoff #include <signal.h> 36*0ad011ecSGleb Smirnoff #include <stdlib.h> 37*0ad011ecSGleb Smirnoff #include <unistd.h> 38*0ad011ecSGleb Smirnoff 39*0ad011ecSGleb Smirnoff #include <netlink/netlink.h> 40*0ad011ecSGleb Smirnoff #include <netlink/netlink_route.h> 41*0ad011ecSGleb Smirnoff 42*0ad011ecSGleb Smirnoff #include <atf-c.h> 43*0ad011ecSGleb Smirnoff 44*0ad011ecSGleb Smirnoff static struct itimerval itv = { 45*0ad011ecSGleb Smirnoff .it_interval = { 0, 0 }, 46*0ad011ecSGleb Smirnoff .it_value = { 1, 0 }, /* one second */ 47*0ad011ecSGleb Smirnoff }; 48*0ad011ecSGleb Smirnoff static sig_atomic_t timer_done = 0; 49*0ad011ecSGleb Smirnoff static void 50*0ad011ecSGleb Smirnoff sigalarm(int sig __unused) 51*0ad011ecSGleb Smirnoff { 52*0ad011ecSGleb Smirnoff 53*0ad011ecSGleb Smirnoff timer_done = 1; 54*0ad011ecSGleb Smirnoff } 55*0ad011ecSGleb Smirnoff 56*0ad011ecSGleb Smirnoff static struct sigaction sigact = { 57*0ad011ecSGleb Smirnoff .sa_handler = sigalarm, 58*0ad011ecSGleb Smirnoff }; 59*0ad011ecSGleb Smirnoff 60*0ad011ecSGleb Smirnoff static struct nlmsghdr hdr = (struct nlmsghdr) { 61*0ad011ecSGleb Smirnoff .nlmsg_type = RTM_GETLINK, 62*0ad011ecSGleb Smirnoff .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, 63*0ad011ecSGleb Smirnoff .nlmsg_len = sizeof(struct nlmsghdr), 64*0ad011ecSGleb Smirnoff }; 65*0ad011ecSGleb Smirnoff 66*0ad011ecSGleb Smirnoff #define BUFLEN 1000 67*0ad011ecSGleb Smirnoff 68*0ad011ecSGleb Smirnoff static int 69*0ad011ecSGleb Smirnoff fullsocket(void) 70*0ad011ecSGleb Smirnoff { 71*0ad011ecSGleb Smirnoff char buf[BUFLEN]; 72*0ad011ecSGleb Smirnoff socklen_t slen = sizeof(int); 73*0ad011ecSGleb Smirnoff int fd, sendspace, recvspace, sendavail, recvavail, rsize; 74*0ad011ecSGleb Smirnoff u_int cnt = 0; 75*0ad011ecSGleb Smirnoff 76*0ad011ecSGleb Smirnoff ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1); 77*0ad011ecSGleb Smirnoff ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace, 78*0ad011ecSGleb Smirnoff &slen) == 0); 79*0ad011ecSGleb Smirnoff ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace, 80*0ad011ecSGleb Smirnoff &slen) == 0); 81*0ad011ecSGleb Smirnoff 82*0ad011ecSGleb Smirnoff /* Check the expected size of reply on a single RTM_GETLINK. */ 83*0ad011ecSGleb Smirnoff ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr)); 84*0ad011ecSGleb Smirnoff ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) == 85*0ad011ecSGleb Smirnoff sizeof(hdr)); 86*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1); 87*0ad011ecSGleb Smirnoff 88*0ad011ecSGleb Smirnoff 89*0ad011ecSGleb Smirnoff /* 90*0ad011ecSGleb Smirnoff * Flood the socket with requests, without reading out the replies. 91*0ad011ecSGleb Smirnoff * While we are flooding, the kernel tries to process the requests. 92*0ad011ecSGleb Smirnoff * Kernel takes off requests from the send buffer and puts replies 93*0ad011ecSGleb Smirnoff * on receive buffer. Once the receive buffer is full it stops working 94*0ad011ecSGleb Smirnoff * on queue in the send buffer. At this point we must get a solid 95*0ad011ecSGleb Smirnoff * failure. However, if we flood faster than kernel taskqueue runs, 96*0ad011ecSGleb Smirnoff * we may get intermittent failures. 97*0ad011ecSGleb Smirnoff */ 98*0ad011ecSGleb Smirnoff do { 99*0ad011ecSGleb Smirnoff ssize_t rv; 100*0ad011ecSGleb Smirnoff 101*0ad011ecSGleb Smirnoff rv = send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT); 102*0ad011ecSGleb Smirnoff if (__predict_true(rv == sizeof(hdr))) 103*0ad011ecSGleb Smirnoff cnt++; 104*0ad011ecSGleb Smirnoff else { 105*0ad011ecSGleb Smirnoff ATF_REQUIRE(errno == EAGAIN); 106*0ad011ecSGleb Smirnoff ATF_REQUIRE(sizeof(hdr) * cnt > sendspace); 107*0ad011ecSGleb Smirnoff } 108*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1); 109*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendavail) != -1); 110*0ad011ecSGleb Smirnoff } while (recvavail <= recvspace - rsize || 111*0ad011ecSGleb Smirnoff sendavail <= sendspace - sizeof(hdr)); 112*0ad011ecSGleb Smirnoff 113*0ad011ecSGleb Smirnoff return (fd); 114*0ad011ecSGleb Smirnoff } 115*0ad011ecSGleb Smirnoff 116*0ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(overflow); 117*0ad011ecSGleb Smirnoff ATF_TC_BODY(overflow, tc) 118*0ad011ecSGleb Smirnoff { 119*0ad011ecSGleb Smirnoff char buf[BUFLEN]; 120*0ad011ecSGleb Smirnoff int fd; 121*0ad011ecSGleb Smirnoff 122*0ad011ecSGleb Smirnoff fd = fullsocket(); 123*0ad011ecSGleb Smirnoff 124*0ad011ecSGleb Smirnoff /* Both buffers full: block. */ 125*0ad011ecSGleb Smirnoff timer_done = 0; 126*0ad011ecSGleb Smirnoff ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0); 127*0ad011ecSGleb Smirnoff ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0); 128*0ad011ecSGleb Smirnoff ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1); 129*0ad011ecSGleb Smirnoff ATF_REQUIRE(errno == EINTR); 130*0ad011ecSGleb Smirnoff ATF_REQUIRE(timer_done == 1); 131*0ad011ecSGleb Smirnoff 132*0ad011ecSGleb Smirnoff /* 133*0ad011ecSGleb Smirnoff * Now, reading something from the receive buffer should wake up the 134*0ad011ecSGleb Smirnoff * taskqueue and send buffer should start getting drained. 135*0ad011ecSGleb Smirnoff */ 136*0ad011ecSGleb Smirnoff ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr)); 137*0ad011ecSGleb Smirnoff timer_done = 0; 138*0ad011ecSGleb Smirnoff ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0); 139*0ad011ecSGleb Smirnoff ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr)); 140*0ad011ecSGleb Smirnoff ATF_REQUIRE(timer_done == 0); 141*0ad011ecSGleb Smirnoff } 142*0ad011ecSGleb Smirnoff 143*0ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(peek); 144*0ad011ecSGleb Smirnoff ATF_TC_BODY(peek, tc) 145*0ad011ecSGleb Smirnoff { 146*0ad011ecSGleb Smirnoff char *buf; 147*0ad011ecSGleb Smirnoff ssize_t ss, ss1; 148*0ad011ecSGleb Smirnoff int fd; 149*0ad011ecSGleb Smirnoff 150*0ad011ecSGleb Smirnoff ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1); 151*0ad011ecSGleb Smirnoff 152*0ad011ecSGleb Smirnoff ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr)); 153*0ad011ecSGleb Smirnoff ss = recv(fd, buf, 0, MSG_WAITALL | MSG_PEEK | MSG_TRUNC); 154*0ad011ecSGleb Smirnoff ATF_REQUIRE((buf = malloc(ss)) != NULL); 155*0ad011ecSGleb Smirnoff ATF_REQUIRE(recv(fd, buf, ss, MSG_WAITALL) == ss); 156*0ad011ecSGleb Smirnoff } 157*0ad011ecSGleb Smirnoff 158*0ad011ecSGleb Smirnoff struct nl_control { 159*0ad011ecSGleb Smirnoff struct nlattr nla; 160*0ad011ecSGleb Smirnoff uint32_t val; 161*0ad011ecSGleb Smirnoff }; 162*0ad011ecSGleb Smirnoff 163*0ad011ecSGleb Smirnoff static void 164*0ad011ecSGleb Smirnoff cmsg_check(struct msghdr *msg) 165*0ad011ecSGleb Smirnoff { 166*0ad011ecSGleb Smirnoff static pid_t pid = 0; 167*0ad011ecSGleb Smirnoff struct cmsghdr *cmsg; 168*0ad011ecSGleb Smirnoff struct nl_control *nlc; 169*0ad011ecSGleb Smirnoff 170*0ad011ecSGleb Smirnoff ATF_REQUIRE((cmsg = CMSG_FIRSTHDR(msg)) != NULL); 171*0ad011ecSGleb Smirnoff ATF_REQUIRE(cmsg->cmsg_level == SOL_NETLINK); 172*0ad011ecSGleb Smirnoff ATF_REQUIRE(cmsg->cmsg_type == NETLINK_MSG_INFO); 173*0ad011ecSGleb Smirnoff nlc = (struct nl_control *)CMSG_DATA(cmsg); 174*0ad011ecSGleb Smirnoff ATF_REQUIRE(nlc[0].nla.nla_type == NLMSGINFO_ATTR_PROCESS_ID); 175*0ad011ecSGleb Smirnoff if (pid == 0) 176*0ad011ecSGleb Smirnoff pid = getpid(); 177*0ad011ecSGleb Smirnoff ATF_REQUIRE(nlc[0].val == pid); 178*0ad011ecSGleb Smirnoff ATF_REQUIRE(nlc[1].nla.nla_type == NLMSGINFO_ATTR_PORT_ID); 179*0ad011ecSGleb Smirnoff /* XXX need another test to test port id */ 180*0ad011ecSGleb Smirnoff ATF_REQUIRE(nlc[1].val == 0); 181*0ad011ecSGleb Smirnoff ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL); 182*0ad011ecSGleb Smirnoff ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0); 183*0ad011ecSGleb Smirnoff } 184*0ad011ecSGleb Smirnoff 185*0ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(sizes); 186*0ad011ecSGleb Smirnoff ATF_TC_BODY(sizes, tc) 187*0ad011ecSGleb Smirnoff { 188*0ad011ecSGleb Smirnoff #define NLMSG_LARGE 2048 /* XXX: match kernel nl_buf */ 189*0ad011ecSGleb Smirnoff char buf[NLMSG_LARGE * 10]; 190*0ad011ecSGleb Smirnoff char cbuf[CMSG_SPACE(sizeof(struct nl_control) * 2)]; 191*0ad011ecSGleb Smirnoff struct iovec iov; 192*0ad011ecSGleb Smirnoff struct msghdr msg = { 193*0ad011ecSGleb Smirnoff .msg_iov = &iov, 194*0ad011ecSGleb Smirnoff .msg_iovlen = 1, 195*0ad011ecSGleb Smirnoff .msg_control = cbuf, 196*0ad011ecSGleb Smirnoff .msg_controllen = sizeof(cbuf), 197*0ad011ecSGleb Smirnoff }; 198*0ad011ecSGleb Smirnoff ssize_t ss; 199*0ad011ecSGleb Smirnoff int fd, size, rsize; 200*0ad011ecSGleb Smirnoff 201*0ad011ecSGleb Smirnoff fd = fullsocket(); 202*0ad011ecSGleb Smirnoff 203*0ad011ecSGleb Smirnoff /* 204*0ad011ecSGleb Smirnoff * Set NETLINK_MSG_INFO, so that later cmsg_check will check that any 205*0ad011ecSGleb Smirnoff * read is accompanied with control data. 206*0ad011ecSGleb Smirnoff */ 207*0ad011ecSGleb Smirnoff ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO, 208*0ad011ecSGleb Smirnoff &(int){1}, sizeof(int)) == 0); 209*0ad011ecSGleb Smirnoff 210*0ad011ecSGleb Smirnoff iov = (struct iovec ){ 211*0ad011ecSGleb Smirnoff .iov_base = &hdr, 212*0ad011ecSGleb Smirnoff .iov_len = sizeof(hdr), 213*0ad011ecSGleb Smirnoff }; 214*0ad011ecSGleb Smirnoff /* Obtain size of the first message in the socket. */ 215*0ad011ecSGleb Smirnoff ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC); 216*0ad011ecSGleb Smirnoff ATF_REQUIRE(ss == hdr.nlmsg_len); 217*0ad011ecSGleb Smirnoff /* And overall amount of data in the socket. */ 218*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1); 219*0ad011ecSGleb Smirnoff cmsg_check(&msg); 220*0ad011ecSGleb Smirnoff 221*0ad011ecSGleb Smirnoff /* Zero-sized read should not affect state of the socket buffer. */ 222*0ad011ecSGleb Smirnoff ATF_REQUIRE(recv(fd, buf, 0, 0) == 0); 223*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1); 224*0ad011ecSGleb Smirnoff ATF_REQUIRE(size == rsize); 225*0ad011ecSGleb Smirnoff 226*0ad011ecSGleb Smirnoff /* 227*0ad011ecSGleb Smirnoff * Undersized read should lose a message. This isn't exactly 228*0ad011ecSGleb Smirnoff * pronounced in the Netlink RFC, but it always says that Netlink 229*0ad011ecSGleb Smirnoff * socket is an analog of the BSD routing socket, and this is how 230*0ad011ecSGleb Smirnoff * a route(4) socket deals with undersized read. 231*0ad011ecSGleb Smirnoff */ 232*0ad011ecSGleb Smirnoff iov = (struct iovec ){ 233*0ad011ecSGleb Smirnoff .iov_base = buf, 234*0ad011ecSGleb Smirnoff .iov_len = sizeof(hdr), 235*0ad011ecSGleb Smirnoff }; 236*0ad011ecSGleb Smirnoff ATF_REQUIRE(recvmsg(fd, &msg, 0) == sizeof(hdr)); 237*0ad011ecSGleb Smirnoff ATF_REQUIRE(msg.msg_flags & MSG_TRUNC); 238*0ad011ecSGleb Smirnoff ATF_REQUIRE(hdr.nlmsg_len > sizeof(hdr)); 239*0ad011ecSGleb Smirnoff size = rsize - hdr.nlmsg_len; 240*0ad011ecSGleb Smirnoff ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1); 241*0ad011ecSGleb Smirnoff ATF_REQUIRE(size == rsize); 242*0ad011ecSGleb Smirnoff cmsg_check(&msg); 243*0ad011ecSGleb Smirnoff 244*0ad011ecSGleb Smirnoff /* 245*0ad011ecSGleb Smirnoff * Large read should span several nl_bufs, seeing no boundaries. 246*0ad011ecSGleb Smirnoff */ 247*0ad011ecSGleb Smirnoff iov = (struct iovec ){ 248*0ad011ecSGleb Smirnoff .iov_base = buf, 249*0ad011ecSGleb Smirnoff .iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize, 250*0ad011ecSGleb Smirnoff }; 251*0ad011ecSGleb Smirnoff ss = recvmsg(fd, &msg, 0); 252*0ad011ecSGleb Smirnoff ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize); 253*0ad011ecSGleb Smirnoff cmsg_check(&msg); 254*0ad011ecSGleb Smirnoff } 255*0ad011ecSGleb Smirnoff 256*0ad011ecSGleb Smirnoff ATF_TP_ADD_TCS(tp) 257*0ad011ecSGleb Smirnoff { 258*0ad011ecSGleb Smirnoff if (modfind("netlink") == -1) 259*0ad011ecSGleb Smirnoff atf_tc_skip("netlink module not loaded"); 260*0ad011ecSGleb Smirnoff 261*0ad011ecSGleb Smirnoff ATF_TP_ADD_TC(tp, overflow); 262*0ad011ecSGleb Smirnoff ATF_TP_ADD_TC(tp, peek); 263*0ad011ecSGleb Smirnoff ATF_TP_ADD_TC(tp, sizes); 264*0ad011ecSGleb Smirnoff 265*0ad011ecSGleb Smirnoff return (atf_no_error()); 266*0ad011ecSGleb Smirnoff } 267