Capsicumize rtsol(8) and rtsold(8).

These programs parse ND6 Router Advertisement messages; rtsold(8) has
required an SA, SA-14:20.rtsold, for a bug in this code.  Thus, they
are good candidates for sandboxing.

The approach taken is to run the main executable in capability mode
and use Casper services to provide functionality that cannot be
implemented within the sandbox.  In particular, several custom services
were required.

- A Casper service is used to send Router Solicitation messages on a
  raw ICMP6 socket.  Initially I took the approach of creating a
  socket for each interface upon startup, and connect(2)ing it to
  the all-routers multicast group for the interface.  This permits
  the use of sendmsg(2) in capability mode, but only works if the
  interface's link is up when rtsol(d) starts.  So, instead, the
  rtsold.sendmsg service is used to transmit RS messages on behalf
  of the main process.  One could alternately define a service
  which simply creates and connects a socket for each destination
  address, and returns the socket to the sandboxed process.  However,
  to implement rtsold's -m option we also need to read the ND6 default
  router list, and this cannot be done in capability mode.
- rtsold may execute resolvconf(8) in response to RDNSS and DNSSL
  options in received RA messages.  A Casper service is used to
  fork and exec resolvconf(8), and to reap the child process.
- A service is used to determine whether a given interface's
  link-local address is useable (i.e., not duplicated or undergoing
  DAD).  This information is supplied by getifaddrs(3), which reads
  a sysctl not available in capability mode.  The SIOCGIFCONF socket
  ioctl provides equivalent information and can be used in capability
  mode, but I decided against it for now because of some limitations
  of that interface.

In addition to these new services, cap_syslog(3) is used to send
messages to syslogd.

Reviewed by:	oshogbo
Tested by:	bz (previous versions)
MFC after:	2 months
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D17572
This commit is contained in:
Mark Johnston 2019-01-05 16:05:39 +00:00
parent cb56711d68
commit 04e9edb544
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=342788
12 changed files with 1076 additions and 704 deletions

View File

@ -18,10 +18,24 @@
PACKAGE=runtime
PROG= rtsol
SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c
SRCS= cap_llflags.c \
cap_script.c \
cap_sendmsg.c \
dump.c \
if.c \
rtsol.c \
rtsold.c \
rtsock.c
MAN=
LIBADD= util
WARNS?= 3
CFLAGS+= -DSMALL
.include <src.opts.mk>
.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 <bsd.prog.mk>

View File

@ -17,8 +17,22 @@
PROG= rtsold
MAN= rtsold.8
MLINKS= rtsold.8 rtsol.8
SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c
SRCS= cap_llflags.c \
cap_script.c \
cap_sendmsg.c \
dump.c \
if.c \
rtsock.c \
rtsol.c \
rtsold.c
WARNS?= 3
LIBADD= util
.include <src.opts.mk>
.if ${MK_CASPER} != "no"
CFLAGS+= -DWITH_CASPER
LIBADD+= casper cap_syslog nv
.endif
.include <bsd.prog.mk>

View File

@ -0,0 +1,157 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018 The FreeBSD Foundation
*
* This software was developed by Mark Johnston under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/dnv.h>
#include <sys/ioctl.h>
#include <sys/nv.h>
#include <sys/queue.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <errno.h>
#include <ifaddrs.h>
#include <string.h>
#include <libcasper.h>
#include <libcasper_service.h>
#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 */

View File

@ -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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/dnv.h>
#include <sys/nv.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <net/if.h>
#include <netinet/in.h>
#include <capsicum_helpers.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libcasper.h>
#include <libcasper_service.h>
#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 */

View File

@ -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 <sys/param.h>
#include <sys/capsicum.h>
#include <sys/dnv.h>
#include <sys/nv.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <capsicum_helpers.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <libcasper.h>
#include <libcasper_service.h>
#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 */

View File

