#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 <netinet/in.h>
#include <arpa/inet.h>
#include <atf-c.h>
static struct itimerval itv = {
.it_interval = { 0, 0 },
.it_value = { 1, 0 },
};
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);
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);
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(overflow);
ATF_TC_HEAD(overflow, tc)
{
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(overflow, tc)
{
char buf[BUFLEN];
int fd;
fd = fullsocket();
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);
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(peek);
ATF_TC_HEAD(peek, tc)
{
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
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);
ATF_REQUIRE(nlc[1].val == 0);
ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL);
ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);
}
ATF_TC(sizes);
ATF_TC_HEAD(sizes, tc)
{
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(sizes, tc)
{
#define NLMSG_LARGE 2048
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, msize, rsize;
#define NMSGS 5
ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
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, &msize) != -1);
for (u_int i = 0; i < NMSGS; i++)
ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
do {
ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
} while (rsize < msize * (NMSGS + 1));
ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO,
&(int){1}, sizeof(int)) == 0);
iov = (struct iovec ){
.iov_base = &hdr,
.iov_len = sizeof(hdr),
};
ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
ATF_REQUIRE(ss == hdr.nlmsg_len);
ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
cmsg_check(&msg);
ATF_REQUIRE(recv(fd, buf, 0, 0) == 0);
ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1);
ATF_REQUIRE(size == rsize);
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);
iov = (struct iovec ){
.iov_base = buf,
.iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
};
ss = recvmsg(fd, &msg, 0);
ATF_REQUIRE(ss > hdr.nlmsg_len);
ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
cmsg_check(&msg);
}
static struct nlattr *
nla_RTA_DST(struct nlattr *start, ssize_t len)
{
struct nlattr *nla;
for (nla = start; (char *)nla < (char *)start + len;
nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len))) {
if (nla->nla_type == RTA_DST)
return (nla);
}
return (NULL);
}
ATF_TC(membership);
ATF_TC_HEAD(membership, tc)
{
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(membership, tc)
{
struct {
struct nlmsghdr hdr;
struct rtmsg rtm;
struct nlattr rta_dst;
struct in_addr dst;
struct nlattr rta_oif;
uint32_t oif;
} reply, msg = {
.hdr.nlmsg_type = RTM_NEWROUTE,
.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
.hdr.nlmsg_len = sizeof(msg),
.rtm.rtm_family = AF_INET,
.rtm.rtm_protocol = RTPROT_STATIC,
.rtm.rtm_type = RTN_UNICAST,
.rtm.rtm_dst_len = 32,
.rta_dst.nla_type = RTA_DST,
.rta_dst.nla_len = sizeof(struct in_addr) +
sizeof(struct nlattr),
.dst.s_addr = inet_addr("127.0.0.127"),
.rta_oif.nla_type = RTA_OIF,
.rta_oif.nla_len = sizeof(uint32_t) + sizeof(struct nlattr),
.oif = 1,
};
struct nlattr *nla;
int fd;
ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
&(int){RTNLGRP_IPV4_ROUTE}, sizeof(int)) == 0);
ATF_REQUIRE(send(fd, &msg, sizeof(msg), 0) == sizeof(msg));
ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
sizeof(struct in_addr)) == 0);
msg.hdr.nlmsg_type = RTM_DELROUTE;
msg.hdr.nlmsg_len -= sizeof(struct nlattr) + sizeof(uint32_t);
ATF_REQUIRE(send(fd, &msg, msg.hdr.nlmsg_len, 0) == msg.hdr.nlmsg_len);
ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
sizeof(struct in_addr)) == 0);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, overflow);
ATF_TP_ADD_TC(tp, peek);
ATF_TP_ADD_TC(tp, sizes);
ATF_TP_ADD_TC(tp, membership);
return (atf_no_error());
}