- Accumulate RA options instead of replacing old ones when a new RA arrived.

RFC 4861 6.3.4 clearly defines handling multiple RAs in this way.

- RDNSS/DNSSL options from multiple RAs on a single link will be
  gathered and sent to resolvconf(8).

- Call "resolvconf -d" only after at least one RDNSS or DNSSL option is
  received and then all of them are expired.

- The rtsold.dump output now supports displaying a list of the RA options.

- Use more human-readable expression for logging values of struct timeval.

Discussed with:	ume
This commit is contained in:
Hiroki Sato 2011-06-08 16:03:29 +00:00
parent d91c258074
commit 06056832ec
4 changed files with 285 additions and 142 deletions

View File

@ -39,6 +39,7 @@
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <time.h>
@ -53,14 +54,16 @@ static FILE *fp;
extern struct ifinfo *iflist;
static void dump_interface_status(void);
static const char *sec2str(time_t);
static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
static void
dump_interface_status(void)
{
struct ifinfo *ifi;
struct rainfo *rai;
struct ra_opt *rao;
struct timeval now;
char ntopbuf[INET6_ADDRSTRLEN];
gettimeofday(&now, NULL);
@ -91,9 +94,33 @@ dump_interface_status(void)
(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));
: sec2str(&ifi->expire));
}
fprintf(fp, " number of valid RAs: %d\n", ifi->racnt);
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
fprintf(fp, " RA from %s\n",
inet_ntop(AF_INET6, &rai->rai_saddr.sin6_addr,
ntopbuf, sizeof(ntopbuf)));
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
fprintf(fp, " option: ");
switch (rao->rao_type) {
case ND_OPT_RDNSS:
fprintf(fp, "RDNSS: %s (expire: %s)\n",
(char *)rao->rao_msg,
sec2str(&rao->rao_expire));
break;
case ND_OPT_DNSSL:
fprintf(fp, "DNSSL: %s (expire: %s)\n",
(char *)rao->rao_msg,
sec2str(&rao->rao_expire));
break;
default:
break;
}
}
fprintf(fp, "\n");
}
}
}
@ -109,8 +136,8 @@ rtsold_dump_file(const char *dumpfile)
fclose(fp);
}
static const char *
sec2str(time_t total)
const char *
sec2str(const struct timeval *total)
{
static char result[256];
int days, hours, mins, secs;
@ -118,11 +145,19 @@ sec2str(time_t total)
char *p = result;
char *ep = &result[sizeof(result)];
int n;
struct timeval now;
time_t tsec;
days = total / 3600 / 24;
hours = (total / 3600) % 24;
mins = (total / 60) % 60;
secs = total % 60;
gettimeofday(&now, NULL);
tsec = total->tv_sec;
tsec += total->tv_usec / 1000000;
tsec -= now.tv_sec;
tsec -= now.tv_usec / 1000000;
days = tsec / 3600 / 24;
hours = (tsec / 3600) % 24;
mins = (tsec / 60) % 60;
secs = tsec % 60;
if (days) {
first = 0;

View File

@ -85,6 +85,7 @@ static const struct sockaddr_in6 sin6_allrouters = {
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 *);
static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
#define _ARGS_OTHER otherconf_script, ifi->ifname
#define _ARGS_RESADD resolvconf_script, "-a", ifi->ifname
@ -240,6 +241,7 @@ rtsol_input(int s)
struct icmp6_hdr *icp;
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
struct rainfo *rai;
char *raoptp;
char *p;
struct in6_addr *addr;
@ -251,6 +253,8 @@ rtsol_input(int s)
char dname[NI_MAXHOST];
struct timeval now;
struct timeval lifetime;
int newent_rai;
int newent_rao;
/* get message. namelen and controllen must always be initialized. */
rcvmhdr.msg_namelen = sizeof(from);
@ -367,22 +371,20 @@ rtsol_input(int s)
ifi->otherconfig = 1;
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);
newent_rai = 0;
rai = find_rainfo(ifi, &from);
if (rai == NULL) {
ELM_MALLOC(rai, exit(1));
rai->rai_ifinfo = ifi;
TAILQ_INIT(&rai->rai_ra_opt);
memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr,
sizeof(rai->rai_saddr.sin6_addr));
newent_rai = 1;
}
#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \
(((struct nd_opt_hdr *)x)->nd_opt_len * 8))
/* Process RA options. */
warnmsg(LOG_DEBUG, __func__, "Processing RA");
raoptp = (char *)icp + sizeof(struct nd_router_advert);
@ -439,25 +441,35 @@ rtsol_input(int s)
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;
newent_rao = 0;
rao = find_raopt(rai, ndo->nd_opt_type, nsbuf,
strlen(nsbuf));
if (rao == NULL) {
ELM_MALLOC(rao, break);
rao->rao_type = ndo->nd_opt_type;
rao->rao_len = strlen(nsbuf);
rao->rao_msg = strdup(nsbuf);
if (rao->rao_msg == NULL) {
warnmsg(LOG_ERR, __func__,
"strdup failed: %s",
strerror(errno));
free(rao);
addr++;
continue;
}
newent_rao = 1;
}
/* Set expiration timer */
memset(&rao->rao_expire, 0, sizeof(rao->rao_expire));
memset(&rao->rao_expire, 0,
sizeof(rao->rao_expire));
memset(&lifetime, 0, sizeof(lifetime));
lifetime.tv_sec = ntohl(rdnss->nd_opt_rdnss_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);
if (newent_rao)
TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
rao, rao_next);
addr++;
}
break;
@ -488,24 +500,35 @@ rtsol_input(int s)
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;
newent_rao = 0;
rao = find_raopt(rai, ndo->nd_opt_type, dname,
strlen(dname));
if (rao == NULL) {
ELM_MALLOC(rao, break);
rao->rao_type = ndo->nd_opt_type;
rao->rao_len = strlen(dname);
rao->rao_msg = strdup(dname);
if (rao->rao_msg == NULL) {
warnmsg(LOG_ERR, __func__,
"strdup failed: %s",
strerror(errno));
free(rao);
addr++;
continue;
}
newent_rao = 1;
}
/* Set expiration timer */
memset(&rao->rao_expire, 0, sizeof(rao->rao_expire));
memset(&rao->rao_expire, 0,
sizeof(rao->rao_expire));
memset(&lifetime, 0, sizeof(lifetime));
lifetime.tv_sec = ntohl(dnssl->nd_opt_dnssl_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);
if (newent_rao)
TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
rao, rao_next);
p += len;
}
break;
@ -515,6 +538,9 @@ rtsol_input(int s)
}
raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
}
if (newent_rai)
TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next);
ra_opt_handler(ifi);
ifi->racnt++;
@ -539,6 +565,7 @@ int
ra_opt_handler(struct ifinfo *ifi)
{
struct ra_opt *rao;
struct rainfo *rai;
struct script_msg *smp1, *smp2, *smp3;
struct timeval now;
TAILQ_HEAD(, script_msg) sm_rdnss_head =
@ -550,70 +577,87 @@ ra_opt_handler(struct ifinfo *ifi)
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)) {
/*
* All options from multiple RAs with the same or different
* source addresses on a single interface will be gathered and
* handled, not overridden. [RFC 4861 6.3.4]
*/
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
switch (rao->rao_type) {
case ND_OPT_RDNSS:
if (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);
smp3->sm_msg = resstr_sh_prefix;
TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3,
smp1->sm_msg = resstr_ns_prefix;
TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1,
sm_next);
smp2->sm_msg = rao->rao_msg;
TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2,
sm_next);
smp3->sm_msg = resstr_nl;
TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3,
sm_next);
ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED;
break;
case ND_OPT_DNSSL:
if (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;
ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED;
default:
break;
}
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;
continue;
free2:
free(smp2);
free(smp2);
free1:
free(smp1);
free(smp1);
}
}
/* Add \n for DNSSL list. */
if (!TAILQ_EMPTY(&sm_dnssl_head)) {
@ -625,10 +669,12 @@ ra_opt_handler(struct ifinfo *ifi)
if (!TAILQ_EMPTY(&sm_rdnss_head))
CALL_SCRIPT(RESADD, &sm_rdnss_head);
#if 0
else
else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED ||
ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) {
CALL_SCRIPT(RESDEL, NULL);
#endif
ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
}
ra_opt_handler_freeit:
/* Clear script message queue. */
@ -638,9 +684,30 @@ ra_opt_handler(struct ifinfo *ifi)
free(smp1);
}
}
if (!TAILQ_EMPTY(&sm_dnssl_head)) {
while ((smp1 = TAILQ_FIRST(&sm_dnssl_head)) != NULL) {
TAILQ_REMOVE(&sm_dnssl_head, smp1, sm_next);
free(smp1);
}
}
return (0);
}
static struct ra_opt *
find_raopt(struct rainfo *rai, int type, void *msg, size_t len)
{
struct ra_opt *rao;
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
if (rao->rao_type == type &&
rao->rao_len == strlen(msg) &&
memcmp(rao->rao_msg, msg, len) == 0)
break;
}
return (rao);
}
static void
call_script(const int argc, const char *const argv[], void *head)
{

View File

@ -44,6 +44,7 @@
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <netinet/in_var.h>
#include <arpa/inet.h>
#include <netinet6/nd6.h>
@ -408,7 +409,9 @@ ifconfig(char *ifname)
}
memset(ifi, 0, sizeof(*ifi));
ifi->sdl = sdl;
ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
TAILQ_INIT(&ifi->ifi_rainfo);
strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname));
/* construct a router solicitation message */
@ -500,6 +503,19 @@ ifreconfig(char *ifname)
}
#endif
struct rainfo *
find_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6)
{
struct rainfo *rai;
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next)
if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr,
sizeof(rai->rai_saddr.sin6_addr)) == 0)
return (rai);
return (NULL);
}
struct ifinfo *
find_ifinfo(int ifindex)
{
@ -556,6 +572,7 @@ rtsol_check_timer(void)
static struct timeval returnval;
struct timeval now, rtsol_timer;
struct ifinfo *ifi;
struct rainfo *rai;
struct ra_opt *rao;
int flags;
@ -565,18 +582,21 @@ rtsol_check_timer(void)
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", ifi->ifname,
ifi->state);
warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, "
"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);
while((rai = TAILQ_FIRST(&ifi->ifi_rainfo)) != NULL) {
/* Remove all RA options. */
TAILQ_REMOVE(&ifi->ifi_rainfo, rai, rai_next);
while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) !=
NULL) {
TAILQ_REMOVE(&rai->rai_ra_opt, rao,
rao_next);
if (rao->rao_msg != NULL)
free(rao->rao_msg);
free(rao);
}
free(rai);
}
switch (ifi->state) {
case IFS_DOWN:
@ -645,21 +665,27 @@ rtsol_check_timer(void)
rtsol_timer_update(ifi);
} else {
/* Expiration check for RA options. */
struct ra_opt *rao_tmp;
int expire = 0;
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, >=)) {
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
warnmsg(LOG_DEBUG, __func__,
"RA expiration timer: expired.");
TAILQ_REMOVE(&ifi->ifi_ra_opt, rao, rao_next);
expire = 1;
"RA expiration timer: "
"type=%d, msg=%s, expire=%s",
rao->rao_type, (char *)rao->rao_msg,
sec2str(&rao->rao_expire));
if (timercmp(&now, &rao->rao_expire,
>=)) {
warnmsg(LOG_DEBUG, __func__,
"RA expiration timer: "
"expired.");
TAILQ_REMOVE(&rai->rai_ra_opt,
rao, rao_next);
if (rao->rao_msg != NULL)
free(rao->rao_msg);
free(rao);
expire = 1;
}
}
}
if (expire)
@ -678,9 +704,10 @@ rtsol_check_timer(void)
else
timersub(&rtsol_timer, &now, &returnval);
if (dflag > 1)
warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
(long)returnval.tv_sec, (long)returnval.tv_usec);
now.tv_sec += returnval.tv_sec;
now.tv_usec += returnval.tv_usec;
warnmsg(LOG_DEBUG, __func__, "New timer is %s",
sec2str(&now));
return (&returnval);
}
@ -751,11 +778,10 @@ rtsol_timer_update(struct ifinfo *ifi)
gettimeofday(&now, NULL);
timeradd(&now, &ifi->timer, &ifi->expire);
if (dflag > 1)
warnmsg(LOG_DEBUG, __func__,
"set timer for %s to %d:%d", ifi->ifname,
(int)ifi->timer.tv_sec,
(int)ifi->timer.tv_usec);
now.tv_sec += ifi->timer.tv_sec;
now.tv_usec += ifi->timer.tv_usec;
warnmsg(LOG_DEBUG, __func__, "set timer for %s to %s",
ifi->ifname, sec2str(&now));
}
#undef MILLION

