xref: /freebsd/tests/sys/netlink/netlink_socket.c (revision 26caf57e0b0c02771b58ed99bc37b4810249d58e)
10ad011ecSGleb Smirnoff /*-
20ad011ecSGleb Smirnoff  * SPDX-License-Identifier: BSD-2-Clause
30ad011ecSGleb Smirnoff  *
40ad011ecSGleb Smirnoff  * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org>
50ad011ecSGleb Smirnoff  *
60ad011ecSGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
70ad011ecSGleb Smirnoff  * modification, are permitted provided that the following conditions
80ad011ecSGleb Smirnoff  * are met:
90ad011ecSGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
100ad011ecSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
110ad011ecSGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
120ad011ecSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
130ad011ecSGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
140ad011ecSGleb Smirnoff  *
150ad011ecSGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160ad011ecSGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170ad011ecSGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180ad011ecSGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190ad011ecSGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200ad011ecSGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210ad011ecSGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220ad011ecSGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230ad011ecSGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240ad011ecSGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250ad011ecSGleb Smirnoff  * SUCH DAMAGE.
260ad011ecSGleb Smirnoff  */
270ad011ecSGleb Smirnoff 
280ad011ecSGleb Smirnoff #include <sys/param.h>
290ad011ecSGleb Smirnoff #include <sys/ioctl.h>
300ad011ecSGleb Smirnoff #include <sys/time.h>
310ad011ecSGleb Smirnoff #include <sys/socket.h>
320ad011ecSGleb Smirnoff #include <sys/module.h>
330ad011ecSGleb Smirnoff #include <errno.h>
340ad011ecSGleb Smirnoff #include <fcntl.h>
350ad011ecSGleb Smirnoff #include <signal.h>
360ad011ecSGleb Smirnoff #include <stdlib.h>
370ad011ecSGleb Smirnoff #include <unistd.h>
380ad011ecSGleb Smirnoff 
390ad011ecSGleb Smirnoff #include <netlink/netlink.h>
400ad011ecSGleb Smirnoff #include <netlink/netlink_route.h>
410ad011ecSGleb Smirnoff 
428338ea1dSGleb Smirnoff #include <netinet/in.h>
438338ea1dSGleb Smirnoff #include <arpa/inet.h>
448338ea1dSGleb Smirnoff 
450ad011ecSGleb Smirnoff #include <atf-c.h>
460ad011ecSGleb Smirnoff 
470ad011ecSGleb Smirnoff static struct itimerval itv = {
480ad011ecSGleb Smirnoff 	.it_interval = { 0, 0 },
490ad011ecSGleb Smirnoff 	.it_value = { 1, 0 },	/* one second */
500ad011ecSGleb Smirnoff };
510ad011ecSGleb Smirnoff static sig_atomic_t timer_done = 0;
520ad011ecSGleb Smirnoff static void
sigalarm(int sig __unused)530ad011ecSGleb Smirnoff sigalarm(int sig __unused)
540ad011ecSGleb Smirnoff {
550ad011ecSGleb Smirnoff 
560ad011ecSGleb Smirnoff 	timer_done = 1;
570ad011ecSGleb Smirnoff }
580ad011ecSGleb Smirnoff 
590ad011ecSGleb Smirnoff static struct sigaction sigact = {
600ad011ecSGleb Smirnoff 	.sa_handler = sigalarm,
610ad011ecSGleb Smirnoff };
620ad011ecSGleb Smirnoff 
630ad011ecSGleb Smirnoff static struct nlmsghdr hdr = (struct nlmsghdr) {
640ad011ecSGleb Smirnoff 	.nlmsg_type = RTM_GETLINK,
650ad011ecSGleb Smirnoff 	.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
660ad011ecSGleb Smirnoff 	.nlmsg_len = sizeof(struct nlmsghdr),
670ad011ecSGleb Smirnoff };
680ad011ecSGleb Smirnoff 
690ad011ecSGleb Smirnoff #define	BUFLEN	1000
700ad011ecSGleb Smirnoff 
710ad011ecSGleb Smirnoff static int
fullsocket(void)720ad011ecSGleb Smirnoff fullsocket(void)
730ad011ecSGleb Smirnoff {
740ad011ecSGleb Smirnoff 	char buf[BUFLEN];
750ad011ecSGleb Smirnoff 	socklen_t slen = sizeof(int);
760ad011ecSGleb Smirnoff 	int fd, sendspace, recvspace, sendavail, recvavail, rsize;
770ad011ecSGleb Smirnoff 	u_int cnt = 0;
780ad011ecSGleb Smirnoff 
790ad011ecSGleb Smirnoff 	ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
800ad011ecSGleb Smirnoff 	ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace,
810ad011ecSGleb Smirnoff 	    &slen) == 0);
820ad011ecSGleb Smirnoff 	ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace,
830ad011ecSGleb Smirnoff 	    &slen) == 0);
840ad011ecSGleb Smirnoff 
850ad011ecSGleb Smirnoff 	/* Check the expected size of reply on a single RTM_GETLINK. */
860ad011ecSGleb Smirnoff 	ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
870ad011ecSGleb Smirnoff 	ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==
880ad011ecSGleb Smirnoff 	    sizeof(hdr));
890ad011ecSGleb Smirnoff 	ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
900ad011ecSGleb Smirnoff 
910ad011ecSGleb Smirnoff 
920ad011ecSGleb Smirnoff 	/*
930ad011ecSGleb Smirnoff 	 * Flood the socket with requests, without reading out the replies.
940ad011ecSGleb Smirnoff 	 * While we are flooding, the kernel tries to process the requests.
950ad011ecSGleb Smirnoff 	 * Kernel takes off requests from the send buffer and puts replies
960ad011ecSGleb Smirnoff 	 * on receive buffer.  Once the receive buffer is full it stops working
970ad011ecSGleb Smirnoff 	 * on queue in the send buffer.  At this point we must get a solid
980ad011ecSGleb Smirnoff 	 * failure.  However, if we flood faster than kernel taskqueue runs,
990ad011ecSGleb Smirnoff 	 * we may get intermittent failures.
1000ad011ecSGleb Smirnoff 	 */
1010ad011ecSGleb Smirnoff 	do {
1020ad011ecSGleb Smirnoff 		ssize_t rv;
1030ad011ecSGleb Smirnoff 
1040ad011ecSGleb Smirnoff 		rv = send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT);
1050ad011ecSGleb Smirnoff 		if (__predict_true(rv == sizeof(hdr)))
1060ad011ecSGleb Smirnoff 			cnt++;
1070ad011ecSGleb Smirnoff 		else {
1080ad011ecSGleb Smirnoff 			ATF_REQUIRE(errno == EAGAIN);
1090ad011ecSGleb Smirnoff 			ATF_REQUIRE(sizeof(hdr) * cnt > sendspace);
1100ad011ecSGleb Smirnoff 		}
1110ad011ecSGleb Smirnoff 		ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1);
1120ad011ecSGleb Smirnoff 		ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendavail) != -1);
1130ad011ecSGleb Smirnoff 	} while (recvavail <= recvspace - rsize ||
1140ad011ecSGleb Smirnoff 		 sendavail <= sendspace - sizeof(hdr));
1150ad011ecSGleb Smirnoff 
1160ad011ecSGleb Smirnoff 	return (fd);
1170ad011ecSGleb Smirnoff }
1180ad011ecSGleb Smirnoff 
1190ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(overflow);
ATF_TC_BODY(overflow,tc)1200ad011ecSGleb Smirnoff ATF_TC_BODY(overflow, tc)
1210ad011ecSGleb Smirnoff {
1220ad011ecSGleb Smirnoff 	char buf[BUFLEN];
1230ad011ecSGleb Smirnoff 	int fd;
1240ad011ecSGleb Smirnoff 
1250ad011ecSGleb Smirnoff 	fd = fullsocket();
1260ad011ecSGleb Smirnoff 
1270ad011ecSGleb Smirnoff 	/* Both buffers full: block. */
1280ad011ecSGleb Smirnoff 	timer_done = 0;
1290ad011ecSGleb Smirnoff 	ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);
1300ad011ecSGleb Smirnoff 	ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
1310ad011ecSGleb Smirnoff 	ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1);
1320ad011ecSGleb Smirnoff 	ATF_REQUIRE(errno == EINTR);
1330ad011ecSGleb Smirnoff 	ATF_REQUIRE(timer_done == 1);
1340ad011ecSGleb Smirnoff 
1350ad011ecSGleb Smirnoff 	/*
1360ad011ecSGleb Smirnoff 	 * Now, reading something from the receive buffer should wake up the
1370ad011ecSGleb Smirnoff 	 * taskqueue and send buffer should start getting drained.
1380ad011ecSGleb Smirnoff 	 */
1390ad011ecSGleb Smirnoff 	ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));
1400ad011ecSGleb Smirnoff 	timer_done = 0;
1410ad011ecSGleb Smirnoff 	ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
1420ad011ecSGleb Smirnoff 	ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
1430ad011ecSGleb Smirnoff 	ATF_REQUIRE(timer_done == 0);
1440ad011ecSGleb Smirnoff }
1450ad011ecSGleb Smirnoff 
1460ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(peek);
ATF_TC_BODY(peek,tc)1470ad011ecSGleb Smirnoff ATF_TC_BODY(peek, tc)
1480ad011ecSGleb Smirnoff {
1490ad011ecSGleb Smirnoff 	char *buf;
1500ad011ecSGleb Smirnoff 	ssize_t ss, ss1;
1510ad011ecSGleb Smirnoff 	int fd;
1520ad011ecSGleb Smirnoff 
1530ad011ecSGleb Smirnoff 	ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
1540ad011ecSGleb Smirnoff 
1550ad011ecSGleb Smirnoff 	ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
1560ad011ecSGleb Smirnoff 	ss = recv(fd, buf, 0, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
1570ad011ecSGleb Smirnoff 	ATF_REQUIRE((buf = malloc(ss)) != NULL);
1580ad011ecSGleb Smirnoff 	ATF_REQUIRE(recv(fd, buf, ss, MSG_WAITALL) == ss);
1590ad011ecSGleb Smirnoff }
1600ad011ecSGleb Smirnoff 
1610ad011ecSGleb Smirnoff struct nl_control {
1620ad011ecSGleb Smirnoff 	struct nlattr nla;
1630ad011ecSGleb Smirnoff 	uint32_t val;
1640ad011ecSGleb Smirnoff };
1650ad011ecSGleb Smirnoff 
1660ad011ecSGleb Smirnoff static void
cmsg_check(struct msghdr * msg)1670ad011ecSGleb Smirnoff cmsg_check(struct msghdr *msg)
1680ad011ecSGleb Smirnoff {
1690ad011ecSGleb Smirnoff 	static pid_t pid = 0;
1700ad011ecSGleb Smirnoff 	struct cmsghdr *cmsg;
1710ad011ecSGleb Smirnoff 	struct nl_control *nlc;
1720ad011ecSGleb Smirnoff 
1730ad011ecSGleb Smirnoff 	ATF_REQUIRE((cmsg = CMSG_FIRSTHDR(msg)) != NULL);
1740ad011ecSGleb Smirnoff 	ATF_REQUIRE(cmsg->cmsg_level == SOL_NETLINK);
1750ad011ecSGleb Smirnoff 	ATF_REQUIRE(cmsg->cmsg_type == NETLINK_MSG_INFO);
1760ad011ecSGleb Smirnoff 	nlc = (struct nl_control *)CMSG_DATA(cmsg);
1770ad011ecSGleb Smirnoff 	ATF_REQUIRE(nlc[0].nla.nla_type == NLMSGINFO_ATTR_PROCESS_ID);
1780ad011ecSGleb Smirnoff 	if (pid == 0)
1790ad011ecSGleb Smirnoff 		pid = getpid();
1800ad011ecSGleb Smirnoff 	ATF_REQUIRE(nlc[0].val == pid);
1810ad011ecSGleb Smirnoff 	ATF_REQUIRE(nlc[1].nla.nla_type == NLMSGINFO_ATTR_PORT_ID);
1820ad011ecSGleb Smirnoff 	/* XXX need another test to test port id */
1830ad011ecSGleb Smirnoff 	ATF_REQUIRE(nlc[1].val == 0);
1840ad011ecSGleb Smirnoff 	ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL);
1850ad011ecSGleb Smirnoff 	ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);
1860ad011ecSGleb Smirnoff }
1870ad011ecSGleb Smirnoff 
1880ad011ecSGleb Smirnoff ATF_TC_WITHOUT_HEAD(sizes);
ATF_TC_BODY(sizes,tc)1890ad011ecSGleb Smirnoff ATF_TC_BODY(sizes, tc)
1900ad011ecSGleb Smirnoff {
1910ad011ecSGleb Smirnoff #define	NLMSG_LARGE 2048		/* XXX: match kernel nl_buf */
1920ad011ecSGleb Smirnoff 	char buf[NLMSG_LARGE * 10];
1930ad011ecSGleb Smirnoff 	char cbuf[CMSG_SPACE(sizeof(struct nl_control) * 2)];
1940ad011ecSGleb Smirnoff 	struct iovec iov;
1950ad011ecSGleb Smirnoff 	struct msghdr msg = {
1960ad011ecSGleb Smirnoff 		.msg_iov = &iov,
1970ad011ecSGleb Smirnoff 		.msg_iovlen = 1,
1980ad011ecSGleb Smirnoff 		.msg_control = cbuf,
1990ad011ecSGleb Smirnoff 		.msg_controllen = sizeof(cbuf),
2000ad011ecSGleb Smirnoff 	};
2010ad011ecSGleb Smirnoff 	ssize_t ss;
2020ad011ecSGleb Smirnoff 	int fd, size, rsize;
2030ad011ecSGleb Smirnoff 
2040ad011ecSGleb Smirnoff 	fd = fullsocket();
2050ad011ecSGleb Smirnoff 
2060ad011ecSGleb Smirnoff 	/*
2070ad011ecSGleb Smirnoff 	 * Set NETLINK_MSG_INFO, so that later cmsg_check will check that any
2080ad011ecSGleb Smirnoff 	 * read is accompanied with control data.
2090ad011ecSGleb Smirnoff 	 */
2100ad011ecSGleb Smirnoff 	ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO,
2110ad011ecSGleb Smirnoff 	    &(int){1}, sizeof(int)) == 0);
2120ad011ecSGleb Smirnoff 
2130ad011ecSGleb Smirnoff 	iov = (struct iovec ){
2140ad011ecSGleb Smirnoff 		.iov_base = &hdr,
2150ad011ecSGleb Smirnoff 		.iov_len = sizeof(hdr),
2160ad011ecSGleb Smirnoff 	};
2170ad011ecSGleb Smirnoff 	/* Obtain size of the first message in the socket. */
2180ad011ecSGleb Smirnoff 	ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
2190ad011ecSGleb Smirnoff 	ATF_REQUIRE(ss == hdr.nlmsg_len);
2200ad011ecSGleb Smirnoff 	/* And overall amount of data in the socket. */
2210ad011ecSGleb Smirnoff 	ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
2220ad011ecSGleb Smirnoff 	cmsg_check(&msg);
2230ad011ecSGleb Smirnoff 
2240ad011ecSGleb Smirnoff 	/* Zero-sized read should not affect state of the socket buffer. */
2250ad011ecSGleb Smirnoff 	ATF_REQUIRE(recv(fd, buf, 0, 0) == 0);
2260ad011ecSGleb Smirnoff 	ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1);
2270ad011ecSGleb Smirnoff 	ATF_REQUIRE(size == rsize);
2280ad011ecSGleb Smirnoff 
2290ad011ecSGleb Smirnoff 	/*
2300ad011ecSGleb Smirnoff 	 * Undersized read should lose a message.  This isn't exactly
2310ad011ecSGleb Smirnoff 	 * pronounced in the Netlink RFC, but it always says that Netlink
2320ad011ecSGleb Smirnoff 	 * socket is an analog of the BSD routing socket, and this is how
2330ad011ecSGleb Smirnoff 	 * a route(4) socket deals with undersized read.
2340ad011ecSGleb Smirnoff 	 */
2350ad011ecSGleb Smirnoff 	iov = (struct iovec ){
2360ad011ecSGleb Smirnoff 		.iov_base = buf,
2370ad011ecSGleb Smirnoff 		.iov_len = sizeof(hdr),
2380ad011ecSGleb Smirnoff 	};
2390ad011ecSGleb Smirnoff 	ATF_REQUIRE(recvmsg(fd, &msg, 0) == sizeof(hdr));
2400ad011ecSGleb Smirnoff 	ATF_REQUIRE(msg.msg_flags & MSG_TRUNC);
2410ad011ecSGleb Smirnoff 	ATF_REQUIRE(hdr.nlmsg_len > sizeof(hdr));
2420ad011ecSGleb Smirnoff 	size = rsize - hdr.nlmsg_len;
2430ad011ecSGleb Smirnoff 	ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
2440ad011ecSGleb Smirnoff 	ATF_REQUIRE(size == rsize);
2450ad011ecSGleb Smirnoff 	cmsg_check(&msg);
2460ad011ecSGleb Smirnoff 
2470ad011ecSGleb Smirnoff 	/*
2480ad011ecSGleb Smirnoff 	 * Large read should span several nl_bufs, seeing no boundaries.
2490ad011ecSGleb Smirnoff 	 */
2500ad011ecSGleb Smirnoff 	iov = (struct iovec ){
2510ad011ecSGleb Smirnoff 		.iov_base = buf,
2520ad011ecSGleb Smirnoff 		.iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
2530ad011ecSGleb Smirnoff 	};
2540ad011ecSGleb Smirnoff 	ss = recvmsg(fd, &msg, 0);
2550ad011ecSGleb Smirnoff 	ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
2560ad011ecSGleb Smirnoff 	cmsg_check(&msg);
2570ad011ecSGleb Smirnoff }
2580ad011ecSGleb Smirnoff 
259*26caf57eSGleb Smirnoff static struct nlattr *
nla_RTA_DST(struct nlattr * start,ssize_t len)260*26caf57eSGleb Smirnoff nla_RTA_DST(struct nlattr *start, ssize_t len)
261*26caf57eSGleb Smirnoff {
262*26caf57eSGleb Smirnoff 	struct nlattr *nla;
263*26caf57eSGleb Smirnoff 
264*26caf57eSGleb Smirnoff 	for (nla = start; (char *)nla < (char *)start + len;
265*26caf57eSGleb Smirnoff 	    nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len))) {
266*26caf57eSGleb Smirnoff 		if (nla->nla_type == RTA_DST)
267*26caf57eSGleb Smirnoff 			return (nla);
268*26caf57eSGleb Smirnoff 	}
269*26caf57eSGleb Smirnoff 
270*26caf57eSGleb Smirnoff 	return (NULL);
271*26caf57eSGleb Smirnoff }
2728338ea1dSGleb Smirnoff /*
2738338ea1dSGleb Smirnoff  * Check that NETLINK_ADD_MEMBERSHIP subscribes us.  Add & delete a temporary
2748338ea1dSGleb Smirnoff  * route and check if announcements came in.
2758338ea1dSGleb Smirnoff  */
2768338ea1dSGleb Smirnoff ATF_TC_WITHOUT_HEAD(membership);
ATF_TC_BODY(membership,tc)2778338ea1dSGleb Smirnoff ATF_TC_BODY(membership, tc)
2788338ea1dSGleb Smirnoff {
2798338ea1dSGleb Smirnoff 	struct {
2808338ea1dSGleb Smirnoff 		struct nlmsghdr hdr;
2818338ea1dSGleb Smirnoff 		struct rtmsg rtm;
2828338ea1dSGleb Smirnoff 		struct nlattr rta_dst;
2838338ea1dSGleb Smirnoff 		struct in_addr dst;
2848338ea1dSGleb Smirnoff 		struct nlattr rta_oif;
2858338ea1dSGleb Smirnoff 		uint32_t oif;
286*26caf57eSGleb Smirnoff 	} reply, msg = {
2878338ea1dSGleb Smirnoff 		.hdr.nlmsg_type = RTM_NEWROUTE,
2888338ea1dSGleb Smirnoff 		.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
2898338ea1dSGleb Smirnoff 		.hdr.nlmsg_len = sizeof(msg),
2908338ea1dSGleb Smirnoff 		.rtm.rtm_family = AF_INET,
2918338ea1dSGleb Smirnoff 		.rtm.rtm_protocol = RTPROT_STATIC,
2928338ea1dSGleb Smirnoff 		.rtm.rtm_type = RTN_UNICAST,
2938338ea1dSGleb Smirnoff 		.rtm.rtm_dst_len = 32,
2948338ea1dSGleb Smirnoff 		.rta_dst.nla_type = RTA_DST,
2958338ea1dSGleb Smirnoff 		.rta_dst.nla_len = sizeof(struct in_addr) +
2968338ea1dSGleb Smirnoff 		    sizeof(struct nlattr),
2978338ea1dSGleb Smirnoff 		.dst.s_addr = inet_addr("127.0.0.127"),
2988338ea1dSGleb Smirnoff 		.rta_oif.nla_type = RTA_OIF,
2998338ea1dSGleb Smirnoff 		.rta_oif.nla_len = sizeof(uint32_t) + sizeof(struct nlattr),
3008338ea1dSGleb Smirnoff 		.oif = 1,
3018338ea1dSGleb Smirnoff 	};
302*26caf57eSGleb Smirnoff 	struct nlattr *nla;
3038338ea1dSGleb Smirnoff 	int fd;
3048338ea1dSGleb Smirnoff 
3058338ea1dSGleb Smirnoff 	ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
3068338ea1dSGleb Smirnoff 	ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
3078338ea1dSGleb Smirnoff 	    &(int){RTNLGRP_IPV4_ROUTE}, sizeof(int)) == 0);
308*26caf57eSGleb Smirnoff 
3098338ea1dSGleb Smirnoff 	ATF_REQUIRE(send(fd, &msg, sizeof(msg), 0) == sizeof(msg));
310*26caf57eSGleb Smirnoff 	ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
311*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
312*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
313*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
314*26caf57eSGleb Smirnoff 	ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
315*26caf57eSGleb Smirnoff 	ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
316*26caf57eSGleb Smirnoff 	    sizeof(struct in_addr)) == 0);
317*26caf57eSGleb Smirnoff 
3188338ea1dSGleb Smirnoff 	msg.hdr.nlmsg_type = RTM_DELROUTE;
3198338ea1dSGleb Smirnoff 	msg.hdr.nlmsg_len -= sizeof(struct nlattr) + sizeof(uint32_t);
3208338ea1dSGleb Smirnoff 	ATF_REQUIRE(send(fd, &msg, msg.hdr.nlmsg_len, 0) == msg.hdr.nlmsg_len);
321*26caf57eSGleb Smirnoff 	ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
322*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
323*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
324*26caf57eSGleb Smirnoff 	ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
325*26caf57eSGleb Smirnoff 	ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
326*26caf57eSGleb Smirnoff 	ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
327*26caf57eSGleb Smirnoff 	    sizeof(struct in_addr)) == 0);
3288338ea1dSGleb Smirnoff }
3298338ea1dSGleb Smirnoff 
ATF_TP_ADD_TCS(tp)3300ad011ecSGleb Smirnoff ATF_TP_ADD_TCS(tp)
3310ad011ecSGleb Smirnoff {
3320ad011ecSGleb Smirnoff 	if (modfind("netlink") == -1)
3330ad011ecSGleb Smirnoff 		atf_tc_skip("netlink module not loaded");
3340ad011ecSGleb Smirnoff 
3350ad011ecSGleb Smirnoff 	ATF_TP_ADD_TC(tp, overflow);
3360ad011ecSGleb Smirnoff 	ATF_TP_ADD_TC(tp, peek);
3370ad011ecSGleb Smirnoff 	ATF_TP_ADD_TC(tp, sizes);
3388338ea1dSGleb Smirnoff 	ATF_TP_ADD_TC(tp, membership);
3390ad011ecSGleb Smirnoff 
3400ad011ecSGleb Smirnoff 	return (atf_no_error());
3410ad011ecSGleb Smirnoff }
342