Index: sbin/rtsol/Makefile =================================================================== --- sbin/rtsol/Makefile +++ sbin/rtsol/Makefile @@ -18,9 +18,15 @@ PACKAGE=runtime PROG= rtsol -SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c +SRCS= rtsold.c rtsol.c if.c cap_probe.c cap_script.c dump.c rtsock.c MAN= +LIBADD= util -CFLAGS+= -DSMALL +.include + +.if ${MK_CASPER} != "no" && !defined(RESCUE) +CFLAGS+= -DWITH_CASPER +LIBADD+= cap_syslog casper nv +.endif .include Index: usr.sbin/rtsold/Makefile =================================================================== --- usr.sbin/rtsold/Makefile +++ usr.sbin/rtsold/Makefile @@ -17,6 +17,15 @@ PROG= rtsold MAN= rtsold.8 MLINKS= rtsold.8 rtsol.8 -SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c +SRCS= rtsold.c rtsol.c if.c cap_probe.c cap_script.c dump.c rtsock.c + +LIBADD= util + +.include + +.if ${MK_CASPER} != "no" +CFLAGS+= -DWITH_CASPER +LIBADD+= casper cap_syslog nv +.endif .include Index: usr.sbin/rtsold/cap_probe.c =================================================================== --- usr.sbin/rtsold/cap_probe.c +++ usr.sbin/rtsold/cap_probe.c @@ -34,12 +34,11 @@ */ #include -#include -#include +#include +#include +#include #include #include -#include -#include #include #include @@ -51,122 +50,71 @@ #include +#include #include -#include +#include #include #include -#include +#include -#include "rtsold.h" +#include +#include -static struct msghdr sndmhdr; -static struct iovec sndiov[2]; -static int probesock; -static void sendprobe(struct in6_addr *, struct ifinfo *); +#include "rtsold.h" -int -probe_init(void) +static int +getsocket(int *sockp) { - int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - static u_char *sndcmsgbuf = NULL; + static int probesock = -1; + cap_rights_t rights; + int sock; - if (sndcmsgbuf == NULL && - (sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) { - warnmsg(LOG_ERR, __func__, "malloc failed"); - return (-1); + if (probesock >= 0) { + *sockp = probesock; + return (0); } - if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { - warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) 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; + cap_rights_init(&rights, CAP_CONNECT, CAP_SEND); + if (caph_rights_limit(sock, &rights) != 0) + return (-1); + *sockp = probesock = sock; 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) +sendprobe(int sock, struct in6_addr *addr, uint32_t ifindex, uint32_t linkid) { - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + struct msghdr hdr; + struct iovec iov; struct sockaddr_in6 sa6_probe; struct in6_pktinfo *pi; struct cmsghdr *cm; - u_int32_t ifindex = ifinfo->sdl->sdl_index; - int hoplimit = 1; + int hoplimit; 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; + sa6_probe.sin6_scope_id = linkid; + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_name = (caddr_t)&sa6_probe; + hdr.msg_namelen = sizeof(sa6_probe); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); - sndmhdr.msg_name = (caddr_t)&sa6_probe; - sndmhdr.msg_iov[0].iov_base = NULL; - sndmhdr.msg_iov[0].iov_len = 0; + iov.iov_base = NULL; + iov.iov_len = 0; - cm = CMSG_FIRSTHDR(&sndmhdr); - /* specify the outgoing interface */ + /* 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)); @@ -174,18 +122,102 @@ 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); + /* Specify the hop limit of the packet for safety. */ + hoplimit = 1; + 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)); - warnmsg(LOG_DEBUG, __func__, "probe a router %s on %s", - inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(ifindex, ifnamebuf)); + (void)sendmsg(sock, &hdr, 0); +} + +static int +probe_defrouters(uint32_t ifindex, uint32_t linkid) +{ + struct in6_defrouter *p, *ep; + char *buf; + size_t len; + int mib[4], sock; - if (sendmsg(probesock, &sndmhdr, 0)) - warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", - if_indextoname(ifindex, ifnamebuf), strerror(errno)); + if (getsocket(&sock) != 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); + + 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; + sendprobe(sock, &p->rtaddr.sin6_addr, ifindex, linkid); + } + free(buf); + + 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); + return (error); +#else + (void)cap; + if (probe_defrouters(ifinfo->sdl->sdl_index, ifinfo->linkid) != 0) + return (errno); + return (0); +#endif } + +#ifdef WITH_CASPER +static int +probe_command(const char *cmd, const nvlist_t *limits __unused, nvlist_t *nvlin, + nvlist_t *nvlout __unused) +{ + uint32_t ifindex, linkid; + int error; + + if (strcmp(cmd, "probe_defrouters") != 0) + return (EINVAL); + + ifindex = (uint32_t)nvlist_get_number(nvlin, "ifindex"); + linkid = (uint32_t)nvlist_get_number(nvlin, "linkid"); + if (ifindex == 0) + return (EINVAL); + error = probe_defrouters(ifindex, linkid); + if (error != 0) + return (errno); + return (0); +} + +CREATE_SERVICE("rtsold.defrouter_probe", NULL, probe_command, 0); +#endif /* WITH_CASPER */ Index: usr.sbin/rtsold/cap_script.c =================================================================== --- /dev/null +++ usr.sbin/rtsold/cap_script.c @@ -0,0 +1,236 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 The FreeBSD Foundation + * + * This software was developed by Mark Johnston under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "rtsold.h" + +/* + * Run the script and return the write end of a pipe to the main process. + * Return -1 and set errno on error. + */ +static int +script_run(char **argv) +{ + pid_t pid; + int fd[2], null; + + if (pipe(fd) != 0) + return (-1); + if ((pid = fork()) < 0) + return (-1); + if (pid == 0) { + (void)close(fd[1]); + null = open("/dev/null", O_RDWR); + if (null < 0) + _exit(1); + if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) + _exit(1); + + closefrom(3); + (void)execve(argv[0], argv, NULL); + _exit(1); + } else + (void)close(fd[0]); + + return (fd[1]); +} + +int +cap_script_run(cap_channel_t *cap, const char *const *argv) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + size_t argc; + int error, wfd; + + for (argc = 0; argv[argc] != NULL; argc++) + ; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "script_run"); + nvlist_add_string_array(nvl, "argv", argv, argc); + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (-1); + + error = (int)dnvlist_get_number(nvl, "error", 0); + if (error == 0) + wfd = nvlist_take_descriptor(nvl, "fd"); + nvlist_destroy(nvl); + if (error != 0) + errno = error; + return (error == 0 ? wfd : -1); +#else + (void)cap; + return (script_run(__DECONST(char **, argv))); +#endif +} + +/* + * Wait for a child process to exit, and return its status. + * Return -1 and set errno upon error. + */ +static int +script_wait(int *statusp) +{ + int error; + + error = wait(statusp); + return (error >= 0 ? 0 : -1); +} + +int +cap_script_wait(cap_channel_t *cap, int *statusp) +{ +#ifdef WITH_CASPER + nvlist_t *nvl; + int error; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "script_wait"); + nvl = cap_xfer_nvlist(cap, nvl); + if (nvl == NULL) + return (-1); + + error = (int)dnvlist_get_number(nvl, "error", 0); + if (error == 0) + *statusp = (int)nvlist_get_number(nvl, "status"); + nvlist_destroy(nvl); + if (error != 0) + errno = error; + return (error == 0 ? 0 : -1); +#else + (void)cap; + return (script_wait(statusp)); +#endif +} + +#ifdef WITH_CASPER +static int +script_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, + nvlist_t *nvlout) +{ + cap_rights_t rights; + const char *const *iargv, *const *scripts; + char **argv; + size_t argc, i, nscripts; + int fd, status; + + if (strcmp(cmd, "script_wait") == 0) { + /* Wait for the result of a previous "script_run" command. */ + if (script_wait(&status) == -1) + return (errno); + nvlist_add_number(nvlout, "status", status); + return (0); + } + if (strcmp(cmd, "script_run") != 0) + return (EINVAL); + + /* + * Validate the argv against the limits specified at initialization + * time. + */ + iargv = nvlist_get_string_array(nvlin, "argv", &argc); + if (argc == 0) + return (EINVAL); + scripts = nvlist_get_string_array(limits, "scripts", &nscripts); + for (i = 0; i < nscripts; i++) + if (strcmp(iargv[0], scripts[i]) == 0) + break; + if (i == nscripts) + return (EINVAL); + + /* + * The nvlist API does not permit NULL pointers in an array, so we have + * to add the nul terminator ourselves. Yuck. + */ + argv = calloc(argc + 1, sizeof(*argv)); + if (argv == NULL) + return (errno); + memcpy(argv, iargv, sizeof(*argv) * argc); + + fd = script_run(argv); + if (fd < 0) + return (errno); + + (void)caph_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE)); + nvlist_move_descriptor(nvlout, "fd", fd); + return (0); +} + +static int +script_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits __unused) +{ + const char *name; + void *cookie; + int nvtype; + bool hasscripts; + + /* Limits may only be set once. */ + if (oldlimits != NULL) + return (ENOTCAPABLE); + + cookie = NULL; + hasscripts = false; + while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { + if (nvtype == NV_TYPE_STRING_ARRAY && + strcmp(name, "scripts") == 0) + hasscripts = true; + else + return (EINVAL); + } + if (!hasscripts) + return (EINVAL); + return (0); +} + +CREATE_SERVICE("rtsold.script", script_limit, script_command, 0); +#endif /* WITH_CASPER */ Index: usr.sbin/rtsold/dump.c =================================================================== --- usr.sbin/rtsold/dump.c +++ usr.sbin/rtsold/dump.c @@ -34,6 +34,7 @@ */ #include +#include #include #include @@ -42,21 +43,20 @@ #include #include -#include -#include +#include +#include #include #include -#include +#include +#include #include "rtsold.h" -static FILE *fp; - -static void dump_interface_status(void); -static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; +static const char * const ifstatstr[] = + { "IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE" }; -static void -dump_interface_status(void) +void +rtsold_dump(FILE *fp) { struct ifinfo *ifi; struct rainfo *rai; @@ -64,7 +64,13 @@ struct timespec now; char ntopbuf[INET6_ADDRSTRLEN]; - clock_gettime(CLOCK_MONOTONIC_FAST, &now); + if (fseek(fp, 0, SEEK_SET) != 0) { + warnmsg(LOG_ERR, __func__, "fseek(): %s", strerror(errno)); + return; + } + (void)ftruncate(fileno(fp), 0); + + (void)clock_gettime(CLOCK_MONOTONIC_FAST, &now); TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { fprintf(fp, "Interface %s\n", ifi->ifname); @@ -121,18 +127,28 @@ 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); + } + + 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); } - dump_interface_status(); - fclose(fp); + return (fp); } const char * Index: usr.sbin/rtsold/if.c =================================================================== --- usr.sbin/rtsold/if.c +++ usr.sbin/rtsold/if.c @@ -34,10 +34,11 @@ */ #include -#include -#include +#include #include #include +#include +#include #include #include @@ -51,6 +52,7 @@ #include #include +#include #include #include #include @@ -63,16 +65,29 @@ #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 Index: usr.sbin/rtsold/rtsock.c =================================================================== --- usr.sbin/rtsold/rtsock.c +++ usr.sbin/rtsold/rtsock.c @@ -33,10 +33,11 @@ */ #include +#include +#include #include -#include #include -#include +#include #include #include @@ -46,6 +47,7 @@ #include #include +#include #include #include #include @@ -72,8 +74,18 @@ int rtsock_open(void) { - - return (socket(PF_ROUTE, SOCK_RAW, 0)); + cap_rights_t rights; + int s; + + 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) { + (void)close(s); + return (-1); + } + return (s); } int Index: usr.sbin/rtsold/rtsol.c =================================================================== --- usr.sbin/rtsold/rtsol.c +++ usr.sbin/rtsold/rtsol.c @@ -35,11 +35,12 @@ */ #include +#include +#include #include +#include #include -#include #include -#include #include #include @@ -54,6 +55,7 @@ #include +#include #include #include #include @@ -67,17 +69,8 @@ #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), @@ -85,10 +78,8 @@ .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, }; -static void call_script(const int, const char *const *, - struct script_msg_head_t *); +static void call_script(const char *const *, struct script_msg_head_t *); static size_t dname_labeldec(char *, size_t, const char *); -static int safefile(const char *); static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, struct script_msg_head_t *, struct script_msg_head_t *); @@ -100,7 +91,7 @@ #define CALL_SCRIPT(name, sm_head) do { \ const char *const sarg[] = { _ARGS_##name, NULL }; \ - call_script(sizeof(sarg), sarg, sm_head); \ + call_script(sarg, sm_head); \ } while (0) #define ELM_MALLOC(p, error_action) do { \ @@ -114,94 +105,132 @@ } 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)); + goto fail; + } + + 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; + } + + return (sock); + +fail: + if (sock >= 0) + (void)close(sock); + return (-1); +} + +int +sendsockopen(uint32_t linkid) +{ + struct icmp6_filter filt; + struct sockaddr_in6 dst; + cap_rights_t rights; + int sock; + + if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { + warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); + goto fail; + } + + /* + * We don't want to receive data on send sockets. + */ + ICMP6_FILTER_SETBLOCKALL(&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; + } + + /* + * Explicitly set the destination address now so that we can send on + * this socket in capability mode. + */ + dst = sin6_allrouters; + dst.sin6_scope_id = linkid; + if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) { + warnmsg(LOG_ERR, __func__, "connect(): %s", strerror(errno)); + 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; + if (caph_rights_limit(sock, cap_rights_init(&rights, CAP_SEND)) < 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) { + uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + struct iovec iov; + struct msghdr hdr; struct in6_pktinfo *pi; struct cmsghdr *cm; int hoplimit = 255; - ssize_t i; - struct sockaddr_in6 dst; + ssize_t n; - dst = sin6_allrouters; - dst.sin6_scope_id = ifi->linkid; + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); - 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; + iov.iov_base = (caddr_t)ifi->rs_data; + iov.iov_len = ifi->rs_datalen; - cm = CMSG_FIRSTHDR(&sndmhdr); - /* specify the outgoing interface */ + /* 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)); @@ -209,35 +238,37 @@ 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); + /* Specify the packet's hop limit. */ + 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)); + cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); + memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); - warnmsg(LOG_DEBUG, __func__, - "send RS on %s, whose state is %d", + warnmsg(LOG_DEBUG, __func__, "sent RS on %s, state is %d", ifi->ifname, ifi->state); - i = sendmsg(rssock, &sndmhdr, 0); - if (i < 0 || (size_t)i != ifi->rs_datalen) { + n = sendmsg(ifi->ifi_sock, &hdr, 0); + if (n < 0 || (size_t)n != 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)); + warnmsg(LOG_ERR, __func__, "sendmsg on %s %zd: %s", + ifi->ifname, n, strerror(errno)); } - - /* update counter */ ifi->probes++; } void -rtsol_input(int s) +rtsol_input(int sock) { - char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; + uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; + struct iovec iov; + struct msghdr hdr; + struct sockaddr_in6 from; + char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; int l, ifindex = 0, *hlimp = NULL; ssize_t msglen; struct in6_pktinfo *pi = NULL; @@ -247,8 +278,7 @@ struct nd_router_advert *nd_ra; struct cmsghdr *cm; struct rainfo *rai; - char *raoptp; - char *p; + char *p, *raoptp; struct in6_addr *addr; struct nd_opt_hdr *ndo; struct nd_opt_rdnss *rdnss; @@ -256,22 +286,28 @@ size_t len; char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; char dname[NI_MAXHOST]; - struct timespec now; - struct timespec lifetime; - int newent_rai; - int newent_rao; - - /* 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) { + struct timespec lifetime, now; + int newent_rai, newent_rao; + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_name = &from; + hdr.msg_namelen = sizeof(from); + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = (caddr_t)answer; + iov.iov_len = sizeof(answer); + + if ((msglen = recvmsg(sock, &hdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } - /* extract optional information via Advanced API */ - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { + /* Extract control message info. */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL; + cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { @@ -301,8 +337,7 @@ return; } - icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; - + icp = (struct icmp6_hdr *)iov.iov_base; if (icp->icmp6_type != ND_ROUTER_ADVERT) { /* * this should not happen because we configured a filter @@ -761,154 +796,43 @@ } static void -call_script(const int argc, const char *const argv[], - struct script_msg_head_t *sm_head) +call_script(const char *const argv[], struct script_msg_head_t *sm_head) { - const char *scriptpath; - int fd[2]; - int error; - pid_t pid, wpid; + struct script_msg *smp; + ssize_t len; + int status, wfd; - if ((scriptpath = argv[0]) == NULL) + if (argv[0] == NULL) return; - fd[0] = fd[1] = -1; - if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { - error = pipe(fd); - if (error) { - warnmsg(LOG_ERR, __func__, - "failed to create a pipe: %s", strerror(errno)); - return; - } - } - - /* launch the script */ - pid = fork(); - if (pid < 0) { + wfd = cap_script_run(capscript, argv); + if (wfd == -1) { warnmsg(LOG_ERR, __func__, - "failed to fork: %s", strerror(errno)); + "failed to run %s: %s", argv[0], strerror(errno)); return; - } else if (pid) { /* parent */ - int wstatus; - - if (fd[0] != -1) { /* Send message to the child if any. */ - ssize_t len; - struct script_msg *smp; - - close(fd[0]); - TAILQ_FOREACH(smp, sm_head, sm_next) { - len = strlen(smp->sm_msg); - warnmsg(LOG_DEBUG, __func__, - "write to child = %s(%zd)", - smp->sm_msg, len); - if (write(fd[1], smp->sm_msg, len) != len) { - warnmsg(LOG_ERR, __func__, - "write to child failed: %s", - strerror(errno)); - break; - } - } - 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; -} - -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); + 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; + } + } } - 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); - } + (void)close(wfd); - return (0); + if (cap_script_wait(capscript, &status) != 0) + warnmsg(LOG_ERR, __func__, "wait(): %s", strerror(errno)); + else + warnmsg(LOG_DEBUG, __func__, "script \"%s\" status %d", + argv[0], status); } /* Decode domain name label encoding in RFC 1035 Section 3.1 */ Index: usr.sbin/rtsold/rtsold.h =================================================================== --- usr.sbin/rtsold/rtsold.h +++ usr.sbin/rtsold/rtsold.h @@ -60,12 +60,14 @@ 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 ifi_sock; /* socket for sending RS messages */ int active; /* interface status */ int probeinterval; /* interval of probe timer (if necessary) */ int probetimer; /* rest of probe timer */ @@ -77,7 +79,6 @@ int dadcount; struct timespec timer; struct timespec expire; - int errors; /* # of errors we've got - detect wedge */ #define IFI_DNSOPT_STATE_NOINFO 0 #define IFI_DNSOPT_STATE_RECEIVED 1 int ifi_rdnss; /* RDNSS option state */ @@ -150,6 +151,7 @@ } while (0) /* rtsold.c */ +struct cap_channel; extern struct timespec tm_max; extern int dflag; extern int aflag; @@ -157,6 +159,8 @@ extern int uflag; extern const char *otherconf_script; extern const char *resolvconf_script; +extern struct cap_channel *capprobe, *capscript; + struct ifinfo *find_ifinfo(int); struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *); void rtsol_timer_update(struct ifinfo *); @@ -165,6 +169,7 @@ extern int ra_opt_handler(struct ifinfo *); /* if.c */ +struct nd_opt_hdr; extern int ifinit(void); extern int interface_up(char *); extern int interface_status(struct ifinfo *); @@ -173,17 +178,21 @@ extern struct sockaddr_dl *if_nametosdl(char *); /* rtsol.c */ -extern int rssock; -extern int sockopen(void); +extern int recvsockopen(void); +extern int sendsockopen(uint32_t); extern void sendpacket(struct ifinfo *); extern void rtsol_input(int); -/* probe.c */ -extern int probe_init(void); -extern void defrouter_probe(struct ifinfo *); +/* cap_probe.c */ +extern int cap_probe_defrouters(struct cap_channel *, struct ifinfo *); + +/* cap_script.c */ +extern int cap_script_run(struct cap_channel *, const char *const *); +extern int cap_script_wait(struct cap_channel *, int *); /* dump.c */ -extern void rtsold_dump_file(const char *); +extern FILE *rtsold_init_dumpfile(const char *); +extern void rtsold_dump(FILE *); extern const char *sec2str(const struct timespec *); /* rtsock.c */ Index: usr.sbin/rtsold/rtsold.c =================================================================== --- usr.sbin/rtsold/rtsold.c +++ usr.sbin/rtsold/rtsold.c @@ -34,6 +34,8 @@ */ #include +#include +#include #include #include @@ -47,23 +49,27 @@ #include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include +#include #include +#include +#include +#include #include -#include -#include -#include -#include -#include +#include + +#include +#include +#include #include "rtsold.h" -#define RTSOL_DUMPFILE "/var/run/rtsold.dump"; -#define RTSOL_PIDFILE "/var/run/rtsold.pid"; +#define RTSOL_DUMPFILE "/var/run/rtsold.dump" struct timespec tm_max; static int log_upto = 999; @@ -77,6 +83,11 @@ const char *otherconf_script; const char *resolvconf_script = "/sbin/resolvconf"; +cap_channel_t *capprobe, *capscript; +#ifdef WITH_CASPER +static cap_channel_t *capsyslog; +#endif + /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ @@ -90,43 +101,40 @@ /* static variables and functions */ static int mobile_node = 0; -static const char *pidfilename = RTSOL_PIDFILE; -#ifndef SMALL -static int do_dump; -static const char *dumpfilename = RTSOL_DUMPFILE; -#endif +static sig_atomic_t do_dump, do_exit; +static struct pidfh *pfh; static char **autoifprobe(void); static int ifconfig(char *ifname); +static int init_capabilities(void); static int make_packet(struct ifinfo *); static struct timespec *rtsol_check_timer(void); -#ifndef SMALL -static void rtsold_set_dump_file(int); -#endif -static void usage(void); +static void set_dumpfile(int); +static void set_exit(int); +static void usage(const char *progname); int main(int argc, char **argv) { - int s, ch, once = 0; + struct kevent events[2]; + FILE *dumpfp; + struct ifinfo *ifi; struct timespec *timeout; - const char *opts; - struct pollfd set[2]; - int rtsock; - char *argv0; - -#ifndef SMALL - /* rtsold */ - opts = "adDfFm1O:p:R:u"; -#else - /* rtsol */ - opts = "adDFO:R:u"; - fflag = 1; - once = 1; -#endif - argv0 = argv[0]; + const char *opts, *pidfilepath, *progname; + int ch, error, kq, once, rcvsock, rtsock; + + progname = basename(argv[0]); + if (strcmp(progname, "rtsold") == 0) { + opts = "adDfFm1O:p:R:u"; + once = 0; + pidfilepath = NULL; + } else { + opts = "adDFO:R:u"; + fflag = 1; + once = 1; + } while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -155,7 +163,7 @@ otherconf_script = optarg; break; case 'p': - pidfilename = optarg; + pidfilepath = optarg; break; case 'R': resolvconf_script = optarg; @@ -164,17 +172,14 @@ uflag = 1; break; default: - usage(); - exit(1); + usage(progname); } } argc -= optind; argv += optind; - if ((!aflag && argc == 0) || (aflag && argc != 0)) { - usage(); - exit(1); - } + if ((!aflag && argc == 0) || (aflag && argc != 0)) + usage(progname); /* Generate maximum time in timespec. */ tm_max.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1)); @@ -188,64 +193,61 @@ else log_upto = LOG_NOTICE; - if (!fflag) { - char *ident; - - ident = strrchr(argv0, '/'); - if (!ident) - ident = argv0; - else - ident++; - openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); - if (log_upto >= 0) - setlogmask(LOG_UPTO(log_upto)); - } - - if (otherconf_script && *otherconf_script != '/') { + if (otherconf_script != NULL && *otherconf_script != '/') errx(1, "configuration script (%s) must be an absolute path", otherconf_script); - } - if (resolvconf_script && *resolvconf_script != '/') { + if (*resolvconf_script != '/') errx(1, "configuration script (%s) must be an absolute path", resolvconf_script); - } - if (pidfilename && *pidfilename != '/') { - errx(1, "pid filename (%s) must be an absolute path", - pidfilename); + + if (!fflag) { + pfh = pidfile_open(pidfilepath, 0644, NULL); + if (pfh == NULL) + errx(1, "failed to open pidfile: %s", strerror(errno)); + if (daemon(0, 0) != 0) + errx(1, "failed to daemonize"); } -#ifndef SMALL - /* initialization to dump internal status to a file */ - signal(SIGUSR1, rtsold_set_dump_file); -#endif + if ((error = init_capabilities()) != 0) + err(1, "failed to initialize capabilities"); - if (!fflag) - daemon(0, 0); /* act as a daemon */ + if (!fflag) { + cap_openlog(capsyslog, progname, LOG_NDELAY | LOG_PID, + LOG_DAEMON); + if (log_upto >= 0) + (void)cap_setlogmask(capsyslog, LOG_UPTO(log_upto)); + (void)signal(SIGTERM, set_exit); + (void)signal(SIGINT, set_exit); + (void)signal(SIGUSR1, set_dumpfile); + dumpfp = rtsold_init_dumpfile(RTSOL_DUMPFILE); + } - /* - * Open a socket for sending RS and receiving RA. - * This should be done before calling ifinit(), since the function - * uses the socket. - */ - if ((s = sockopen()) < 0) { - warnmsg(LOG_ERR, __func__, "failed to open a socket"); + kq = kqueue(); + if (kq < 0) { + warnmsg(LOG_ERR, __func__, "failed to create a kqueue: %s", + strerror(errno)); exit(1); } - set[0].fd = s; - set[0].events = POLLIN; - set[1].fd = -1; + /* Open global sockets and register for read events. */ if ((rtsock = rtsock_open()) < 0) { - warnmsg(LOG_ERR, __func__, "failed to open a socket"); + warnmsg(LOG_ERR, __func__, "failed to open routing socket"); + exit(1); + } + if ((rcvsock = recvsockopen()) < 0) { + warnmsg(LOG_ERR, __func__, "failed to open receive socket"); + exit(1); + } + EV_SET(&events[0], rtsock, EVFILT_READ, EV_ADD, 0, 0, NULL); + EV_SET(&events[1], rcvsock, EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(kq, events, 2, NULL, 0, NULL) < 0) { + warnmsg(LOG_ERR, __func__, "kevent(): %s", strerror(errno)); exit(1); } - set[1].fd = rtsock; - set[1].events = POLLIN; - /* configuration per interface */ - if (ifinit()) { - warnmsg(LOG_ERR, __func__, - "failed to initialize interfaces"); + /* Probe network interfaces and set up tracking info. */ + if (ifinit() != 0) { + warnmsg(LOG_ERR, __func__, "failed to initialize interfaces"); exit(1); } if (aflag) @@ -259,42 +261,36 @@ 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. */ + if (caph_enter_casper() != 0) { + warnmsg(LOG_ERR, __func__, "caph_enter(): %s", strerror(errno)); + exit(1); + } - if ((fp = fopen(pidfilename, "w")) == NULL) - warnmsg(LOG_ERR, __func__, - "failed to open a pid log file(%s): %s", - pidfilename, strerror(errno)); - else { - fprintf(fp, "%d\n", pid); - fclose(fp); + for (;;) { + if (do_exit) { + /* Handle SIGTERM, SIGINT. */ + if (pfh != NULL) + pidfile_remove(pfh); + break; } - } - while (1) { /* main loop */ - int e; -#ifndef SMALL - if (do_dump) { /* SIGUSR1 */ + if (do_dump) { + /* Handle SIGUSR1. */ do_dump = 0; - rtsold_dump_file(dumpfilename); + if (dumpfp != NULL) + rtsold_dump(dumpfp); } -#endif timeout = rtsol_check_timer(); if (once) { - struct ifinfo *ifi; - /* if we have no timeout, we are done (or failed) */ if (timeout == NULL) break; @@ -307,23 +303,60 @@ 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); + } + + return (0); +} + +static int +init_capabilities(void) +{ +#ifdef WITH_CASPER + const char *const scripts[2] = { resolvconf_script, otherconf_script }; + cap_channel_t *capcasper; + nvlist_t *limits; + + capcasper = cap_init(); + if (capcasper == NULL) + return (errno); + + if (!fflag) { + capsyslog = cap_service_open(capcasper, "system.syslog"); + if (capsyslog == NULL) + return (errno); } - /* NOTREACHED */ + if (mobile_node) { + capprobe = cap_service_open(capcasper, + "rtsold.defrouter_probe"); + if (capprobe == NULL) + return (errno); + } + + capscript = cap_service_open(capcasper, "rtsold.script"); + if (capscript == NULL) + return (errno); + limits = nvlist_create(0); + nvlist_add_string_array(limits, "scripts", scripts, + otherconf_script != NULL ? 2 : 1); + if (cap_limit_set(capscript, limits) != 0) + return (errno); + + cap_close(capcasper); +#endif /* WITH_CASPER */ return (0); } @@ -396,6 +429,10 @@ ifi->linkid = ifi->sdl->sdl_index; #endif + ifi->ifi_sock = sendsockopen(ifi->linkid); + if (ifi->ifi_sock == -1) + goto bad; + /* * check if the interface is available. * also check if SIOCGIFMEDIA ioctl is OK on the interface. @@ -501,7 +538,7 @@ struct ifinfo *ifi; struct rainfo *rai; struct ra_opt *rao, *raotmp; - int flags; + int error, flags; clock_gettime(CLOCK_MONOTONIC_FAST, &now); @@ -568,9 +605,14 @@ */ if (probe) ifi->otherconfig = 0; - - if (probe && mobile_node) - defrouter_probe(ifi); + if (probe && mobile_node) { + error = cap_probe_defrouters(capprobe, + ifi); + if (error != 0) + warnmsg(LOG_DEBUG, __func__, + "failed to probe routers: %d", + error); + } break; } case IFS_DELAY: @@ -660,10 +702,9 @@ ifi->timer.tv_sec = 1; break; case IFS_IDLE: - if (mobile_node) { + if (mobile_node) /* XXX should be configurable */ ifi->timer.tv_sec = 3; - } else ifi->timer = tm_max; /* stop timer(valid?) */ break; @@ -675,7 +716,7 @@ case IFS_PROBE: if (ifi->probes < MAX_RTR_SOLICITATIONS) ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL; - else { + else /* * After sending MAX_RTR_SOLICITATIONS solicitations, * we're just waiting for possible replies; there @@ -684,7 +725,6 @@ * on RFC 2461, Section 6.3.7. */ ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; - } break; default: warnmsg(LOG_ERR, __func__, @@ -711,28 +751,36 @@ #undef MILLION } -#ifndef SMALL static void -rtsold_set_dump_file(int sig __unused) +set_dumpfile(int sig __unused) { + do_dump = 1; } -#endif static void -usage(void) +set_exit(int sig __unused) { -#ifndef SMALL - fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " - "[-p pidfile] [-R script-name] interface ...\n"); - fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " - "[-p pidfile] [-R script-name] -a\n"); -#else - fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " - "[-p pidfile] [-R script-name] interface ...\n"); - fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " - "[-p pidfile] [-R script-name] -a\n"); -#endif + + do_exit = 1; +} + +static void +usage(const char *progname) +{ + + if (strcmp(progname, "rtsold") == 0) { + fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " + "[-p pidfile] [-R script-name] interface ...\n"); + fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " + "[-p pidfile] [-R script-name] -a\n"); + } else { + fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " + "[-p pidfile] [-R script-name] interface ...\n"); + fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " + "[-p pidfile] [-R script-name] -a\n"); + } + exit(1); } void @@ -750,7 +798,7 @@ } else { snprintf(buf, sizeof(buf), "<%s> %s", func, msg); msg = buf; - vsyslog(priority, msg, ap); + cap_vsyslog(capsyslog, priority, msg, ap); } va_end(ap); }