Path: blob/main/tests/sys/netlink/netlink_socket.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2023 Gleb Smirnoff <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/ioctl.h>29#include <sys/time.h>30#include <sys/socket.h>31#include <sys/module.h>32#include <errno.h>33#include <fcntl.h>34#include <signal.h>35#include <stdlib.h>36#include <unistd.h>3738#include <netlink/netlink.h>39#include <netlink/netlink_route.h>4041#include <netinet/in.h>42#include <arpa/inet.h>4344#include <atf-c.h>4546static struct itimerval itv = {47.it_interval = { 0, 0 },48.it_value = { 1, 0 }, /* one second */49};50static sig_atomic_t timer_done = 0;51static void52sigalarm(int sig __unused)53{5455timer_done = 1;56}5758static struct sigaction sigact = {59.sa_handler = sigalarm,60};6162static struct nlmsghdr hdr = (struct nlmsghdr) {63.nlmsg_type = RTM_GETLINK,64.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,65.nlmsg_len = sizeof(struct nlmsghdr),66};6768#define BUFLEN 10006970static int71fullsocket(void)72{73char buf[BUFLEN];74socklen_t slen = sizeof(int);75int fd, sendspace, recvspace, sendavail, recvavail, rsize;76u_int cnt = 0;7778ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);79ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace,80&slen) == 0);81ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace,82&slen) == 0);8384/* Check the expected size of reply on a single RTM_GETLINK. */85ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));86ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==87sizeof(hdr));88ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);899091/*92* Flood the socket with requests, without reading out the replies.93* While we are flooding, the kernel tries to process the requests.94* Kernel takes off requests from the send buffer and puts replies95* on receive buffer. Once the receive buffer is full it stops working96* on queue in the send buffer. At this point we must get a solid97* failure. However, if we flood faster than kernel taskqueue runs,98* we may get intermittent failures.99*/100do {101ssize_t rv;102103rv = send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT);104if (__predict_true(rv == sizeof(hdr)))105cnt++;106else {107ATF_REQUIRE(errno == EAGAIN);108ATF_REQUIRE(sizeof(hdr) * cnt > sendspace);109}110ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1);111ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendavail) != -1);112} while (recvavail <= recvspace - rsize ||113sendavail <= sendspace - sizeof(hdr));114115return (fd);116}117118ATF_TC(overflow);119ATF_TC_HEAD(overflow, tc)120{121atf_tc_set_md_var(tc, "require.kmods", "netlink");122}123ATF_TC_BODY(overflow, tc)124{125char buf[BUFLEN];126int fd;127128fd = fullsocket();129130/* Both buffers full: block. */131timer_done = 0;132ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);133ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);134ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1);135ATF_REQUIRE(errno == EINTR);136ATF_REQUIRE(timer_done == 1);137138/*139* Now, reading something from the receive buffer should wake up the140* taskqueue and send buffer should start getting drained.141*/142ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));143timer_done = 0;144ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);145ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));146ATF_REQUIRE(timer_done == 0);147}148149ATF_TC(peek);150ATF_TC_HEAD(peek, tc)151{152atf_tc_set_md_var(tc, "require.kmods", "netlink");153}154ATF_TC_BODY(peek, tc)155{156char *buf;157ssize_t ss, ss1;158int fd;159160ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);161162ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));163ss = recv(fd, buf, 0, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);164ATF_REQUIRE((buf = malloc(ss)) != NULL);165ATF_REQUIRE(recv(fd, buf, ss, MSG_WAITALL) == ss);166}167168struct nl_control {169struct nlattr nla;170uint32_t val;171};172173static void174cmsg_check(struct msghdr *msg)175{176static pid_t pid = 0;177struct cmsghdr *cmsg;178struct nl_control *nlc;179180ATF_REQUIRE((cmsg = CMSG_FIRSTHDR(msg)) != NULL);181ATF_REQUIRE(cmsg->cmsg_level == SOL_NETLINK);182ATF_REQUIRE(cmsg->cmsg_type == NETLINK_MSG_INFO);183nlc = (struct nl_control *)CMSG_DATA(cmsg);184ATF_REQUIRE(nlc[0].nla.nla_type == NLMSGINFO_ATTR_PROCESS_ID);185if (pid == 0)186pid = getpid();187ATF_REQUIRE(nlc[0].val == pid);188ATF_REQUIRE(nlc[1].nla.nla_type == NLMSGINFO_ATTR_PORT_ID);189/* XXX need another test to test port id */190ATF_REQUIRE(nlc[1].val == 0);191ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL);192ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);193}194195ATF_TC(sizes);196ATF_TC_HEAD(sizes, tc)197{198atf_tc_set_md_var(tc, "require.kmods", "netlink");199}200ATF_TC_BODY(sizes, tc)201{202#define NLMSG_LARGE 2048 /* XXX: match kernel nl_buf */203char buf[NLMSG_LARGE * 10];204char cbuf[CMSG_SPACE(sizeof(struct nl_control) * 2)];205struct iovec iov;206struct msghdr msg = {207.msg_iov = &iov,208.msg_iovlen = 1,209.msg_control = cbuf,210.msg_controllen = sizeof(cbuf),211};212ssize_t ss;213int fd, size, msize, rsize;214215/*216* Create a socket with NMSGS messages in the receive buffer.217*/218#define NMSGS 5219ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);220ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));221ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==222sizeof(hdr));223ATF_REQUIRE(ioctl(fd, FIONREAD, &msize) != -1);224for (u_int i = 0; i < NMSGS; i++)225ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));226do {227ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);228} while (rsize < msize * (NMSGS + 1));229230/*231* Set NETLINK_MSG_INFO, so that later cmsg_check will check that any232* read is accompanied with control data.233*/234ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO,235&(int){1}, sizeof(int)) == 0);236237iov = (struct iovec ){238.iov_base = &hdr,239.iov_len = sizeof(hdr),240};241/* Obtain size of the first message in the socket. */242ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);243ATF_REQUIRE(ss == hdr.nlmsg_len);244/* And overall amount of data in the socket. */245ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);246cmsg_check(&msg);247248/* Zero-sized read should not affect state of the socket buffer. */249ATF_REQUIRE(recv(fd, buf, 0, 0) == 0);250ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1);251ATF_REQUIRE(size == rsize);252253/*254* Undersized read should lose a message. This isn't exactly255* pronounced in the Netlink RFC, but it always says that Netlink256* socket is an analog of the BSD routing socket, and this is how257* a route(4) socket deals with undersized read.258*/259iov = (struct iovec ){260.iov_base = buf,261.iov_len = sizeof(hdr),262};263ATF_REQUIRE(recvmsg(fd, &msg, 0) == sizeof(hdr));264ATF_REQUIRE(msg.msg_flags & MSG_TRUNC);265ATF_REQUIRE(hdr.nlmsg_len > sizeof(hdr));266size = rsize - hdr.nlmsg_len;267ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);268ATF_REQUIRE(size == rsize);269cmsg_check(&msg);270271/*272* Large read should span several nl_bufs, seeing no boundaries.273*/274iov = (struct iovec ){275.iov_base = buf,276.iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,277};278ss = recvmsg(fd, &msg, 0);279ATF_REQUIRE(ss > hdr.nlmsg_len);280ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);281cmsg_check(&msg);282}283284static struct nlattr *285nla_RTA_DST(struct nlattr *start, ssize_t len)286{287struct nlattr *nla;288289for (nla = start; (char *)nla < (char *)start + len;290nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len))) {291if (nla->nla_type == RTA_DST)292return (nla);293}294295return (NULL);296}297/*298* Check that NETLINK_ADD_MEMBERSHIP subscribes us. Add & delete a temporary299* route and check if announcements came in.300*/301ATF_TC(membership);302ATF_TC_HEAD(membership, tc)303{304atf_tc_set_md_var(tc, "require.kmods", "netlink");305}306ATF_TC_BODY(membership, tc)307{308struct {309struct nlmsghdr hdr;310struct rtmsg rtm;311struct nlattr rta_dst;312struct in_addr dst;313struct nlattr rta_oif;314uint32_t oif;315} reply, msg = {316.hdr.nlmsg_type = RTM_NEWROUTE,317.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,318.hdr.nlmsg_len = sizeof(msg),319.rtm.rtm_family = AF_INET,320.rtm.rtm_protocol = RTPROT_STATIC,321.rtm.rtm_type = RTN_UNICAST,322.rtm.rtm_dst_len = 32,323.rta_dst.nla_type = RTA_DST,324.rta_dst.nla_len = sizeof(struct in_addr) +325sizeof(struct nlattr),326.dst.s_addr = inet_addr("127.0.0.127"),327.rta_oif.nla_type = RTA_OIF,328.rta_oif.nla_len = sizeof(uint32_t) + sizeof(struct nlattr),329.oif = 1,330};331struct nlattr *nla;332int fd;333334ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);335ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,336&(int){RTNLGRP_IPV4_ROUTE}, sizeof(int)) == 0);337338ATF_REQUIRE(send(fd, &msg, sizeof(msg), 0) == sizeof(msg));339ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));340ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);341ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);342ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);343ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));344ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),345sizeof(struct in_addr)) == 0);346347msg.hdr.nlmsg_type = RTM_DELROUTE;348msg.hdr.nlmsg_len -= sizeof(struct nlattr) + sizeof(uint32_t);349ATF_REQUIRE(send(fd, &msg, msg.hdr.nlmsg_len, 0) == msg.hdr.nlmsg_len);350ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));351ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);352ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);353ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);354ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));355ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),356sizeof(struct in_addr)) == 0);357}358359ATF_TP_ADD_TCS(tp)360{361ATF_TP_ADD_TC(tp, overflow);362ATF_TP_ADD_TC(tp, peek);363ATF_TP_ADD_TC(tp, sizes);364ATF_TP_ADD_TC(tp, membership);365366return (atf_no_error());367}368369370