summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rtsold
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/rtsold')
-rw-r--r--usr.sbin/rtsold/Makefile2
-rw-r--r--usr.sbin/rtsold/dump.c40
-rw-r--r--usr.sbin/rtsold/if.c58
-rw-r--r--usr.sbin/rtsold/probe.c9
-rw-r--r--usr.sbin/rtsold/rtsock.c8
-rw-r--r--usr.sbin/rtsold/rtsol.c503
-rw-r--r--usr.sbin/rtsold/rtsold.829
-rw-r--r--usr.sbin/rtsold/rtsold.c396
-rw-r--r--usr.sbin/rtsold/rtsold.h51
9 files changed, 777 insertions, 319 deletions
diff --git a/usr.sbin/rtsold/Makefile b/usr.sbin/rtsold/Makefile
index efc322c..9e2b480 100644
--- a/usr.sbin/rtsold/Makefile
+++ b/usr.sbin/rtsold/Makefile
@@ -19,7 +19,7 @@ MAN= rtsold.8
MLINKS= rtsold.8 rtsol.8
SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c
-WARNS?= 3
+WARNS?= 6
CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H
DPADD= ${LIBKVM}
LDADD= -lkvm
diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c
index 2756b87..2e6b4ca 100644
--- a/usr.sbin/rtsold/dump.c
+++ b/usr.sbin/rtsold/dump.c
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/queue.h>
#include <net/if.h>
#include <netinet/in.h>
@@ -58,41 +59,41 @@ static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENT
static void
dump_interface_status(void)
{
- struct ifinfo *ifinfo;
+ struct ifinfo *ifi;
struct timeval now;
gettimeofday(&now, NULL);
- for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
- fprintf(fp, "Interface %s\n", ifinfo->ifname);
+ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
+ fprintf(fp, "Interface %s\n", ifi->ifname);
fprintf(fp, " probe interval: ");
- if (ifinfo->probeinterval) {
- fprintf(fp, "%d\n", ifinfo->probeinterval);
- fprintf(fp, " probe timer: %d\n", ifinfo->probetimer);
+ if (ifi->probeinterval) {
+ fprintf(fp, "%d\n", ifi->probeinterval);
+ fprintf(fp, " probe timer: %d\n", ifi->probetimer);
} else {
fprintf(fp, "infinity\n");
fprintf(fp, " no probe timer\n");
}
fprintf(fp, " interface status: %s\n",
- ifinfo->active > 0 ? "active" : "inactive");
+ ifi->active > 0 ? "active" : "inactive");
fprintf(fp, " other config: %s\n",
- ifinfo->otherconfig ? "on" : "off");
- fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]);
+ ifi->otherconfig ? "on" : "off");
+ fprintf(fp, " rtsold status: %s\n", ifstatstr[ifi->state]);
fprintf(fp, " carrier detection: %s\n",
- ifinfo->mediareqok ? "available" : "unavailable");
+ ifi->mediareqok ? "available" : "unavailable");
fprintf(fp, " probes: %d, dadcount = %d\n",
- ifinfo->probes, ifinfo->dadcount);
- if (ifinfo->timer.tv_sec == tm_max.tv_sec &&
- ifinfo->timer.tv_usec == tm_max.tv_usec)
+ ifi->probes, ifi->dadcount);
+ if (ifi->timer.tv_sec == tm_max.tv_sec &&
+ ifi->timer.tv_usec == tm_max.tv_usec)
fprintf(fp, " no timer\n");
else {
fprintf(fp, " timer: interval=%d:%d, expire=%s\n",
- (int)ifinfo->timer.tv_sec,
- (int)ifinfo->timer.tv_usec,
- (ifinfo->expire.tv_sec < now.tv_sec) ? "expired"
- : sec2str(ifinfo->expire.tv_sec - now.tv_sec));
+ (int)ifi->timer.tv_sec,
+ (int)ifi->timer.tv_usec,
+ (ifi->expire.tv_sec < now.tv_sec) ? "expired"
+ : sec2str(ifi->expire.tv_sec - now.tv_sec));
}
- fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt);
+ fprintf(fp, " number of valid RAs: %d\n", ifi->racnt);
}
}
@@ -145,5 +146,6 @@ sec2str(time_t total)
p += n;
}
snprintf(p, ep - p, "%ds", secs);
- return(result);
+
+ return (result);
}
diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c
index c555d2a..58ec514 100644
--- a/usr.sbin/rtsold/if.c
+++ b/usr.sbin/rtsold/if.c
@@ -91,25 +91,25 @@ interface_up(char *name)
if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s",
strerror(errno));
- return(-1);
+ return (-1);
}
if (!(ifr.ifr_flags & IFF_UP)) {
ifr.ifr_flags |= IFF_UP;
if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
warnmsg(LOG_ERR, __func__,
"ioctl(SIOCSIFFLAGS): %s", strerror(errno));
- return(-1);
+ return (-1);
}
if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
warnmsg(LOG_WARNING, __func__, "socket(AF_INET6, SOCK_DGRAM): %s",
strerror(errno));
- return(-1);
+ return (-1);
}
if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s",
strerror(errno));
close(s);
- return(-1);
+ return (-1);
}
warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name);
@@ -122,13 +122,13 @@ interface_up(char *name)
"ioctl(SIOCSIFINFO_IN6): %s",
strerror(errno));
close(s);
- return(-1);
+ return (-1);
}
} else {
warnmsg(LOG_WARNING, __func__,
"%s is disabled.", name);
close(s);
- return(-1);
+ return (-1);
}
}
if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) {
@@ -139,13 +139,13 @@ interface_up(char *name)
"ioctl(SIOCSIFINFO_IN6): %s",
strerror(errno));
close(s);
- return(-1);
+ return (-1);
}
} else {
warnmsg(LOG_WARNING, __func__,
"%s does not accept Router Advertisement.", name);
close(s);
- return(-1);
+ return (-1);
}
}
close(s);
@@ -154,22 +154,22 @@ interface_up(char *name)
if (llflag < 0) {
warnmsg(LOG_WARNING, __func__,
"get_llflag() failed, anyway I'll try");
- return 0;
+ return (0);
}
if (!(llflag & IN6_IFF_NOTREADY)) {
warnmsg(LOG_DEBUG, __func__, "%s is ready", name);
- return(0);
+ return (0);
} else {
if (llflag & IN6_IFF_TENTATIVE) {
warnmsg(LOG_DEBUG, __func__, "%s is tentative",
name);
- return IFS_TENTATIVE;
+ return (IFS_TENTATIVE);
}
if (llflag & IN6_IFF_DUPLICATED)
warnmsg(LOG_DEBUG, __func__, "%s is duplicated",
name);
- return -1;
+ return (-1);
}
}
@@ -186,16 +186,14 @@ interface_status(struct ifinfo *ifinfo)
if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s",
ifname, strerror(errno));
- return(-1);
+ return (-1);
}
/*
* if one of UP and RUNNING flags is dropped,
* the interface is not active.
*/
- if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
goto inactive;
- }
-
/* Next, check carrier on the interface, if possible */
if (!ifinfo->mediareqok)
goto active;
@@ -232,10 +230,10 @@ interface_status(struct ifinfo *ifinfo)
}
inactive:
- return(0);
+ return (0);
active:
- return(1);
+ return (1);
}
#define ROUNDUP(a, size) \
@@ -254,9 +252,9 @@ lladdropt_length(struct sockaddr_dl *sdl)
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
- return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ return (ROUNDUP8(ETHER_ADDR_LEN + 2));
default:
- return(0);
+ return (0);
}
}
@@ -301,7 +299,7 @@ if_nametosdl(char *name)
return(NULL);
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
free(buf);
- return(NULL);
+ return (NULL);
}
lim = buf + len;
@@ -327,17 +325,17 @@ if_nametosdl(char *name)
if (next == lim) {
/* search failed */
free(buf);
- return(NULL);
+ return (NULL);
}
if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) {
free(buf);
- return(NULL);
+ return (NULL);
}
memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
free(buf);
- return(ret_sdl);
+ return (ret_sdl);
}
int
@@ -350,9 +348,9 @@ getinet6sysctl(int code)
mib[3] = code;
size = sizeof(value);
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
- return -1;
+ return (-1);
else
- return value;
+ return (value);
}
int
@@ -366,9 +364,9 @@ setinet6sysctl(int code, int newval)
size = sizeof(value);
if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size,
&newval, sizeof(newval)) < 0)
- return -1;
+ return (-1);
else
- return value;
+ return (value);
}
/*------------------------------------------------------------*/
@@ -414,12 +412,12 @@ get_llflag(const char *name)
freeifaddrs(ifap);
close(s);
- return ifr6.ifr_ifru.ifru_flags6;
+ return (ifr6.ifr_ifru.ifru_flags6);
}
freeifaddrs(ifap);
close(s);
- return -1;
+ return (-1);
}
diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c
index 6d0ea79..657ae40 100644
--- a/usr.sbin/rtsold/probe.c
+++ b/usr.sbin/rtsold/probe.c
@@ -72,18 +72,18 @@ probe_init(void)
if (sndcmsgbuf == NULL &&
(sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) {
warnmsg(LOG_ERR, __func__, "malloc failed");
- return(-1);
+ return (-1);
}
if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
- return(-1);
+ return (-1);
}
/* make the socket send-only */
if (shutdown(probesock, 0)) {
warnmsg(LOG_ERR, __func__, "shutdown: %s", strerror(errno));
- return(-1);
+ return (-1);
}
/* initialize msghdr for sending packets */
@@ -92,7 +92,8 @@ probe_init(void)
sndmhdr.msg_iovlen = 1;
sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
sndmhdr.msg_controllen = scmsglen;
- return(0);
+
+ return (0);
}
/*
diff --git a/usr.sbin/rtsold/rtsock.c b/usr.sbin/rtsold/rtsock.c
index 726c1e6..fe22bc7 100644
--- a/usr.sbin/rtsold/rtsock.c
+++ b/usr.sbin/rtsold/rtsock.c
@@ -83,7 +83,7 @@ int
rtsock_open(void)
{
- return socket(PF_ROUTE, SOCK_RAW, 0);
+ return (socket(PF_ROUTE, SOCK_RAW, 0));
}
int
@@ -130,7 +130,7 @@ rtsock_input(int s)
}
}
- return ret;
+ return (ret);
}
#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
@@ -142,7 +142,7 @@ rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim)
ifan = (struct if_announcemsghdr *)rtm;
if ((char *)(ifan + 1) > lim)
- return -1;
+ return (-1);
switch (ifan->ifan_what) {
case IFAN_ARRIVAL:
@@ -170,6 +170,6 @@ rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim)
break;
}
- return 0;
+ return (0);
}
#endif
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
index be2a9b8..afa935e 100644
--- a/usr.sbin/rtsold/rtsol.c
+++ b/usr.sbin/rtsold/rtsol.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * Copyright (C) 2011 Hiroki Sato
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,13 +44,16 @@
#include <net/route.h>
#include <net/if_dl.h>
+#define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */
#include <netinet/in.h>
+#undef __BSD_VISIBLE
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
@@ -61,8 +65,6 @@
#include <syslog.h>
#include "rtsold.h"
-#define ALLROUTER "ff02::2"
-
static struct msghdr rcvmhdr;
static struct msghdr sndmhdr;
static struct iovec rcviov[2];
@@ -71,15 +73,40 @@ static struct sockaddr_in6 from;
static int rcvcmsglen;
int rssock;
+struct ifinfo_head_t ifinfo_head =
+ TAILQ_HEAD_INITIALIZER(ifinfo_head);
-static struct sockaddr_in6 sin6_allrouters = {
+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(char *, char *);
+static void call_script(const int, const char *const *, void *);
+static size_t dname_labeldec(char *, size_t, const char *);
static int safefile(const char *);
+#define _ARGS_OTHER otherconf_script, ifi->ifname
+#define _ARGS_RESADD resolvconf_script, "-a", ifi->ifname
+#define _ARGS_RESDEL resolvconf_script, "-d", ifi->ifname
+
+#define CALL_SCRIPT(name, sm_head) \
+ do { \
+ const char *const sarg[] = { _ARGS_##name, NULL }; \
+ call_script(sizeof(sarg), sarg, sm_head); \
+ } while(0)
+
+#define ELM_MALLOC(p,error_action) \
+ do { \
+ p = malloc(sizeof(*p)); \
+ if (p == NULL) { \
+ warnmsg(LOG_ERR, __func__, "malloc failed: %s", \
+ strerror(errno)); \
+ error_action; \
+ } \
+ memset(p, 0, sizeof(*p)); \
+ } while(0)
+
int
sockopen(void)
{
@@ -93,63 +120,35 @@ sockopen(void)
if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
warnmsg(LOG_ERR, __func__,
"malloc for receive msghdr failed");
- return(-1);
+ return (-1);
}
if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
warnmsg(LOG_ERR, __func__,
"malloc for send msghdr failed");
- return(-1);
- }
- memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
- sin6_allrouters.sin6_family = AF_INET6;
- sin6_allrouters.sin6_len = sizeof(sin6_allrouters);
- if (inet_pton(AF_INET6, ALLROUTER,
- &sin6_allrouters.sin6_addr.s6_addr) != 1) {
- warnmsg(LOG_ERR, __func__, "inet_pton failed for %s",
- ALLROUTER);
- return(-1);
+ return (-1);
}
-
if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
- return(-1);
+ return (-1);
}
/* specify to tell receiving interface */
on = 1;
-#ifdef IPV6_RECVPKTINFO
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
sizeof(on)) < 0) {
warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s",
strerror(errno));
exit(1);
}
-#else /* old adv. API */
- if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
- sizeof(on)) < 0) {
- warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s",
- strerror(errno));
- exit(1);
- }
-#endif
- on = 1;
/* specify to tell value of hoplimit field of received IP6 hdr */
-#ifdef IPV6_RECVHOPLIMIT
+ on = 1;
if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
sizeof(on)) < 0) {
warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s",
strerror(errno));
exit(1);
}
-#else /* old adv. API */
- if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
- sizeof(on)) < 0) {
- warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s",
- strerror(errno));
- exit(1);
- }
-#endif
/* specfiy to accept only router advertisements on the socket */
ICMP6_FILTER_SETBLOCKALL(&filt);
@@ -176,11 +175,11 @@ sockopen(void)
sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
sndmhdr.msg_controllen = sndcmsglen;
- return(rssock);
+ return (rssock);
}
void
-sendpacket(struct ifinfo *ifinfo)
+sendpacket(struct ifinfo *ifi)
{
struct in6_pktinfo *pi;
struct cmsghdr *cm;
@@ -189,11 +188,11 @@ sendpacket(struct ifinfo *ifinfo)
struct sockaddr_in6 dst;
dst = sin6_allrouters;
- dst.sin6_scope_id = ifinfo->linkid;
+ dst.sin6_scope_id = ifi->linkid;
sndmhdr.msg_name = (caddr_t)&dst;
- sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
- sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
+ 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 */
@@ -202,7 +201,7 @@ sendpacket(struct ifinfo *ifinfo)
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pi = (struct in6_pktinfo *)CMSG_DATA(cm);
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
- pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
+ pi->ipi6_ifindex = ifi->sdl->sdl_index;
/* specify the hop limit of the packet */
cm = CMSG_NXTHDR(&sndmhdr, cm);
@@ -213,38 +212,50 @@ sendpacket(struct ifinfo *ifinfo)
warnmsg(LOG_DEBUG, __func__,
"send RS on %s, whose state is %d",
- ifinfo->ifname, ifinfo->state);
+ ifi->ifname, ifi->state);
i = sendmsg(rssock, &sndmhdr, 0);
- if (i < 0 || (size_t)i != ifinfo->rs_datalen) {
+ 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",
- ifinfo->ifname, strerror(errno));
+ ifi->ifname, strerror(errno));
}
/* update counter */
- ifinfo->probes++;
+ ifi->probes++;
}
void
rtsol_input(int s)
{
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
- int ifindex = 0, *hlimp = NULL;
- ssize_t i;
+ int l, ifindex = 0, *hlimp = NULL;
+ ssize_t msglen;
struct in6_pktinfo *pi = NULL;
struct ifinfo *ifi = NULL;
+ struct ra_opt *rao = NULL;
struct icmp6_hdr *icp;
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
+ char *raoptp;
+ char *p;
+ struct in6_addr *addr;
+ struct nd_opt_hdr *ndo;
+ struct nd_opt_rdnss *rdnss;
+ struct nd_opt_dnssl *dnssl;
+ size_t len;
+ char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
+ char dname[NI_MAXHOST];
+ struct timeval now;
+ struct timeval lifetime;
/* get message. namelen and controllen must always be initialized. */
rcvmhdr.msg_namelen = sizeof(from);
rcvmhdr.msg_controllen = rcvcmsglen;
- if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) {
warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
return;
}
@@ -275,9 +286,9 @@ rtsol_input(int s)
return;
}
- if ((size_t)i < sizeof(struct nd_router_advert)) {
+ if ((size_t)msglen < sizeof(struct nd_router_advert)) {
warnmsg(LOG_INFO, __func__,
- "packet size(%zd) is too short", i);
+ "packet size(%zd) is too short", msglen);
return;
}
@@ -354,9 +365,157 @@ rtsol_input(int s)
warnmsg(LOG_DEBUG, __func__,
"OtherConfigFlag on %s is turned on", ifi->ifname);
ifi->otherconfig = 1;
- call_script(otherconf_script, ifi->ifname);
+ CALL_SCRIPT(OTHER, NULL);
}
+ /* Initialize ra_opt per-interface structure. */
+ gettimeofday(&now, NULL);
+ if (!TAILQ_EMPTY(&ifi->ifi_ra_opt))
+ while ((rao = TAILQ_FIRST(&ifi->ifi_ra_opt)) != NULL) {
+ if (rao->rao_msg != NULL)
+ free(rao->rao_msg);
+ TAILQ_REMOVE(&ifi->ifi_ra_opt, rao, rao_next);
+ free(rao);
+ }
+ else
+ TAILQ_INIT(&ifi->ifi_ra_opt);
+
+#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \
+ (((struct nd_opt_hdr *)x)->nd_opt_len * 8))
+
+ /* Process RA options. */
+ warnmsg(LOG_DEBUG, __func__, "Processing RA");
+ raoptp = (char *)icp + sizeof(struct nd_router_advert);
+ while (raoptp < (char *)icp + msglen) {
+ ndo = (struct nd_opt_hdr *)raoptp;
+ warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp);
+ warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d",
+ ndo->nd_opt_type);
+ warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d",
+ ndo->nd_opt_len);
+
+ switch (ndo->nd_opt_type) {
+ case ND_OPT_RDNSS:
+ rdnss = (struct nd_opt_rdnss *)raoptp;
+
+ /* Optlen sanity check (Section 5.3.1 in RFC 6106) */
+ if (rdnss->nd_opt_rdnss_len < 3) {
+ warnmsg(LOG_INFO, __func__,
+ "too short RDNSS option"
+ "in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ break;
+ }
+
+ addr = (struct in6_addr *)(raoptp + sizeof(*rdnss));
+ while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) {
+ if (inet_ntop(AF_INET6, addr, ntopbuf,
+ INET6_ADDRSTRLEN) == NULL) {
+ warnmsg(LOG_INFO, __func__,
+ "an invalid address in RDNSS option"
+ " in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ addr++;
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ /* XXX: % has to be escaped here */
+ l = snprintf(nsbuf, sizeof(nsbuf),
+ "%s%c%s", ntopbuf,
+ SCOPE_DELIMITER,
+ ifi->ifname);
+ else
+ l = snprintf(nsbuf, sizeof(nsbuf),
+ "%s", ntopbuf);
+ if (l < 0 || (size_t)l >= sizeof(nsbuf)) {
+ warnmsg(LOG_ERR, __func__,
+ "address copying error in "
+ "RDNSS option: %d.", l);
+ addr++;
+ continue;
+ }
+ warnmsg(LOG_DEBUG, __func__, "nsbuf = %s",
+ nsbuf);
+
+ ELM_MALLOC(rao, break);
+ rao->rao_type = ndo->nd_opt_type;
+ rao->rao_len = strlen(nsbuf);
+ rao->rao_msg = strdup(nsbuf);
+ if (rao->rao_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(rao);
+ addr++;
+ continue;
+ }
+ /* Set expiration timer */
+ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire));
+ memset(&lifetime, 0, sizeof(lifetime));
+ lifetime.tv_sec = ntohl(rdnss->nd_opt_rdnss_lifetime);
+ timeradd(&now, &lifetime, &rao->rao_expire);
+
+ TAILQ_INSERT_TAIL(&ifi->ifi_ra_opt, rao, rao_next);
+ addr++;
+ }
+ break;
+ case ND_OPT_DNSSL:
+ dnssl = (struct nd_opt_dnssl *)raoptp;
+
+ /* Optlen sanity check (Section 5.3.1 in RFC 6106) */
+ if (dnssl->nd_opt_dnssl_len < 2) {
+ warnmsg(LOG_INFO, __func__,
+ "too short DNSSL option"
+ "in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ break;
+ }
+
+ /*
+ * Ensure NUL-termination in DNSSL in case of
+ * malformed field.
+ */
+ p = (char *)RA_OPT_NEXT_HDR(raoptp);
+ *(p - 1) = '\0';
+
+ p = raoptp + sizeof(*dnssl);
+ while (1 < (len = dname_labeldec(dname, sizeof(dname),
+ p))) {
+ /* length == 1 means empty string */
+ warnmsg(LOG_DEBUG, __func__, "dname = %s",
+ dname);
+
+ ELM_MALLOC(rao, break);
+ rao->rao_type = ndo->nd_opt_type;
+ rao->rao_len = strlen(dname);
+ rao->rao_msg = strdup(dname);
+ if (rao->rao_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(rao);
+ break;
+ }
+ /* Set expiration timer */
+ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire));
+ memset(&lifetime, 0, sizeof(lifetime));
+ lifetime.tv_sec = ntohl(dnssl->nd_opt_dnssl_lifetime);
+ timeradd(&now, &lifetime, &rao->rao_expire);
+
+ TAILQ_INSERT_TAIL(&ifi->ifi_ra_opt, rao, rao_next);
+ p += len;
+ }
+ break;
+ default:
+ /* nothing to do for other options */
+ break;
+ }
+ raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
+ }
+ ra_opt_handler(ifi);
ifi->racnt++;
switch (ifi->state) {
@@ -371,23 +530,166 @@ rtsol_input(int s)
}
}
+static char resstr_ns_prefix[] = "nameserver ";
+static char resstr_sh_prefix[] = "search ";
+static char resstr_nl[] = "\n";
+static char resstr_sp[] = " ";
+
+int
+ra_opt_handler(struct ifinfo *ifi)
+{
+ struct ra_opt *rao;
+ struct script_msg *smp1, *smp2, *smp3;
+ struct timeval now;
+ TAILQ_HEAD(, script_msg) sm_rdnss_head =
+ TAILQ_HEAD_INITIALIZER(sm_rdnss_head);
+ TAILQ_HEAD(, script_msg) sm_dnssl_head =
+ TAILQ_HEAD_INITIALIZER(sm_dnssl_head);
+ int dcount, dlen;
+
+ dcount = 0;
+ dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl);
+ gettimeofday(&now, NULL);
+ TAILQ_FOREACH(rao, &ifi->ifi_ra_opt, rao_next) {
+ switch (rao->rao_type) {
+ case ND_OPT_RDNSS:
+ if (timercmp(&now, &rao->rao_expire, >)) {
+ warnmsg(LOG_INFO, __func__,
+ "expired rdnss entry: %s",
+ (char *)rao->rao_msg);
+ break;
+ }
+ ELM_MALLOC(smp1, continue);
+ ELM_MALLOC(smp2, goto free1);
+ ELM_MALLOC(smp3, goto free2);
+ smp1->sm_msg = resstr_ns_prefix;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1, sm_next);
+ smp2->sm_msg = rao->rao_msg;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2, sm_next);
+ smp3->sm_msg = resstr_nl;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3, sm_next);
+
+ break;
+ case ND_OPT_DNSSL:
+ if (timercmp(&now, &rao->rao_expire, >)) {
+ warnmsg(LOG_INFO, __func__,
+ "expired dnssl entry: %s",
+ (char *)rao->rao_msg);
+ break;
+ }
+ dcount++;
+ /* Check resolv.conf(5) restrictions. */
+ if (dcount > 6) {
+ warnmsg(LOG_INFO, __func__,
+ "dnssl entry exceeding maximum count (%d>6)"
+ ": %s", dcount, (char *)rao->rao_msg);
+ break;
+ }
+ if (256 < dlen + strlen(rao->rao_msg) +
+ strlen(resstr_sp)) {
+ warnmsg(LOG_INFO, __func__,
+ "dnssl entry exceeding maximum length "
+ "(>256): %s", (char *)rao->rao_msg);
+ break;
+ }
+ ELM_MALLOC(smp1, continue);
+ ELM_MALLOC(smp2, goto free1);
+ if (TAILQ_EMPTY(&sm_dnssl_head)) {
+ ELM_MALLOC(smp3, goto free2);
+ smp3->sm_msg = resstr_sh_prefix;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3,
+ sm_next);
+ }
+ smp1->sm_msg = rao->rao_msg;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, sm_next);
+ smp2->sm_msg = resstr_sp;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2, sm_next);
+ dlen += strlen(rao->rao_msg) + strlen(resstr_sp);
+ break;
+ default:
+ break;
+ }
+ continue;
+free2:
+ free(smp2);
+free1:
+ free(smp1);
+ }
+ /* Add \n for DNSSL list. */
+ if (!TAILQ_EMPTY(&sm_dnssl_head)) {
+ ELM_MALLOC(smp1, goto ra_opt_handler_freeit);
+ smp1->sm_msg = resstr_nl;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, sm_next);
+ }
+ TAILQ_CONCAT(&sm_rdnss_head, &sm_dnssl_head, sm_next);
+
+ if (!TAILQ_EMPTY(&sm_rdnss_head))
+ CALL_SCRIPT(RESADD, &sm_rdnss_head);
+ else
+ CALL_SCRIPT(RESDEL, NULL);
+
+ra_opt_handler_freeit:
+ /* Clear script message queue. */
+ if (!TAILQ_EMPTY(&sm_rdnss_head)) {
+ while ((smp1 = TAILQ_FIRST(&sm_rdnss_head)) != NULL) {
+ TAILQ_REMOVE(&sm_rdnss_head, smp1, sm_next);
+ free(smp1);
+ }
+ }
+ return (0);
+}
+
static void
-call_script(char *scriptpath, char *ifname)
+call_script(const int argc, const char *const argv[], void *head)
{
+ const char *scriptpath;
+ int fd[2];
+ int error;
pid_t pid, wpid;
+ TAILQ_HEAD(, script_msg) *sm_head;
- if (scriptpath == NULL)
+ if ((scriptpath = argv[0]) == NULL)
return;
+ fd[0] = fd[1] = -1;
+ sm_head = head;
+ 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) {
warnmsg(LOG_ERR, __func__,
"failed to fork: %s", strerror(errno));
return;
- } else if (pid) {
+ } 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);
@@ -395,17 +697,12 @@ call_script(char *scriptpath, char *ifname)
if (wpid < 0)
warnmsg(LOG_ERR, __func__,
"wait: %s", strerror(errno));
- else {
+ else
warnmsg(LOG_DEBUG, __func__,
"script \"%s\" terminated", scriptpath);
- }
- } else {
- char *argv[3];
- int fd;
-
- argv[0] = scriptpath;
- argv[1] = ifname;
- argv[2] = NULL;
+ } else { /* child */
+ int nullfd;
+ char **_argv;
if (safefile(scriptpath)) {
warnmsg(LOG_ERR, __func__,
@@ -413,20 +710,42 @@ call_script(char *scriptpath, char *ifname)
scriptpath);
exit(1);
}
-
- if ((fd = open("/dev/null", O_RDWR)) != -1) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
+ nullfd = open("/dev/null", O_RDWR);
+ if (nullfd < 0) {
+ warnmsg(LOG_ERR, __func__,
+ "open /dev/null: %s", strerror(errno));
+ exit(1);
}
-
- execv(scriptpath, argv);
-
+ 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(0);
+ exit(1);
}
return;
@@ -471,3 +790,37 @@ safefile(const char *path)
return (0);
}
+
+/* Decode domain name label encoding in RFC 1035 Section 3.1 */
+static size_t
+dname_labeldec(char *dst, size_t dlen, const char *src)
+{
+ size_t len;
+ const char *src_origin;
+ const char *src_last;
+ const char *dst_origin;
+
+ src_origin = src;
+ src_last = strchr(src, '\0');
+ dst_origin = dst;
+ memset(dst, '\0', dlen);
+ while (src && (len = (uint8_t)(*src++) & 0x3f) &&
+ (src + len) <= src_last) {
+ if (dst != dst_origin)
+ *dst++ = '.';
+ warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len);
+ memcpy(dst, src, len);
+ src += len;
+ dst += len;
+ }
+ *dst = '\0';
+
+ /*
+ * XXX validate that domain name only contains valid characters
+ * for two reasons: 1) correctness, 2) we do not want to pass
+ * possible malicious, unescaped characters like `` to a script
+ * or program that could be exploited that way.
+ */
+
+ return (src - src_origin);
+}
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
index 9ce0bb0..8eb2539 100644
--- a/usr.sbin/rtsold/rtsold.8
+++ b/usr.sbin/rtsold/rtsold.8
@@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 2, 2009
+.Dd May 28, 2011
.Dt RTSOLD 8
.Os
.\"
@@ -41,18 +41,24 @@
.Nm
.Op Fl dDfFm1
.Op Fl O Ar script-name
+.Op Fl P Ar pidfile
+.Op Fl R Ar script-name
.Ar interface ...
.Nm
.Op Fl dDfFm1
.Op Fl O Ar script-name
+.Op Fl P Ar pidfile
+.Op Fl R Ar script-name
.Fl a
.Nm rtsol
-.Op Fl dDF
+.Op Fl dD
.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Ar interface ...
.Nm rtsol
.Op Fl dD
.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Fl a
.\"
.Sh DESCRIPTION
@@ -221,6 +227,24 @@ configuration.
must be the absolute path from root to the script file, be a regular
file, and be created by the same owner who runs
.Nm .
+.It Fl P Ar pidfile
+Writes the process ID of
+.Nm
+to
+.Pa pidfile
+instead of the default PID file
+.Pa /var/run/rtsold.pid .
+.It Fl R Ar script-name
+Specifies a script to run when router advertisment options
+.Dv RDNSS Pq Recursive DNS Server
+or
+.Dv DNSSL Pq DNS Search List
+are encountered.
+The information of DNS servers and DNS search domains will be sent to
+standard input of this script.
+The
+.Xr resolvconf 8
+script is used by default.
.El
.Sh FILES
.Bl -tag -width /var/run/rtsold.dump -compact
@@ -235,6 +259,7 @@ dumps internal state on.
.Ex -std
.\"
.Sh SEE ALSO
+.Xr resolvconf 8 ,
.Xr rtadvd 8 ,
.Xr sysctl 8
.\"
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
index 0db050c..a8b7ca7 100644
--- a/usr.sbin/rtsold/rtsold.c
+++ b/usr.sbin/rtsold/rtsold.c
@@ -63,6 +63,9 @@
#include "rtsold.h"
+#define RTSOL_DUMPFILE "/var/run/rtsold.dump";
+#define RTSOL_PIDFILE "/var/run/rtsold.pid";
+
struct ifinfo *iflist;
struct timeval tm_max = {0x7fffffff, 0x7fffffff};
static int log_upto = 999;
@@ -72,7 +75,8 @@ int Fflag = 0; /* force setting sysctl parameters */
int aflag = 0;
int dflag = 0;
-char *otherconf_script;
+const char *otherconf_script;
+const char *resolvconf_script = "/sbin/resolvconf";
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
@@ -85,35 +89,32 @@ char *otherconf_script;
*/
#define PROBE_INTERVAL 60
-int main(int, char **);
-
/* 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 = "/var/run/rtsold.dump"; /* XXX: should be configurable */
-#endif
-#if 1
-static const char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
+static const char *dumpfilename = RTSOL_DUMPFILE;
#endif
#if 0
static int ifreconfig(char *);
#endif
+
static int make_packet(struct ifinfo *);
static struct timeval *rtsol_check_timer(void);
#ifndef SMALL
static void rtsold_set_dump_file(int);
#endif
-static void usage(char *);
+static void usage(void);
int
main(int argc, char **argv)
{
int s, ch, once = 0;
struct timeval *timeout;
- char *argv0;
const char *opts;
#ifdef HAVE_POLL_H
struct pollfd set[2];
@@ -124,19 +125,15 @@ main(int argc, char **argv)
#endif
int rtsock;
- /*
- * Initialization
- */
- argv0 = argv[0];
-
- /* get option */
- if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
- fflag = 1;
- once = 1;
- opts = "adDFO:";
- } else
- opts = "adDfFm1O:";
-
+#ifndef SMALL
+ /* rtsold */
+ opts = "adDfFm1O:P:R:";
+#else
+ /* rtsol */
+ opts = "adDFO:P:R:";
+ fflag = 1;
+ once = 1;
+#endif
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
case 'a':
@@ -163,17 +160,23 @@ main(int argc, char **argv)
case 'O':
otherconf_script = optarg;
break;
+ case 'P':
+ pidfilename = optarg;
+ break;
+ case 'R':
+ resolvconf_script = optarg;
+ break;
default:
- usage(argv0);
- /*NOTREACHED*/
+ usage();
+ exit(1);
}
}
argc -= optind;
argv += optind;
if ((!aflag && argc == 0) || (aflag && argc != 0)) {
- usage(argv0);
- /*NOTREACHED*/
+ usage();
+ exit(1);
}
/* set log level */
@@ -182,9 +185,9 @@ main(int argc, char **argv)
if (!fflag) {
char *ident;
- ident = strrchr(argv0, '/');
+ ident = strrchr(argv[0], '/');
if (!ident)
- ident = argv0;
+ ident = argv[0];
else
ident++;
openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
@@ -196,7 +199,14 @@ main(int argc, char **argv)
errx(1, "configuration script (%s) must be an absolute path",
otherconf_script);
}
-
+ if (resolvconf_script && *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);
+ }
#ifndef HAVE_ARC4RANDOM
/* random value initialization */
srandom((u_long)time(NULL));
@@ -226,7 +236,6 @@ main(int argc, char **argv)
if ((s = sockopen()) < 0) {
warnmsg(LOG_ERR, __func__, "failed to open a socket");
exit(1);
- /*NOTREACHED*/
}
#ifdef HAVE_POLL_H
set[0].fd = s;
@@ -242,7 +251,6 @@ main(int argc, char **argv)
if ((rtsock = rtsock_open()) < 0) {
warnmsg(LOG_ERR, __func__, "failed to open a socket");
exit(1);
- /*NOTREACHED*/
}
#ifdef HAVE_POLL_H
set[1].fd = rtsock;
@@ -255,12 +263,12 @@ main(int argc, char **argv)
#ifndef HAVE_POLL_H
fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
if ((fdsetp = malloc(fdmasks)) == NULL) {
- err(1, "malloc");
- /*NOTREACHED*/
+ warnmsg(LOG_ERR, __func__, "malloc");
+ exit(1);
}
if ((selectfdp = malloc(fdmasks)) == NULL) {
- err(1, "malloc");
- /*NOTREACHED*/
+ warnmsg(LOG_ERR, __func__, "malloc");
+ exit(1);
}
#endif
@@ -269,7 +277,6 @@ main(int argc, char **argv)
warnmsg(LOG_ERR, __func__,
"failed to initialize interfaces");
exit(1);
- /*NOTREACHED*/
}
if (aflag)
argv = autoifprobe();
@@ -278,7 +285,6 @@ main(int argc, char **argv)
warnmsg(LOG_ERR, __func__,
"failed to initialize %s", *argv);
exit(1);
- /*NOTREACHED*/
}
argv++;
}
@@ -291,7 +297,6 @@ main(int argc, char **argv)
/*NOTREACHED*/
}
-#if 1
/* dump the current pid */
if (!once) {
pid_t pid = getpid();
@@ -306,8 +311,6 @@ main(int argc, char **argv)
fclose(fp);
}
}
-#endif
-
#ifndef HAVE_POLL_H
memset(fdsetp, 0, fdmasks);
FD_SET(s, fdsetp);
@@ -337,7 +340,7 @@ main(int argc, char **argv)
break;
/* if all interfaces have got RA packet, we are done */
- for (ifi = iflist; ifi; ifi = ifi->next) {
+ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
if (ifi->state != IFS_DOWN && ifi->racnt == 0)
break;
}
@@ -373,102 +376,97 @@ main(int argc, char **argv)
}
/* NOTREACHED */
- return 0;
+ return (0);
}
int
ifconfig(char *ifname)
{
- struct ifinfo *ifinfo;
+ struct ifinfo *ifi;
struct sockaddr_dl *sdl;
int flags;
if ((sdl = if_nametosdl(ifname)) == NULL) {
warnmsg(LOG_ERR, __func__,
"failed to get link layer information for %s", ifname);
- return(-1);
+ return (-1);
}
if (find_ifinfo(sdl->sdl_index)) {
warnmsg(LOG_ERR, __func__,
"interface %s was already configured", ifname);
free(sdl);
- return(-1);
+ return (-1);
}
- if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
+ if ((ifi = malloc(sizeof(*ifi))) == NULL) {
warnmsg(LOG_ERR, __func__, "memory allocation failed");
free(sdl);
- return(-1);
+ return (-1);
}
- memset(ifinfo, 0, sizeof(*ifinfo));
- ifinfo->sdl = sdl;
+ memset(ifi, 0, sizeof(*ifi));
+ ifi->sdl = sdl;
- strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
+ strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname));
/* construct a router solicitation message */
- if (make_packet(ifinfo))
+ if (make_packet(ifi))
goto bad;
/* set link ID of this interface. */
#ifdef HAVE_SCOPELIB
- if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid))
+ if (inet_zoneid(AF_INET6, 2, ifname, &ifi->linkid))
goto bad;
#else
/* XXX: assume interface IDs as link IDs */
- ifinfo->linkid = ifinfo->sdl->sdl_index;
+ ifi->linkid = ifi->sdl->sdl_index;
#endif
/*
* check if the interface is available.
* also check if SIOCGIFMEDIA ioctl is OK on the interface.
*/
- ifinfo->mediareqok = 1;
- ifinfo->active = interface_status(ifinfo);
- if (!ifinfo->mediareqok) {
+ ifi->mediareqok = 1;
+ ifi->active = interface_status(ifi);
+ if (!ifi->mediareqok) {
/*
* probe routers periodically even if the link status
* does not change.
*/
- ifinfo->probeinterval = PROBE_INTERVAL;
+ ifi->probeinterval = PROBE_INTERVAL;
}
/* activate interface: interface_up returns 0 on success */
- flags = interface_up(ifinfo->ifname);
+ flags = interface_up(ifi->ifname);
if (flags == 0)
- ifinfo->state = IFS_DELAY;
+ ifi->state = IFS_DELAY;
else if (flags == IFS_TENTATIVE)
- ifinfo->state = IFS_TENTATIVE;
+ ifi->state = IFS_TENTATIVE;
else
- ifinfo->state = IFS_DOWN;
-
- rtsol_timer_update(ifinfo);
+ ifi->state = IFS_DOWN;
- /* link into chain */
- if (iflist)
- ifinfo->next = iflist;
- iflist = ifinfo;
+ rtsol_timer_update(ifi);
- return(0);
+ TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next);
+ return (0);
bad:
- free(ifinfo->sdl);
- free(ifinfo);
- return(-1);
+ free(ifi->sdl);
+ free(ifi);
+ return (-1);
}
void
iflist_init(void)
{
- struct ifinfo *ifi, *next;
+ struct ifinfo *ifi;
- for (ifi = iflist; ifi; ifi = next) {
- next = ifi->next;
- if (ifi->sdl)
+ while ((ifi = TAILQ_FIRST(&ifinfo_head)) != NULL) {
+ TAILQ_REMOVE(&ifinfo_head, ifi, ifi_next);
+ if (ifi->sdl != NULL)
free(ifi->sdl);
- if (ifi->rs_data)
+ if (ifi->rs_data != NULL)
free(ifi->rs_data);
free(ifi);
- iflist = NULL;
}
}
@@ -480,7 +478,7 @@ ifreconfig(char *ifname)
int rv;
prev = NULL;
- for (ifi = iflist; ifi; ifi = ifi->next) {
+ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
break;
prev = ifi;
@@ -494,7 +492,8 @@ ifreconfig(char *ifname)
free(ifi->rs_data);
free(ifi->sdl);
free(ifi);
- return rv;
+
+ return (rv);
}
#endif
@@ -503,34 +502,35 @@ find_ifinfo(int ifindex)
{
struct ifinfo *ifi;
- for (ifi = iflist; ifi; ifi = ifi->next)
+ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
if (ifi->sdl->sdl_index == ifindex)
- return(ifi);
- return(NULL);
+ return (ifi);
+ }
+ return (NULL);
}
static int
-make_packet(struct ifinfo *ifinfo)
+make_packet(struct ifinfo *ifi)
{
size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
struct nd_router_solicit *rs;
char *buf;
- if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
+ if ((lladdroptlen = lladdropt_length(ifi->sdl)) == 0) {
warnmsg(LOG_INFO, __func__,
"link-layer address option has null length"
- " on %s. Treat as not included.", ifinfo->ifname);
+ " on %s. Treat as not included.", ifi->ifname);
}
packlen += lladdroptlen;
- ifinfo->rs_datalen = packlen;
+ ifi->rs_datalen = packlen;
/* allocate buffer */
if ((buf = malloc(packlen)) == NULL) {
warnmsg(LOG_ERR, __func__,
- "memory allocation failed for %s", ifinfo->ifname);
- return(-1);
+ "memory allocation failed for %s", ifi->ifname);
+ return (-1);
}
- ifinfo->rs_data = buf;
+ ifi->rs_data = buf;
/* fill in the message */
rs = (struct nd_router_solicit *)buf;
@@ -542,9 +542,9 @@ make_packet(struct ifinfo *ifinfo)
/* fill in source link-layer address option */
if (lladdroptlen)
- lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
+ lladdropt_fill(ifi->sdl, (struct nd_opt_hdr *)buf);
- return(0);
+ return (0);
}
static struct timeval *
@@ -552,56 +552,64 @@ rtsol_check_timer(void)
{
static struct timeval returnval;
struct timeval now, rtsol_timer;
- struct ifinfo *ifinfo;
+ struct ifinfo *ifi;
+ struct ra_opt *rao;
int flags;
gettimeofday(&now, NULL);
rtsol_timer = tm_max;
- for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
- if (timercmp(&ifinfo->expire, &now, <=)) {
+ TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) {
+ if (timercmp(&ifi->expire, &now, <=)) {
if (dflag > 1)
warnmsg(LOG_DEBUG, __func__,
"timer expiration on %s, "
- "state = %d", ifinfo->ifname,
- ifinfo->state);
-
- switch (ifinfo->state) {
+ "state = %d", ifi->ifname,
+ ifi->state);
+
+ /* Remove all RA options. */
+ while ((rao = TAILQ_FIRST(&ifi->ifi_ra_opt)) != NULL) {
+ if (rao->rao_msg != NULL)
+ free(rao->rao_msg);
+ TAILQ_REMOVE(&ifi->ifi_ra_opt, rao, rao_next);
+ free(rao);
+ }
+ switch (ifi->state) {
case IFS_DOWN:
case IFS_TENTATIVE:
/* interface_up returns 0 on success */
- flags = interface_up(ifinfo->ifname);
+ flags = interface_up(ifi->ifname);
if (flags == 0)
- ifinfo->state = IFS_DELAY;
+ ifi->state = IFS_DELAY;
else if (flags == IFS_TENTATIVE)
- ifinfo->state = IFS_TENTATIVE;
+ ifi->state = IFS_TENTATIVE;
else
- ifinfo->state = IFS_DOWN;
+ ifi->state = IFS_DOWN;
break;
case IFS_IDLE:
{
- int oldstatus = ifinfo->active;
+ int oldstatus = ifi->active;
int probe = 0;
- ifinfo->active = interface_status(ifinfo);
+ ifi->active = interface_status(ifi);
- if (oldstatus != ifinfo->active) {
+ if (oldstatus != ifi->active) {
warnmsg(LOG_DEBUG, __func__,
"%s status is changed"
" from %d to %d",
- ifinfo->ifname,
- oldstatus, ifinfo->active);
+ ifi->ifname,
+ oldstatus, ifi->active);
probe = 1;
- ifinfo->state = IFS_DELAY;
- } else if (ifinfo->probeinterval &&
- (ifinfo->probetimer -=
- ifinfo->timer.tv_sec) <= 0) {
+ ifi->state = IFS_DELAY;
+ } else if (ifi->probeinterval &&
+ (ifi->probetimer -=
+ ifi->timer.tv_sec) <= 0) {
/* probe timer expired */
- ifinfo->probetimer =
- ifinfo->probeinterval;
+ ifi->probetimer =
+ ifi->probeinterval;
probe = 1;
- ifinfo->state = IFS_PROBE;
+ ifi->state = IFS_PROBE;
}
/*
@@ -609,38 +617,58 @@ rtsol_check_timer(void)
* status wrt the "other" configuration.
*/
if (probe)
- ifinfo->otherconfig = 0;
+ ifi->otherconfig = 0;
if (probe && mobile_node)
- defrouter_probe(ifinfo);
+ defrouter_probe(ifi);
break;
}
case IFS_DELAY:
- ifinfo->state = IFS_PROBE;
- sendpacket(ifinfo);
+ ifi->state = IFS_PROBE;
+ sendpacket(ifi);
break;
case IFS_PROBE:
- if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
- sendpacket(ifinfo);
+ if (ifi->probes < MAX_RTR_SOLICITATIONS)
+ sendpacket(ifi);
else {
warnmsg(LOG_INFO, __func__,
"No answer after sending %d RSs",
- ifinfo->probes);
- ifinfo->probes = 0;
- ifinfo->state = IFS_IDLE;
+ ifi->probes);
+ ifi->probes = 0;
+ ifi->state = IFS_IDLE;
}
break;
}
- rtsol_timer_update(ifinfo);
- }
+ rtsol_timer_update(ifi);
+ } else {
+ /* Expiration check for RA options. */
+ struct ra_opt *rao_tmp;
+ int expire = 0;
- if (timercmp(&ifinfo->expire, &rtsol_timer, <))
- rtsol_timer = ifinfo->expire;
+ TAILQ_FOREACH_SAFE(rao, &ifi->ifi_ra_opt, rao_next, rao_tmp) {
+ warnmsg(LOG_DEBUG, __func__,
+ "RA expiration timer: "
+ "type=%d, msg=%s, timer=%ld:%08ld",
+ rao->rao_type, (char *)rao->rao_msg,
+ (long)rao->rao_expire.tv_sec,
+ (long)rao->rao_expire.tv_usec);
+ if (timercmp(&now, &rao->rao_expire, >=)) {
+ warnmsg(LOG_DEBUG, __func__,
+ "RA expiration timer: expired.");
+ TAILQ_REMOVE(&ifi->ifi_ra_opt, rao, rao_next);
+ expire = 1;
+ }
+ }
+ if (expire)
+ ra_opt_handler(ifi);
+ }
+ if (timercmp(&ifi->expire, &rtsol_timer, <))
+ rtsol_timer = ifi->expire;
}
if (timercmp(&rtsol_timer, &tm_max, ==)) {
warnmsg(LOG_DEBUG, __func__, "there is no timer");
- return(NULL);
+ return (NULL);
} else if (timercmp(&rtsol_timer, &now, <))
/* this may occur when the interval is too small */
returnval.tv_sec = returnval.tv_usec = 0;
@@ -651,35 +679,35 @@ rtsol_check_timer(void)
warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
(long)returnval.tv_sec, (long)returnval.tv_usec);
- return(&returnval);
+ return (&returnval);
}
void
-rtsol_timer_update(struct ifinfo *ifinfo)
+rtsol_timer_update(struct ifinfo *ifi)
{
#define MILLION 1000000
#define DADRETRY 10 /* XXX: adhoc */
long interval;
struct timeval now;
- bzero(&ifinfo->timer, sizeof(ifinfo->timer));
+ bzero(&ifi->timer, sizeof(ifi->timer));
- switch (ifinfo->state) {
+ switch (ifi->state) {
case IFS_DOWN:
case IFS_TENTATIVE:
- if (++ifinfo->dadcount > DADRETRY) {
- ifinfo->dadcount = 0;
- ifinfo->timer.tv_sec = PROBE_INTERVAL;
+ if (++ifi->dadcount > DADRETRY) {
+ ifi->dadcount = 0;
+ ifi->timer.tv_sec = PROBE_INTERVAL;
} else
- ifinfo->timer.tv_sec = 1;
+ ifi->timer.tv_sec = 1;
break;
case IFS_IDLE:
if (mobile_node) {
/* XXX should be configurable */
- ifinfo->timer.tv_sec = 3;
+ ifi->timer.tv_sec = 3;
}
else
- ifinfo->timer = tm_max; /* stop timer(valid?) */
+ ifi->timer = tm_max; /* stop timer(valid?) */
break;
case IFS_DELAY:
#ifndef HAVE_ARC4RANDOM
@@ -687,12 +715,12 @@ rtsol_timer_update(struct ifinfo *ifinfo)
#else
interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION);
#endif
- ifinfo->timer.tv_sec = interval / MILLION;
- ifinfo->timer.tv_usec = interval % MILLION;
+ ifi->timer.tv_sec = interval / MILLION;
+ ifi->timer.tv_usec = interval % MILLION;
break;
case IFS_PROBE:
- if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
- ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
+ if (ifi->probes < MAX_RTR_SOLICITATIONS)
+ ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
else {
/*
* After sending MAX_RTR_SOLICITATIONS solicitations,
@@ -701,30 +729,30 @@ rtsol_timer_update(struct ifinfo *ifinfo)
* the timer value to MAX_RTR_SOLICITATION_DELAY based
* on RFC 2461, Section 6.3.7.
*/
- ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
+ ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
}
break;
default:
warnmsg(LOG_ERR, __func__,
"illegal interface state(%d) on %s",
- ifinfo->state, ifinfo->ifname);
+ ifi->state, ifi->ifname);
return;
}
/* reset the timer */
- if (timercmp(&ifinfo->timer, &tm_max, ==)) {
- ifinfo->expire = tm_max;
+ if (timercmp(&ifi->timer, &tm_max, ==)) {
+ ifi->expire = tm_max;
warnmsg(LOG_DEBUG, __func__,
- "stop timer for %s", ifinfo->ifname);
+ "stop timer for %s", ifi->ifname);
} else {
gettimeofday(&now, NULL);
- timeradd(&now, &ifinfo->timer, &ifinfo->expire);
+ timeradd(&now, &ifi->timer, &ifi->expire);
if (dflag > 1)
warnmsg(LOG_DEBUG, __func__,
- "set timer for %s to %d:%d", ifinfo->ifname,
- (int)ifinfo->timer.tv_sec,
- (int)ifinfo->timer.tv_usec);
+ "set timer for %s to %d:%d", ifi->ifname,
+ (int)ifi->timer.tv_sec,
+ (int)ifi->timer.tv_usec);
}
#undef MILLION
@@ -742,28 +770,23 @@ rtsold_set_dump_file(int sig __unused)
#endif
static void
-usage(char *progname)
+usage(void)
{
- if (progname && progname[strlen(progname) - 1] != 'd') {
- fprintf(stderr, "usage: rtsol [-dDF] interfaces...\n");
- fprintf(stderr, "usage: rtsol [-dDF] -a\n");
- } else {
- fprintf(stderr, "usage: rtsold [-adDfFm1] interfaces...\n");
- fprintf(stderr, "usage: rtsold [-dDfFm1] -a\n");
- }
- exit(1);
+#ifndef SMALL
+ fprintf(stderr, "usage: rtsold [-adDfFm1] [-O script-name] "
+ "[-P pidfile] [-R script-name] interfaces...\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] interfaces...\n");
+ fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] "
+ "[-P pidfile] [-R script-name] -a\n");
+#endif
}
void
-#if __STDC__
warnmsg(int priority, const char *func, const char *msg, ...)
-#else
-warnmsg(priority, func, msg, va_alist)
- int priority;
- const char *func;
- const char *msg;
- va_dcl
-#endif
{
va_list ap;
char buf[BUFSIZ];
@@ -805,11 +828,11 @@ autoifprobe(void)
n = 0;
if (getifaddrs(&ifap) != 0)
- return NULL;
+ return (NULL);
if (!Fflag && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- err(1, "socket");
- /* NOTREACHED */
+ warnmsg(LOG_ERR, __func__, "socket");
+ exit(1);
}
target = NULL;
@@ -845,8 +868,9 @@ autoifprobe(void)
memset(&nd, 0, sizeof(nd));
strlcpy(nd.ifname, ifa->ifa_name, sizeof(nd.ifname));
if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
- err(1, "ioctl(SIOCGIFINFO_IN6)");
- /* NOTREACHED */
+ warnmsg(LOG_ERR, __func__,
+ "ioctl(SIOCGIFINFO_IN6)");
+ exit(1);
}
if ((nd.ndi.flags & ND6_IFF_IFDISABLED))
continue;
@@ -856,32 +880,40 @@ autoifprobe(void)
/* if we find multiple candidates, just warn. */
if (n != 0 && dflag > 1)
- warnx("multiple interfaces found");
+ warnmsg(LOG_WARNING, __func__,
+ "multiple interfaces found");
a = (char **)realloc(argv, (n + 1) * sizeof(char **));
- if (a == NULL)
- err(1, "realloc");
+ if (a == NULL) {
+ warnmsg(LOG_ERR, __func__, "realloc");
+ exit(1);
+ }
argv = a;
argv[n] = strdup(ifa->ifa_name);
- if (!argv[n])
- err(1, "malloc");
+ if (!argv[n]) {
+ warnmsg(LOG_ERR, __func__, "malloc");
+ exit(1);
+ }
n++;
}
if (n) {
a = (char **)realloc(argv, (n + 1) * sizeof(char **));
- if (a == NULL)
- err(1, "realloc");
+ if (a == NULL) {
+ warnmsg(LOG_ERR, __func__, "realloc");
+ exit(1);
+ }
argv = a;
argv[n] = NULL;
if (dflag > 0) {
for (i = 0; i < n; i++)
- warnx("probing %s", argv[i]);
+ warnmsg(LOG_WARNING, __func__, "probing %s",
+ argv[i]);
}
}
if (!Fflag)
close(s);
freeifaddrs(ifap);
- return argv;
+ return (argv);
}
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
index 8aef490..1c395d0 100644
--- a/usr.sbin/rtsold/rtsold.h
+++ b/usr.sbin/rtsold/rtsold.h
@@ -31,8 +31,23 @@
* $FreeBSD$
*/
+struct script_msg {
+ TAILQ_ENTRY(script_msg) sm_next;
+
+ char *sm_msg;
+};
+
+struct ra_opt {
+ TAILQ_ENTRY(ra_opt) rao_next;
+
+ u_int8_t rao_type;
+ struct timeval rao_expire;
+ size_t rao_len;
+ void *rao_msg;
+};
+
struct ifinfo {
- struct ifinfo *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[IF_NAMESIZE]; /* interface name */
@@ -54,6 +69,8 @@ struct ifinfo {
size_t rs_datalen;
u_char *rs_data;
+
+ TAILQ_HEAD(, ra_opt) ifi_ra_opt;
};
/* per interface status */
@@ -63,12 +80,41 @@ struct ifinfo {
#define IFS_DOWN 3
#define IFS_TENTATIVE 4
+/* Interface list */
+extern TAILQ_HEAD(ifinfo_head_t, ifinfo) ifinfo_head;
+
+/*
+ * RFC 3542 API deprecates IPV6_PKTINFO in favor of
+ * IPV6_RECVPKTINFO
+ */
+#ifndef IPV6_RECVPKTINFO
+#ifdef IPV6_PKTINFO
+#define IPV6_RECVPKTINFO IPV6_PKTINFO
+#endif
+#endif
+/*
+ * RFC 3542 API deprecates IPV6_HOPLIMIT in favor of
+ * IPV6_RECVHOPLIMIT
+ */
+#ifndef IPV6_RECVHOPLIMIT
+#ifdef IPV6_HOPLIMIT
+#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
+#endif
+#endif
+
+#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
+#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
+#endif
+
/* rtsold.c */
extern struct timeval tm_max;
extern int dflag;
extern int aflag;
extern int Fflag;
-extern char *otherconf_script;
+extern const char *otherconf_script;
+extern const char *resolvconf_script;
extern int ifconfig(char *);
extern void iflist_init(void);
struct ifinfo *find_ifinfo(int);
@@ -76,6 +122,7 @@ void rtsol_timer_update(struct ifinfo *);
extern void warnmsg(int, const char *, const char *, ...)
__attribute__((__format__(__printf__, 3, 4)));
extern char **autoifprobe(void);
+extern int ra_opt_handler(struct ifinfo *);
/* if.c */
extern int ifinit(void);
OpenPOWER on IntegriCloud