From 04e9edb5442826a14616157962361ff81e4a38fe Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Sat, 5 Jan 2019 16:05:39 +0000 Subject: [PATCH] Capsicumize rtsol(8) and rtsold(8). These programs parse ND6 Router Advertisement messages; rtsold(8) has required an SA, SA-14:20.rtsold, for a bug in this code. Thus, they are good candidates for sandboxing. The approach taken is to run the main executable in capability mode and use Casper services to provide functionality that cannot be implemented within the sandbox. In particular, several custom services were required. - A Casper service is used to send Router Solicitation messages on a raw ICMP6 socket. Initially I took the approach of creating a socket for each interface upon startup, and connect(2)ing it to the all-routers multicast group for the interface. This permits the use of sendmsg(2) in capability mode, but only works if the interface's link is up when rtsol(d) starts. So, instead, the rtsold.sendmsg service is used to transmit RS messages on behalf of the main process. One could alternately define a service which simply creates and connects a socket for each destination address, and returns the socket to the sandboxed process. However, to implement rtsold's -m option we also need to read the ND6 default router list, and this cannot be done in capability mode. - rtsold may execute resolvconf(8) in response to RDNSS and DNSSL options in received RA messages. A Casper service is used to fork and exec resolvconf(8), and to reap the child process. - A service is used to determine whether a given interface's link-local address is useable (i.e., not duplicated or undergoing DAD). This information is supplied by getifaddrs(3), which reads a sysctl not available in capability mode. The SIOCGIFCONF socket ioctl provides equivalent information and can be used in capability mode, but I decided against it for now because of some limitations of that interface. In addition to these new services, cap_syslog(3) is used to send messages to syslogd. Reviewed by: oshogbo Tested by: bz (previous versions) MFC after: 2 months Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D17572 --- sbin/rtsol/Makefile | 20 +- usr.sbin/rtsold/Makefile | 18 +- usr.sbin/rtsold/cap_llflags.c | 157 +++++++++++++++ usr.sbin/rtsold/cap_script.c | 236 ++++++++++++++++++++++ usr.sbin/rtsold/cap_sendmsg.c | 282 +++++++++++++++++++++++++++ usr.sbin/rtsold/dump.c | 48 +++-- usr.sbin/rtsold/if.c | 85 +++----- usr.sbin/rtsold/probe.c | 191 ------------------ usr.sbin/rtsold/rtsock.c | 22 ++- usr.sbin/rtsold/rtsol.c | 355 ++++++++-------------------------- usr.sbin/rtsold/rtsold.c | 336 ++++++++++++++++++-------------- usr.sbin/rtsold/rtsold.h | 30 ++- 12 files changed, 1076 insertions(+), 704 deletions(-) create mode 100644 usr.sbin/rtsold/cap_llflags.c create mode 100644 usr.sbin/rtsold/cap_script.c create mode 100644 usr.sbin/rtsold/cap_sendmsg.c delete mode 100644 usr.sbin/rtsold/probe.c diff --git a/sbin/rtsol/Makefile b/sbin/rtsol/Makefile index 80b38a6af77a..ee22f5756b64 100644 --- a/sbin/rtsol/Makefile +++ b/sbin/rtsol/Makefile @@ -18,10 +18,24 @@ PACKAGE=runtime PROG= rtsol -SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c +SRCS= cap_llflags.c \ + cap_script.c \ + cap_sendmsg.c \ + dump.c \ + if.c \ + rtsol.c \ + rtsold.c \ + rtsock.c MAN= +LIBADD= util -WARNS?= 3 -CFLAGS+= -DSMALL +.include + +.if ${MK_DYNAMICROOT} == "no" +.warning ${PROG} built without libcasper support +.elif ${MK_CASPER} != "no" && !defined(RESCUE) +CFLAGS+= -DWITH_CASPER +LIBADD+= cap_syslog casper nv +.endif .include diff --git a/usr.sbin/rtsold/Makefile b/usr.sbin/rtsold/Makefile index 84cf0b2500d4..34da996b0f79 100644 --- a/usr.sbin/rtsold/Makefile +++ b/usr.sbin/rtsold/Makefile @@ -17,8 +17,22 @@ PROG= rtsold MAN= rtsold.8 MLINKS= rtsold.8 rtsol.8 -SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c +SRCS= cap_llflags.c \ + cap_script.c \ + cap_sendmsg.c \ + dump.c \ + if.c \ + rtsock.c \ + rtsol.c \ + rtsold.c -WARNS?= 3 +LIBADD= util + +.include + +.if ${MK_CASPER} != "no" +CFLAGS+= -DWITH_CASPER +LIBADD+= casper cap_syslog nv +.endif .include diff --git a/usr.sbin/rtsold/cap_llflags.c b/usr.sbin/rtsold/cap_llflags.c new file mode 100644 index 000000000000..2b1d31b48444 --- /dev/null +++ b/usr.sbin/rtsold/cap_llflags.c @@ -0,0 +1,157 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 The FreeBSD Foundation + * + * This software was developed by Mark Johnston under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "rtsold.h" + +/* + * A service to fetch the flags for the link-local IPv6 address on the specified + * interface. This cannot easily be done in capability mode because we need to + * use the routing socket sysctl API to find the link-local address of a + * particular interface. The SIOCGIFCONF ioctl is one other option, but as + * currently implemented it is less flexible (it cannot report the required + * buffer length), and hard-codes a buffer length limit. + */ + +static int +llflags_get(const char *ifname, int *flagsp) +{ + struct in6_ifreq ifr6; + struct ifaddrs *ifap, *ifa; + struct sockaddr_in6 *sin6; + int error, s; + + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s < 0) + return (-1); + + if (getifaddrs(&ifap) != 0) + return (-1); + error = -1; + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, ifname) != 0) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + + memset(&ifr6, 0, sizeof(ifr6)); + if (strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name)) >= + sizeof(ifr6.ifr_name)) { + freeifaddrs(ifap); + errno = EINVAL; + return (-1); + } + memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + error = errno; + freeifaddrs(ifap); + errno = error; + return (-1); + } + + *flagsp = ifr6.ifr_ifru.ifru_flags6; + error = 0; + break; + } + (void)close(s); + freeifaddrs(ifap); + if (error == -1) + errno = ENOENT; + return (error); +} + +int +cap_llflags_get(cap_channel_t *cap, const char *ifname, int *flagsp) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + int error; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "get"); + nvlist_add_string(nvl, "ifname", ifname); + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (-1); + error = (int)dnvlist_get_number(nvl, "error", 0); + if (error == 0) + *flagsp = (int)nvlist_get_number(nvl, "flags"); + nvlist_destroy(nvl); + if (error != 0) + errno = error; + return (error == 0 ? 0 : -1); +#else + (void)cap; + return (llflags_get(ifname, flagsp)); +#endif +} + +#ifdef WITH_CASPER +static int +llflags_command(const char *cmd, const nvlist_t *limits __unused, + nvlist_t *nvlin, nvlist_t *nvlout) +{ + const char *ifname; + int flags; + + if (strcmp(cmd, "get") != 0) + return (EINVAL); + ifname = nvlist_get_string(nvlin, "ifname"); + if (llflags_get(ifname, &flags) != 0) + return (errno); + nvlist_add_number(nvlout, "flags", flags); + return (0); +} + +CREATE_SERVICE("rtsold.llflags", NULL, llflags_command, 0); +#endif /* WITH_CASPER */ diff --git a/usr.sbin/rtsold/cap_script.c b/usr.sbin/rtsold/cap_script.c new file mode 100644 index 000000000000..c5d4dbcb90dd --- /dev/null +++ b/usr.sbin/rtsold/cap_script.c @@ -0,0 +1,236 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 The FreeBSD Foundation + * + * This software was developed by Mark Johnston under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "rtsold.h" + +/* + * Run the script and return the write end of a pipe to the main process. + * Return -1 and set errno on error. + */ +static int +script_run(char **argv) +{ + pid_t pid; + int fd[2], null; + + if (pipe(fd) != 0) + return (-1); + if ((pid = fork()) < 0) + return (-1); + if (pid == 0) { + (void)close(fd[1]); + null = open("/dev/null", O_RDWR); + if (null < 0) + _exit(1); + if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) + _exit(1); + + closefrom(3); + (void)execve(argv[0], argv, NULL); + _exit(1); + } else + (void)close(fd[0]); + + return (fd[1]); +} + +int +cap_script_run(cap_channel_t *cap, const char *const *argv) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + size_t argc; + int error, wfd; + + for (argc = 0; argv[argc] != NULL; argc++) + ; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "script_run"); + nvlist_add_string_array(nvl, "argv", argv, argc); + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (-1); + + error = (int)dnvlist_get_number(nvl, "error", 0); + if (error == 0) + wfd = nvlist_take_descriptor(nvl, "fd"); + nvlist_destroy(nvl); + if (error != 0) + errno = error; + return (error == 0 ? wfd : -1); +#else + (void)cap; + return (script_run(__DECONST(char **, argv))); +#endif +} + +/* + * Wait for a child process to exit, and return its status. + * Return -1 and set errno upon error. + */ +static int +script_wait(int *statusp) +{ + int error; + + error = wait(statusp); + return (error >= 0 ? 0 : -1); +} + +int +cap_script_wait(cap_channel_t *cap, int *statusp) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + int error; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "script_wait"); + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (-1); + + error = (int)dnvlist_get_number(nvl, "error", 0); + if (error == 0) + *statusp = (int)nvlist_get_number(nvl, "status"); + nvlist_destroy(nvl); + if (error != 0) + errno = error; + return (error == 0 ? 0 : -1); +#else + (void)cap; + return (script_wait(statusp)); +#endif +} + +#ifdef WITH_CASPER +static int +script_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, + nvlist_t *nvlout) +{ + cap_rights_t rights; + const char *const *iargv, *const *scripts; + char **argv; + size_t argc, i, nscripts; + int fd, status; + + if (strcmp(cmd, "script_wait") == 0) { + /* Wait for the result of a previous "script_run" command. */ + if (script_wait(&status) == -1) + return (errno); + nvlist_add_number(nvlout, "status", status); + return (0); + } + if (strcmp(cmd, "script_run") != 0) + return (EINVAL); + + /* + * Validate the argv against the limits specified at initialization + * time. + */ + iargv = nvlist_get_string_array(nvlin, "argv", &argc); + if (argc == 0) + return (EINVAL); + scripts = nvlist_get_string_array(limits, "scripts", &nscripts); + for (i = 0; i < nscripts; i++) + if (strcmp(iargv[0], scripts[i]) == 0) + break; + if (i == nscripts) + return (EINVAL); + + /* + * The nvlist API does not permit NULL pointers in an array, so we have + * to add the nul terminator ourselves. Yuck. + */ + argv = calloc(argc + 1, sizeof(*argv)); + if (argv == NULL) + return (errno); + memcpy(argv, iargv, sizeof(*argv) * argc); + + fd = script_run(argv); + if (fd < 0) + return (errno); + + (void)caph_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE)); + nvlist_move_descriptor(nvlout, "fd", fd); + return (0); +} + +static int +script_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits __unused) +{ + const char *name; + void *cookie; + int nvtype; + bool hasscripts; + + /* Limits may only be set once. */ + if (oldlimits != NULL) + return (ENOTCAPABLE); + + cookie = NULL; + hasscripts = false; + while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { + if (nvtype == NV_TYPE_STRING_ARRAY && + strcmp(name, "scripts") == 0) + hasscripts = true; + else + return (EINVAL); + } + if (!hasscripts) + return (EINVAL); + return (0); +} + +CREATE_SERVICE("rtsold.script", script_limit, script_command, 0); +#endif /* WITH_CASPER */ diff --git a/usr.sbin/rtsold/cap_sendmsg.c b/usr.sbin/rtsold/cap_sendmsg.c new file mode 100644 index 000000000000..b81993514eaf --- /dev/null +++ b/usr.sbin/rtsold/cap_sendmsg.c @@ -0,0 +1,282 @@ +/* $KAME: probe.c,v 1.17 2003/10/05 00:09:36 itojun Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rtsold.h" + +static int +getsocket(int *sockp, int proto) +{ + cap_rights_t rights; + int sock; + + if (*sockp >= 0) + return (0); + + if ((sock = socket(AF_INET6, SOCK_RAW, proto)) < 0) + return (-1); + cap_rights_init(&rights, CAP_CONNECT, CAP_SEND); + if (caph_rights_limit(sock, &rights) != 0) + return (-1); + *sockp = sock; + + return (0); +} + +static ssize_t +sendpacket(int sock, struct sockaddr_in6 *dst, uint32_t ifindex, int hoplimit, + const void *data, size_t len) +{ + uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + struct msghdr hdr; + struct iovec iov; + struct in6_pktinfo *pi; + struct cmsghdr *cm; + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_name = dst; + hdr.msg_namelen = sizeof(*dst); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = __DECONST(void *, data); + iov.iov_len = len; + + /* Specify the outbound interface. */ + cm = CMSG_FIRSTHDR(&hdr); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); + memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ + pi->ipi6_ifindex = ifindex; + + /* Specify the hop limit of the packet for safety. */ + cm = CMSG_NXTHDR(&hdr, cm); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + cm->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); + + return (sendmsg(sock, &hdr, 0)); +} + +static int +probe_defrouters(uint32_t ifindex, uint32_t linkid) +{ + static int probesock = -1; + struct sockaddr_in6 dst; + struct in6_defrouter *p, *ep; + char *buf; + size_t len; + int mib[4]; + + if (ifindex == 0) + return (0); + if (getsocket(&probesock, IPPROTO_NONE) != 0) + return (-1); + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_ICMPV6; + mib[3] = ICMPV6CTL_ND6_DRLIST; + if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0) + return (-1); + if (len == 0) + return (0); + + memset(&dst, 0, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(dst); + + buf = malloc(len); + if (buf == NULL) + return (-1); + if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) < 0) + return (-1); + ep = (struct in6_defrouter *)(void *)(buf + len); + for (p = (struct in6_defrouter *)(void *)buf; p < ep; p++) { + if (ifindex != p->if_index) + continue; + if (!IN6_IS_ADDR_LINKLOCAL(&p->rtaddr.sin6_addr)) + continue; + dst.sin6_addr = p->rtaddr.sin6_addr; + dst.sin6_scope_id = linkid; + (void)sendpacket(probesock, &dst, ifindex, 1, NULL, 0); + } + free(buf); + + return (0); +} + +static int +rssend(uint32_t ifindex, uint32_t linkid, const void *data, size_t len) +{ + static int rssock = -1; + struct sockaddr_in6 dst; + ssize_t n; + + if (getsocket(&rssock, IPPROTO_ICMPV6) != 0) + return (-1); + + memset(&dst, 0, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(dst); + dst.sin6_addr = (struct in6_addr)IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; + dst.sin6_scope_id = linkid; + + n = sendpacket(rssock, &dst, ifindex, 255, data, len); + if (n < 0 || (size_t)n != len) + return (-1); + return (0); +} + +int +cap_probe_defrouters(cap_channel_t *cap, struct ifinfo *ifinfo) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + int error; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "probe_defrouters"); + nvlist_add_number(nvl, "ifindex", ifinfo->sdl->sdl_index); + nvlist_add_number(nvl, "linkid", ifinfo->linkid); + + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (errno); + error = (int)dnvlist_get_number(nvl, "error", 0); + nvlist_destroy(nvl); + errno = error; + return (error == 0 ? 0 : -1); +#else + (void)cap; + return (probe_defrouters(ifinfo->sdl->sdl_index, ifinfo->linkid)); +#endif +} + +int +cap_rssend(cap_channel_t *cap, struct ifinfo *ifinfo) +{ + int error; + +#ifdef WITH_CASPER + nvlist_t *nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "rssend"); + nvlist_add_number(nvl, "ifindex", ifinfo->sdl->sdl_index); + nvlist_add_number(nvl, "linkid", ifinfo->linkid); + nvlist_add_binary(nvl, "data", ifinfo->rs_data, ifinfo->rs_datalen); + + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (errno); + error = (int)dnvlist_get_number(nvl, "error", 0); + nvlist_destroy(nvl); + errno = error; +#else + (void)cap; + error = rssend(ifinfo->sdl->sdl_index, ifinfo->linkid, ifinfo->rs_data, + ifinfo->rs_datalen); +#endif + + ifinfo->probes++; + if (error != 0 && (errno != ENETDOWN || dflag > 0)) { + error = errno; + warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", + ifinfo->ifname, strerror(errno)); + errno = error; + } + return (error == 0 ? 0 : -1); +} + +#ifdef WITH_CASPER +static int +sendmsg_command(const char *cmd, const nvlist_t *limits __unused, nvlist_t *nvlin, + nvlist_t *nvlout __unused) +{ + const void *data; + size_t len; + uint32_t ifindex, linkid; + int error; + + if (strcmp(cmd, "probe_defrouters") != 0 && + strcmp(cmd, "rssend") != 0) + return (EINVAL); + + ifindex = (uint32_t)nvlist_get_number(nvlin, "ifindex"); + linkid = (uint32_t)nvlist_get_number(nvlin, "linkid"); + if (strcmp(cmd, "probe_defrouters") == 0) { + error = probe_defrouters(ifindex, linkid); + } else { + data = nvlist_get_binary(nvlin, "data", &len); + error = rssend(ifindex, linkid, data, len); + } + if (error != 0) + return (errno); + return (0); +} + +CREATE_SERVICE("rtsold.sendmsg", NULL, sendmsg_command, 0); +#endif /* WITH_CASPER */ diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c index 282b71d049d5..daaefc6bc88d 100644 --- a/usr.sbin/rtsold/dump.c +++ b/usr.sbin/rtsold/dump.c @@ -34,6 +34,7 @@ */ #include +#include #include #include @@ -42,21 +43,20 @@ #include #include -#include -#include +#include +#include #include #include -#include +#include +#include #include "rtsold.h" -static FILE *fp; +static const char * const ifstatstr[] = + { "IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE" }; -static void dump_interface_status(void); -static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; - -static void -dump_interface_status(void) +void +rtsold_dump(FILE *fp) { struct ifinfo *ifi; struct rainfo *rai; @@ -64,7 +64,13 @@ dump_interface_status(void) struct timespec now; char ntopbuf[INET6_ADDRSTRLEN]; - clock_gettime(CLOCK_MONOTONIC_FAST, &now); + if (fseek(fp, 0, SEEK_SET) != 0) { + warnmsg(LOG_ERR, __func__, "fseek(): %s", strerror(errno)); + return; + } + (void)ftruncate(fileno(fp), 0); + + (void)clock_gettime(CLOCK_MONOTONIC_FAST, &now); TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { fprintf(fp, "Interface %s\n", ifi->ifname); @@ -121,18 +127,28 @@ dump_interface_status(void) fprintf(fp, "\n"); } } + fflush(fp); } -void -rtsold_dump_file(const char *dumpfile) +FILE * +rtsold_init_dumpfile(const char *dumpfile) { + cap_rights_t rights; + FILE *fp; + if ((fp = fopen(dumpfile, "w")) == NULL) { - warnmsg(LOG_WARNING, __func__, "open a dump file(%s): %s", + warnmsg(LOG_WARNING, __func__, "opening a dump file(%s): %s", dumpfile, strerror(errno)); - return; + return (NULL); } - dump_interface_status(); - fclose(fp); + + cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE); + if (caph_rights_limit(fileno(fp), &rights) != 0) { + warnmsg(LOG_WARNING, __func__, "caph_rights_limit(%s): %s", + dumpfile, strerror(errno)); + return (NULL); + } + return (fp); } const char * diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c index 5b9adc8edb69..2cc5fa41c29b 100644 --- a/usr.sbin/rtsold/if.c +++ b/usr.sbin/rtsold/if.c @@ -34,10 +34,11 @@ */ #include -#include -#include +#include #include #include +#include +#include #include #include @@ -51,6 +52,7 @@ #include #include +#include #include #include #include @@ -63,16 +65,28 @@ #include "rtsold.h" static int ifsock; - -static int get_llflag(const char *); static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); int ifinit(void) { - ifsock = rssock; + cap_rights_t rights; + int sock; - return(0); + sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (sock < 0) { + warnmsg(LOG_ERR, __func__, "socket(): %s", + strerror(errno)); + return (-1); + } + if (caph_rights_limit(sock, cap_rights_init(&rights, CAP_IOCTL)) < 0) { + warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s", + strerror(errno)); + (void)close(sock); + return (-1); + } + ifsock = sock; + return (0); } int @@ -150,10 +164,9 @@ interface_up(char *name) } close(s); - llflag = get_llflag(name); - if (llflag < 0) { + if (cap_llflags_get(capllflags, name, &llflag) != 0) { warnmsg(LOG_WARNING, __func__, - "get_llflag() failed, anyway I'll try"); + "cap_llflags_get() failed, anyway I'll try"); return (0); } @@ -273,8 +286,6 @@ lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) "unsupported link type(%d)", sdl->sdl_type); exit(1); } - - return; } struct sockaddr_dl * @@ -332,58 +343,6 @@ if_nametosdl(char *name) return (ret_sdl); } -/*------------------------------------------------------------*/ - -/* get ia6_flags for link-local addr on if. returns -1 on error. */ -static int -get_llflag(const char *name) -{ - struct ifaddrs *ifap, *ifa; - struct in6_ifreq ifr6; - struct sockaddr_in6 *sin6; - int s; - - if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) { - warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s", - strerror(errno)); - exit(1); - } - if (getifaddrs(&ifap) != 0) { - warnmsg(LOG_ERR, __func__, "getifaddrs: %s", - strerror(errno)); - exit(1); - } - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (strlen(ifa->ifa_name) != strlen(name) || - strncmp(ifa->ifa_name, name, strlen(name)) != 0) - continue; - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr; - if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) - continue; - - memset(&ifr6, 0, sizeof(ifr6)); - strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); - memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len); - if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { - warnmsg(LOG_ERR, __func__, - "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno)); - exit(1); - } - - freeifaddrs(ifap); - close(s); - return (ifr6.ifr_ifru.ifru_flags6); - } - - freeifaddrs(ifap); - close(s); - return (-1); -} - - static void get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) { diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c deleted file mode 100644 index 2a5ddfcad06e..000000000000 --- a/usr.sbin/rtsold/probe.c +++ /dev/null @@ -1,191 +0,0 @@ -/* $KAME: probe.c,v 1.17 2003/10/05 00:09:36 itojun Exp $ */ - -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (C) 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "rtsold.h" - -static struct msghdr sndmhdr; -static struct iovec sndiov[2]; -static int probesock; -static void sendprobe(struct in6_addr *, struct ifinfo *); - -int -probe_init(void) -{ - int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - static u_char *sndcmsgbuf = NULL; - - if (sndcmsgbuf == NULL && - (sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) { - warnmsg(LOG_ERR, __func__, "malloc failed"); - return (-1); - } - - if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { - warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); - return (-1); - } - - /* initialize msghdr for sending packets */ - sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); - sndmhdr.msg_iov = sndiov; - sndmhdr.msg_iovlen = 1; - sndmhdr.msg_control = (caddr_t)sndcmsgbuf; - sndmhdr.msg_controllen = scmsglen; - - return (0); -} - -/* - * Probe if each router in the default router list is still alive. - */ -void -defrouter_probe(struct ifinfo *ifinfo) -{ - struct in6_defrouter *p, *ep; - int ifindex, mib[4]; - char *buf, ntopbuf[INET6_ADDRSTRLEN]; - size_t l; - - ifindex = ifinfo->sdl->sdl_index; - if (ifindex == 0) - return; - mib[0] = CTL_NET; - mib[1] = PF_INET6; - mib[2] = IPPROTO_ICMPV6; - mib[3] = ICMPV6CTL_ND6_DRLIST; - if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { - warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", - strerror(errno)); - return; - } - if (l == 0) - return; - buf = malloc(l); - if (buf == NULL) { - warnmsg(LOG_ERR, __func__, "malloc(): %s", strerror(errno)); - return; - } - if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { - warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", - strerror(errno)); - free(buf); - return; - } - ep = (struct in6_defrouter *)(void *)(buf + l); - for (p = (struct in6_defrouter *)(void *)buf; p < ep; p++) { - if (ifindex != p->if_index) - continue; - if (!IN6_IS_ADDR_LINKLOCAL(&p->rtaddr.sin6_addr)) { - warnmsg(LOG_ERR, __func__, - "default router list contains a " - "non-link-local address(%s)", - inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN)); - continue; /* ignore the address */ - } - sendprobe(&p->rtaddr.sin6_addr, ifinfo); - } - free(buf); -} - -static void -sendprobe(struct in6_addr *addr, struct ifinfo *ifinfo) -{ - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - struct sockaddr_in6 sa6_probe; - struct in6_pktinfo *pi; - struct cmsghdr *cm; - u_int32_t ifindex = ifinfo->sdl->sdl_index; - int hoplimit = 1; - - memset(&sa6_probe, 0, sizeof(sa6_probe)); - sa6_probe.sin6_family = AF_INET6; - sa6_probe.sin6_len = sizeof(sa6_probe); - sa6_probe.sin6_addr = *addr; - sa6_probe.sin6_scope_id = ifinfo->linkid; - - sndmhdr.msg_name = (caddr_t)&sa6_probe; - sndmhdr.msg_iov[0].iov_base = NULL; - sndmhdr.msg_iov[0].iov_len = 0; - - cm = CMSG_FIRSTHDR(&sndmhdr); - /* specify the outgoing interface */ - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); - memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ - pi->ipi6_ifindex = ifindex; - - /* specify the hop limit of the packet for safety */ - cm = CMSG_NXTHDR(&sndmhdr, cm); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_HOPLIMIT; - cm->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); - - warnmsg(LOG_DEBUG, __func__, "probe a router %s on %s", - inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(ifindex, ifnamebuf)); - - if (sendmsg(probesock, &sndmhdr, 0)) - warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", - if_indextoname(ifindex, ifnamebuf), strerror(errno)); -} diff --git a/usr.sbin/rtsold/rtsock.c b/usr.sbin/rtsold/rtsock.c index c87f1b511e68..37f7df29d0b7 100644 --- a/usr.sbin/rtsold/rtsock.c +++ b/usr.sbin/rtsold/rtsock.c @@ -33,10 +33,11 @@ */ #include -#include -#include -#include +#include #include +#include +#include +#include #include #include @@ -46,6 +47,7 @@ #include #include +#include #include #include #include @@ -72,8 +74,20 @@ static struct { int rtsock_open(void) { + cap_rights_t rights; + int error, s; - return (socket(PF_ROUTE, SOCK_RAW, 0)); + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + return (s); + cap_rights_init(&rights, CAP_EVENT, CAP_READ); + if (caph_rights_limit(s, &rights) != 0) { + error = errno; + (void)close(s); + errno = errno; + return (-1); + } + return (s); } int diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c index a10cd4c510d6..06f860e3984e 100644 --- a/usr.sbin/rtsold/rtsol.c +++ b/usr.sbin/rtsold/rtsol.c @@ -35,11 +35,12 @@ */ #include -#include -#include +#include #include -#include +#include #include +#include +#include #include #include @@ -54,6 +55,7 @@ #include +#include #include #include #include @@ -67,28 +69,11 @@ #include #include "rtsold.h" -static struct msghdr rcvmhdr; -static struct msghdr sndmhdr; -static struct iovec rcviov[2]; -static struct iovec sndiov[2]; -static struct sockaddr_in6 from; -static int rcvcmsglen; - -int rssock; static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST]; -struct ifinfo_head_t ifinfo_head = - TAILQ_HEAD_INITIALIZER(ifinfo_head); +struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head); -static const struct sockaddr_in6 sin6_allrouters = { - .sin6_len = sizeof(sin6_allrouters), - .sin6_family = AF_INET6, - .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, -}; - -static void call_script(const int, const char *const *, - struct script_msg_head_t *); +static void call_script(const char *const *, struct script_msg_head_t *); static size_t dname_labeldec(char *, size_t, const char *); -static int safefile(const char *); static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, struct script_msg_head_t *, struct script_msg_head_t *); @@ -100,7 +85,7 @@ static char *make_rsid(const char *, const char *, struct rainfo *); #define CALL_SCRIPT(name, sm_head) do { \ const char *const sarg[] = { _ARGS_##name, NULL }; \ - call_script(sizeof(sarg), sarg, sm_head); \ + call_script(sarg, sm_head); \ } while (0) #define ELM_MALLOC(p, error_action) do { \ @@ -114,130 +99,69 @@ static char *make_rsid(const char *, const char *, struct rainfo *); } while (0) int -sockopen(void) +recvsockopen(void) { - static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL; - int sndcmsglen, on; - static u_char answer[1500]; struct icmp6_filter filt; + cap_rights_t rights; + int on, sock; - sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { - warnmsg(LOG_ERR, __func__, - "malloc for receive msghdr failed"); - return (-1); - } - if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) { - warnmsg(LOG_ERR, __func__, - "malloc for send msghdr failed"); - return (-1); - } - if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); - return (-1); + goto fail; } - /* specify to tell receiving interface */ + /* Provide info about the receiving interface. */ on = 1; - if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { - warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s", + warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVPKTINFO): %s", strerror(errno)); - exit(1); + goto fail; } - /* specify to tell value of hoplimit field of received IP6 hdr */ + /* Include the hop limit from the received header. */ on = 1; - if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) { - warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s", + warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVHOPLIMIT): %s", strerror(errno)); - exit(1); + goto fail; } - /* specfiy to accept only router advertisements on the socket */ + /* Filter out everything except for Router Advertisements. */ ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); - if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) == -1) { warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s", strerror(errno)); - return(-1); + goto fail; } - /* initialize msghdr for receiving packets */ - rcviov[0].iov_base = (caddr_t)answer; - rcviov[0].iov_len = sizeof(answer); - rcvmhdr.msg_name = (caddr_t)&from; - rcvmhdr.msg_iov = rcviov; - rcvmhdr.msg_iovlen = 1; - rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; + cap_rights_init(&rights, CAP_EVENT, CAP_RECV); + if (caph_rights_limit(sock, &rights) < 0) { + warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s", + strerror(errno)); + goto fail; + } - /* initialize msghdr for sending packets */ - sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); - sndmhdr.msg_iov = sndiov; - sndmhdr.msg_iovlen = 1; - sndmhdr.msg_control = (caddr_t)sndcmsgbuf; - sndmhdr.msg_controllen = sndcmsglen; + return (sock); - return (rssock); +fail: + if (sock >= 0) + (void)close(sock); + return (-1); } void -sendpacket(struct ifinfo *ifi) +rtsol_input(int sock) { - struct in6_pktinfo *pi; - struct cmsghdr *cm; - int hoplimit = 255; - ssize_t i; - struct sockaddr_in6 dst; - - dst = sin6_allrouters; - dst.sin6_scope_id = ifi->linkid; - - sndmhdr.msg_name = (caddr_t)&dst; - sndmhdr.msg_iov[0].iov_base = (caddr_t)ifi->rs_data; - sndmhdr.msg_iov[0].iov_len = ifi->rs_datalen; - - cm = CMSG_FIRSTHDR(&sndmhdr); - /* specify the outgoing interface */ - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); - memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ - pi->ipi6_ifindex = ifi->sdl->sdl_index; - - /* specify the hop limit of the packet */ - cm = CMSG_NXTHDR(&sndmhdr, cm); - cm->cmsg_level = IPPROTO_IPV6; - cm->cmsg_type = IPV6_HOPLIMIT; - cm->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); - - warnmsg(LOG_DEBUG, __func__, - "send RS on %s, whose state is %d", - ifi->ifname, ifi->state); - i = sendmsg(rssock, &sndmhdr, 0); - if (i < 0 || (size_t)i != ifi->rs_datalen) { - /* - * ENETDOWN is not so serious, especially when using several - * network cards on a mobile node. We ignore it. - */ - if (errno != ENETDOWN || dflag > 0) - warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", - ifi->ifname, strerror(errno)); - } - - /* update counter */ - ifi->probes++; -} - -void -rtsol_input(int s) -{ - char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + struct iovec iov; + struct msghdr hdr; + struct sockaddr_in6 from; + char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; int l, ifindex = 0, *hlimp = NULL; ssize_t msglen; struct in6_pktinfo *pi = NULL; @@ -247,8 +171,7 @@ rtsol_input(int s) struct nd_router_advert *nd_ra; struct cmsghdr *cm; struct rainfo *rai; - char *raoptp; - char *p; + char *p, *raoptp; struct in6_addr *addr; struct nd_opt_hdr *ndo; struct nd_opt_rdnss *rdnss; @@ -256,22 +179,28 @@ rtsol_input(int s) size_t len; char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; char dname[NI_MAXHOST]; - struct timespec now; - struct timespec lifetime; - int newent_rai; - int newent_rao; + struct timespec lifetime, now; + int newent_rai, newent_rao; - /* get message. namelen and controllen must always be initialized. */ - rcvmhdr.msg_namelen = sizeof(from); - rcvmhdr.msg_controllen = rcvcmsglen; - if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_name = &from; + hdr.msg_namelen = sizeof(from); + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = (caddr_t)answer; + iov.iov_len = sizeof(answer); + + if ((msglen = recvmsg(sock, &hdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } - /* extract optional information via Advanced API */ - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + /* Extract control message info. */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL; + cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { @@ -301,8 +230,7 @@ rtsol_input(int s) return; } - icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; - + icp = (struct icmp6_hdr *)iov.iov_base; if (icp->icmp6_type != ND_ROUTER_ADVERT) { /* * this should not happen because we configured a filter @@ -761,154 +689,43 @@ find_raopt(struct rainfo *rai, int type, void *msg, size_t len) } static void -call_script(const int argc, const char *const argv[], - struct script_msg_head_t *sm_head) +call_script(const char *const argv[], struct script_msg_head_t *sm_head) { - const char *scriptpath; - int fd[2]; - int error; - pid_t pid, wpid; + struct script_msg *smp; + ssize_t len; + int status, wfd; - if ((scriptpath = argv[0]) == NULL) + if (argv[0] == NULL) return; - fd[0] = fd[1] = -1; - if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { - error = pipe(fd); - if (error) { - warnmsg(LOG_ERR, __func__, - "failed to create a pipe: %s", strerror(errno)); - return; - } - } - - /* launch the script */ - pid = fork(); - if (pid < 0) { + wfd = cap_script_run(capscript, argv); + if (wfd == -1) { warnmsg(LOG_ERR, __func__, - "failed to fork: %s", strerror(errno)); + "failed to run %s: %s", argv[0], strerror(errno)); return; - } else if (pid) { /* parent */ - int wstatus; + } - if (fd[0] != -1) { /* Send message to the child if any. */ - ssize_t len; - struct script_msg *smp; - - close(fd[0]); - TAILQ_FOREACH(smp, sm_head, sm_next) { - len = strlen(smp->sm_msg); - warnmsg(LOG_DEBUG, __func__, - "write to child = %s(%zd)", - smp->sm_msg, len); - if (write(fd[1], smp->sm_msg, len) != len) { - warnmsg(LOG_ERR, __func__, - "write to child failed: %s", - strerror(errno)); - break; - } + if (sm_head != NULL) { + TAILQ_FOREACH(smp, sm_head, sm_next) { + len = strlen(smp->sm_msg); + warnmsg(LOG_DEBUG, __func__, "write to child = %s(%zd)", + smp->sm_msg, len); + if (write(wfd, smp->sm_msg, len) != len) { + warnmsg(LOG_ERR, __func__, + "write to child failed: %s", + strerror(errno)); + break; } - close(fd[1]); } - do { - wpid = wait(&wstatus); - } while (wpid != pid && wpid > 0); - - if (wpid < 0) - warnmsg(LOG_ERR, __func__, - "wait: %s", strerror(errno)); - else - warnmsg(LOG_DEBUG, __func__, - "script \"%s\" terminated", scriptpath); - } else { /* child */ - int nullfd; - char **_argv; - - if (safefile(scriptpath)) { - warnmsg(LOG_ERR, __func__, - "script \"%s\" cannot be executed safely", - scriptpath); - exit(1); - } - nullfd = open("/dev/null", O_RDWR); - if (nullfd < 0) { - warnmsg(LOG_ERR, __func__, - "open /dev/null: %s", strerror(errno)); - exit(1); - } - if (fd[0] != -1) { /* Receive message from STDIN if any. */ - close(fd[1]); - if (fd[0] != STDIN_FILENO) { - /* Connect a pipe read-end to child's STDIN. */ - if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { - warnmsg(LOG_ERR, __func__, - "dup2 STDIN: %s", strerror(errno)); - exit(1); - } - close(fd[0]); - } - } else - dup2(nullfd, STDIN_FILENO); - - dup2(nullfd, STDOUT_FILENO); - dup2(nullfd, STDERR_FILENO); - if (nullfd > STDERR_FILENO) - close(nullfd); - - _argv = malloc(sizeof(*_argv) * argc); - if (_argv == NULL) { - warnmsg(LOG_ERR, __func__, - "malloc: %s", strerror(errno)); - exit(1); - } - memcpy(_argv, argv, (size_t)argc); - execv(scriptpath, (char *const *)_argv); - warnmsg(LOG_ERR, __func__, "child: exec failed: %s", - strerror(errno)); - exit(1); } - return; -} + (void)close(wfd); -static int -safefile(const char *path) -{ - struct stat s; - uid_t myuid; - - /* no setuid */ - if (getuid() != geteuid()) { - warnmsg(LOG_NOTICE, __func__, - "setuid'ed execution not allowed\n"); - return (-1); - } - - if (lstat(path, &s) != 0) { - warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", - strerror(errno)); - return (-1); - } - - /* the file must be owned by the running uid */ - myuid = getuid(); - if (s.st_uid != myuid) { - warnmsg(LOG_NOTICE, __func__, - "%s has invalid owner uid\n", path); - return (-1); - } - - switch (s.st_mode & S_IFMT) { - case S_IFREG: - break; - default: - warnmsg(LOG_NOTICE, __func__, - "%s is an invalid file type 0x%o\n", - path, (s.st_mode & S_IFMT)); - return (-1); - } - - return (0); + if (cap_script_wait(capscript, &status) != 0) + warnmsg(LOG_ERR, __func__, "wait(): %s", strerror(errno)); + else + warnmsg(LOG_DEBUG, __func__, "script \"%s\" status %d", + argv[0], status); } /* Decode domain name label encoding in RFC 1035 Section 3.1 */ diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c index 8d31b43aad0d..ceacca6a258a 100644 --- a/usr.sbin/rtsold/rtsold.c +++ b/usr.sbin/rtsold/rtsold.c @@ -34,6 +34,8 @@ */ #include +#include +#include #include #include @@ -47,23 +49,27 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include "rtsold.h" -#define RTSOL_DUMPFILE "/var/run/rtsold.dump"; -#define RTSOL_PIDFILE "/var/run/rtsold.pid"; +#define RTSOL_DUMPFILE "/var/run/rtsold.dump" struct timespec tm_max; static int log_upto = 999; @@ -77,6 +83,8 @@ int uflag = 0; const char *otherconf_script; const char *resolvconf_script = "/sbin/resolvconf"; +cap_channel_t *capllflags, *capscript, *capsendmsg, *capsyslog; + /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ @@ -90,43 +98,40 @@ const char *resolvconf_script = "/sbin/resolvconf"; /* static variables and functions */ static int mobile_node = 0; -static const char *pidfilename = RTSOL_PIDFILE; -#ifndef SMALL -static int do_dump; -static const char *dumpfilename = RTSOL_DUMPFILE; -#endif +static sig_atomic_t do_dump, do_exit; +static struct pidfh *pfh; static char **autoifprobe(void); static int ifconfig(char *ifname); +static int init_capabilities(void); static int make_packet(struct ifinfo *); static struct timespec *rtsol_check_timer(void); -#ifndef SMALL -static void rtsold_set_dump_file(int); -#endif -static void usage(void); +static void set_dumpfile(int); +static void set_exit(int); +static void usage(const char *progname); int main(int argc, char **argv) { - int s, ch, once = 0; + struct kevent events[2]; + FILE *dumpfp; + struct ifinfo *ifi; struct timespec *timeout; - const char *opts; - struct pollfd set[2]; - int rtsock; - char *argv0; + const char *opts, *pidfilepath, *progname; + int ch, error, kq, once, rcvsock, rtsock; -#ifndef SMALL - /* rtsold */ - opts = "adDfFm1O:p:R:u"; -#else - /* rtsol */ - opts = "adDFO:R:u"; - fflag = 1; - once = 1; -#endif - argv0 = argv[0]; + progname = basename(argv[0]); + if (strcmp(progname, "rtsold") == 0) { + opts = "adDfFm1O:p:R:u"; + once = 0; + pidfilepath = NULL; + } else { + opts = "adDFO:R:u"; + fflag = 1; + once = 1; + } while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -155,7 +160,7 @@ main(int argc, char **argv) otherconf_script = optarg; break; case 'p': - pidfilename = optarg; + pidfilepath = optarg; break; case 'R': resolvconf_script = optarg; @@ -164,17 +169,14 @@ main(int argc, char **argv) uflag = 1; break; default: - usage(); - exit(1); + usage(progname); } } argc -= optind; argv += optind; - if ((!aflag && argc == 0) || (aflag && argc != 0)) { - usage(); - exit(1); - } + if ((!aflag && argc == 0) || (aflag && argc != 0)) + usage(progname); /* Generate maximum time in timespec. */ tm_max.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1)); @@ -188,64 +190,62 @@ main(int argc, char **argv) else log_upto = LOG_NOTICE; - if (!fflag) { - char *ident; - - ident = strrchr(argv0, '/'); - if (!ident) - ident = argv0; - else - ident++; - openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); - if (log_upto >= 0) - setlogmask(LOG_UPTO(log_upto)); - } - - if (otherconf_script && *otherconf_script != '/') { + if (otherconf_script != NULL && *otherconf_script != '/') errx(1, "configuration script (%s) must be an absolute path", otherconf_script); - } - if (resolvconf_script && *resolvconf_script != '/') { + if (*resolvconf_script != '/') errx(1, "configuration script (%s) must be an absolute path", resolvconf_script); - } - if (pidfilename && *pidfilename != '/') { - errx(1, "pid filename (%s) must be an absolute path", - pidfilename); + + if (!fflag) { + pfh = pidfile_open(pidfilepath, 0644, NULL); + if (pfh == NULL) + errx(1, "failed to open pidfile: %s", strerror(errno)); + if (daemon(0, 0) != 0) + errx(1, "failed to daemonize"); } -#ifndef SMALL - /* initialization to dump internal status to a file */ - signal(SIGUSR1, rtsold_set_dump_file); -#endif + if ((error = init_capabilities()) != 0) + err(1, "failed to initialize capabilities"); - if (!fflag) - daemon(0, 0); /* act as a daemon */ + if (!fflag) { + cap_openlog(capsyslog, progname, LOG_NDELAY | LOG_PID, + LOG_DAEMON); + if (log_upto >= 0) + (void)cap_setlogmask(capsyslog, LOG_UPTO(log_upto)); + (void)signal(SIGTERM, set_exit); + (void)signal(SIGINT, set_exit); + (void)signal(SIGUSR1, set_dumpfile); + dumpfp = rtsold_init_dumpfile(RTSOL_DUMPFILE); + } else + dumpfp = NULL; - /* - * Open a socket for sending RS and receiving RA. - * This should be done before calling ifinit(), since the function - * uses the socket. - */ - if ((s = sockopen()) < 0) { - warnmsg(LOG_ERR, __func__, "failed to open a socket"); + kq = kqueue(); + if (kq < 0) { + warnmsg(LOG_ERR, __func__, "failed to create a kqueue: %s", + strerror(errno)); exit(1); } - set[0].fd = s; - set[0].events = POLLIN; - set[1].fd = -1; + /* Open global sockets and register for read events. */ if ((rtsock = rtsock_open()) < 0) { - warnmsg(LOG_ERR, __func__, "failed to open a socket"); + warnmsg(LOG_ERR, __func__, "failed to open routing socket"); + exit(1); + } + if ((rcvsock = recvsockopen()) < 0) { + warnmsg(LOG_ERR, __func__, "failed to open receive socket"); + exit(1); + } + EV_SET(&events[0], rtsock, EVFILT_READ, EV_ADD, 0, 0, NULL); + EV_SET(&events[1], rcvsock, EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(kq, events, 2, NULL, 0, NULL) < 0) { + warnmsg(LOG_ERR, __func__, "kevent(): %s", strerror(errno)); exit(1); } - set[1].fd = rtsock; - set[1].events = POLLIN; - /* configuration per interface */ - if (ifinit()) { - warnmsg(LOG_ERR, __func__, - "failed to initialize interfaces"); + /* Probe network interfaces and set up tracking info. */ + if (ifinit() != 0) { + warnmsg(LOG_ERR, __func__, "failed to initialize interfaces"); exit(1); } if (aflag) @@ -259,42 +259,37 @@ main(int argc, char **argv) argv++; } - /* setup for probing default routers */ - if (probe_init()) { + /* Write to our pidfile. */ + if (pfh != NULL && pidfile_write(pfh) != 0) { warnmsg(LOG_ERR, __func__, - "failed to setup for probing routers"); + "failed to open pidfile: %s", strerror(errno)); exit(1); - /*NOTREACHED*/ } - /* dump the current pid */ - if (!once) { - pid_t pid = getpid(); - FILE *fp; + /* Enter capability mode. */ + caph_cache_catpages(); + if (caph_enter_casper() != 0) { + warnmsg(LOG_ERR, __func__, "caph_enter(): %s", strerror(errno)); + exit(1); + } - if ((fp = fopen(pidfilename, "w")) == NULL) - warnmsg(LOG_ERR, __func__, - "failed to open a pid log file(%s): %s", - pidfilename, strerror(errno)); - else { - fprintf(fp, "%d\n", pid); - fclose(fp); + for (;;) { + if (do_exit) { + /* Handle SIGTERM, SIGINT. */ + if (pfh != NULL) + pidfile_remove(pfh); + break; } - } - while (1) { /* main loop */ - int e; -#ifndef SMALL - if (do_dump) { /* SIGUSR1 */ + if (do_dump) { + /* Handle SIGUSR1. */ do_dump = 0; - rtsold_dump_file(dumpfilename); + if (dumpfp != NULL) + rtsold_dump(dumpfp); } -#endif timeout = rtsol_check_timer(); if (once) { - struct ifinfo *ifi; - /* if we have no timeout, we are done (or failed) */ if (timeout == NULL) break; @@ -307,26 +302,64 @@ main(int argc, char **argv) if (ifi == NULL) break; } - e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM); - if (e < 1) { - if (e < 0 && errno != EINTR) { - warnmsg(LOG_ERR, __func__, "select: %s", + + error = kevent(kq, NULL, 0, &events[0], 1, timeout); + if (error < 1) { + if (error < 0 && errno != EINTR) + warnmsg(LOG_ERR, __func__, "kevent(): %s", strerror(errno)); - } continue; } - /* packet reception */ - if (set[1].revents & POLLIN) + if (events[0].ident == (uintptr_t)rtsock) rtsock_input(rtsock); - if (set[0].revents & POLLIN) - rtsol_input(s); + else + rtsol_input(rcvsock); } - /* NOTREACHED */ return (0); } +static int +init_capabilities(void) +{ +#ifdef WITH_CASPER + const char *const scripts[2] = { resolvconf_script, otherconf_script }; + cap_channel_t *capcasper; + nvlist_t *limits; + + capcasper = cap_init(); + if (capcasper == NULL) + return (-1); + + capllflags = cap_service_open(capcasper, "rtsold.llflags"); + if (capllflags == NULL) + return (-1); + + capscript = cap_service_open(capcasper, "rtsold.script"); + if (capscript == NULL) + return (-1); + limits = nvlist_create(0); + nvlist_add_string_array(limits, "scripts", scripts, + otherconf_script != NULL ? 2 : 1); + if (cap_limit_set(capscript, limits) != 0) + return (-1); + + capsendmsg = cap_service_open(capcasper, "rtsold.sendmsg"); + if (capsendmsg == NULL) + return (-1); + + if (!fflag) { + capsyslog = cap_service_open(capcasper, "system.syslog"); + if (capsyslog == NULL) + return (-1); + } + + cap_close(capcasper); +#endif /* WITH_CASPER */ + return (0); +} + static int ifconfig(char *ifname) { @@ -501,7 +534,7 @@ rtsol_check_timer(void) struct ifinfo *ifi; struct rainfo *rai; struct ra_opt *rao, *raotmp; - int flags; + int error, flags; clock_gettime(CLOCK_MONOTONIC_FAST, &now); @@ -568,18 +601,23 @@ rtsol_check_timer(void) */ if (probe) ifi->otherconfig = 0; - - if (probe && mobile_node) - defrouter_probe(ifi); + if (probe && mobile_node) { + error = cap_probe_defrouters(capsendmsg, + ifi); + if (error != 0) + warnmsg(LOG_DEBUG, __func__, + "failed to probe routers: %d", + error); + } break; } case IFS_DELAY: ifi->state = IFS_PROBE; - sendpacket(ifi); + (void)cap_rssend(capsendmsg, ifi); break; case IFS_PROBE: if (ifi->probes < MAX_RTR_SOLICITATIONS) - sendpacket(ifi); + (void)cap_rssend(capsendmsg, ifi); else { warnmsg(LOG_INFO, __func__, "No answer after sending %d RSs", @@ -660,10 +698,9 @@ rtsol_timer_update(struct ifinfo *ifi) ifi->timer.tv_sec = 1; break; case IFS_IDLE: - if (mobile_node) { + if (mobile_node) /* XXX should be configurable */ ifi->timer.tv_sec = 3; - } else ifi->timer = tm_max; /* stop timer(valid?) */ break; @@ -675,7 +712,7 @@ rtsol_timer_update(struct ifinfo *ifi) case IFS_PROBE: if (ifi->probes < MAX_RTR_SOLICITATIONS) ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL; - else { + else /* * After sending MAX_RTR_SOLICITATIONS solicitations, * we're just waiting for possible replies; there @@ -684,7 +721,6 @@ rtsol_timer_update(struct ifinfo *ifi) * on RFC 2461, Section 6.3.7. */ ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; - } break; default: warnmsg(LOG_ERR, __func__, @@ -711,28 +747,36 @@ rtsol_timer_update(struct ifinfo *ifi) #undef MILLION } -#ifndef SMALL static void -rtsold_set_dump_file(int sig __unused) +set_dumpfile(int sig __unused) { + do_dump = 1; } -#endif static void -usage(void) +set_exit(int sig __unused) { -#ifndef SMALL - fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " - "[-p pidfile] [-R script-name] interface ...\n"); - fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " - "[-p pidfile] [-R script-name] -a\n"); -#else - fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " - "[-p pidfile] [-R script-name] interface ...\n"); - fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " - "[-p pidfile] [-R script-name] -a\n"); -#endif + + do_exit = 1; +} + +static void +usage(const char *progname) +{ + + if (strcmp(progname, "rtsold") == 0) { + fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " + "[-p pidfile] [-R script-name] interface ...\n"); + fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " + "[-p pidfile] [-R script-name] -a\n"); + } else { + fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " + "[-p pidfile] [-R script-name] interface ...\n"); + fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " + "[-p pidfile] [-R script-name] -a\n"); + } + exit(1); } void @@ -750,7 +794,7 @@ warnmsg(int priority, const char *func, const char *msg, ...) } else { snprintf(buf, sizeof(buf), "<%s> %s", func, msg); msg = buf; - vsyslog(priority, msg, ap); + cap_vsyslog(capsyslog, priority, msg, ap); } va_end(ap); } diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h index 0a0632b8e6fe..ab8d5ae5d971 100644 --- a/usr.sbin/rtsold/rtsold.h +++ b/usr.sbin/rtsold/rtsold.h @@ -60,12 +60,13 @@ struct rainfo { TAILQ_HEAD(, ra_opt) rai_ra_opt; }; +/* Per-interface tracking info. */ struct ifinfo { - TAILQ_ENTRY(ifinfo) ifi_next; /* pointer to the next interface */ + TAILQ_ENTRY(ifinfo) ifi_next; /* pointer to the next interface */ struct sockaddr_dl *sdl; /* link-layer address */ char ifname[IFNAMSIZ]; /* interface name */ - u_int32_t linkid; /* link ID of this interface */ + uint32_t linkid; /* link ID of this interface */ int active; /* interface status */ int probeinterval; /* interval of probe timer (if necessary) */ int probetimer; /* rest of probe timer */ @@ -77,7 +78,6 @@ struct ifinfo { int dadcount; struct timespec timer; struct timespec expire; - int errors; /* # of errors we've got - detect wedge */ #define IFI_DNSOPT_STATE_NOINFO 0 #define IFI_DNSOPT_STATE_RECEIVED 1 int ifi_rdnss; /* RDNSS option state */ @@ -150,6 +150,7 @@ extern TAILQ_HEAD(ifinfo_head_t, ifinfo) ifinfo_head; } while (0) /* rtsold.c */ +struct cap_channel; extern struct timespec tm_max; extern int dflag; extern int aflag; @@ -157,6 +158,8 @@ extern int Fflag; extern int uflag; extern const char *otherconf_script; extern const char *resolvconf_script; +extern struct cap_channel *capllflags, *capscript, *capsendmsg, *capsyslog; + struct ifinfo *find_ifinfo(int); struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *); void rtsol_timer_update(struct ifinfo *); @@ -165,6 +168,7 @@ extern void warnmsg(int, const char *, const char *, ...) extern int ra_opt_handler(struct ifinfo *); /* if.c */ +struct nd_opt_hdr; extern int ifinit(void); extern int interface_up(char *); extern int interface_status(struct ifinfo *); @@ -173,17 +177,23 @@ extern void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *); extern struct sockaddr_dl *if_nametosdl(char *); /* rtsol.c */ -extern int rssock; -extern int sockopen(void); -extern void sendpacket(struct ifinfo *); +extern int recvsockopen(void); extern void rtsol_input(int); -/* probe.c */ -extern int probe_init(void); -extern void defrouter_probe(struct ifinfo *); +/* cap_llflags.c */ +extern int cap_llflags_get(struct cap_channel *, const char *, int *); + +/* cap_script.c */ +extern int cap_script_run(struct cap_channel *, const char *const *); +extern int cap_script_wait(struct cap_channel *, int *); + +/* cap_sendmsg.c */ +extern int cap_probe_defrouters(struct cap_channel *, struct ifinfo *); +extern int cap_rssend(struct cap_channel *, struct ifinfo *); /* dump.c */ -extern void rtsold_dump_file(const char *); +extern FILE *rtsold_init_dumpfile(const char *); +extern void rtsold_dump(FILE *); extern const char *sec2str(const struct timespec *); /* rtsock.c */