xref: /freebsd/tests/sys/netlink/netlink_socket.c (revision 0ad011ececb978e22a9bff2acf76633b094f1ff6)
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