Index: stable/12/sbin/rtsol/Makefile =================================================================== --- stable/12/sbin/rtsol/Makefile (revision 365629) +++ stable/12/sbin/rtsol/Makefile (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/probe.c =================================================================== --- stable/12/usr.sbin/rtsold/probe.c (revision 365629) +++ stable/12/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: stable/12/usr.sbin/rtsold/probe.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: stable/12/usr.sbin/rtsold/Makefile =================================================================== --- stable/12/usr.sbin/rtsold/Makefile (revision 365629) +++ stable/12/usr.sbin/rtsold/Makefile (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/cap_llflags.c =================================================================== --- stable/12/usr.sbin/rtsold/cap_llflags.c (nonexistent) +++ stable/12/usr.sbin/rtsold/cap_llflags.c (revision 365630) @@ -0,0 +1,158 @@ +/*- + * 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 "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: stable/12/usr.sbin/rtsold/cap_llflags.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/12/usr.sbin/rtsold/cap_script.c =================================================================== --- stable/12/usr.sbin/rtsold/cap_script.c (nonexistent) +++ stable/12/usr.sbin/rtsold/cap_script.c (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/cap_script.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/12/usr.sbin/rtsold/cap_sendmsg.c =================================================================== --- stable/12/usr.sbin/rtsold/cap_sendmsg.c (nonexistent) +++ stable/12/usr.sbin/rtsold/cap_sendmsg.c (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/cap_sendmsg.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/12/usr.sbin/rtsold/dump.c =================================================================== --- stable/12/usr.sbin/rtsold/dump.c (revision 365629) +++ stable/12/usr.sbin/rtsold/dump.c (revision 365630) @@ -1,185 +1,203 @@ /* $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, " managed config: %s\n", + ifi->managedconfig ? "on" : "off"); 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: stable/12/usr.sbin/rtsold/if.c =================================================================== --- stable/12/usr.sbin/rtsold/if.c (revision 365629) +++ stable/12/usr.sbin/rtsold/if.c (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/rtsock.c =================================================================== --- stable/12/usr.sbin/rtsold/rtsock.c (revision 365629) +++ stable/12/usr.sbin/rtsold/rtsock.c (revision 365630) @@ -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: stable/12/usr.sbin/rtsold/rtsol.c =================================================================== --- stable/12/usr.sbin/rtsold/rtsol.c (revision 365629) +++ stable/12/usr.sbin/rtsold/rtsol.c (revision 365630) @@ -1,947 +1,783 @@ /* $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_MANAGED managedconf_script, ifi->ifname #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 "M bit." + * If the value of ManagedConfigFlag changes from FALSE to TRUE, the + * host should invoke the stateful autoconfiguration protocol, + * requesting information. + * [RFC 4861 Section 4.2] + * XXX ??? [draft-ietf-v6ops-dhcpv6-slaac-problem-07] + */ + if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_MANAGED) && + !ifi->managedconfig) { + warnmsg(LOG_DEBUG, __func__, + "ManagedConfigFlag on %s is turned on", ifi->ifname); + ifi->managedconfig = 1; + CALL_SCRIPT(MANAGED, NULL); + } + + /* * 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] + * requesting information unless the "M bit" was set as well in + * which case the "O bit" is redundant. + * [RFC 4861 Section 4.2] */ 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); + if (!ifi->managedconfig) + 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: stable/12/usr.sbin/rtsold/rtsold.8 =================================================================== --- stable/12/usr.sbin/rtsold/rtsold.8 (revision 365629) +++ stable/12/usr.sbin/rtsold/rtsold.8 (revision 365630) @@ -1,304 +1,324 @@ .\" $KAME: rtsold.8,v 1.20 2003/04/11 12:46:12 jinmei Exp $ .\" .\" 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$ .\" -.Dd June 14, 2011 +.Dd August 19, 2020 .Dt RTSOLD 8 .Os .\" .Sh NAME .Nm rtsold , rtsol .Nd router solicitation daemon .\" .Sh SYNOPSIS .Nm .Op Fl dDfFmu1 +.Op Fl M Ar script-name .Op Fl O Ar script-name .Op Fl p Ar pidfile .Op Fl R Ar script-name .Ar interface ... .Nm .Op Fl dDfFmu1 +.Op Fl M Ar script-name .Op Fl O Ar script-name .Op Fl p Ar pidfile .Op Fl R Ar script-name .Fl a .Nm rtsol .Op Fl dDu +.Op Fl M Ar script-name .Op Fl O Ar script-name .Op Fl R Ar script-name .Ar interface ... .Nm rtsol .Op Fl dDu +.Op Fl M Ar script-name .Op Fl O Ar script-name .Op Fl R Ar script-name .Fl a .\" .Sh DESCRIPTION .Nm is the daemon program to send ICMPv6 Router Solicitation messages on the specified interfaces. If a node (re)attaches to a link, .Nm sends some Router Solicitations on the link destined to the link-local scope all-routers multicast address to discover new routers and to get non link-local addresses. .Pp .Nm should be used on IPv6 hosts .Pq non-router nodes only. .Pp If you invoke the program as .Nm rtsol , it will transmit probes from the specified .Ar interface , without becoming a daemon. In other words, .Nm rtsol behaves as .Do .Nm .Fl f1 .Ar interfaces .Dc . .Pp Specifically, .Nm sends at most 3 Router Solicitations on an interface after one of the following events: .Pp .Bl -bullet -compact .It Just after invocation of .Nm daemon. .It The interface is up after a temporary interface failure. .Nm detects such failures by periodically probing to see if the status of the interface is active or not. Note that some network cards and drivers do not allow the extraction of link state. In such cases, .Nm cannot detect the change of the interface status. .It Every 60 seconds if the .Fl m option is specified and the .Nm daemon cannot get the interface status. This feature does not conform to the IPv6 neighbor discovery specification, but is provided for mobile stations. The default interval for router advertisements, which is on the order of 10 minutes, is slightly long for mobile stations. This feature is provided for such stations so that they can find new routers as soon as possible when they attach to another link. .El .Lp Once .Nm has sent a Router Solicitation, and has received a valid Router Advertisement, it refrains from sending additional solicitations on that interface, until the next time one of the above events occurs. .Lp When sending a Router Solicitation on an interface, .Nm includes a Source Link-layer address option if the interface has a link-layer address. .Lp .Nm manages a per-interface parameter to detect if a separate protocol is needed for configuration parameters other than host's addresses. At the invocation time, the flag is FALSE, and becomes TRUE when the daemon receives a router advertisement with the OtherConfig flag being set. A script file can be specified to deal with the case .Pq see below . When .Nm start resending router solicitation messages by one of the conditions events, the daemon resets the parameter because the event may indicate a change on the attached link. .Pp Upon receipt of signal .Dv SIGUSR1 , .Nm will dump the current internal state into .Pa /var/run/rtsold.dump . .\" .Pp The options are as follows: .Bl -tag -width indent .It Fl a Autoprobe outgoing interfaces. .Nm will try to find any non-loopback, non-point-to-point, IPv6-capable interfaces and send router solicitation messages on all of them. .It Fl d Enable debugging. .It Fl D Enable more debugging including the printing of internal timer information. .It Fl f Prevent .Nm from becoming a daemon (foreground mode). Warning messages are generated to standard error instead of .Xr syslog 3 . .It Fl F Explicitly configure the kernel to accept Router Advertisements and disable IPv6 forwarding. These settings are required for proper .Nm operation. Without this option, the current settings will be obeyed; if they are incompatible with proper operation, warning messages will be generated, but Router Solicitations will still be sent. The settings may be changed manually with .Xr sysctl 8 and .Xr ifconfig 8 . .It Fl m Enable mobility support. If this option is specified, .Nm sends probing packets to default routers that have advertised Router Advertisements when the node (re)attaches to an interface. Moreover, if the option is specified, .Nm periodically sends Router Solicitation on an interface that does not support .Dv SIOCGIFMEDIA ioctl. .It Fl 1 Perform only one probe. Transmit Router Solicitation packets until at least one valid Router Advertisement packet has arrived on each .Ar interface , then exit. +.It Fl M Ar script-name +Specifies a supplement script file to handle the Managed Configuration +flag of the router advertisement. +When the flag changes from FALSE to TRUE, +.Nm +will invoke +.Ar script-name +with a single argument of the receiving interface name, +expecting the script will then start a protocol for the managed +configuration. +.Ar script-name +must be the absolute path from root to the script file, be a regular +file, and be created by the same owner who runs +.Nm . .It Fl O Ar script-name Specifies a supplement script file to handle the Other Configuration flag of the router advertisement. When the flag changes from FALSE to TRUE, .Nm will invoke .Ar script-name with a single argument of the receiving interface name, expecting the script will then start a protocol for the other configuration. +The script will not be run if the Managed Configuration flag in the +router advertisement is also TRUE. .Ar script-name must be the absolute path from root to the script file, be a regular file, and be created by the same owner who runs .Nm . .It Fl p Ar pidfile Writes the process ID of .Nm to .Pa pidfile instead of the default PID file .Pa /var/run/rtsold.pid . .It Fl R Ar script-name Specifies a script to run when router advertisement options .Dv RDNSS Pq Recursive DNS Server or .Dv DNSSL Pq DNS Search List are encountered. The information of DNS servers and DNS search domains will be sent to standard input of this script. The .Xr resolvconf 8 script is used by default. .It Fl u Specifies whether to add the source address of Router Advertisement messages to the interface name in the parameters of the RDNSS and DNSSL scripts. .Pp If .Fl u is specified, the interface name in the script parameters will be .Ql ifname:slaac:[RA-source-address] . .Pp Otherwise it will be .Ql ifname:slaac . .El .Sh FILES .Bl -tag -width /var/run/rtsold.dump -compact .It Pa /var/run/rtsold.pid The PID of the currently running .Nm . .It Pa /var/run/rtsold.dump Internal state dump file. .El .\" .Sh EXIT STATUS .Ex -std .\" .Sh SEE ALSO .Xr resolvconf 8 , .Xr rtadvd 8 , .Xr sysctl 8 .\" .Sh HISTORY The .Nm command is based on the .Nm rtsol command, which first appeared in WIDE/KAME IPv6 protocol stack kit. .Nm rtsol is now integrated into .Xr rtsold 8 . .\" .Sh BUGS In some operating systems, when a PCMCIA network card is removed and reinserted, the corresponding interface index is changed. However, .Nm assumes such changes will not occur, and always uses the index that it got at invocation. As a result, .Nm may not work if you reinsert a network card. In such a case, .Nm should be killed and restarted. .Pp The IPv6 autoconfiguration specification assumes a single-interface host. You may see kernel error messages if you try to autoconfigure a host with multiple interfaces. Also, it seems contradictory for .Nm to accept multiple .Ar interface arguments. Index: stable/12/usr.sbin/rtsold/rtsold.c =================================================================== --- stable/12/usr.sbin/rtsold/rtsold.c (revision 365629) +++ stable/12/usr.sbin/rtsold/rtsold.c (revision 365630) @@ -1,866 +1,924 @@ /* $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 *managedconf_script; 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 = "adDfFm1M:O:p:R:u"; + once = 0; + pidfilepath = NULL; + } else { + opts = "adDFM:O: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 'M': + managedconf_script = optarg; + 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 (managedconf_script != NULL && *managedconf_script != '/') errx(1, "configuration script (%s) must be an absolute path", + managedconf_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[] = + { resolvconf_script, managedconf_script, otherconf_script }; + cap_channel_t *capcasper; + nvlist_t *limits; + int count; + + 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); + count = 0; + for (size_t i = 0; i < nitems(scripts); i++) + if (scripts[i] != NULL) + count++; + limits = nvlist_create(0); + nvlist_add_string_array(limits, "scripts", scripts, count); + 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. + * status wrt the "managed/other" configuration. */ - if (probe) + if (probe) { + ifi->managedconfig = 0; 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) vwarnx(msg, ap); } 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: stable/12/usr.sbin/rtsold/rtsold.h =================================================================== --- stable/12/usr.sbin/rtsold/rtsold.h (revision 365629) +++ stable/12/usr.sbin/rtsold/rtsold.h (revision 365630) @@ -1,191 +1,204 @@ /* $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 managedconfig; /* need a separate protocol for the "managed" + * configuration */ 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 *managedconf_script; 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); Index: stable/12 =================================================================== --- stable/12 (revision 365629) +++ stable/12 (revision 365630) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r342788,354926,364771