@ -34,6 +34,7 @@
*/
#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/socket.h>
#include <sys/queue.h>
@ -42,21 +43,20 @@
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <time.h>
#include <capsicum_helpers.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <time.h>
#include "rtsold.h"
static FILE *fp;
static const char * const ifstatstr[] =
{ "IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE" };
static void dump_interface_status(void);
static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
static void
dump_interface_status(void)
void
rtsold_dump(FILE *fp)
{
struct ifinfo *ifi;
struct rainfo *rai;
@ -64,7 +64,13 @@ dump_interface_status(void)
struct timespec now;
char ntopbuf[INET6_ADDRSTRLEN];
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
if (fseek(fp, 0, SEEK_SET) != 0) {
warnmsg(LOG_ERR, __func__, "fseek(): %s", strerror(errno));
return;
}
(void)ftruncate(fileno(fp), 0);
(void)clock_gettime(CLOCK_MONOTONIC_FAST, &now);
TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
fprintf(fp, "Interface %s\n", ifi->ifname);
@ -121,18 +127,28 @@ dump_interface_status(void)
fprintf(fp, "\n");
}
}
fflush(fp);
}
void
rtsold_dump_file(const char *dumpfile)
FILE *
rtsold_init_dumpfile(const char *dumpfile)
{
cap_rights_t rights;
FILE *fp;
if ((fp = fopen(dumpfile, "w")) == NULL) {
warnmsg(LOG_WARNING, __func__, "open a dump file(%s): %s",
warnmsg(LOG_WARNING, __func__, "opening a dump file(%s): %s",
dumpfile, strerror(errno));
return;
return (NULL);
}
dump_interface_status();
fclose(fp);
cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE);
if (caph_rights_limit(fileno(fp), &rights) != 0) {
warnmsg(LOG_WARNING, __func__, "caph_rights_limit(%s): %s",
dumpfile, strerror(errno));
return (NULL);
}
return (fp);
}
const char *

View File

@ -34,10 +34,11 @@
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/capsicum.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_types.h>
@ -51,6 +52,7 @@
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <capsicum_helpers.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@ -63,16 +65,28 @@
#include "rtsold.h"
static int ifsock;
static int get_llflag(const char *);
static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
int
ifinit(void)
{
ifsock = rssock;
cap_rights_t rights;
int sock;
return(0);
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (sock < 0) {
warnmsg(LOG_ERR, __func__, "socket(): %s",
strerror(errno));
return (-1);
}
if (caph_rights_limit(sock, cap_rights_init(&rights, CAP_IOCTL)) < 0) {
warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s",
strerror(errno));
(void)close(sock);
return (-1);
}
ifsock = sock;
return (0);
}
int
@ -150,10 +164,9 @@ interface_up(char *name)
}
close(s);
llflag = get_llflag(name);
if (llflag < 0) {
if (cap_llflags_get(capllflags, name, &llflag) != 0) {
warnmsg(LOG_WARNING, __func__,
"get_llflag() failed, anyway I'll try");
"cap_llflags_get() failed, anyway I'll try");
return (0);
}
@ -273,8 +286,6 @@ lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
"unsupported link type(%d)", sdl->sdl_type);
exit(1);
}
return;
}
struct sockaddr_dl *
@ -332,58 +343,6 @@ if_nametosdl(char *name)
return (ret_sdl);
}
/*------------------------------------------------------------*/
/* get ia6_flags for link-local addr on if. returns -1 on error. */
static int
get_llflag(const char *name)
{
struct ifaddrs *ifap, *ifa;
struct in6_ifreq ifr6;
struct sockaddr_in6 *sin6;
int s;
if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s",
strerror(errno));
exit(1);
}
if (getifaddrs(&ifap) != 0) {
warnmsg(LOG_ERR, __func__, "getifaddrs: %s",
strerror(errno));
exit(1);
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (strlen(ifa->ifa_name) != strlen(name) ||
strncmp(ifa->ifa_name, name, strlen(name)) != 0)
continue;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr;
if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
continue;
memset(&ifr6, 0, sizeof(ifr6));
strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
warnmsg(LOG_ERR, __func__,
"ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
exit(1);
}
freeifaddrs(ifap);
close(s);
return (ifr6.ifr_ifru.ifru_flags6);
}
freeifaddrs(ifap);
close(s);
return (-1);
}
static void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
{

View File

@ -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 <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/queue.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#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));
}

View File

