Index: head/sbin/rtsol/Makefile =================================================================== --- head/sbin/rtsol/Makefile (revision 342787) +++ head/sbin/rtsol/Makefile (revision 342788) @@ -1,27 +1,41 @@ # Copyright (c) 1996 WIDE Project. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modifications, are permitted provided that the above copyright notice # and this paragraph are duplicated in all such forms and that any # documentation, advertising materials, and other materials related to # such distribution and use acknowledge that the software was developed # by the WIDE Project, Japan. The name of the Project may not be used to # endorse or promote products derived from this software without # specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. # # $FreeBSD$ .PATH: ${SRCTOP}/usr.sbin/rtsold 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/probe.c =================================================================== --- head/usr.sbin/rtsold/probe.c (revision 342787) +++ head/usr.sbin/rtsold/probe.c (nonexistent) @@ -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)); -} Property changes on: head/usr.sbin/rtsold/probe.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.sbin/rtsold/Makefile =================================================================== --- head/usr.sbin/rtsold/Makefile (revision 342787) +++ head/usr.sbin/rtsold/Makefile (revision 342788) @@ -1,24 +1,38 @@ # Copyright (c) 1996 WIDE Project. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modifications, are permitted provided that the above copyright notice # and this paragraph are duplicated in all such forms and that any # documentation, advertising materials, and other materials related to # such distribution and use acknowledge that the software was developed # by the WIDE Project, Japan. The name of the Project may not be used to # endorse or promote products derived from this software without # specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. # # $FreeBSD$ 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 (nonexistent) +++ head/usr.sbin/rtsold/cap_llflags.c (revision 342788) @@ -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 */ Property changes on: head/usr.sbin/rtsold/cap_llflags.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/usr.sbin/rtsold/cap_script.c =================================================================== --- head/usr.sbin/rtsold/cap_script.c (nonexistent) +++ head/usr.sbin/rtsold/cap_script.c (revision 342788) @@ -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 */ Property changes on: head/usr.sbin/rtsold/cap_script.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/usr.sbin/rtsold/cap_sendmsg.c =================================================================== --- head/usr.sbin/rtsold/cap_sendmsg.c (nonexistent) +++ head/usr.sbin/rtsold/cap_sendmsg.c (revision 342788) @@ -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 */ Property changes on: head/usr.sbin/rtsold/cap_sendmsg.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/usr.sbin/rtsold/dump.c =================================================================== --- head/usr.sbin/rtsold/dump.c (revision 342787) +++ head/usr.sbin/rtsold/dump.c (revision 342788) @@ -1,185 +1,201 @@ /* $KAME: dump.c,v 1.13 2003/10/05 00:09:36 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1999 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 "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; struct ra_opt *rao; 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: "); if (ifi->probeinterval) { fprintf(fp, "%d\n", ifi->probeinterval); fprintf(fp, " probe timer: %d\n", ifi->probetimer); } else { fprintf(fp, "infinity\n"); fprintf(fp, " no probe timer\n"); } fprintf(fp, " interface status: %s\n", ifi->active > 0 ? "active" : "inactive"); fprintf(fp, " other config: %s\n", ifi->otherconfig ? "on" : "off"); fprintf(fp, " rtsold status: %s\n", ifstatstr[ifi->state]); fprintf(fp, " carrier detection: %s\n", ifi->mediareqok ? "available" : "unavailable"); fprintf(fp, " probes: %d, dadcount = %d\n", ifi->probes, ifi->dadcount); if (ifi->timer.tv_sec == tm_max.tv_sec && ifi->timer.tv_nsec == tm_max.tv_nsec) fprintf(fp, " no timer\n"); else { fprintf(fp, " timer: interval=%d:%d, expire=%s\n", (int)ifi->timer.tv_sec, (int)ifi->timer.tv_nsec / 1000, (ifi->expire.tv_sec < now.tv_sec) ? "expired" : sec2str(&ifi->expire)); } fprintf(fp, " number of valid RAs: %d\n", ifi->racnt); TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { fprintf(fp, " RA from %s\n", inet_ntop(AF_INET6, &rai->rai_saddr.sin6_addr, ntopbuf, sizeof(ntopbuf))); TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { fprintf(fp, " option: "); switch (rao->rao_type) { case ND_OPT_RDNSS: fprintf(fp, "RDNSS: %s (expire: %s)\n", (char *)rao->rao_msg, sec2str(&rao->rao_expire)); break; case ND_OPT_DNSSL: fprintf(fp, "DNSSL: %s (expire: %s)\n", (char *)rao->rao_msg, sec2str(&rao->rao_expire)); break; default: break; } } 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 * sec2str(const struct timespec *total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; char *ep = &result[sizeof(result)]; int n; struct timespec now; time_t tsec; clock_gettime(CLOCK_MONOTONIC_FAST, &now); tsec = total->tv_sec; tsec += total->tv_nsec / 1000 / 1000000; tsec -= now.tv_sec; tsec -= now.tv_nsec / 1000 / 1000000; days = tsec / 3600 / 24; hours = (tsec / 3600) % 24; mins = (tsec / 60) % 60; secs = tsec % 60; if (days) { first = 0; n = snprintf(p, ep - p, "%dd", days); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || hours) { first = 0; n = snprintf(p, ep - p, "%dh", hours); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || mins) { first = 0; n = snprintf(p, ep - p, "%dm", mins); if (n < 0 || n >= ep - p) return "?"; p += n; } snprintf(p, ep - p, "%ds", secs); return (result); } Index: head/usr.sbin/rtsold/if.c =================================================================== --- head/usr.sbin/rtsold/if.c (revision 342787) +++ head/usr.sbin/rtsold/if.c (revision 342788) @@ -1,399 +1,358 @@ /* $KAME: if.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 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 #include #include #include #include #include #include #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 interface_up(char *name) { struct ifreq ifr; struct in6_ndireq nd; int llflag; int s; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); memset(&nd, 0, sizeof(nd)); strlcpy(nd.ifname, name, sizeof(nd.ifname)); if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s", strerror(errno)); return (-1); } if (!(ifr.ifr_flags & IFF_UP)) { ifr.ifr_flags |= IFF_UP; if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) warnmsg(LOG_ERR, __func__, "ioctl(SIOCSIFFLAGS): %s", strerror(errno)); return (-1); } if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warnmsg(LOG_WARNING, __func__, "socket(AF_INET6, SOCK_DGRAM): %s", strerror(errno)); return (-1); } if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s", strerror(errno)); close(s); return (-1); } warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name); if (nd.ndi.flags & ND6_IFF_IFDISABLED) { if (Fflag) { nd.ndi.flags &= ~ND6_IFF_IFDISABLED; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { warnmsg(LOG_WARNING, __func__, "ioctl(SIOCSIFINFO_IN6): %s", strerror(errno)); close(s); return (-1); } } else { warnmsg(LOG_WARNING, __func__, "%s is disabled.", name); close(s); return (-1); } } if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) { if (Fflag) { nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { warnmsg(LOG_WARNING, __func__, "ioctl(SIOCSIFINFO_IN6): %s", strerror(errno)); close(s); return (-1); } } else { warnmsg(LOG_WARNING, __func__, "%s does not accept Router Advertisement.", name); close(s); return (-1); } } 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); } if (!(llflag & IN6_IFF_NOTREADY)) { warnmsg(LOG_DEBUG, __func__, "%s is ready", name); return (0); } else { if (llflag & IN6_IFF_TENTATIVE) { warnmsg(LOG_DEBUG, __func__, "%s is tentative", name); return (IFS_TENTATIVE); } if (llflag & IN6_IFF_DUPLICATED) warnmsg(LOG_DEBUG, __func__, "%s is duplicated", name); return (-1); } } int interface_status(struct ifinfo *ifinfo) { char *ifname = ifinfo->ifname; struct ifreq ifr; struct ifmediareq ifmr; /* get interface flags */ memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s", ifname, strerror(errno)); return (-1); } /* * if one of UP and RUNNING flags is dropped, * the interface is not active. */ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) goto inactive; /* Next, check carrier on the interface, if possible */ if (!ifinfo->mediareqok) goto active; memset(&ifmr, 0, sizeof(ifmr)); strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { if (errno != EINVAL) { warnmsg(LOG_DEBUG, __func__, "ioctl(SIOCGIFMEDIA) on %s: %s", ifname, strerror(errno)); return(-1); } /* * EINVAL simply means that the interface does not support * the SIOCGIFMEDIA ioctl. We regard it alive. */ ifinfo->mediareqok = 0; goto active; } if (ifmr.ifm_status & IFM_AVALID) { switch (ifmr.ifm_active & IFM_NMASK) { case IFM_ETHER: case IFM_IEEE80211: if (ifmr.ifm_status & IFM_ACTIVE) goto active; else goto inactive; break; default: goto inactive; } } inactive: return (0); active: return (1); } #define ROUNDUP(a, size) \ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) #define NEXT_SA(ap) (ap) = (struct sockaddr *) \ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ sizeof(u_long)) : sizeof(u_long))) #define ROUNDUP8(a) (1 + (((a) - 1) | 7)) int lladdropt_length(struct sockaddr_dl *sdl) { switch (sdl->sdl_type) { case IFT_ETHER: return (ROUNDUP8(ETHER_ADDR_LEN + 2)); default: return (0); } } void lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt) { char *addr; ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */ switch (sdl->sdl_type) { case IFT_ETHER: ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3; addr = (char *)(ndopt + 1); memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN); break; default: warnmsg(LOG_ERR, __func__, "unsupported link type(%d)", sdl->sdl_type); exit(1); } - - return; } struct sockaddr_dl * if_nametosdl(char *name) { int mib[] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; char *buf, *next, *lim; size_t len; struct if_msghdr *ifm; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_dl *sdl = NULL, *ret_sdl; if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0) return(NULL); if ((buf = malloc(len)) == NULL) return(NULL); if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) < 0) { free(buf); return (NULL); } lim = buf + len; for (next = buf; next < lim; next += ifm->ifm_msglen) { ifm = (struct if_msghdr *)(void *)next; if (ifm->ifm_type == RTM_IFINFO) { sa = (struct sockaddr *)(ifm + 1); get_rtaddrs(ifm->ifm_addrs, sa, rti_info); if ((sa = rti_info[RTAX_IFP]) != NULL) { if (sa->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)(void *)sa; if (strlen(name) != sdl->sdl_nlen) continue; /* not same len */ if (strncmp(&sdl->sdl_data[0], name, sdl->sdl_nlen) == 0) { break; } } } } } if (next == lim) { /* search failed */ free(buf); return (NULL); } if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) { free(buf); return (NULL); } memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); 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) { int i; for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rti_info[i] = sa; NEXT_SA(sa); } else rti_info[i] = NULL; } } Index: head/usr.sbin/rtsold/rtsock.c =================================================================== --- head/usr.sbin/rtsold/rtsock.c (revision 342787) +++ head/usr.sbin/rtsold/rtsock.c (revision 342788) @@ -1,163 +1,177 @@ /* $KAME: rtsock.c,v 1.3 2000/10/10 08:46:45 itojun Exp $ */ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2000 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. */ #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" static int rtsock_input_ifannounce(int, struct rt_msghdr *, char *); static struct { u_char type; size_t minlen; int (*func)(int, struct rt_msghdr *, char *); } rtsock_dispatch[] = { { RTM_IFANNOUNCE, sizeof(struct if_announcemsghdr), rtsock_input_ifannounce }, { 0, 0, NULL }, }; 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 rtsock_input(int s) { ssize_t n; char msg[2048]; char *lim, *next; struct rt_msghdr *rtm; int idx; ssize_t len; int ret = 0; const ssize_t lenlim = offsetof(struct rt_msghdr, rtm_msglen) + sizeof(rtm->rtm_msglen); n = read(s, msg, sizeof(msg)); lim = msg + n; for (next = msg; next < lim; next += len) { rtm = (struct rt_msghdr *)(void *)next; if (lim - next < lenlim) break; len = rtm->rtm_msglen; if (len < lenlim) break; if (dflag > 1) { warnmsg(LOG_INFO, __func__, "rtmsg type %d, len=%lu", rtm->rtm_type, (u_long)len); } for (idx = 0; rtsock_dispatch[idx].func; idx++) { if (rtm->rtm_type != rtsock_dispatch[idx].type) continue; if (rtm->rtm_msglen < rtsock_dispatch[idx].minlen) { warnmsg(LOG_INFO, __func__, "rtmsg type %d too short!", rtm->rtm_type); continue; } ret = (*rtsock_dispatch[idx].func)(s, rtm, lim); break; } } return (ret); } static int rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim) { struct if_announcemsghdr *ifan; struct ifinfo *ifi; ifan = (struct if_announcemsghdr *)rtm; if ((char *)(ifan + 1) > lim) return (-1); switch (ifan->ifan_what) { case IFAN_ARRIVAL: /* * XXX for NetBSD 1.5, interface index will monotonically be * increased as new pcmcia card gets inserted. * we may be able to do a name-based interface match, * and call ifreconfig() to enable the interface again. */ warnmsg(LOG_INFO, __func__, "interface %s inserted", ifan->ifan_name); break; case IFAN_DEPARTURE: warnmsg(LOG_WARNING, __func__, "interface %s removed", ifan->ifan_name); ifi = find_ifinfo(ifan->ifan_index); if (ifi) { if (dflag > 1) { warnmsg(LOG_INFO, __func__, "bring interface %s to DOWN state", ifan->ifan_name); } ifi->state = IFS_DOWN; } break; } return (0); } Index: head/usr.sbin/rtsold/rtsol.c =================================================================== --- head/usr.sbin/rtsold/rtsol.c (revision 342787) +++ head/usr.sbin/rtsold/rtsol.c (revision 342788) @@ -1,947 +1,764 @@ /* $KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * Copyright (C) 2011 Hiroki Sato * 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 #define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ #include #undef __BSD_VISIBLE #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #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 *); static char *make_rsid(const char *, const char *, struct rainfo *); #define _ARGS_OTHER otherconf_script, ifi->ifname #define _ARGS_RESADD resolvconf_script, "-a", rsid #define _ARGS_RESDEL resolvconf_script, "-d", rsid #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 { \ p = malloc(sizeof(*p)); \ if (p == NULL) { \ warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ strerror(errno)); \ error_action; \ } \ memset(p, 0, sizeof(*p)); \ } 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; struct ifinfo *ifi = NULL; struct ra_opt *rao = NULL; struct icmp6_hdr *icp; 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; struct nd_opt_dnssl *dnssl; 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))) { pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm)); ifindex = pi->ipi6_ifindex; } if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) hlimp = (int *)(void *)CMSG_DATA(cm); } if (ifindex == 0) { warnmsg(LOG_ERR, __func__, "failed to get receiving interface"); return; } if (hlimp == NULL) { warnmsg(LOG_ERR, __func__, "failed to get receiving hop limit"); return; } if ((size_t)msglen < sizeof(struct nd_router_advert)) { warnmsg(LOG_INFO, __func__, "packet size(%zd) is too short", msglen); 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 * that only passes RAs on the receiving socket. */ warnmsg(LOG_ERR, __func__, "invalid icmp type(%d) from %s on %s", icp->icmp6_type, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code != 0) { warnmsg(LOG_INFO, __func__, "invalid icmp code(%d) from %s on %s", icp->icmp6_code, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (*hlimp != 255) { warnmsg(LOG_INFO, __func__, "invalid RA with hop limit(%d) from %s on %s", *hlimp, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { warnmsg(LOG_INFO, __func__, "invalid RA with non link-local source from %s on %s", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* xxx: more validation? */ if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { warnmsg(LOG_DEBUG, __func__, "received RA from %s on an unexpected IF(%s)", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } warnmsg(LOG_DEBUG, __func__, "received RA from %s on %s, state is %d", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), ifi->ifname, ifi->state); nd_ra = (struct nd_router_advert *)icp; /* * Process the "O bit." * If the value of OtherConfigFlag changes from FALSE to TRUE, the * host should invoke the stateful autoconfiguration protocol, * requesting information. * [RFC 2462 Section 5.5.3] */ if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && !ifi->otherconfig) { warnmsg(LOG_DEBUG, __func__, "OtherConfigFlag on %s is turned on", ifi->ifname); ifi->otherconfig = 1; CALL_SCRIPT(OTHER, NULL); } clock_gettime(CLOCK_MONOTONIC_FAST, &now); newent_rai = 0; rai = find_rainfo(ifi, &from); if (rai == NULL) { ELM_MALLOC(rai, exit(1)); rai->rai_ifinfo = ifi; TAILQ_INIT(&rai->rai_ra_opt); rai->rai_saddr.sin6_family = AF_INET6; rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr); memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr, sizeof(rai->rai_saddr.sin6_addr)); newent_rai = 1; } #define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) /* Process RA options. */ warnmsg(LOG_DEBUG, __func__, "Processing RA"); raoptp = (char *)icp + sizeof(struct nd_router_advert); while (raoptp < (char *)icp + msglen) { ndo = (struct nd_opt_hdr *)raoptp; warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp); warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d", ndo->nd_opt_type); warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d", ndo->nd_opt_len); switch (ndo->nd_opt_type) { case ND_OPT_RDNSS: rdnss = (struct nd_opt_rdnss *)raoptp; /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ if (rdnss->nd_opt_rdnss_len < 3) { warnmsg(LOG_INFO, __func__, "too short RDNSS option" "in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); break; } addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss)); while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { if (inet_ntop(AF_INET6, addr, ntopbuf, sizeof(ntopbuf)) == NULL) { warnmsg(LOG_INFO, __func__, "an invalid address in RDNSS option" " in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); addr++; continue; } if (IN6_IS_ADDR_LINKLOCAL(addr)) /* XXX: % has to be escaped here */ l = snprintf(nsbuf, sizeof(nsbuf), "%s%c%s", ntopbuf, SCOPE_DELIMITER, ifi->ifname); else l = snprintf(nsbuf, sizeof(nsbuf), "%s", ntopbuf); if (l < 0 || (size_t)l >= sizeof(nsbuf)) { warnmsg(LOG_ERR, __func__, "address copying error in " "RDNSS option: %d.", l); addr++; continue; } warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", nsbuf); newent_rao = 0; rao = find_raopt(rai, ndo->nd_opt_type, nsbuf, strlen(nsbuf)); if (rao == NULL) { ELM_MALLOC(rao, break); rao->rao_type = ndo->nd_opt_type; rao->rao_len = strlen(nsbuf); rao->rao_msg = strdup(nsbuf); if (rao->rao_msg == NULL) { warnmsg(LOG_ERR, __func__, "strdup failed: %s", strerror(errno)); free(rao); addr++; continue; } newent_rao = 1; } /* Set expiration timer */ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire)); memset(&lifetime, 0, sizeof(lifetime)); lifetime.tv_sec = ntohl(rdnss->nd_opt_rdnss_lifetime); TS_ADD(&now, &lifetime, &rao->rao_expire); if (newent_rao) TAILQ_INSERT_TAIL(&rai->rai_ra_opt, rao, rao_next); addr++; } break; case ND_OPT_DNSSL: dnssl = (struct nd_opt_dnssl *)raoptp; /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ if (dnssl->nd_opt_dnssl_len < 2) { warnmsg(LOG_INFO, __func__, "too short DNSSL option" "in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); break; } /* * Ensure NUL-termination in DNSSL in case of * malformed field. */ p = (char *)RA_OPT_NEXT_HDR(raoptp); *(p - 1) = '\0'; p = raoptp + sizeof(*dnssl); while (1 < (len = dname_labeldec(dname, sizeof(dname), p))) { /* length == 1 means empty string */ warnmsg(LOG_DEBUG, __func__, "dname = %s", dname); newent_rao = 0; rao = find_raopt(rai, ndo->nd_opt_type, dname, strlen(dname)); if (rao == NULL) { ELM_MALLOC(rao, break); rao->rao_type = ndo->nd_opt_type; rao->rao_len = strlen(dname); rao->rao_msg = strdup(dname); if (rao->rao_msg == NULL) { warnmsg(LOG_ERR, __func__, "strdup failed: %s", strerror(errno)); free(rao); addr++; continue; } newent_rao = 1; } /* Set expiration timer */ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire)); memset(&lifetime, 0, sizeof(lifetime)); lifetime.tv_sec = ntohl(dnssl->nd_opt_dnssl_lifetime); TS_ADD(&now, &lifetime, &rao->rao_expire); if (newent_rao) TAILQ_INSERT_TAIL(&rai->rai_ra_opt, rao, rao_next); p += len; } break; default: /* nothing to do for other options */ break; } raoptp = (char *)RA_OPT_NEXT_HDR(raoptp); } if (newent_rai) TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next); ra_opt_handler(ifi); ifi->racnt++; switch (ifi->state) { case IFS_IDLE: /* should be ignored */ case IFS_DELAY: /* right? */ break; case IFS_PROBE: ifi->state = IFS_IDLE; ifi->probes = 0; rtsol_timer_update(ifi); break; } } static char resstr_ns_prefix[] = "nameserver "; static char resstr_sh_prefix[] = "search "; static char resstr_nl[] = "\n"; static char resstr_sp[] = " "; int ra_opt_handler(struct ifinfo *ifi) { struct ra_opt *rao; struct rainfo *rai; struct script_msg *smp1, *smp2, *smp3; struct timespec now; struct script_msg_head_t sm_rdnss_head = TAILQ_HEAD_INITIALIZER(sm_rdnss_head); struct script_msg_head_t sm_dnssl_head = TAILQ_HEAD_INITIALIZER(sm_dnssl_head); int dcount, dlen; dcount = 0; dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl); clock_gettime(CLOCK_MONOTONIC_FAST, &now); /* * All options from multiple RAs with the same or different * source addresses on a single interface will be gathered and * handled, not overridden. [RFC 4861 6.3.4] */ TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { switch (rao->rao_type) { case ND_OPT_RDNSS: if (TS_CMP(&now, &rao->rao_expire, >)) { warnmsg(LOG_INFO, __func__, "expired rdnss entry: %s", (char *)rao->rao_msg); break; } ELM_MALLOC(smp1, continue); ELM_MALLOC(smp2, goto free1); ELM_MALLOC(smp3, goto free2); smp1->sm_msg = resstr_ns_prefix; TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1, sm_next); smp2->sm_msg = rao->rao_msg; TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2, sm_next); smp3->sm_msg = resstr_nl; TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3, sm_next); ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED; break; case ND_OPT_DNSSL: if (TS_CMP(&now, &rao->rao_expire, >)) { warnmsg(LOG_INFO, __func__, "expired dnssl entry: %s", (char *)rao->rao_msg); break; } dcount++; /* Check resolv.conf(5) restrictions. */ if (dcount > 6) { warnmsg(LOG_INFO, __func__, "dnssl entry exceeding maximum count (%d>6)" ": %s", dcount, (char *)rao->rao_msg); break; } if (256 < dlen + strlen(rao->rao_msg) + strlen(resstr_sp)) { warnmsg(LOG_INFO, __func__, "dnssl entry exceeding maximum length " "(>256): %s", (char *)rao->rao_msg); break; } ELM_MALLOC(smp1, continue); ELM_MALLOC(smp2, goto free1); if (TAILQ_EMPTY(&sm_dnssl_head)) { ELM_MALLOC(smp3, goto free2); smp3->sm_msg = resstr_sh_prefix; TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3, sm_next); } smp1->sm_msg = rao->rao_msg; TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, sm_next); smp2->sm_msg = resstr_sp; TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2, sm_next); dlen += strlen(rao->rao_msg) + strlen(resstr_sp); ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED; break; } continue; free2: free(smp2); free1: free(smp1); } /* Call the script for each information source. */ if (uflag) ra_opt_rdnss_dispatch(ifi, rai, &sm_rdnss_head, &sm_dnssl_head); } /* Call the script for each interface. */ if (!uflag) ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head, &sm_dnssl_head); return (0); } char * make_rsid(const char *ifname, const char *origin, struct rainfo *rai) { char hbuf[NI_MAXHOST]; if (rai == NULL) sprintf(rsid, "%s:%s", ifname, origin); else { if (!IN6_IS_ADDR_LINKLOCAL(&rai->rai_saddr.sin6_addr)) return (NULL); if (getnameinfo((struct sockaddr *)&rai->rai_saddr, rai->rai_saddr.sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) return (NULL); sprintf(rsid, "%s:%s:[%s]", ifname, origin, hbuf); } warnmsg(LOG_DEBUG, __func__, "rsid = [%s]", rsid); return (rsid); } int ra_opt_rdnss_dispatch(struct ifinfo *ifi, struct rainfo *rai, struct script_msg_head_t *sm_rdnss_head, struct script_msg_head_t *sm_dnssl_head) { struct script_msg *smp1; const char *r; int error; error = 0; /* Add \n for DNSSL list. */ if (!TAILQ_EMPTY(sm_dnssl_head)) { ELM_MALLOC(smp1, goto ra_opt_rdnss_freeit); smp1->sm_msg = resstr_nl; TAILQ_INSERT_TAIL(sm_dnssl_head, smp1, sm_next); } TAILQ_CONCAT(sm_rdnss_head, sm_dnssl_head, sm_next); r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, uflag ? rai : NULL); if (r == NULL) { warnmsg(LOG_ERR, __func__, "make_rsid() failed. " "Script was not invoked."); error = 1; goto ra_opt_rdnss_freeit; } if (!TAILQ_EMPTY(sm_rdnss_head)) CALL_SCRIPT(RESADD, sm_rdnss_head); else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED || ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) { CALL_SCRIPT(RESDEL, NULL); ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; } ra_opt_rdnss_freeit: /* Clear script message queue. */ if (!TAILQ_EMPTY(sm_rdnss_head)) { while ((smp1 = TAILQ_FIRST(sm_rdnss_head)) != NULL) { TAILQ_REMOVE(sm_rdnss_head, smp1, sm_next); free(smp1); } } if (!TAILQ_EMPTY(sm_dnssl_head)) { while ((smp1 = TAILQ_FIRST(sm_dnssl_head)) != NULL) { TAILQ_REMOVE(sm_dnssl_head, smp1, sm_next); free(smp1); } } return (error); } static struct ra_opt * find_raopt(struct rainfo *rai, int type, void *msg, size_t len) { struct ra_opt *rao; TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { if (rao->rao_type == type && rao->rao_len == strlen(msg) && memcmp(rao->rao_msg, msg, len) == 0) break; } return (rao); } 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 */ static size_t dname_labeldec(char *dst, size_t dlen, const char *src) { size_t len; const char *src_origin; const char *src_last; const char *dst_origin; src_origin = src; src_last = strchr(src, '\0'); dst_origin = dst; memset(dst, '\0', dlen); while (src && (len = (uint8_t)(*src++) & 0x3f) && (src + len) <= src_last && (dst - dst_origin < (ssize_t)dlen)) { if (dst != dst_origin) *dst++ = '.'; warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len); memcpy(dst, src, len); src += len; dst += len; } *dst = '\0'; /* * XXX validate that domain name only contains valid characters * for two reasons: 1) correctness, 2) we do not want to pass * possible malicious, unescaped characters like `` to a script * or program that could be exploited that way. */ return (src - src_origin); } Index: head/usr.sbin/rtsold/rtsold.c =================================================================== --- head/usr.sbin/rtsold/rtsold.c (revision 342787) +++ head/usr.sbin/rtsold/rtsold.c (revision 342788) @@ -1,868 +1,912 @@ /* $KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 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 +#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; static int fflag = 0; int Fflag = 0; /* force setting sysctl parameters */ int aflag = 0; int dflag = 0; 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 */ #define MAX_RTR_SOLICITATIONS 3 /* times */ /* * implementation dependent constants in seconds * XXX: should be configurable */ #define PROBE_INTERVAL 60 /* 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) { case 'a': aflag = 1; break; case 'd': dflag += 1; break; case 'D': dflag += 2; break; case 'f': fflag = 1; break; case 'F': Fflag = 1; break; case 'm': mobile_node = 1; break; case '1': once = 1; break; case 'O': otherconf_script = optarg; break; case 'p': - pidfilename = optarg; + pidfilepath = optarg; break; case 'R': resolvconf_script = optarg; break; case 'u': 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)); tm_max.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1)); /* set log level */ if (dflag > 1) log_upto = LOG_DEBUG; else if (dflag > 0) log_upto = LOG_INFO; 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) argv = autoifprobe(); while (argv && *argv) { if (ifconfig(*argv)) { warnmsg(LOG_ERR, __func__, "failed to initialize %s", *argv); exit(1); } 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; /* if all interfaces have got RA packet, we are done */ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { if (ifi->state != IFS_DOWN && ifi->racnt == 0) break; } 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; struct sockaddr_dl *sdl; int flags; ifi = NULL; if ((sdl = if_nametosdl(ifname)) == NULL) { warnmsg(LOG_ERR, __func__, "failed to get link layer information for %s", ifname); goto bad; } if (find_ifinfo(sdl->sdl_index)) { warnmsg(LOG_ERR, __func__, "interface %s was already configured", ifname); goto bad; } if (Fflag) { struct in6_ndireq nd; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warnmsg(LOG_ERR, __func__, "socket() failed."); goto bad; } memset(&nd, 0, sizeof(nd)); strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { warnmsg(LOG_ERR, __func__, "cannot get accept_rtadv flag"); (void)close(s); goto bad; } nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { warnmsg(LOG_ERR, __func__, "cannot set accept_rtadv flag"); (void)close(s); goto bad; } (void)close(s); } if ((ifi = malloc(sizeof(*ifi))) == NULL) { warnmsg(LOG_ERR, __func__, "memory allocation failed"); goto bad; } memset(ifi, 0, sizeof(*ifi)); ifi->sdl = sdl; ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; TAILQ_INIT(&ifi->ifi_rainfo); strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname)); /* construct a router solicitation message */ if (make_packet(ifi)) goto bad; /* set link ID of this interface. */ #ifdef HAVE_SCOPELIB if (inet_zoneid(AF_INET6, 2, ifname, &ifi->linkid)) goto bad; #else /* XXX: assume interface IDs as link IDs */ ifi->linkid = ifi->sdl->sdl_index; #endif /* * check if the interface is available. * also check if SIOCGIFMEDIA ioctl is OK on the interface. */ ifi->mediareqok = 1; ifi->active = interface_status(ifi); if (!ifi->mediareqok) { /* * probe routers periodically even if the link status * does not change. */ ifi->probeinterval = PROBE_INTERVAL; } /* activate interface: interface_up returns 0 on success */ flags = interface_up(ifi->ifname); if (flags == 0) ifi->state = IFS_DELAY; else if (flags == IFS_TENTATIVE) ifi->state = IFS_TENTATIVE; else ifi->state = IFS_DOWN; rtsol_timer_update(ifi); TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next); return (0); bad: free(sdl); free(ifi); return (-1); } struct rainfo * find_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6) { struct rainfo *rai; TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr, sizeof(rai->rai_saddr.sin6_addr)) == 0) return (rai); return (NULL); } struct ifinfo * find_ifinfo(int ifindex) { struct ifinfo *ifi; TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { if (ifi->sdl->sdl_index == ifindex) return (ifi); } return (NULL); } static int make_packet(struct ifinfo *ifi) { size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; struct nd_router_solicit *rs; char *buf; if ((lladdroptlen = lladdropt_length(ifi->sdl)) == 0) { warnmsg(LOG_INFO, __func__, "link-layer address option has null length" " on %s. Treat as not included.", ifi->ifname); } packlen += lladdroptlen; ifi->rs_datalen = packlen; /* allocate buffer */ if ((buf = malloc(packlen)) == NULL) { warnmsg(LOG_ERR, __func__, "memory allocation failed for %s", ifi->ifname); return (-1); } ifi->rs_data = buf; /* fill in the message */ rs = (struct nd_router_solicit *)buf; rs->nd_rs_type = ND_ROUTER_SOLICIT; rs->nd_rs_code = 0; rs->nd_rs_cksum = 0; rs->nd_rs_reserved = 0; buf += sizeof(*rs); /* fill in source link-layer address option */ if (lladdroptlen) lladdropt_fill(ifi->sdl, (struct nd_opt_hdr *)buf); return (0); } static struct timespec * rtsol_check_timer(void) { static struct timespec returnval; struct timespec now, rtsol_timer; struct ifinfo *ifi; struct rainfo *rai; struct ra_opt *rao, *raotmp; - int flags; + int error, flags; clock_gettime(CLOCK_MONOTONIC_FAST, &now); rtsol_timer = tm_max; TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { if (TS_CMP(&ifi->expire, &now, <=)) { warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, " "state = %d", ifi->ifname, ifi->state); while((rai = TAILQ_FIRST(&ifi->ifi_rainfo)) != NULL) { /* Remove all RA options. */ TAILQ_REMOVE(&ifi->ifi_rainfo, rai, rai_next); while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) != NULL) { TAILQ_REMOVE(&rai->rai_ra_opt, rao, rao_next); if (rao->rao_msg != NULL) free(rao->rao_msg); free(rao); } free(rai); } switch (ifi->state) { case IFS_DOWN: case IFS_TENTATIVE: /* interface_up returns 0 on success */ flags = interface_up(ifi->ifname); if (flags == 0) ifi->state = IFS_DELAY; else if (flags == IFS_TENTATIVE) ifi->state = IFS_TENTATIVE; else ifi->state = IFS_DOWN; break; case IFS_IDLE: { int oldstatus = ifi->active; int probe = 0; ifi->active = interface_status(ifi); if (oldstatus != ifi->active) { warnmsg(LOG_DEBUG, __func__, "%s status is changed" " from %d to %d", ifi->ifname, oldstatus, ifi->active); probe = 1; ifi->state = IFS_DELAY; } else if (ifi->probeinterval && (ifi->probetimer -= ifi->timer.tv_sec) <= 0) { /* probe timer expired */ ifi->probetimer = ifi->probeinterval; probe = 1; ifi->state = IFS_PROBE; } /* * If we need a probe, clear the previous * status wrt the "other" configuration. */ 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", ifi->probes); ifi->probes = 0; ifi->state = IFS_IDLE; } break; } rtsol_timer_update(ifi); } else { /* Expiration check for RA options. */ int expire = 0; TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { TAILQ_FOREACH_SAFE(rao, &rai->rai_ra_opt, rao_next, raotmp) { warnmsg(LOG_DEBUG, __func__, "RA expiration timer: " "type=%d, msg=%s, expire=%s", rao->rao_type, (char *)rao->rao_msg, sec2str(&rao->rao_expire)); if (TS_CMP(&now, &rao->rao_expire, >=)) { warnmsg(LOG_DEBUG, __func__, "RA expiration timer: " "expired."); TAILQ_REMOVE(&rai->rai_ra_opt, rao, rao_next); if (rao->rao_msg != NULL) free(rao->rao_msg); free(rao); expire = 1; } } } if (expire) ra_opt_handler(ifi); } if (TS_CMP(&ifi->expire, &rtsol_timer, <)) rtsol_timer = ifi->expire; } if (TS_CMP(&rtsol_timer, &tm_max, ==)) { warnmsg(LOG_DEBUG, __func__, "there is no timer"); return (NULL); } else if (TS_CMP(&rtsol_timer, &now, <)) /* this may occur when the interval is too small */ returnval.tv_sec = returnval.tv_nsec = 0; else TS_SUB(&rtsol_timer, &now, &returnval); now.tv_sec += returnval.tv_sec; now.tv_nsec += returnval.tv_nsec; warnmsg(LOG_DEBUG, __func__, "New timer is %s", sec2str(&now)); return (&returnval); } void rtsol_timer_update(struct ifinfo *ifi) { #define MILLION 1000000 #define DADRETRY 10 /* XXX: adhoc */ long interval; struct timespec now; bzero(&ifi->timer, sizeof(ifi->timer)); switch (ifi->state) { case IFS_DOWN: case IFS_TENTATIVE: if (++ifi->dadcount > DADRETRY) { ifi->dadcount = 0; ifi->timer.tv_sec = PROBE_INTERVAL; } else 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; case IFS_DELAY: interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION); ifi->timer.tv_sec = interval / MILLION; ifi->timer.tv_nsec = (interval % MILLION) * 1000; break; 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 * will be no more solicitation. Thus, we change * the timer value to MAX_RTR_SOLICITATION_DELAY based * on RFC 2461, Section 6.3.7. */ ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; - } break; default: warnmsg(LOG_ERR, __func__, "illegal interface state(%d) on %s", ifi->state, ifi->ifname); return; } /* reset the timer */ if (TS_CMP(&ifi->timer, &tm_max, ==)) { ifi->expire = tm_max; warnmsg(LOG_DEBUG, __func__, "stop timer for %s", ifi->ifname); } else { clock_gettime(CLOCK_MONOTONIC_FAST, &now); TS_ADD(&now, &ifi->timer, &ifi->expire); now.tv_sec += ifi->timer.tv_sec; now.tv_nsec += ifi->timer.tv_nsec; warnmsg(LOG_DEBUG, __func__, "set timer for %s to %s", ifi->ifname, sec2str(&now)); } #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, ...) { va_list ap; char buf[BUFSIZ]; va_start(ap, msg); if (fflag) { if (priority <= log_upto) { (void)vfprintf(stderr, msg, ap); (void)fprintf(stderr, "\n"); } } else { snprintf(buf, sizeof(buf), "<%s> %s", func, msg); msg = buf; - vsyslog(priority, msg, ap); + cap_vsyslog(capsyslog, priority, msg, ap); } va_end(ap); } /* * return a list of interfaces which is suitable to sending an RS. */ static char ** autoifprobe(void) { static char **argv = NULL; static int n = 0; char **a; int s = 0, i, found; struct ifaddrs *ifap, *ifa; struct in6_ndireq nd; /* initialize */ while (n--) free(argv[n]); if (argv) { free(argv); argv = NULL; } n = 0; if (getifaddrs(&ifap) != 0) return (NULL); if (!Fflag && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warnmsg(LOG_ERR, __func__, "socket"); exit(1); } /* find an ethernet */ for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if ((ifa->ifa_flags & IFF_UP) == 0) continue; if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) continue; if ((ifa->ifa_flags & IFF_MULTICAST) == 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; found = 0; for (i = 0; i < n; i++) { if (strcmp(argv[i], ifa->ifa_name) == 0) { found++; break; } } if (found) continue; /* * Skip the interfaces which IPv6 and/or accepting RA * is disabled. */ if (!Fflag) { memset(&nd, 0, sizeof(nd)); strlcpy(nd.ifname, ifa->ifa_name, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFINFO_IN6)"); exit(1); } if ((nd.ndi.flags & ND6_IFF_IFDISABLED)) continue; if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) continue; } /* if we find multiple candidates, just warn. */ if (n != 0 && dflag > 1) warnmsg(LOG_WARNING, __func__, "multiple interfaces found"); a = realloc(argv, (n + 1) * sizeof(char *)); if (a == NULL) { warnmsg(LOG_ERR, __func__, "realloc"); exit(1); } argv = a; argv[n] = strdup(ifa->ifa_name); if (!argv[n]) { warnmsg(LOG_ERR, __func__, "malloc"); exit(1); } n++; } if (n) { a = realloc(argv, (n + 1) * sizeof(char *)); if (a == NULL) { warnmsg(LOG_ERR, __func__, "realloc"); exit(1); } argv = a; argv[n] = NULL; if (dflag > 0) { for (i = 0; i < n; i++) warnmsg(LOG_WARNING, __func__, "probing %s", argv[i]); } } if (!Fflag) close(s); freeifaddrs(ifap); return (argv); } Index: head/usr.sbin/rtsold/rtsold.h =================================================================== --- head/usr.sbin/rtsold/rtsold.h (revision 342787) +++ head/usr.sbin/rtsold/rtsold.h (revision 342788) @@ -1,191 +1,201 @@ /* $KAME: rtsold.h,v 1.19 2003/04/16 09:48:15 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 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$ */ struct script_msg { TAILQ_ENTRY(script_msg) sm_next; char *sm_msg; }; TAILQ_HEAD(script_msg_head_t, script_msg); struct ra_opt { TAILQ_ENTRY(ra_opt) rao_next; u_int8_t rao_type; struct timespec rao_expire; size_t rao_len; void *rao_msg; }; TAILQ_HEAD(rainfo_head, ra_opt); struct rainfo { TAILQ_ENTRY(rainfo) rai_next; struct ifinfo *rai_ifinfo; struct sockaddr_in6 rai_saddr; 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 */ int mediareqok; /* whether the IF supports SIOCGIFMEDIA */ int otherconfig; /* need a separate protocol for the "other" * configuration */ int state; int probes; 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 */ int ifi_dnssl; /* DNSSL option state */ int racnt; /* total # of valid RAs it have got */ TAILQ_HEAD(, rainfo) ifi_rainfo; size_t rs_datalen; u_char *rs_data; }; /* per interface status */ #define IFS_IDLE 0 #define IFS_DELAY 1 #define IFS_PROBE 2 #define IFS_DOWN 3 #define IFS_TENTATIVE 4 /* Interface list */ extern TAILQ_HEAD(ifinfo_head_t, ifinfo) ifinfo_head; #define DNSINFO_ORIGIN_LABEL "slaac" /* * RFC 3542 API deprecates IPV6_PKTINFO in favor of * IPV6_RECVPKTINFO */ #ifndef IPV6_RECVPKTINFO #ifdef IPV6_PKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif #endif /* * RFC 3542 API deprecates IPV6_HOPLIMIT in favor of * IPV6_RECVHOPLIMIT */ #ifndef IPV6_RECVHOPLIMIT #ifdef IPV6_HOPLIMIT #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT #endif #endif #ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} #endif #define TS_CMP(tsp, usp, cmp) \ (((tsp)->tv_sec == (usp)->tv_sec) ? \ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ ((tsp)->tv_sec cmp (usp)->tv_sec)) #define TS_ADD(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ if ((vsp)->tv_nsec >= 1000000000L) { \ (vsp)->tv_sec++; \ (vsp)->tv_nsec -= 1000000000L; \ } \ } while (0) #define TS_SUB(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ if ((vsp)->tv_nsec < 0) { \ (vsp)->tv_sec--; \ (vsp)->tv_nsec += 1000000000L; \ } \ } while (0) /* rtsold.c */ +struct cap_channel; extern struct timespec tm_max; extern int dflag; extern int aflag; 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 *); extern void warnmsg(int, const char *, const char *, ...) __attribute__((__format__(__printf__, 3, 4))); 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 *); extern int lladdropt_length(struct sockaddr_dl *); 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 */ extern int rtsock_open(void); extern int rtsock_input(int);