View File

@ -46,6 +46,16 @@ struct ra_opt {
void *rao_msg;
};
TAILQ_HEAD(rainfo_head, ra_opt);
struct rainfo {
TAILQ_ENTRY(rainfo) rai_next;
struct ifinfo *rai_ifinfo;
struct sockaddr_in6 rai_saddr;
TAILQ_HEAD(, ra_opt) rai_ra_opt;
};
struct ifinfo {
TAILQ_ENTRY(ifinfo) ifi_next; /* pointer to the next interface */
@ -64,13 +74,16 @@ struct ifinfo {
struct timeval timer;
struct timeval expire;
int errors; /* # of errors we've got - detect wedge */
#define IFI_DNSOPT_STATE_NOINFO 0
#define IFI_DNSOPT_STATE_RECEIVED 1
int ifi_rdnss; /* RDNSS option state */
int ifi_dnssl; /* DNSSL option state */
int racnt; /* total # of valid RAs it have got */
TAILQ_HEAD(, rainfo) ifi_rainfo;
size_t rs_datalen;
u_char *rs_data;
TAILQ_HEAD(, ra_opt) ifi_ra_opt;
};
/* per interface status */
@ -118,6 +131,7 @@ extern const char *resolvconf_script;
extern int ifconfig(char *);
extern void iflist_init(void);
struct ifinfo *find_ifinfo(int);
struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *);
void rtsol_timer_update(struct ifinfo *);
extern void warnmsg(int, const char *, const char *, ...)
__attribute__((__format__(__printf__, 3, 4)));
@ -145,6 +159,7 @@ extern void defrouter_probe(struct ifinfo *);
/* dump.c */
extern void rtsold_dump_file(const char *);
extern const char *sec2str(const struct timeval *);
/* rtsock.c */
extern int rtsock_open(void);