@ -33,10 +33,11 @@
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/capsicum.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <net/if.h>
#include <net/route.h>
@ -46,6 +47,7 @@
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <capsicum_helpers.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
@ -72,8 +74,20 @@ static struct {
int
rtsock_open(void)
{
cap_rights_t rights;
int error, s;
return (socket(PF_ROUTE, SOCK_RAW, 0));
s = socket(PF_ROUTE, SOCK_RAW, 0);
if (s < 0)
return (s);
cap_rights_init(&rights, CAP_EVENT, CAP_READ);
if (caph_rights_limit(s, &rights) != 0) {
error = errno;
(void)close(s);
errno = errno;
return (-1);
}
return (s);
}
int

View File

@ -35,11 +35,12 @@
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/capsicum.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <net/if.h>
#include <net/route.h>
@ -54,6 +55,7 @@
#include <arpa/inet.h>
#include <capsicum_helpers.h>
#include <netdb.h>
#include <time.h>
#include <fcntl.h>
@ -67,28 +69,11 @@
#include <syslog.h>
#include "rtsold.h"
static struct msghdr rcvmhdr;
static struct msghdr sndmhdr;
static struct iovec rcviov[2];
static struct iovec sndiov[2];
static struct sockaddr_in6 from;
static int rcvcmsglen;
int rssock;
static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST];
struct ifinfo_head_t ifinfo_head =
TAILQ_HEAD_INITIALIZER(ifinfo_head);
struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head);
static const struct sockaddr_in6 sin6_allrouters = {
.sin6_len = sizeof(sin6_allrouters),
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
};
static void call_script(const int, const char *const *,
struct script_msg_head_t *);
static void call_script(const char *const *, struct script_msg_head_t *);
static size_t dname_labeldec(char *, size_t, const char *);
static int safefile(const char *);
static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
struct script_msg_head_t *, struct script_msg_head_t *);
@ -100,7 +85,7 @@ static char *make_rsid(const char *, const char *, struct rainfo *);
#define CALL_SCRIPT(name, sm_head) do { \
const char *const sarg[] = { _ARGS_##name, NULL }; \
call_script(sizeof(sarg), sarg, sm_head); \
call_script(sarg, sm_head); \
} while (0)
#define ELM_MALLOC(p, error_action) do { \
@ -114,130 +99,69 @@ static char *make_rsid(const char *, const char *, struct rainfo *);
} while (0)
int
sockopen(void)
recvsockopen(void)
{
static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
int sndcmsglen, on;
static u_char answer[1500];
struct icmp6_filter filt;
cap_rights_t rights;
int on, sock;
sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
CMSG_SPACE(sizeof(int));
if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
warnmsg(LOG_ERR, __func__,
"malloc for receive msghdr failed");
return (-1);
}
if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
warnmsg(LOG_ERR, __func__,
"malloc for send msghdr failed");
return (-1);
}
if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
return (-1);
goto fail;
}
/* specify to tell receiving interface */
/* Provide info about the receiving interface. */
on = 1;
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
sizeof(on)) < 0) {
warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s",
warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVPKTINFO): %s",
strerror(errno));
exit(1);
goto fail;
}
/* specify to tell value of hoplimit field of received IP6 hdr */
/* Include the hop limit from the received header. */
on = 1;
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
sizeof(on)) < 0) {
warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s",
warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVHOPLIMIT): %s",
strerror(errno));
exit(1);
goto fail;
}
/* specfiy to accept only router advertisements on the socket */
/* Filter out everything except for Router Advertisements. */
ICMP6_FILTER_SETBLOCKALL(&filt);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
sizeof(filt)) == -1) {
warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
strerror(errno));
return(-1);
goto fail;
}
/* initialize msghdr for receiving packets */
rcviov[0].iov_base = (caddr_t)answer;
rcviov[0].iov_len = sizeof(answer);
rcvmhdr.msg_name = (caddr_t)&from;
rcvmhdr.msg_iov = rcviov;
rcvmhdr.msg_iovlen = 1;
rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
cap_rights_init(&rights, CAP_EVENT, CAP_RECV);
if (caph_rights_limit(sock, &rights) < 0) {
warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s",
strerror(errno));
goto fail;
}
/* initialize msghdr for sending packets */
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
sndmhdr.msg_iov = sndiov;
sndmhdr.msg_iovlen = 1;
sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
sndmhdr.msg_controllen = sndcmsglen;
return (sock);
return (rssock);
fail:
if (sock >= 0)
(void)close(sock);
return (-1);
}
void
sendpacket(struct ifinfo *ifi)
rtsol_input(int sock)
{
struct in6_pktinfo *pi;
struct cmsghdr *cm;
int hoplimit = 255;
ssize_t i;
struct sockaddr_in6 dst;
dst = sin6_allrouters;
dst.sin6_scope_id = ifi->linkid;
sndmhdr.msg_name = (caddr_t)&dst;
sndmhdr.msg_iov[0].iov_base = (caddr_t)ifi->rs_data;
sndmhdr.msg_iov[0].iov_len = ifi->rs_datalen;
cm = CMSG_FIRSTHDR(&sndmhdr);
/* specify the outgoing interface */
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm);
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
pi->ipi6_ifindex = ifi->sdl->sdl_index;
/* specify the hop limit of the packet */
cm = CMSG_NXTHDR(&sndmhdr, cm);
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_HOPLIMIT;
cm->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
warnmsg(LOG_DEBUG, __func__,
"send RS on %s, whose state is %d",
ifi->ifname, ifi->state);
i = sendmsg(rssock, &sndmhdr, 0);
if (i < 0 || (size_t)i != ifi->rs_datalen) {
/*
* ENETDOWN is not so serious, especially when using several
* network cards on a mobile node. We ignore it.
*/
if (errno != ENETDOWN || dflag > 0)
warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s",
ifi->ifname, strerror(errno));
}
/* update counter */
ifi->probes++;
}
void
rtsol_input(int s)
{
char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
CMSG_SPACE(sizeof(int))];
struct iovec iov;
struct msghdr hdr;
struct sockaddr_in6 from;
char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
int l, ifindex = 0, *hlimp = NULL;
ssize_t msglen;
struct in6_pktinfo *pi = NULL;
@ -247,8 +171,7 @@ rtsol_input(int s)
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
struct rainfo *rai;
char *raoptp;
char *p;
char *p, *raoptp;
struct in6_addr *addr;
struct nd_opt_hdr *ndo;
struct nd_opt_rdnss *rdnss;
@ -256,22 +179,28 @@ rtsol_input(int s)
size_t len;
char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
char dname[NI_MAXHOST];
struct timespec now;
struct timespec lifetime;
int newent_rai;
int newent_rao;
struct timespec lifetime, now;
int newent_rai, newent_rao;
/* get message. namelen and controllen must always be initialized. */
rcvmhdr.msg_namelen = sizeof(from);
rcvmhdr.msg_controllen = rcvcmsglen;
if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) {
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
hdr.msg_name = &from;
hdr.msg_namelen = sizeof(from);
hdr.msg_control = cmsg;
hdr.msg_controllen = sizeof(cmsg);
iov.iov_base = (caddr_t)answer;
iov.iov_len = sizeof(answer);
if ((msglen = recvmsg(sock, &hdr, 0)) < 0) {
warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
return;
}
/* extract optional information via Advanced API */
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
/* Extract control message info. */
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL;
cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) {
if (cm->cmsg_level == IPPROTO_IPV6 &&
cm->cmsg_type == IPV6_PKTINFO &&
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
@ -301,8 +230,7 @@ rtsol_input(int s)
return;
}
icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
icp = (struct icmp6_hdr *)iov.iov_base;
if (icp->icmp6_type != ND_ROUTER_ADVERT) {
/*
* this should not happen because we configured a filter
@ -761,154 +689,43 @@ find_raopt(struct rainfo *rai, int type, void *msg, size_t len)
}
static void
call_script(const int argc, const char *const argv[],
struct script_msg_head_t *sm_head)
call_script(const char *const argv[], struct script_msg_head_t *sm_head)
{
const char *scriptpath;
int fd[2];
int error;
pid_t pid, wpid;
struct script_msg *smp;
ssize_t len;
int status, wfd;
if ((scriptpath = argv[0]) == NULL)
if (argv[0] == NULL)
return;
fd[0] = fd[1] = -1;
if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) {
error = pipe(fd);
if (error) {
warnmsg(LOG_ERR, __func__,
"failed to create a pipe: %s", strerror(errno));
return;
}
}
/* launch the script */
pid = fork();
if (pid < 0) {
wfd = cap_script_run(capscript, argv);
if (wfd == -1) {
warnmsg(LOG_ERR, __func__,
"failed to fork: %s", strerror(errno));
"failed to run %s: %s", argv[0], strerror(errno));
return;
} else if (pid) { /* parent */
int wstatus;
}
if (fd[0] != -1) { /* Send message to the child if any. */
ssize_t len;
struct script_msg *smp;
close(fd[0]);
TAILQ_FOREACH(smp, sm_head, sm_next) {
len = strlen(smp->sm_msg);
warnmsg(LOG_DEBUG, __func__,
"write to child = %s(%zd)",
smp->sm_msg, len);
if (write(fd[1], smp->sm_msg, len) != len) {
warnmsg(LOG_ERR, __func__,
"write to child failed: %s",
strerror(errno));
break;
}
if (sm_head != NULL) {
TAILQ_FOREACH(smp, sm_head, sm_next) {
len = strlen(smp->sm_msg);
warnmsg(LOG_DEBUG, __func__, "write to child = %s(%zd)",
smp->sm_msg, len);
if (write(wfd, smp->sm_msg, len) != len) {
warnmsg(LOG_ERR, __func__,
"write to child failed: %s",
strerror(errno));
break;
}
close(fd[1]);
}
do {
wpid = wait(&wstatus);
} while (wpid != pid && wpid > 0);
if (wpid < 0)
warnmsg(LOG_ERR, __func__,
"wait: %s", strerror(errno));
else
warnmsg(LOG_DEBUG, __func__,
"script \"%s\" terminated", scriptpath);
} else { /* child */
int nullfd;
char **_argv;
if (safefile(scriptpath)) {
warnmsg(LOG_ERR, __func__,
"script \"%s\" cannot be executed safely",
scriptpath);
exit(1);
}
nullfd = open("/dev/null", O_RDWR);
if (nullfd < 0) {
warnmsg(LOG_ERR, __func__,
"open /dev/null: %s", strerror(errno));
exit(1);
}
if (fd[0] != -1) { /* Receive message from STDIN if any. */
close(fd[1]);
if (fd[0] != STDIN_FILENO) {
/* Connect a pipe read-end to child's STDIN. */
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
warnmsg(LOG_ERR, __func__,
"dup2 STDIN: %s", strerror(errno));
exit(1);
}
close(fd[0]);
}
} else
dup2(nullfd, STDIN_FILENO);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
if (nullfd > STDERR_FILENO)
close(nullfd);
_argv = malloc(sizeof(*_argv) * argc);
if (_argv == NULL) {
warnmsg(LOG_ERR, __func__,
"malloc: %s", strerror(errno));
exit(1);
}
memcpy(_argv, argv, (size_t)argc);
execv(scriptpath, (char *const *)_argv);
warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
strerror(errno));
exit(1);
}
return;
}
(void)close(wfd);
static int
safefile(const char *path)
{
struct stat s;
uid_t myuid;
/* no setuid */
if (getuid() != geteuid()) {
warnmsg(LOG_NOTICE, __func__,
"setuid'ed execution not allowed\n");
return (-1);
}
if (lstat(path, &s) != 0) {
warnmsg(LOG_NOTICE, __func__, "lstat failed: %s",
strerror(errno));
return (-1);
}
/* the file must be owned by the running uid */
myuid = getuid();
if (s.st_uid != myuid) {
warnmsg(LOG_NOTICE, __func__,
"%s has invalid owner uid\n", path);
return (-1);
}
switch (s.st_mode & S_IFMT) {
case S_IFREG:
break;
default:
warnmsg(LOG_NOTICE, __func__,
"%s is an invalid file type 0x%o\n",
path, (s.st_mode & S_IFMT));
return (-1);
}
return (0);
if (cap_script_wait(capscript, &status) != 0)
warnmsg(LOG_ERR, __func__, "wait(): %s", strerror(errno));
else
warnmsg(LOG_DEBUG, __func__, "script \"%s\" status %d",
argv[0], status);
}
/* Decode domain name label encoding in RFC 1035 Section 3.1 */

View File

@ -34,6 +34,8 @@
*/
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/event.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@ -47,23 +49,27 @@
#include <netinet6/nd6.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <capsicum_helpers.h>
#include <err.h>
#include <stdarg.h>
#include <errno.h>
#include <ifaddrs.h>
#include <poll.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <libcasper.h>
#include <casper/cap_syslog.h>
#include <libutil.h>
#include "rtsold.h"
#define RTSOL_DUMPFILE "/var/run/rtsold.dump";
#define RTSOL_PIDFILE "/var/run/rtsold.pid";
#define RTSOL_DUMPFILE "/var/run/rtsold.dump"
struct timespec tm_max;
static int log_upto = 999;
@ -77,6 +83,8 @@ int uflag = 0;
const char *otherconf_script;
const char *resolvconf_script = "/sbin/resolvconf";
cap_channel_t *capllflags, *capscript, *capsendmsg, *capsyslog;
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
@ -90,43 +98,40 @@ const char *resolvconf_script = "/sbin/resolvconf";
/* static variables and functions */
static int mobile_node = 0;
static const char *pidfilename = RTSOL_PIDFILE;
#ifndef SMALL
static int do_dump;
static const char *dumpfilename = RTSOL_DUMPFILE;
#endif
static sig_atomic_t do_dump, do_exit;
static struct pidfh *pfh;
static char **autoifprobe(void);
static int ifconfig(char *ifname);
static int init_capabilities(void);
static int make_packet(struct ifinfo *);
static struct timespec *rtsol_check_timer(void);
#ifndef SMALL
static void rtsold_set_dump_file(int);
#endif
static void usage(void);
static void set_dumpfile(int);
static void set_exit(int);
static void usage(const char *progname);
int
main(int argc, char **argv)
{
int s, ch, once = 0;
struct kevent events[2];
FILE *dumpfp;
struct ifinfo *ifi;
struct timespec *timeout;
const char *opts;
struct pollfd set[2];
int rtsock;
char *argv0;
const char *opts, *pidfilepath, *progname;
int ch, error, kq, once, rcvsock, rtsock;
#ifndef SMALL
/* rtsold */
opts = "adDfFm1O:p:R:u";
#else
/* rtsol */
opts = "adDFO:R:u";
fflag = 1;
once = 1;
#endif
argv0 = argv[0];
progname = basename(argv[0]);
if (strcmp(progname, "rtsold") == 0) {
opts = "adDfFm1O:p:R:u";
once = 0;
pidfilepath = NULL;
} else {
opts = "adDFO:R:u";
fflag = 1;
once = 1;
}
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
@ -155,7 +160,7 @@ main(int argc, char **argv)
otherconf_script = optarg;
break;
case 'p':
pidfilename = optarg;
pidfilepath = optarg;
break;
case 'R':
resolvconf_script = optarg;
@ -164,17 +169,14 @@ main(int argc, char **argv)
uflag = 1;
break;
default:
usage();
exit(1);
usage(progname);
}
}
argc -= optind;
argv += optind;
if ((!aflag && argc == 0) || (aflag && argc != 0)) {
usage();
exit(1);
}
if ((!aflag && argc == 0) || (aflag && argc != 0))
usage(progname);
/* Generate maximum time in timespec. */
tm_max.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1));
@ -188,64 +190,62 @@ main(int argc, char **argv)
else
log_upto = LOG_NOTICE;
if (!fflag) {
char *ident;
ident = strrchr(argv0, '/');
if (!ident)
ident = argv0;
else
ident++;
openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
if (log_upto >= 0)
setlogmask(LOG_UPTO(log_upto));
}
if (otherconf_script && *otherconf_script != '/') {
if (otherconf_script != NULL && *otherconf_script != '/')
errx(1, "configuration script (%s) must be an absolute path",
otherconf_script);
}
if (resolvconf_script && *resolvconf_script != '/') {
if (*resolvconf_script != '/')
errx(1, "configuration script (%s) must be an absolute path",
resolvconf_script);
}
if (pidfilename && *pidfilename != '/') {
errx(1, "pid filename (%s) must be an absolute path",
pidfilename);
if (!fflag) {
pfh = pidfile_open(pidfilepath, 0644, NULL);
if (pfh == NULL)
errx(1, "failed to open pidfile: %s", strerror(errno));
if (daemon(0, 0) != 0)
errx(1, "failed to daemonize");
}
#ifndef SMALL
/* initialization to dump internal status to a file */
signal(SIGUSR1, rtsold_set_dump_file);
#endif
if ((error = init_capabilities()) != 0)
err(1, "failed to initialize capabilities");
if (!fflag)
daemon(0, 0); /* act as a daemon */
if (!fflag) {
cap_openlog(capsyslog, progname, LOG_NDELAY | LOG_PID,
LOG_DAEMON);
if (log_upto >= 0)
(void)cap_setlogmask(capsyslog, LOG_UPTO(log_upto));
(void)signal(SIGTERM, set_exit);
(void)signal(SIGINT, set_exit);
(void)signal(SIGUSR1, set_dumpfile);
dumpfp = rtsold_init_dumpfile(RTSOL_DUMPFILE);
} else
dumpfp = NULL;
/*
* Open a socket for sending RS and receiving RA.
* This should be done before calling ifinit(), since the function
* uses the socket.
*/
if ((s = sockopen()) < 0) {
warnmsg(LOG_ERR, __func__, "failed to open a socket");
kq = kqueue();
if (kq < 0) {
warnmsg(LOG_ERR, __func__, "failed to create a kqueue: %s",
strerror(errno));
exit(1);
}
set[0].fd = s;
set[0].events = POLLIN;
set[1].fd = -1;
/* Open global sockets and register for read events. */
if ((rtsock = rtsock_open()) < 0) {
warnmsg(LOG_ERR, __func__, "failed to open a socket");
warnmsg(LOG_ERR, __func__, "failed to open routing socket");
exit(1);
}
if ((rcvsock = recvsockopen()) < 0) {
warnmsg(LOG_ERR, __func__, "failed to open receive socket");
exit(1);
}
EV_SET(&events[0], rtsock, EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&events[1], rcvsock, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, events, 2, NULL, 0, NULL) < 0) {
warnmsg(LOG_ERR, __func__, "kevent(): %s", strerror(errno));
exit(1);
}
set[1].fd = rtsock;
set[1].events = POLLIN;
/* configuration per interface */
if (ifinit()) {
warnmsg(LOG_ERR, __func__,
"failed to initialize interfaces");
/* Probe network interfaces and set up tracking info. */
if (ifinit() != 0) {
warnmsg(LOG_ERR, __func__, "failed to initialize interfaces");
exit(1);
}
if (aflag)
@ -259,42 +259,37 @@ main(int argc, char **argv)
argv++;
}
/* setup for probing default routers */
if (probe_init()) {
/* Write to our pidfile. */
if (pfh != NULL && pidfile_write(pfh) != 0) {
warnmsg(LOG_ERR, __func__,
"failed to setup for probing routers");
"failed to open pidfile: %s", strerror(errno));
exit(1);
/*NOTREACHED*/
}
/* dump the current pid */
if (!once) {
pid_t pid = getpid();
FILE *fp;
/* Enter capability mode. */
caph_cache_catpages();
if (caph_enter_casper() != 0) {
warnmsg(LOG_ERR, __func__, "caph_enter(): %s", strerror(errno));
exit(1);
}
if ((fp = fopen(pidfilename, "w")) == NULL)
warnmsg(LOG_ERR, __func__,
"failed to open a pid log file(%s): %s",
pidfilename, strerror(errno));
else {
fprintf(fp, "%d\n", pid);
fclose(fp);
for (;;) {
if (do_exit) {
/* Handle SIGTERM, SIGINT. */
if (pfh != NULL)
pidfile_remove(pfh);
break;
}
}
while (1) { /* main loop */
int e;
#ifndef SMALL
if (do_dump) { /* SIGUSR1 */
if (do_dump) {
/* Handle SIGUSR1. */
do_dump = 0;
rtsold_dump_file(dumpfilename);
if (dumpfp != NULL)
rtsold_dump(dumpfp);
}
#endif
timeout = rtsol_check_timer();
if (once) {
struct ifinfo *ifi;
/* if we have no timeout, we are done (or failed) */
if (timeout == NULL)
break;
@ -307,26 +302,64 @@ main(int argc, char **argv)
if (ifi == NULL)
break;
}
e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM);
if (e < 1) {
if (e < 0 && errno != EINTR) {
warnmsg(LOG_ERR, __func__, "select: %s",
error = kevent(kq, NULL, 0, &events[0], 1, timeout);
if (error < 1) {
if (error < 0 && errno != EINTR)
warnmsg(LOG_ERR, __func__, "kevent(): %s",
strerror(errno));
}
continue;
}
/* packet reception */
if (set[1].revents & POLLIN)
if (events[0].ident == (uintptr_t)rtsock)
rtsock_input(rtsock);
if (set[0].revents & POLLIN)
rtsol_input(s);
else
rtsol_input(rcvsock);
}
/* NOTREACHED */
return (0);
}
static int
init_capabilities(void)
{
#ifdef WITH_CASPER
const char *const scripts[2] = { resolvconf_script, otherconf_script };
cap_channel_t *capcasper;
nvlist_t *limits;
capcasper = cap_init();
if (capcasper == NULL)
return (-1);
capllflags = cap_service_open(capcasper, "rtsold.llflags");
if (capllflags == NULL)
return (-1);
capscript = cap_service_open(capcasper, "rtsold.script");
if (capscript == NULL)
return (-1);
limits = nvlist_create(0);
nvlist_add_string_array(limits, "scripts", scripts,
otherconf_script != NULL ? 2 : 1);
if (cap_limit_set(capscript, limits) != 0)
return (-1);
capsendmsg = cap_service_open(capcasper, "rtsold.sendmsg");
if (capsendmsg == NULL)
return (-1);
if (!fflag) {
capsyslog = cap_service_open(capcasper, "system.syslog");
if (capsyslog == NULL)
return (-1);
}
cap_close(capcasper);
#endif /* WITH_CASPER */
return (0);
}
static int
ifconfig(char *ifname)
{
@ -501,7 +534,7 @@ rtsol_check_timer(void)
struct ifinfo *ifi;
struct rainfo *rai;
struct ra_opt *rao, *raotmp;
int flags;
int error, flags;
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
@ -568,18 +601,23 @@ rtsol_check_timer(void)
*/
if (probe)
ifi->otherconfig = 0;
if (probe && mobile_node)
defrouter_probe(ifi);
if (probe && mobile_node) {
error = cap_probe_defrouters(capsendmsg,
ifi);
if (error != 0)
warnmsg(LOG_DEBUG, __func__,
"failed to probe routers: %d",
error);
}
break;
}
case IFS_DELAY:
ifi->state = IFS_PROBE;
sendpacket(ifi);
(void)cap_rssend(capsendmsg, ifi);
break;
case IFS_PROBE:
if (ifi->probes < MAX_RTR_SOLICITATIONS)
sendpacket(ifi);
(void)cap_rssend(capsendmsg, ifi);
else {
warnmsg(LOG_INFO, __func__,
"No answer after sending %d RSs",
@ -660,10 +698,9 @@ rtsol_timer_update(struct ifinfo *ifi)
ifi->timer.tv_sec = 1;
break;
case IFS_IDLE:
if (mobile_node) {
if (mobile_node)
/* XXX should be configurable */
ifi->timer.tv_sec = 3;
}
else
ifi->timer = tm_max; /* stop timer(valid?) */
break;
@ -675,7 +712,7 @@ rtsol_timer_update(struct ifinfo *ifi)
case IFS_PROBE:
if (ifi->probes < MAX_RTR_SOLICITATIONS)
ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
else {
else
/*
* After sending MAX_RTR_SOLICITATIONS solicitations,
* we're just waiting for possible replies; there
@ -684,7 +721,6 @@ rtsol_timer_update(struct ifinfo *ifi)
* on RFC 2461, Section 6.3.7.
*/
ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
}
break;
default:
warnmsg(LOG_ERR, __func__,
@ -711,28 +747,36 @@ rtsol_timer_update(struct ifinfo *ifi)
#undef MILLION
}
#ifndef SMALL
static void
rtsold_set_dump_file(int sig __unused)
set_dumpfile(int sig __unused)
{
do_dump = 1;
}
#endif
static void
usage(void)
set_exit(int sig __unused)
{
#ifndef SMALL
fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
"[-p pidfile] [-R script-name] interface ...\n");
fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
"[-p pidfile] [-R script-name] -a\n");
#else
fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
"[-p pidfile] [-R script-name] interface ...\n");
fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
"[-p pidfile] [-R script-name] -a\n");
#endif
do_exit = 1;
}
static void
usage(const char *progname)
{
if (strcmp(progname, "rtsold") == 0) {
fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
"[-p pidfile] [-R script-name] interface ...\n");
fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] "
"[-p pidfile] [-R script-name] -a\n");
} else {
fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
"[-p pidfile] [-R script-name] interface ...\n");
fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
"[-p pidfile] [-R script-name] -a\n");
}
exit(1);
}
void
@ -750,7 +794,7 @@ warnmsg(int priority, const char *func, const char *msg, ...)
} else {
snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
msg = buf;
vsyslog(priority, msg, ap);
cap_vsyslog(capsyslog, priority, msg, ap);
}
va_end(ap);
}

