summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rtsold
diff options
context:
space:
mode:
authorhrs <hrs@FreeBSD.org>2011-06-06 03:06:43 +0000
committerhrs <hrs@FreeBSD.org>2011-06-06 03:06:43 +0000
commit10df0af5a54c03a258caf94e9931b16eec080d79 (patch)
tree7fcbcd5609e82351ec883059d7bfb466d7cb8ef6 /usr.sbin/rtsold
parentacbda2ccc11fcdfe5fa5175c97ac29b4bb729bb5 (diff)
downloadFreeBSD-src-10df0af5a54c03a258caf94e9931b16eec080d79.zip
FreeBSD-src-10df0af5a54c03a258caf94e9931b16eec080d79.tar.gz
- Implement RDNSS and DNSSL options (RFC 6106, IPv6 Router Advertisement
Options for DNS Configuration) into rtadvd(8) and rtsold(8). DNS information received by rtsold(8) will go to resolv.conf(5) by resolvconf(8) script. This is based on work by J.R. Oldroyd (kern/156259) but revised extensively[1]. - rtadvd(8) now supports "noifprefix" to disable gathering on-link prefixes from interfaces when no "addr" is specified[2]. An entry in rtadvd.conf with "noifprefix" + no "addr" generates an RA message with no prefix information option. - rtadvd(8) now supports RTM_IFANNOUNCE message to fix crashes when an interface is added or removed. - Correct bogus ND_OPT_ROUTE_INFO value to one in RFC 4191. Reviewed by: bz[1] PR: kern/156259 [1] PR: bin/152458 [2]
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