Index: head/sbin/rtsol/Makefile =================================================================== --- head/sbin/rtsol/Makefile +++ head/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 Index: head/usr.sbin/rtsold/Makefile =================================================================== --- head/usr.sbin/rtsold/Makefile +++ head/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 Index: head/usr.sbin/rtsold/cap_llflags.c =================================================================== --- head/usr.sbin/rtsold/cap_llflags.c +++ head/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 */ Index: head/usr.sbin/rtsold/cap_script.c =================================================================== --- head/usr.sbin/rtsold/cap_script.c +++ head/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 */ Index: head/usr.sbin/rtsold/cap_sendmsg.c =================================================================== --- head/usr.sbin/rtsold/cap_sendmsg.c +++ head/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 */ Index: head/usr.sbin/rtsold/dump.c =================================================================== --- head/usr.sbin/rtsold/dump.c +++ head/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,8 +64,14 @@ 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); fprintf(fp, " probe interval: "); @@ -121,18 +127,28 @@ 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 * Index: head/usr.sbin/rtsold/if.c =================================================================== --- head/usr.sbin/rtsold/if.c +++ head/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 @@ } 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 @@ "unsupported link type(%d)", sdl->sdl_type); exit(1); } - - return; } struct sockaddr_dl * @@ -331,58 +342,6 @@ free(buf); 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) Index: head/usr.sbin/rtsold/probe.c =================================================================== --- head/usr.sbin/rtsold/probe.c +++ head/usr.sbin/rtsold/probe.c @@ -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)); -} Index: head/usr.sbin/rtsold/rtsock.c =================================================================== --- head/usr.sbin/rtsold/rtsock.c +++ head/usr.sbin/rtsold/rtsock.c @@ -33,10 +33,11 @@ */ #include +#include +#include #include -#include #include -#include +#include #include #include @@ -46,6 +47,7 @@ #include #include +#include #include #include #include @@ -72,8 +74,20 @@ 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 Index: head/usr.sbin/rtsold/rtsol.c =================================================================== --- head/usr.sbin/rtsold/rtsol.c +++ head/usr.sbin/rtsold/rtsol.c @@ -35,11 +35,12 @@ */ #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 @@ #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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ } 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 */ Index: head/usr.sbin/rtsold/rtsold.h =================================================================== --- head/usr.sbin/rtsold/rtsold.h +++ head/usr.sbin/rtsold/rtsold.h @@ -60,12 +60,13 @@ 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 @@ 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 @@ } while (0) /* rtsold.c */ +struct cap_channel; extern struct timespec tm_max; extern int dflag; extern int aflag; @@ -157,6 +158,8 @@ 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 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 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 */ Index: head/usr.sbin/rtsold/rtsold.c =================================================================== --- head/usr.sbin/rtsold/rtsold.c +++ head/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 "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 @@ 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 @@ /* 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 @@ otherconf_script = optarg; break; case 'p': - pidfilename = optarg; + pidfilepath = optarg; break; case 'R': resolvconf_script = optarg; @@ -164,17 +169,14 @@ 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 @@ 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 (!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"); } - if (pidfilename && *pidfilename != '/') { - errx(1, "pid filename (%s) must be an absolute path", - pidfilename); - } -#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); } - set[1].fd = rtsock; - set[1].events = POLLIN; + 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); + } - /* 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 @@ 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,27 +302,65 @@ 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) { struct ifinfo *ifi; @@ -501,7 +534,7 @@ 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 @@ */ 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 @@ 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 @@ 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 @@ * on RFC 2461, Section 6.3.7. */ ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; - } break; default: warnmsg(LOG_ERR, __func__, @@ -711,30 +747,38 @@ #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 warnmsg(int priority, const char *func, const char *msg, ...) { @@ -750,7 +794,7 @@ } else { snprintf(buf, sizeof(buf), "<%s> %s", func, msg); msg = buf; - vsyslog(priority, msg, ap); + cap_vsyslog(capsyslog, priority, msg, ap); } va_end(ap); }