View File

@ -60,12 +60,13 @@ struct rainfo {
TAILQ_HEAD(, ra_opt) rai_ra_opt;
};
/* Per-interface tracking info. */
struct ifinfo {
TAILQ_ENTRY(ifinfo) ifi_next; /* pointer to the next interface */
TAILQ_ENTRY(ifinfo) ifi_next; /* pointer to the next interface */
struct sockaddr_dl *sdl; /* link-layer address */
char ifname[IFNAMSIZ]; /* interface name */
u_int32_t linkid; /* link ID of this interface */
uint32_t linkid; /* link ID of this interface */
int active; /* interface status */
int probeinterval; /* interval of probe timer (if necessary) */
int probetimer; /* rest of probe timer */
@ -77,7 +78,6 @@ struct ifinfo {
int dadcount;
struct timespec timer;
struct timespec expire;
int errors; /* # of errors we've got - detect wedge */
#define IFI_DNSOPT_STATE_NOINFO 0
#define IFI_DNSOPT_STATE_RECEIVED 1
int ifi_rdnss; /* RDNSS option state */
@ -150,6 +150,7 @@ extern TAILQ_HEAD(ifinfo_head_t, ifinfo) ifinfo_head;
} while (0)
/* rtsold.c */
struct cap_channel;
extern struct timespec tm_max;
extern int dflag;
extern int aflag;
@ -157,6 +158,8 @@ extern int Fflag;
extern int uflag;
extern const char *otherconf_script;
extern const char *resolvconf_script;
extern struct cap_channel *capllflags, *capscript, *capsendmsg, *capsyslog;
struct ifinfo *find_ifinfo(int);
struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *);
void rtsol_timer_update(struct ifinfo *);
@ -165,6 +168,7 @@ extern void warnmsg(int, const char *, const char *, ...)
extern int ra_opt_handler(struct ifinfo *);
/* if.c */
struct nd_opt_hdr;
extern int ifinit(void);
extern int interface_up(char *);
extern int interface_status(struct ifinfo *);
@ -173,17 +177,23 @@ extern void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
extern struct sockaddr_dl *if_nametosdl(char *);
/* rtsol.c */
extern int rssock;
extern int sockopen(void);
extern void sendpacket(struct ifinfo *);
extern int recvsockopen(void);
extern void rtsol_input(int);
/* probe.c */
extern int probe_init(void);
extern void defrouter_probe(struct ifinfo *);
/* cap_llflags.c */
extern int cap_llflags_get(struct cap_channel *, const char *, int *);
/* cap_script.c */
extern int cap_script_run(struct cap_channel *, const char *const *);
extern int cap_script_wait(struct cap_channel *, int *);
/* cap_sendmsg.c */
extern int cap_probe_defrouters(struct cap_channel *, struct ifinfo *);
extern int cap_rssend(struct cap_channel *, struct ifinfo *);
/* dump.c */
extern void rtsold_dump_file(const char *);
extern FILE *rtsold_init_dumpfile(const char *);
extern void rtsold_dump(FILE *);
extern const char *sec2str(const struct timespec *);
/* rtsock.c */