Property changes on: sys ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys:r222732 Index: sys/netinet/icmp6.h =================================================================== --- sys/netinet/icmp6.h (revision 222972) +++ sys/netinet/icmp6.h (working copy) @@ -297,9 +297,10 @@ #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */ +#define ND_OPT_RDNSS 25 /* RFC 6016 */ +#define ND_OPT_DNSSL 31 /* RFC 6016 */ -#define ND_OPT_ROUTE_INFO 200 /* draft-ietf-ipngwg-router-preference, not officially assigned yet */ - struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; u_int8_t nd_opt_pi_len; @@ -338,6 +339,22 @@ /* prefix follows */ } __packed; +struct nd_opt_rdnss { /* RDNSS option (RFC 6106) */ + u_int8_t nd_opt_rdnss_type; + u_int8_t nd_opt_rdnss_len; + u_int16_t nd_opt_rdnss_reserved; + u_int32_t nd_opt_rdnss_lifetime; + /* followed by list of recursive DNS servers */ +} __packed; + +struct nd_opt_dnssl { /* DNSSL option (RFC 6106) */ + u_int8_t nd_opt_dnssl_type; + u_int8_t nd_opt_dnssl_len; + u_int16_t nd_opt_dnssl_reserved; + u_int32_t nd_opt_dnssl_lifetime; + /* followed by list of DNS search domains */ +} __packed; + /* * icmp6 namelookup */ Property changes on: sys/contrib/pf ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/contrib/pf:r222732 Property changes on: sys/contrib/dev/acpica ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/contrib/dev/acpica:r222732 Property changes on: sys/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/cddl/contrib/opensolaris:r222732 Property changes on: sys/amd64/include/xen ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/amd64/include/xen:r222732 Property changes on: usr.sbin/rtadvd ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/usr.sbin/rtadvd:r222732,222743,222771,222820,222824,222848,222861,222972 Index: usr.sbin/rtadvd/advcap.c =================================================================== --- usr.sbin/rtadvd/advcap.c (revision 222972) +++ usr.sbin/rtadvd/advcap.c (working copy) @@ -64,8 +64,6 @@ #define V_TERM "HOST" #endif -char *RM; - /* * termcap - routines for dealing with the terminal capability data base * @@ -83,12 +81,10 @@ static char *tbuf; static int hopcount; /* detect infinite loops in termcap, init 0 */ -static char *remotefile; +extern const char *conffile; -extern char *conffile; - int tgetent(char *, char *); -int getent(char *, char *, char *); +int getent(char *, char *, const char *); int tnchktc(void); int tnamatch(char *); static char *tskip(char *); @@ -103,22 +99,18 @@ * we just notice escaped newlines. */ int -tgetent(bp, name) - char *bp, *name; +tgetent(char *bp, char *name) { - char *cp; - - remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; - return (getent(bp, name, cp)); + return (getent(bp, name, conffile)); } int -getent(bp, name, cp) - char *bp, *name, *cp; +getent(char *bp, char *name, const char *cfile) { int c; int i = 0, cnt = 0; char ibuf[BUFSIZ]; + char *cp; int tf; tbuf = bp; @@ -130,9 +122,9 @@ * use so we don't have to read the file. In this case it * has to already have the newlines crunched out. */ - if (cp && *cp) { - tf = open(RM = cp, O_RDONLY); - } + if (cfile && *cfile) + tf = open(cfile, O_RDONLY); + if (tf < 0) { syslog(LOG_INFO, "<%s> open: %s", __func__, strerror(errno)); @@ -184,7 +176,7 @@ * Note that this works because of the left to right scan. */ int -tnchktc() +tnchktc(void) { char *p, *q; char tcname[16]; /* name of similar terminal */ @@ -211,7 +203,7 @@ write(STDERR_FILENO, "Infinite tc= loop\n", 18); return (0); } - if (getent(tcbuf, tcname, remotefile) != 1) { + if (getent(tcbuf, tcname, conffile) != 1) { return (0); } for (q = tcbuf; *q++ != ':'; ) @@ -233,8 +225,7 @@ * name (before the first field) stops us. */ int -tnamatch(np) - char *np; +tnamatch(char *np) { char *Np, *Bp; @@ -260,8 +251,7 @@ * into the termcap file in octal. */ static char * -tskip(bp) - char *bp; +tskip(char *bp) { int dquote; @@ -305,8 +295,7 @@ * Note that we handle octal numbers beginning with 0. */ int64_t -tgetnum(id) - char *id; +tgetnum(char *id) { int64_t i; int base; @@ -341,8 +330,7 @@ * not given. */ int -tgetflag(id) - char *id; +tgetflag(char *id) { char *bp = tbuf; @@ -369,8 +357,7 @@ * No checking on area overflow. */ char * -tgetstr(id, area) - char *id, **area; +tgetstr(char *id, char **area) { char *bp = tbuf; @@ -395,13 +382,11 @@ * string capability escapes. */ static char * -tdecode(str, area) - char *str; - char **area; +tdecode(char *str, char **area) { char *cp; int c; - char *dp; + const char *dp; int i; char term; Index: usr.sbin/rtadvd/if.c =================================================================== --- usr.sbin/rtadvd/if.c (revision 222972) +++ usr.sbin/rtadvd/if.c (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -44,34 +44,35 @@ #include #include #include +#include #include #include #include #include "rtadvd.h" #include "if.h" -#define ROUNDUP(a, size) \ +#define ROUNDUP(a, size) \ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) -#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ - ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ - sizeof(u_long)) :\ - sizeof(u_long))) +#define NEXT_SA(ap) \ + (ap) = (struct sockaddr *)((caddr_t)(ap) + \ + ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) : \ + sizeof(u_long))) struct if_msghdr **iflist; int iflist_init_ok; size_t ifblock_size; char *ifblock; -static void get_iflist(char **buf, size_t *size); -static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, - size_t bufsize); +static void get_iflist(char **buf, size_t *size); +static void parse_iflist(struct if_msghdr ***ifmlist_p, + char *buf, size_t bufsize); static void get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) { int i; - + for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rti_info[i] = sa; @@ -93,12 +94,12 @@ struct sockaddr_dl *sdl = NULL, *ret_sdl; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) - return(NULL); + return (NULL); if ((buf = malloc(len)) == NULL) - return(NULL); + return (NULL); if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { free(buf); - return(NULL); + return (NULL); } lim = buf + len; @@ -124,7 +125,7 @@ if (next == lim) { /* search failed */ free(buf); - return(NULL); + return (NULL); } if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) @@ -133,7 +134,7 @@ end: free(buf); - return(ret_sdl); + return (ret_sdl); } int @@ -144,7 +145,7 @@ u_long mtu = 0; if (getifaddrs(&ifap) < 0) - return(0); + return (0); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcmp(ifa->ifa_name, name) == 0) { ifd = ifa->ifa_data; @@ -161,14 +162,14 @@ int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) - return(0); + return (0); ifr.ifr_addr.sa_family = AF_INET6; strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) { close(s); - return(0); + return (0); } close(s); @@ -176,7 +177,7 @@ } #endif - return(mtu); + return (mtu); } /* give interface index and its old flags, then new flags returned */ @@ -188,14 +189,14 @@ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); return (oifflags & ~IFF_UP); } if_indextoname(ifindex, ifr.ifr_name); if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s", - __func__, ifr.ifr_name); + __func__, ifr.ifr_name); close(s); return (oifflags & ~IFF_UP); } @@ -209,9 +210,9 @@ { switch (sdl->sdl_type) { case IFT_ETHER: - return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + return (ROUNDUP8(ETHER_ADDR_LEN + 2)); default: - return(0); + return (0); } } @@ -238,16 +239,15 @@ } int -rtbuf_len() +rtbuf_len(void) { size_t len; - int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0}; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) - return(-1); + return (-1); - return(len); + return (len); } #define FILTER_MATCH(type, filter) ((0x1 << type) & filter) @@ -267,14 +267,21 @@ /* just for safety */ if (!rtm->rtm_msglen) { syslog(LOG_WARNING, "<%s> rtm_msglen is 0 " - "(buf=%p lim=%p rtm=%p)", __func__, - buf, lim, rtm); + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); break; } - if (FILTER_MATCH(rtm->rtm_type, filter) == 0) { + if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) { + syslog(LOG_WARNING, + "<%s> routing message version mismatch " + "(buf=%p lim=%p rtm=%p)", __func__, + buf, lim, rtm); continue; } + if (FILTER_MATCH(rtm->rtm_type, filter) == 0) + continue; + switch (rtm->rtm_type) { case RTM_GET: case RTM_ADD: @@ -328,6 +335,7 @@ return (char *)rtm; /* NOTREACHED */ case RTM_IFINFO: + case RTM_IFANNOUNCE: /* found */ *lenp = rtm->rtm_msglen; return (char *)rtm; @@ -335,7 +343,7 @@ } } - return (char *)rtm; + return ((char *)rtm); } #undef FILTER_MATCH @@ -348,7 +356,7 @@ sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); - return(&SIN6(rti_info[RTAX_DST])->sin6_addr); + return (&SIN6(rti_info[RTAX_DST])->sin6_addr); } int @@ -360,7 +368,7 @@ sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); - return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); + return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index); } int @@ -393,7 +401,7 @@ struct rt_msghdr *rtm = (struct rt_msghdr *)buf; struct sockaddr *sa, *rti_info[RTAX_MAX]; u_char *p, *lim; - + sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); sa = rti_info[RTAX_NETMASK]; @@ -437,11 +445,11 @@ case 0x00: break; default: - return(-1); + return (-1); } } - return(masklen); + return (masklen); } int @@ -449,7 +457,7 @@ { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; - return(rtm->rtm_type); + return (rtm->rtm_type); } int @@ -457,7 +465,7 @@ { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; - return(rtm->rtm_msglen); + return (rtm->rtm_msglen); } int @@ -465,7 +473,7 @@ { struct if_msghdr *ifm = (struct if_msghdr *)buf; - return(ifm->ifm_msglen); + return (ifm->ifm_msglen); } /* @@ -486,7 +494,7 @@ if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { syslog(LOG_ERR, "<%s> sysctl: iflist size get failed", - __func__); + __func__); exit(1); } if ((*buf = malloc(*size)) == NULL) { @@ -495,7 +503,7 @@ } if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) { syslog(LOG_ERR, "<%s> sysctl: iflist get failed", - __func__); + __func__); exit(1); } return; @@ -529,8 +537,8 @@ for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { if (ifm->ifm_msglen == 0) { syslog(LOG_WARNING, "<%s> ifm_msglen is 0 " - "(buf=%p lim=%p ifm=%p)", __func__, - buf, lim, ifm); + "(buf=%p lim=%p ifm=%p)", __func__, + buf, lim, ifm); return; } @@ -538,10 +546,10 @@ (*ifmlist_p)[ifm->ifm_index] = ifm; } else { syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n" - "expected %d, got %d\n msglen = %d\n" - "buf:%p, ifm:%p, lim:%p\n", - RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, - buf, ifm, lim); + "expected %d, got %d\n msglen = %d\n" + "buf:%p, ifm:%p, lim:%p\n", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, + buf, ifm, lim); exit (1); } for (ifam = (struct ifa_msghdr *) @@ -552,8 +560,8 @@ /* just for safety */ if (!ifam->ifam_msglen) { syslog(LOG_WARNING, "<%s> ifa_msglen is 0 " - "(buf=%p lim=%p ifam=%p)", __func__, - buf, lim, ifam); + "(buf=%p lim=%p ifam=%p)", __func__, + buf, lim, ifam); return; } if (ifam->ifam_type != RTM_NEWADDR) @@ -564,8 +572,11 @@ } void -init_iflist() +init_iflist(void) { + syslog(LOG_DEBUG, + "<%s> generate iflist.", __func__); + if (ifblock) { free(ifblock); ifblock_size = 0; Index: usr.sbin/rtadvd/if.h =================================================================== --- usr.sbin/rtadvd/if.h (revision 222972) +++ usr.sbin/rtadvd/if.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE Index: usr.sbin/rtadvd/config.c =================================================================== --- usr.sbin/rtadvd/config.c (revision 222972) +++ usr.sbin/rtadvd/config.c (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -53,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -65,29 +65,55 @@ #include "if.h" #include "config.h" +/* label of tcapcode + number + domain name + zero octet */ +static char entbuf[10 + 3 + NI_MAXHOST + 1]; +static char oentbuf[10 + 3 + NI_MAXHOST + 1]; +static char abuf[DNAME_LABELENC_MAXLEN]; + static time_t prefix_timo = (60 * 120); /* 2 hours. * XXX: should be configurable. */ -extern struct rainfo *ralist; static struct rtadvd_timer *prefix_timeout(void *); -static void makeentry(char *, size_t, int, char *); -static int getinet6sysctl(int); +static void makeentry(char *, size_t, int, const char *); +static size_t dname_labelenc(char *, const char *); -void -getconfig(intface) - char *intface; +/* Encode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labelenc(char *dst, const char *src) { - int stat, i; - char tbuf[BUFSIZ]; - struct rainfo *tmp; - long val; - int64_t val64; - char buf[BUFSIZ]; - char *bp = buf; - char *addr, *flagstr; - static int forwarding = -1; + char *dst_origin; + char *p; + size_t len; -#define MUSTHAVE(var, cap) \ + dst_origin = dst; + len = strlen(src); + + /* Length fields per 63 octets + '\0' (<= DNAME_LABELENC_MAXLEN) */ + memset(dst, 0, len + len / 64 + 1 + 1); + + syslog(LOG_DEBUG, "<%s> labelenc = %s", __func__, src); + while (src && (len = strlen(src)) != 0) { + /* Put a length field with 63 octet limitation first. */ + p = strchr(src, '.'); + if (p == NULL) + *dst++ = len = MIN(63, len); + else + *dst++ = len = MIN(63, p - src); + /* Copy 63 octets at most. */ + memcpy(dst, src, len); + dst += len; + if (p == NULL) /* the last label */ + break; + src = p + 1; + } + /* Always need a 0-length label at the tail. */ + *dst++ = '\0'; + + syslog(LOG_DEBUG, "<%s> labellen = %td", __func__, dst - dst_origin); + return (dst - dst_origin); +} + +#define MUSTHAVE(var, cap) \ do { \ int64_t t; \ if ((t = agetnum(cap)) < 0) { \ @@ -97,60 +123,184 @@ } \ var = t; \ } while (0) -#define MAYHAVE(var, cap, def) \ + +#define MAYHAVE(var, cap, def) \ do { \ if ((var = agetnum(cap)) < 0) \ var = def; \ } while (0) +#define ELM_MALLOC(p,error_action) \ + do { \ + p = malloc(sizeof(*p)); \ + if (p == NULL) { \ + syslog(LOG_ERR, "<%s> malloc failed: %s", \ + __func__, strerror(errno)); \ + error_action; \ + } \ + memset(p, 0, sizeof(*p)); \ + } while(0) + +int +loadconfig(char *ifl_names[], const int ifl_len) +{ + int i; + int idx; + int error; + + for (i = 0; i < ifl_len; i++) { + idx = if_nametoindex(ifl_names[i]); + if (idx == 0) { + syslog(LOG_ERR, + "<%s> interface %s not found. " + "Ignored at this moment.", __func__, ifl_names[i]); + continue; + } + syslog(LOG_INFO, + "<%s> loading config for %s.", __func__, ifl_names[i]); + error = getconfig(idx); + if (error) + syslog(LOG_ERR, + "<%s> invalid configuration for %s. " + "Ignored at this moment.", __func__, ifl_names[i]); + } + + return (0); +} + +int +rmconfig(int idx) +{ + struct rainfo *rai; + struct prefix *pfx; + struct soliciter *sol; + struct rdnss *rdn; + struct rdnss_addr *rdna; + struct dnssl *dns; + struct rtinfo *rti; + + rai = if_indextorainfo(idx); + if (rai == NULL) { + syslog(LOG_ERR, "<%s>: rainfo not found (idx=%d)", + __func__, idx); + return (-1); + } + + TAILQ_REMOVE(&railist, rai, rai_next); + syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.", + __func__, idx); + + /* Free all of allocated memories for this entry. */ + rtadvd_remove_timer(rai->rai_timer); + + if (rai->rai_ra_data != NULL) + free(rai->rai_ra_data); + + if (rai->rai_sdl != NULL) + free(rai->rai_sdl); + + while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) { + TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); + free(pfx); + } + while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { + TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); + free(sol); + } + while ((rdn = TAILQ_FIRST(&rai->rai_rdnss)) != NULL) { + TAILQ_REMOVE(&rai->rai_rdnss, rdn, rd_next); + while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { + TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); + free(rdna); + } + free(rdn); + } + while ((dns = TAILQ_FIRST(&rai->rai_dnssl)) != NULL) { + TAILQ_REMOVE(&rai->rai_dnssl, dns, dn_next); + free(dns); + } + while ((rti = TAILQ_FIRST(&rai->rai_route)) != NULL) { + TAILQ_REMOVE(&rai->rai_route, rti, rti_next); + free(rti); + } + free(rai); + + return (0); +} + +int +getconfig(int idx) +{ + int stat, i; + char tbuf[BUFSIZ]; + struct rainfo *rai; + struct rainfo *rai_old; + long val; + int64_t val64; + char buf[BUFSIZ]; + char *bp = buf; + char *addr, *flagstr; + char intface[IFNAMSIZ]; + + if (if_indextoname(idx, intface) == NULL) { + syslog(LOG_ERR, "<%s> invalid index number (%d)", + __func__, idx); + return (-1); + } + + TAILQ_FOREACH(rai_old, &railist, rai_next) + if (idx == rai_old->rai_ifindex) + break; + if ((stat = agetent(tbuf, intface)) <= 0) { memset(tbuf, 0, sizeof(tbuf)); syslog(LOG_INFO, - "<%s> %s isn't defined in the configuration file" - " or the configuration file doesn't exist." - " Treat it as default", - __func__, intface); + "<%s> %s isn't defined in the configuration file" + " or the configuration file doesn't exist." + " Treat it as default", + __func__, intface); } - tmp = (struct rainfo *)malloc(sizeof(*ralist)); - if (tmp == NULL) { - syslog(LOG_INFO, "<%s> %s: can't allocate enough memory", - __func__, intface); - exit(1); - } - memset(tmp, 0, sizeof(*tmp)); - tmp->prefix.next = tmp->prefix.prev = &tmp->prefix; + ELM_MALLOC(rai, exit(1)); + TAILQ_INIT(&rai->rai_prefix); #ifdef ROUTEINFO - tmp->route.next = tmp->route.prev = &tmp->route; + TAILQ_INIT(&rai->rai_route); #endif + TAILQ_INIT(&rai->rai_rdnss); + TAILQ_INIT(&rai->rai_dnssl); + TAILQ_INIT(&rai->rai_soliciter); - /* check if we are allowed to forward packets (if not determined) */ - if (forwarding < 0) { - if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) - exit(1); - } + /* gather on-link prefixes from the network interfaces. */ + if (agetflag("noifprefix")) + rai->rai_advifprefix = 0; + else + rai->rai_advifprefix = 1; /* get interface information */ if (agetflag("nolladdr")) - tmp->advlinkopt = 0; + rai->rai_advlinkopt = 0; else - tmp->advlinkopt = 1; - if (tmp->advlinkopt) { - if ((tmp->sdl = if_nametosdl(intface)) == NULL) { + rai->rai_advlinkopt = 1; + if (rai->rai_advlinkopt) { + if ((rai->rai_sdl = if_nametosdl(intface)) == NULL) { syslog(LOG_ERR, - "<%s> can't get information of %s", - __func__, intface); - exit(1); + "<%s> can't get information of %s", + __func__, intface); + goto getconfig_free_rai; } - tmp->ifindex = tmp->sdl->sdl_index; + rai->rai_ifindex = rai->rai_sdl->sdl_index; } else - tmp->ifindex = if_nametoindex(intface); - strncpy(tmp->ifname, intface, sizeof(tmp->ifname)); - if ((tmp->phymtu = if_getmtu(intface)) == 0) { - tmp->phymtu = IPV6_MMTU; + rai->rai_ifindex = if_nametoindex(intface); + strncpy(rai->rai_ifname, intface, sizeof(rai->rai_ifname)); + syslog(LOG_DEBUG, + "<%s> ifindex = %d on %s", __func__, rai->rai_ifindex, + rai->rai_ifname); + + if ((rai->rai_phymtu = if_getmtu(intface)) == 0) { + rai->rai_phymtu = IPV6_MMTU; syslog(LOG_WARNING, - "<%s> can't get interface mtu of %s. Treat as %d", - __func__, intface, IPV6_MMTU); + "<%s> can't get interface mtu of %s. Treat as %d", + __func__, intface, IPV6_MMTU); } /* @@ -159,25 +309,27 @@ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { syslog(LOG_ERR, - "<%s> maxinterval (%ld) on %s is invalid " - "(must be between %u and %u)", __func__, val, - intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); - exit(1); + "<%s> maxinterval (%ld) on %s is invalid " + "(must be between %u and %u)", __func__, val, + intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); + goto getconfig_free_rai; } - tmp->maxinterval = (u_int)val; - MAYHAVE(val, "mininterval", tmp->maxinterval/3); - if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { + rai->rai_maxinterval = (u_int)val; + + MAYHAVE(val, "mininterval", rai->rai_maxinterval/3); + if ((u_int)val < MIN_MININTERVAL || + (u_int)val > (rai->rai_maxinterval * 3) / 4) { syslog(LOG_ERR, - "<%s> mininterval (%ld) on %s is invalid " - "(must be between %d and %d)", - __func__, val, intface, MIN_MININTERVAL, - (tmp->maxinterval * 3) / 4); - exit(1); + "<%s> mininterval (%ld) on %s is invalid " + "(must be between %d and %d)", + __func__, val, intface, MIN_MININTERVAL, + (rai->rai_maxinterval * 3) / 4); + goto getconfig_free_rai; } - tmp->mininterval = (u_int)val; + rai->rai_mininterval = (u_int)val; MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); - tmp->hoplimit = val & 0xff; + rai->rai_hoplimit = val & 0xff; if ((flagstr = (char *)agetstr("raflags", &bp))) { val = 0; @@ -191,77 +343,61 @@ if ((val & ND_RA_FLAG_RTPREF_HIGH)) { syslog(LOG_ERR, "<%s> the \'h\' and \'l\'" " router flags are exclusive", __func__); - exit(1); + goto getconfig_free_rai; } val |= ND_RA_FLAG_RTPREF_LOW; } - } else { + } else MAYHAVE(val, "raflags", 0); - } - tmp->managedflg = val & ND_RA_FLAG_MANAGED; - tmp->otherflg = val & ND_RA_FLAG_OTHER; + + rai->rai_managedflg = val & ND_RA_FLAG_MANAGED; + rai->rai_otherflg = val & ND_RA_FLAG_OTHER; #ifndef ND_RA_FLAG_RTPREF_MASK #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ #endif - tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK; - if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) { + rai->rai_rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) { syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s", - __func__, tmp->rtpref, intface); - exit(1); + __func__, rai->rai_rtpref, intface); + goto getconfig_free_rai; } - MAYHAVE(val, "rltime", tmp->maxinterval * 3); - if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { + MAYHAVE(val, "rltime", rai->rai_maxinterval * 3); + if ((u_int)val && ((u_int)val < rai->rai_maxinterval || + (u_int)val > MAXROUTERLIFETIME)) { syslog(LOG_ERR, - "<%s> router lifetime (%ld) on %s is invalid " - "(must be 0 or between %d and %d)", - __func__, val, intface, - tmp->maxinterval, - MAXROUTERLIFETIME); - exit(1); + "<%s> router lifetime (%ld) on %s is invalid " + "(must be 0 or between %d and %d)", + __func__, val, intface, rai->rai_maxinterval, + MAXROUTERLIFETIME); + goto getconfig_free_rai; } - /* - * Basically, hosts MUST NOT send Router Advertisement messages at any - * time (RFC 2461, Section 6.2.3). However, it would sometimes be - * useful to allow hosts to advertise some parameters such as prefix - * information and link MTU. Thus, we allow hosts to invoke rtadvd - * only when router lifetime (on every advertising interface) is - * explicitly set zero. (see also the above section) - */ - if (val && forwarding == 0) { - syslog(LOG_ERR, - "<%s> non zero router lifetime is specified for %s, " - "which must not be allowed for hosts. you must " - "change router lifetime or enable IPv6 forwarding.", - __func__, intface); - exit(1); - } - tmp->lifetime = val & 0xffff; + rai->rai_lifetime = val & 0xffff; MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); if (val < 0 || val > MAXREACHABLETIME) { syslog(LOG_ERR, - "<%s> reachable time (%ld) on %s is invalid " - "(must be no greater than %d)", - __func__, val, intface, MAXREACHABLETIME); - exit(1); + "<%s> reachable time (%ld) on %s is invalid " + "(must be no greater than %d)", + __func__, val, intface, MAXREACHABLETIME); + goto getconfig_free_rai; } - tmp->reachabletime = (u_int32_t)val; + rai->rai_reachabletime = (u_int32_t)val; MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range", - __func__, (long long)val64, intface); - exit(1); + __func__, (long long)val64, intface); + goto getconfig_free_rai; } - tmp->retranstimer = (u_int32_t)val64; + rai->rai_retranstimer = (u_int32_t)val64; if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { syslog(LOG_ERR, - "<%s> mobile-ip6 configuration not supported", - __func__); - exit(1); + "<%s> mobile-ip6 configuration not supported", + __func__); + goto getconfig_free_rai; } /* prefix information */ @@ -271,12 +407,11 @@ * checking consistency of advertised lifetimes. */ MAYHAVE(val, "clockskew", 0); - tmp->clockskew = val; + rai->rai_clockskew = val; - tmp->pfxs = 0; + rai->rai_pfxs = 0; for (i = -1; i < MAXPREFIX; i++) { struct prefix *pfx; - char entbuf[256]; makeentry(entbuf, sizeof(entbuf), i, "addr"); addr = (char *)agetstr(entbuf, &bp); @@ -284,49 +419,38 @@ continue; /* allocate memory to store prefix information */ - if ((pfx = malloc(sizeof(struct prefix))) == NULL) { - syslog(LOG_ERR, - "<%s> can't allocate enough memory", - __func__); - exit(1); - } - memset(pfx, 0, sizeof(*pfx)); + ELM_MALLOC(pfx, exit(1)); + pfx->pfx_rainfo = rai; + pfx->pfx_origin = PREFIX_FROM_CONFIG; - /* link into chain */ - insque(pfx, &tmp->prefix); - tmp->pfxs++; - pfx->rainfo = tmp; - - pfx->origin = PREFIX_FROM_CONFIG; - - if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) { + if (inet_pton(AF_INET6, addr, &pfx->pfx_prefix) != 1) { syslog(LOG_ERR, - "<%s> inet_pton failed for %s", - __func__, addr); - exit(1); + "<%s> inet_pton failed for %s", + __func__, addr); + goto getconfig_free_pfx; } - if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { + if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) { syslog(LOG_ERR, - "<%s> multicast prefix (%s) must " - "not be advertised on %s", - __func__, addr, intface); - exit(1); + "<%s> multicast prefix (%s) must " + "not be advertised on %s", + __func__, addr, intface); + goto getconfig_free_pfx; } - if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) + if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix)) syslog(LOG_NOTICE, - "<%s> link-local prefix (%s) will be" - " advertised on %s", - __func__, addr, intface); + "<%s> link-local prefix (%s) will be" + " advertised on %s", + __func__, addr, intface); makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); MAYHAVE(val, entbuf, 64); if (val < 0 || val > 128) { syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s " - "on %s out of range", - __func__, val, addr, intface); - exit(1); + "on %s out of range", + __func__, val, addr, intface); + goto getconfig_free_pfx; } - pfx->prefixlen = (int)val; + pfx->pfx_prefixlen = (int)val; makeentry(entbuf, sizeof(entbuf), i, "pinfoflags"); if ((flagstr = (char *)agetstr(entbuf, &bp))) { @@ -339,8 +463,8 @@ MAYHAVE(val, entbuf, (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); } - pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; - pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; + pfx->pfx_onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; + pfx->pfx_autoconfflg = val & ND_OPT_PI_FLAG_AUTO; makeentry(entbuf, sizeof(entbuf), i, "vltime"); MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); @@ -348,17 +472,17 @@ syslog(LOG_ERR, "<%s> vltime (%lld) for " "%s/%d on %s is out of range", __func__, (long long)val64, - addr, pfx->prefixlen, intface); - exit(1); + addr, pfx->pfx_prefixlen, intface); + goto getconfig_free_pfx; } - pfx->validlifetime = (u_int32_t)val64; + pfx->pfx_validlifetime = (u_int32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); if (agetflag(entbuf)) { struct timeval now; gettimeofday(&now, 0); - pfx->vltimeexpire = - now.tv_sec + pfx->validlifetime; + pfx->pfx_vltimeexpire = + now.tv_sec + pfx->pfx_validlifetime; } makeentry(entbuf, sizeof(entbuf), i, "pltime"); @@ -368,44 +492,51 @@ "<%s> pltime (%lld) for %s/%d on %s " "is out of range", __func__, (long long)val64, - addr, pfx->prefixlen, intface); - exit(1); + addr, pfx->pfx_prefixlen, intface); + goto getconfig_free_pfx; } - pfx->preflifetime = (u_int32_t)val64; + pfx->pfx_preflifetime = (u_int32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); if (agetflag(entbuf)) { struct timeval now; gettimeofday(&now, 0); - pfx->pltimeexpire = - now.tv_sec + pfx->preflifetime; + pfx->pfx_pltimeexpire = + now.tv_sec + pfx->pfx_preflifetime; } + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); + rai->rai_pfxs++; + continue; +getconfig_free_pfx: + free(pfx); } - if (tmp->pfxs == 0) - get_prefix(tmp); + if (rai->rai_advifprefix && rai->rai_pfxs == 0) + get_prefix(rai); MAYHAVE(val, "mtu", 0); - if (val < 0 || val > 0xffffffff) { + if (val < 0 || (u_int)val > 0xffffffff) { syslog(LOG_ERR, - "<%s> mtu (%ld) on %s out of range", - __func__, val, intface); - exit(1); + "<%s> mtu (%ld) on %s out of range", + __func__, val, intface); + goto getconfig_free_rai; } - tmp->linkmtu = (u_int32_t)val; - if (tmp->linkmtu == 0) { + rai->rai_linkmtu = (u_int32_t)val; + if (rai->rai_linkmtu == 0) { char *mtustr; if ((mtustr = (char *)agetstr("mtu", &bp)) && strcmp(mtustr, "auto") == 0) - tmp->linkmtu = tmp->phymtu; + rai->rai_linkmtu = rai->rai_phymtu; } - else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { + else if (rai->rai_linkmtu < IPV6_MMTU || + rai->rai_linkmtu > rai->rai_phymtu) { syslog(LOG_ERR, - "<%s> advertised link mtu (%lu) on %s is invalid (must " - "be between least MTU (%d) and physical link MTU (%d)", - __func__, (unsigned long)tmp->linkmtu, intface, - IPV6_MMTU, tmp->phymtu); - exit(1); + "<%s> advertised link mtu (%lu) on %s is invalid (must " + "be between least MTU (%d) and physical link MTU (%d)", + __func__, (unsigned long)rai->rai_linkmtu, intface, + IPV6_MMTU, rai->rai_phymtu); + goto getconfig_free_rai; } #ifdef SIOCSIFINFO_IN6 @@ -415,65 +546,52 @@ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); exit(1); } memset(&ndi, 0, sizeof(ndi)); strncpy(ndi.ifname, intface, IFNAMSIZ); - if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) { + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s", - __func__, intface, strerror(errno)); - } + __func__, intface, strerror(errno)); /* reflect the RA info to the host variables in kernel */ - ndi.ndi.chlim = tmp->hoplimit; - ndi.ndi.retrans = tmp->retranstimer; - ndi.ndi.basereachable = tmp->reachabletime; - if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) { + ndi.ndi.chlim = rai->rai_hoplimit; + ndi.ndi.retrans = rai->rai_retranstimer; + ndi.ndi.basereachable = rai->rai_reachabletime; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s", - __func__, intface, strerror(errno)); - } + __func__, intface, strerror(errno)); + close(s); } #endif /* route information */ #ifdef ROUTEINFO - tmp->routes = 0; + rai->rai_routes = 0; for (i = -1; i < MAXROUTE; i++) { struct rtinfo *rti; - char entbuf[256], oentbuf[256]; makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix"); addr = (char *)agetstr(oentbuf, &bp); - if (addr) { + if (addr) fprintf(stderr, "%s was obsoleted. Use %s.\n", - oentbuf, entbuf); - } + oentbuf, entbuf); } if (addr == NULL) continue; /* allocate memory to store prefix information */ - if ((rti = malloc(sizeof(struct rtinfo))) == NULL) { - syslog(LOG_ERR, - "<%s> can't allocate enough memory", - __func__); - exit(1); - } - memset(rti, 0, sizeof(*rti)); + ELM_MALLOC(rti, exit(1)); - /* link into chain */ - insque(rti, &tmp->route); - tmp->routes++; - - if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { + if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", - __func__, addr); - exit(1); + __func__, addr); + goto getconfig_free_rti; } #if 0 /* @@ -485,17 +603,17 @@ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { syslog(LOG_ERR, - "<%s> multicast route (%s) must " - "not be advertised on %s", - __func__, addr, intface); - exit(1); + "<%s> multicast route (%s) must " + "not be advertised on %s", + __func__, addr, intface); + goto getconfig_free_rti; } if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { syslog(LOG_NOTICE, - "<%s> link-local route (%s) will " - "be advertised on %s", - __func__, addr, intface); - exit(1); + "<%s> link-local route (%s) will " + "be advertised on %s", + __func__, addr, intface); + goto getconfig_free_rti; } #endif @@ -505,19 +623,19 @@ if (val == 256) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen"); MAYHAVE(val, oentbuf, 256); - if (val != 256) { + if (val != 256) fprintf(stderr, "%s was obsoleted. Use %s.\n", - oentbuf, entbuf); - } else + oentbuf, entbuf); + else val = 64; } if (val < 0 || val > 128) { syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s " - "out of range", - __func__, val, addr, intface); - exit(1); + "out of range", + __func__, val, addr, intface); + goto getconfig_free_rti; } - rti->prefixlen = (int)val; + rti->rti_prefixlen = (int)val; makeentry(entbuf, sizeof(entbuf), i, "rtflags"); if ((flagstr = (char *)agetstr(entbuf, &bp))) { @@ -530,7 +648,7 @@ "<%s> the \'h\' and \'l\' route" " preferences are exclusive", __func__); - exit(1); + goto getconfig_free_rti; } val |= ND_RA_FLAG_RTPREF_LOW; } @@ -541,17 +659,17 @@ MAYHAVE(val, oentbuf, 256); if (val != 256) { fprintf(stderr, "%s was obsoleted. Use %s.\n", - oentbuf, entbuf); + oentbuf, entbuf); } else val = 0; } - rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; - if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { + rti->rti_rtpref = val & ND_RA_FLAG_RTPREF_MASK; + if (rti->rti_rtpref == ND_RA_FLAG_RTPREF_RSV) { syslog(LOG_ERR, "<%s> invalid route preference (%02x) " - "for %s/%d on %s", - __func__, rti->rtpref, addr, - rti->prefixlen, intface); - exit(1); + "for %s/%d on %s", + __func__, rti->rti_rtpref, addr, + rti->rti_prefixlen, intface); + goto getconfig_free_rti; } /* @@ -565,60 +683,191 @@ if (val64 == -1) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime"); MAYHAVE(val64, oentbuf, -1); - if (val64 != -1) { + if (val64 != -1) fprintf(stderr, "%s was obsoleted. Use %s.\n", - oentbuf, entbuf); - } else { + oentbuf, entbuf); + else { fprintf(stderr, "%s should be specified " - "for interface %s.\n", - entbuf, intface); - val64 = tmp->lifetime; + "for interface %s.\n", entbuf, intface); + val64 = rai->rai_lifetime; } } if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> route lifetime (%lld) for " "%s/%d on %s out of range", __func__, - (long long)val64, addr, rti->prefixlen, intface); - exit(1); + (long long)val64, addr, rti->rti_prefixlen, intface); + goto getconfig_free_rti; } - rti->ltime = (u_int32_t)val64; + rti->rti_ltime = (u_int32_t)val64; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); + rai->rai_routes++; + continue; +getconfig_free_rti: + free(rti); } #endif + /* DNS server and DNS search list information */ + for (i = -1; i < MAXRDNSSENT ; i++) { + struct rdnss *rdn; + struct rdnss_addr *rdna; + char *ap; + int c; - /* okey */ - tmp->next = ralist; - ralist = tmp; + makeentry(entbuf, sizeof(entbuf), i, "rdnss"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + ELM_MALLOC(rdn, exit(1)); + TAILQ_INIT(&rdn->rd_list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(rdna, goto getconfig_free_rdn); + if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, abuf); + free(rdna); + goto getconfig_free_rdn; + } + TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next); + } + + makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); + MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); + if ((u_int)val < rai->rai_maxinterval || + (u_int)val > rai->rai_maxinterval * 2) { + syslog(LOG_ERR, "%s (%ld) on %s is invalid " + "(must be between %d and %d)", + entbuf, val, intface, rai->rai_maxinterval, + rai->rai_maxinterval * 2); + goto getconfig_free_rdn; + } + rdn->rd_ltime = val; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next); + continue; +getconfig_free_rdn: + while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { + TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); + free(rdna); + } + free(rdn); + } + + for (i = -1; i < MAXDNSSLENT ; i++) { + struct dnssl *dns; + struct dnssl_addr *dnsa; + char *ap; + int c; + + makeentry(entbuf, sizeof(entbuf), i, "dnssl"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + + ELM_MALLOC(dns, exit(1)); + + TAILQ_INIT(&dns->dn_list); + + for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + ELM_MALLOC(dnsa, goto getconfig_free_dns); + dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf); + syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__, + dnsa->da_len); + TAILQ_INSERT_TAIL(&dns->dn_list, dnsa, da_next); + } + + makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); + MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); + if ((u_int)val < rai->rai_maxinterval || + (u_int)val > rai->rai_maxinterval * 2) { + syslog(LOG_ERR, "%s (%ld) on %s is invalid " + "(must be between %d and %d)", + entbuf, val, intface, rai->rai_maxinterval, + rai->rai_maxinterval * 2); + goto getconfig_free_dns; + } + dns->dn_ltime = val; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next); + continue; +getconfig_free_dns: + while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) { + TAILQ_REMOVE(&dns->dn_list, dnsa, da_next); + free(dnsa); + } + free(dns); + } /* construct the sending packet */ - make_packet(tmp); + make_packet(rai); + /* + * If an entry with the same ifindex exists, remove it first. + * Before the removal, RDNSS and DNSSL options with + * zero-lifetime will be sent. + */ + if (rai_old != NULL) { + const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; + struct rdnss *rdn; + struct dnssl *dns; + + rai_old->rai_lifetime = 0; + TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next) + dns->dn_ltime = 0; + + make_packet(rai_old); + for (i = 0; i < retrans; i++) { + ra_output(rai_old); + sleep(MIN_DELAY_BETWEEN_RAS); + } + rmconfig(idx); + } + TAILQ_INSERT_TAIL(&railist, rai, rai_next); + /* set timer */ - tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, - tmp, tmp); - ra_timer_update((void *)tmp, &tmp->timer->tm); - rtadvd_set_timer(&tmp->timer->tm, tmp->timer); + rai->rai_timer = rtadvd_add_timer(ra_timeout, ra_timer_update, + rai, rai); + ra_timer_update((void *)rai, &rai->rai_timer->rat_tm); + rtadvd_set_timer(&rai->rai_timer->rat_tm, rai->rai_timer); + + return (0); +getconfig_free_rai: + free(rai); + return (-1); } void get_prefix(struct rainfo *rai) { struct ifaddrs *ifap, *ifa; - struct prefix *pp; + struct prefix *pfx; struct in6_addr *a; u_char *p, *ep, *m, *lim; u_char ntopbuf[INET6_ADDRSTRLEN]; if (getifaddrs(&ifap) < 0) { syslog(LOG_ERR, - "<%s> can't get interface addresses", - __func__); + "<%s> can't get interface addresses", + __func__); exit(1); } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { int plen; - if (strcmp(ifa->ifa_name, rai->ifname) != 0) + if (strcmp(ifa->ifa_name, rai->rai_ifname) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -631,8 +880,8 @@ plen = prefixlen(m, lim); if (plen <= 0 || plen > 128) { syslog(LOG_ERR, "<%s> failed to get prefixlen " - "or prefix is invalid", - __func__); + "or prefix is invalid", + __func__); exit(1); } if (plen == 128) /* XXX */ @@ -643,56 +892,46 @@ } /* allocate memory to store prefix info. */ - if ((pp = malloc(sizeof(*pp))) == NULL) { - syslog(LOG_ERR, - "<%s> can't get allocate buffer for prefix", - __func__); - exit(1); - } - memset(pp, 0, sizeof(*pp)); + ELM_MALLOC(pfx, exit(1)); /* set prefix, sweep bits outside of prefixlen */ - pp->prefixlen = plen; - memcpy(&pp->prefix, a, sizeof(*a)); - p = (u_char *)&pp->prefix; - ep = (u_char *)(&pp->prefix + 1); + pfx->pfx_prefixlen = plen; + memcpy(&pfx->pfx_prefix, a, sizeof(*a)); + p = (u_char *)&pfx->pfx_prefix; + ep = (u_char *)(&pfx->pfx_prefix + 1); while (m < lim && p < ep) *p++ &= *m++; while (p < ep) *p++ = 0x00; - if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf, + if (!inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf))) { syslog(LOG_ERR, "<%s> inet_ntop failed", __func__); exit(1); } syslog(LOG_DEBUG, - "<%s> add %s/%d to prefix list on %s", - __func__, ntopbuf, pp->prefixlen, rai->ifname); + "<%s> add %s/%d to prefix list on %s", + __func__, ntopbuf, pfx->pfx_prefixlen, rai->rai_ifname); /* set other fields with protocol defaults */ - pp->validlifetime = DEF_ADVVALIDLIFETIME; - pp->preflifetime = DEF_ADVPREFERREDLIFETIME; - pp->onlinkflg = 1; - pp->autoconfflg = 1; - pp->origin = PREFIX_FROM_KERNEL; - pp->rainfo = rai; + pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME; + pfx->pfx_preflifetime = DEF_ADVPREFERREDLIFETIME; + pfx->pfx_onlinkflg = 1; + pfx->pfx_autoconfflg = 1; + pfx->pfx_origin = PREFIX_FROM_KERNEL; + pfx->pfx_rainfo = rai; /* link into chain */ - insque(pp, &rai->prefix); + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); /* counter increment */ - rai->pfxs++; + rai->rai_pfxs++; } freeifaddrs(ifap); } static void -makeentry(buf, len, id, string) - char *buf; - size_t len; - int id; - char *string; +makeentry(char *buf, size_t len, int id, const char *string) { if (id < 0) @@ -711,37 +950,28 @@ static void add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) { - struct prefix *prefix; + struct prefix *pfx; u_char ntopbuf[INET6_ADDRSTRLEN]; - if ((prefix = malloc(sizeof(*prefix))) == NULL) { - syslog(LOG_ERR, "<%s> memory allocation failed", - __func__); - return; /* XXX: error or exit? */ - } - memset(prefix, 0, sizeof(*prefix)); - prefix->prefix = ipr->ipr_prefix.sin6_addr; - prefix->prefixlen = ipr->ipr_plen; - prefix->validlifetime = ipr->ipr_vltime; - prefix->preflifetime = ipr->ipr_pltime; - prefix->onlinkflg = ipr->ipr_raf_onlink; - prefix->autoconfflg = ipr->ipr_raf_auto; - prefix->origin = PREFIX_FROM_DYNAMIC; + ELM_MALLOC(pfx, return); + pfx->pfx_prefix = ipr->ipr_prefix.sin6_addr; + pfx->pfx_prefixlen = ipr->ipr_plen; + pfx->pfx_validlifetime = ipr->ipr_vltime; + pfx->pfx_preflifetime = ipr->ipr_pltime; + pfx->pfx_onlinkflg = ipr->ipr_raf_onlink; + pfx->pfx_autoconfflg = ipr->ipr_raf_auto; + pfx->pfx_origin = PREFIX_FROM_DYNAMIC; - insque(prefix, &rai->prefix); - prefix->rainfo = rai; + TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); + pfx->pfx_rainfo = rai; syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", - __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - ipr->ipr_plen, rai->ifname); + __func__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_plen, rai->rai_ifname); - /* free the previous packet */ - free(rai->ra_data); - rai->ra_data = NULL; - /* reconstruct the packet */ - rai->pfxs++; + rai->rai_pfxs++; make_packet(rai); } @@ -751,30 +981,33 @@ * The prefix must be in the list. */ void -delete_prefix(struct prefix *prefix) +delete_prefix(struct prefix *pfx) { u_char ntopbuf[INET6_ADDRSTRLEN]; - struct rainfo *rai = prefix->rainfo; + struct rainfo *rai; - remque(prefix); + rai = pfx->pfx_rainfo; + TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", - __func__, inet_ntop(AF_INET6, &prefix->prefix, - ntopbuf, INET6_ADDRSTRLEN), - prefix->prefixlen, rai->ifname); - if (prefix->timer) - rtadvd_remove_timer(&prefix->timer); - free(prefix); - rai->pfxs--; + __func__, + inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_prefixlen, rai->rai_ifname); + if (pfx->pfx_timer) + rtadvd_remove_timer(pfx->pfx_timer); + free(pfx); + rai->rai_pfxs--; + make_packet(rai); } void -invalidate_prefix(struct prefix *prefix) +invalidate_prefix(struct prefix *pfx) { u_char ntopbuf[INET6_ADDRSTRLEN]; struct timeval timo; - struct rainfo *rai = prefix->rainfo; + struct rainfo *rai; - if (prefix->timer) { /* sanity check */ + rai = pfx->pfx_rainfo; + if (pfx->pfx_timer) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer already exists", __func__); @@ -783,38 +1016,38 @@ syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, " "will expire in %ld seconds", __func__, - inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN), - prefix->prefixlen, rai->ifname, (long)prefix_timo); + inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), + pfx->pfx_prefixlen, rai->rai_ifname, (long)prefix_timo); /* set the expiration timer */ - prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL); - if (prefix->timer == NULL) { + pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL); + if (pfx->pfx_timer == NULL) { syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. " "remove the prefix", __func__); - delete_prefix(prefix); + delete_prefix(pfx); } timo.tv_sec = prefix_timo; timo.tv_usec = 0; - rtadvd_set_timer(&timo, prefix->timer); + rtadvd_set_timer(&timo, pfx->pfx_timer); } static struct rtadvd_timer * prefix_timeout(void *arg) { - struct prefix *prefix = (struct prefix *)arg; - - delete_prefix(prefix); - return(NULL); + delete_prefix((struct prefix *)arg); + + return (NULL); } void -update_prefix(struct prefix * prefix) +update_prefix(struct prefix *pfx) { u_char ntopbuf[INET6_ADDRSTRLEN]; - struct rainfo *rai = prefix->rainfo; + struct rainfo *rai; - if (prefix->timer == NULL) { /* sanity check */ + rai = pfx->pfx_rainfo; + if (pfx->pfx_timer == NULL) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer does not exist", __func__); @@ -822,11 +1055,12 @@ } syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s", - __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, - INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname); + __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_prefixlen, rai->rai_ifname); /* stop the expiration timer */ - rtadvd_remove_timer(&prefix->timer); + rtadvd_remove_timer(pfx->pfx_timer); + pfx->pfx_timer = NULL; } /* @@ -842,13 +1076,13 @@ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); exit(1); } if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__, - strerror(errno)); + strerror(errno)); ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; @@ -860,22 +1094,22 @@ u_char ntopbuf[INET6_ADDRSTRLEN]; syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" - "lower than PR_ORIG_RR(router renumbering)." - "This should not happen if I am router", __func__, - inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, - sizeof(ntopbuf)), ipr->ipr_origin); + "lower than PR_ORIG_RR(router renumbering)." + "This should not happen if I am router", __func__, + inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, + sizeof(ntopbuf)), ipr->ipr_origin); close(s); - return 1; + return (1); } close(s); - return 0; + return (0); #else ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; ipr->ipr_raf_onlink = 1; ipr->ipr_raf_auto = 1; - return 0; + return (0); #endif } @@ -887,8 +1121,8 @@ memset(&ipr, 0, sizeof(ipr)); if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't" - "exist. This should not happen! %s", __func__, - ifindex, strerror(errno)); + "exist. This should not happen! %s", __func__, + ifindex, strerror(errno)); exit(1); } ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); @@ -902,7 +1136,7 @@ } void -make_packet(struct rainfo *rainfo) +make_packet(struct rainfo *rai) { size_t packlen, lladdroptlen = 0; char *buf; @@ -913,45 +1147,68 @@ struct nd_opt_route_info *ndopt_rti; struct rtinfo *rti; #endif + struct nd_opt_rdnss *ndopt_rdnss; + struct rdnss *rdn; + struct nd_opt_dnssl *ndopt_dnssl; + struct dnssl *dns; + size_t len; struct prefix *pfx; /* calculate total length */ packlen = sizeof(struct nd_router_advert); - if (rainfo->advlinkopt) { - if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { + if (rai->rai_advlinkopt) { + if ((lladdroptlen = lladdropt_length(rai->rai_sdl)) == 0) { syslog(LOG_INFO, - "<%s> link-layer address option has" - " null length on %s. Treat as not included.", - __func__, rainfo->ifname); - rainfo->advlinkopt = 0; + "<%s> link-layer address option has" + " null length on %s. Treat as not included.", + __func__, rai->rai_ifname); + rai->rai_advlinkopt = 0; } packlen += lladdroptlen; } - if (rainfo->pfxs) - packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; - if (rainfo->linkmtu) + if (rai->rai_pfxs) + packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs; + if (rai->rai_linkmtu) packlen += sizeof(struct nd_opt_mtu); #ifdef ROUTEINFO - for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) - packlen += sizeof(struct nd_opt_route_info) + - ((rti->prefixlen + 0x3f) >> 6) * 8; + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) + packlen += sizeof(struct nd_opt_route_info) + + ((rti->rti_prefixlen + 0x3f) >> 6) * 8; #endif + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + struct rdnss_addr *rdna; + packlen += sizeof(struct nd_opt_rdnss); + TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) + packlen += sizeof(rdna->ra_dns); + } + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + struct dnssl_addr *dnsa; + + packlen += sizeof(struct nd_opt_dnssl); + len = 0; + TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) + len += dnsa->da_len; + + /* A zero octet and 8 octet boundary */ + len++; + len += (len % 8) ? 8 - len % 8 : 0; + + packlen += len; + } /* allocate memory for the packet */ if ((buf = malloc(packlen)) == NULL) { syslog(LOG_ERR, - "<%s> can't get enough memory for an RA packet", - __func__); + "<%s> can't get enough memory for an RA packet", + __func__); exit(1); } - if (rainfo->ra_data) { - /* free the previous packet */ - free(rainfo->ra_data); - rainfo->ra_data = NULL; - } - rainfo->ra_data = buf; + memset(buf, 0, packlen); + if (rai->rai_ra_data) /* Free old data if any. */ + free(rai->rai_ra_data); + rai->rai_ra_data = buf; /* XXX: what if packlen > 576? */ - rainfo->ra_datalen = packlen; + rai->rai_ra_datalen = packlen; /* * construct the packet @@ -960,71 +1217,70 @@ ra->nd_ra_type = ND_ROUTER_ADVERT; ra->nd_ra_code = 0; ra->nd_ra_cksum = 0; - ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit); + ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rai->rai_hoplimit); ra->nd_ra_flags_reserved = 0; /* just in case */ /* * XXX: the router preference field, which is a 2-bit field, should be * initialized before other fields. */ - ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref; + ra->nd_ra_flags_reserved = 0xff & rai->rai_rtpref; ra->nd_ra_flags_reserved |= - rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; + rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0; ra->nd_ra_flags_reserved |= - rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; - ra->nd_ra_router_lifetime = htons(rainfo->lifetime); - ra->nd_ra_reachable = htonl(rainfo->reachabletime); - ra->nd_ra_retransmit = htonl(rainfo->retranstimer); + rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0; + ra->nd_ra_router_lifetime = htons(rai->rai_lifetime); + ra->nd_ra_reachable = htonl(rai->rai_reachabletime); + ra->nd_ra_retransmit = htonl(rai->rai_retranstimer); buf += sizeof(*ra); - if (rainfo->advlinkopt) { - lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); + if (rai->rai_advlinkopt) { + lladdropt_fill(rai->rai_sdl, (struct nd_opt_hdr *)buf); buf += lladdroptlen; } - if (rainfo->linkmtu) { + if (rai->rai_linkmtu) { ndopt_mtu = (struct nd_opt_mtu *)buf; ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; ndopt_mtu->nd_opt_mtu_len = 1; ndopt_mtu->nd_opt_mtu_reserved = 0; - ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu); + ndopt_mtu->nd_opt_mtu_mtu = htonl(rai->rai_linkmtu); buf += sizeof(struct nd_opt_mtu); } - for (pfx = rainfo->prefix.next; - pfx != &rainfo->prefix; pfx = pfx->next) { + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { u_int32_t vltime, pltime; struct timeval now; ndopt_pi = (struct nd_opt_prefix_info *)buf; ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; ndopt_pi->nd_opt_pi_len = 4; - ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen; + ndopt_pi->nd_opt_pi_prefix_len = pfx->pfx_prefixlen; ndopt_pi->nd_opt_pi_flags_reserved = 0; - if (pfx->onlinkflg) + if (pfx->pfx_onlinkflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; - if (pfx->autoconfflg) + if (pfx->pfx_autoconfflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; - if (pfx->timer) + if (pfx->pfx_timer) vltime = 0; else { - if (pfx->vltimeexpire || pfx->pltimeexpire) + if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire) gettimeofday(&now, NULL); - if (pfx->vltimeexpire == 0) - vltime = pfx->validlifetime; + if (pfx->pfx_vltimeexpire == 0) + vltime = pfx->pfx_validlifetime; else - vltime = (pfx->vltimeexpire > now.tv_sec) ? - pfx->vltimeexpire - now.tv_sec : 0; + vltime = (pfx->pfx_vltimeexpire > now.tv_sec) ? + pfx->pfx_vltimeexpire - now.tv_sec : 0; } - if (pfx->timer) + if (pfx->pfx_timer) pltime = 0; else { - if (pfx->pltimeexpire == 0) - pltime = pfx->preflifetime; + if (pfx->pfx_pltimeexpire == 0) + pltime = pfx->pfx_preflifetime; else - pltime = (pfx->pltimeexpire > now.tv_sec) ? - pfx->pltimeexpire - now.tv_sec : 0; + pltime = (pfx->pfx_pltimeexpire > now.tv_sec) ? + pfx->pfx_pltimeexpire - now.tv_sec : 0; } if (vltime < pltime) { /* @@ -1036,45 +1292,72 @@ ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); ndopt_pi->nd_opt_pi_reserved2 = 0; - ndopt_pi->nd_opt_pi_prefix = pfx->prefix; + ndopt_pi->nd_opt_pi_prefix = pfx->pfx_prefix; buf += sizeof(struct nd_opt_prefix_info); } #ifdef ROUTEINFO - for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) { - u_int8_t psize = (rti->prefixlen + 0x3f) >> 6; + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + u_int8_t psize = (rti->rti_prefixlen + 0x3f) >> 6; ndopt_rti = (struct nd_opt_route_info *)buf; ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; ndopt_rti->nd_opt_rti_len = 1 + psize; - ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; - ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; - ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime); - memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); + ndopt_rti->nd_opt_rti_prefixlen = rti->rti_prefixlen; + ndopt_rti->nd_opt_rti_flags = 0xff & rti->rti_rtpref; + ndopt_rti->nd_opt_rti_lifetime = htonl(rti->rti_ltime); + memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8); buf += sizeof(struct nd_opt_route_info) + psize * 8; } #endif + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + struct rdnss_addr *rdna; - return; -} + ndopt_rdnss = (struct nd_opt_rdnss *)buf; + ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; + ndopt_rdnss->nd_opt_rdnss_len = 0; + ndopt_rdnss->nd_opt_rdnss_reserved = 0; + ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdn->rd_ltime); + buf += sizeof(struct nd_opt_rdnss); -static int -getinet6sysctl(int code) -{ - int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; - int value; - size_t size; + TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) { + memcpy(buf, &rdna->ra_dns, sizeof(rdna->ra_dns)); + buf += sizeof(rdna->ra_dns); + } + /* Length field should be in 8 octets */ + ndopt_rdnss->nd_opt_rdnss_len = (buf - (char *)ndopt_rdnss) / 8; - mib[3] = code; - size = sizeof(value); - if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) - < 0) { - syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", - __func__, code, - strerror(errno)); - return(-1); + syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__, + ndopt_rdnss->nd_opt_rdnss_len); } - else - return(value); + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + struct dnssl_addr *dnsa; + + ndopt_dnssl = (struct nd_opt_dnssl *)buf; + ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL; + ndopt_dnssl->nd_opt_dnssl_len = 0; + ndopt_dnssl->nd_opt_dnssl_reserved = 0; + ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dns->dn_ltime); + buf += sizeof(*ndopt_dnssl); + + TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) { + memcpy(buf, dnsa->da_dom, dnsa->da_len); + buf += dnsa->da_len; + } + + /* A zero octet after encoded DNS server list. */ + *buf++ = '\0'; + + /* Padding to next 8 octets boundary */ + len = buf - (char *)ndopt_dnssl; + len += (len % 8) ? 8 - len % 8 : 0; + + /* Length field must be in 8 octets */ + ndopt_dnssl->nd_opt_dnssl_len = len / 8; + + syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__, + ndopt_dnssl->nd_opt_dnssl_len); + } + return; } Index: usr.sbin/rtadvd/dump.c =================================================================== --- usr.sbin/rtadvd/dump.c (revision 222972) +++ usr.sbin/rtadvd/dump.c (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 2000 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -45,6 +45,7 @@ #include +#include #include #include #include @@ -63,8 +64,9 @@ static char *ether_str(struct sockaddr_dl *); static void if_dump(void); +static size_t dname_labeldec(char *, size_t, const char *); -static char *rtpref_str[] = { +static const char *rtpref_str[] = { "medium", /* 00 */ "high", /* 01 */ "rsv", /* 10 */ @@ -72,8 +74,7 @@ }; static char * -ether_str(sdl) - struct sockaddr_dl *sdl; +ether_str(struct sockaddr_dl *sdl) { static char hbuf[32]; u_char *cp; @@ -85,84 +86,86 @@ } else snprintf(hbuf, sizeof(hbuf), "NONE"); - return(hbuf); + return (hbuf); } static void -if_dump() +if_dump(void) { struct rainfo *rai; struct prefix *pfx; #ifdef ROUTEINFO struct rtinfo *rti; #endif + struct rdnss *rdn; + struct dnssl *dns; char prefixbuf[INET6_ADDRSTRLEN]; - int first; struct timeval now; gettimeofday(&now, NULL); /* XXX: unused in most cases */ - for (rai = ralist; rai; rai = rai->next) { - fprintf(fp, "%s:\n", rai->ifname); + TAILQ_FOREACH(rai, &railist, rai_next) { + fprintf(fp, "%s:\n", rai->rai_ifname); fprintf(fp, " Status: %s\n", - (iflist[rai->ifindex]->ifm_flags & IFF_UP) ? "UP" : - "DOWN"); + (iflist[rai->rai_ifindex]->ifm_flags & IFF_UP) ? "UP" : + "DOWN"); /* control information */ - if (rai->lastsent.tv_sec) { + if (rai->rai_lastsent.tv_sec) { /* note that ctime() appends CR by itself */ fprintf(fp, " Last RA sent: %s", - ctime((time_t *)&rai->lastsent.tv_sec)); + ctime((time_t *)&rai->rai_lastsent.tv_sec)); } - if (rai->timer) { + if (rai->rai_timer) fprintf(fp, " Next RA will be sent: %s", - ctime((time_t *)&rai->timer->tm.tv_sec)); - } + ctime((time_t *)&rai->rai_timer->rat_tm.tv_sec)); else fprintf(fp, " RA timer is stopped"); fprintf(fp, " waits: %d, initcount: %d\n", - rai->waiting, rai->initcounter); + rai->rai_waiting, rai->rai_initcounter); /* statistics */ fprintf(fp, " statistics: RA(out/in/inconsistent): " "%llu/%llu/%llu, ", - (unsigned long long)rai->raoutput, - (unsigned long long)rai->rainput, - (unsigned long long)rai->rainconsistent); + (unsigned long long)rai->rai_raoutput, + (unsigned long long)rai->rai_rainput, + (unsigned long long)rai->rai_rainconsistent); fprintf(fp, "RS(input): %llu\n", - (unsigned long long)rai->rsinput); + (unsigned long long)rai->rai_rsinput); /* interface information */ - if (rai->advlinkopt) + if (rai->rai_advlinkopt) fprintf(fp, " Link-layer address: %s\n", - ether_str(rai->sdl)); - fprintf(fp, " MTU: %d\n", rai->phymtu); + ether_str(rai->rai_sdl)); + fprintf(fp, " MTU: %d\n", rai->rai_phymtu); /* Router configuration variables */ fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, " - "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval, - rai->mininterval); - fprintf(fp, " Flags: %s%s%s, ", - rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", ""); + "MinAdvInterval: %d\n", rai->rai_lifetime, + rai->rai_maxinterval, rai->rai_mininterval); + fprintf(fp, " Flags: "); + if (rai->rai_managedflg || rai->rai_otherflg) { + fprintf(fp, "%s", rai->rai_managedflg ? "M" : ""); + fprintf(fp, "%s", rai->rai_otherflg ? "O" : ""); + } else + fprintf(fp, ""); + fprintf(fp, ", "); fprintf(fp, "Preference: %s, ", - rtpref_str[(rai->rtpref >> 3) & 0xff]); - fprintf(fp, "MTU: %d\n", rai->linkmtu); + rtpref_str[(rai->rai_rtpref >> 3) & 0xff]); + fprintf(fp, "MTU: %d\n", rai->rai_linkmtu); fprintf(fp, " ReachableTime: %d, RetransTimer: %d, " - "CurHopLimit: %d\n", rai->reachabletime, - rai->retranstimer, rai->hoplimit); - if (rai->clockskew) + "CurHopLimit: %d\n", rai->rai_reachabletime, + rai->rai_retranstimer, rai->rai_hoplimit); + if (rai->rai_clockskew) fprintf(fp, " Clock skew: %ldsec\n", - rai->clockskew); - for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix; - pfx = pfx->next) { - if (first) { + rai->rai_clockskew); + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + if (pfx == TAILQ_FIRST(&rai->rai_prefix)) fprintf(fp, " Prefixes:\n"); - first = 0; - } fprintf(fp, " %s/%d(", - inet_ntop(AF_INET6, &pfx->prefix, prefixbuf, - sizeof(prefixbuf)), pfx->prefixlen); - switch (pfx->origin) { + inet_ntop(AF_INET6, &pfx->pfx_prefix, prefixbuf, + sizeof(prefixbuf)), pfx->pfx_prefixlen); + switch (pfx->pfx_origin) { case PREFIX_FROM_KERNEL: fprintf(fp, "KERNEL, "); break; @@ -173,36 +176,42 @@ fprintf(fp, "DYNAMIC, "); break; } - if (pfx->validlifetime == ND6_INFINITE_LIFETIME) + if (pfx->pfx_validlifetime == ND6_INFINITE_LIFETIME) fprintf(fp, "vltime: infinity"); else fprintf(fp, "vltime: %ld", - (long)pfx->validlifetime); - if (pfx->vltimeexpire != 0) - fprintf(fp, "(decr,expire %ld), ", (long) - pfx->vltimeexpire > now.tv_sec ? - pfx->vltimeexpire - now.tv_sec : 0); + (long)pfx->pfx_validlifetime); + if (pfx->pfx_vltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", + (long)pfx->pfx_vltimeexpire > now.tv_sec ? + (long)pfx->pfx_vltimeexpire - now.tv_sec : + 0); else fprintf(fp, ", "); - if (pfx->preflifetime == ND6_INFINITE_LIFETIME) + if (pfx->pfx_preflifetime == ND6_INFINITE_LIFETIME) fprintf(fp, "pltime: infinity"); else fprintf(fp, "pltime: %ld", - (long)pfx->preflifetime); - if (pfx->pltimeexpire != 0) - fprintf(fp, "(decr,expire %ld), ", (long) - pfx->pltimeexpire > now.tv_sec ? - pfx->pltimeexpire - now.tv_sec : 0); + (long)pfx->pfx_preflifetime); + if (pfx->pfx_pltimeexpire != 0) + fprintf(fp, "(decr,expire %ld), ", + (long)pfx->pfx_pltimeexpire > now.tv_sec ? + (long)pfx->pfx_pltimeexpire - now.tv_sec : + 0); else fprintf(fp, ", "); - fprintf(fp, "flags: %s%s%s", - pfx->onlinkflg ? "L" : "", - pfx->autoconfflg ? "A" : "", - ""); - if (pfx->timer) { + fprintf(fp, "flags: "); + if (pfx->pfx_onlinkflg || pfx->pfx_autoconfflg) { + fprintf(fp, "%s", + pfx->pfx_onlinkflg ? "L" : ""); + fprintf(fp, "%s", + pfx->pfx_autoconfflg ? "A" : ""); + } else + fprintf(fp, ""); + if (pfx->pfx_timer) { struct timeval *rest; - rest = rtadvd_timer_rest(pfx->timer); + rest = rtadvd_timer_rest(pfx->pfx_timer); if (rest) { /* XXX: what if not? */ fprintf(fp, ", expire in: %ld", (long)rest->tv_sec); @@ -211,31 +220,64 @@ fprintf(fp, ")\n"); } #ifdef ROUTEINFO - for (first = 1, rti = rai->route.next; rti != &rai->route; - rti = rti->next) { - if (first) { + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + if (rti == TAILQ_FIRST(&rai->rai_route)) fprintf(fp, " Route Information:\n"); - first = 0; - } fprintf(fp, " %s/%d (", - inet_ntop(AF_INET6, &rti->prefix, - prefixbuf, sizeof(prefixbuf)), - rti->prefixlen); + inet_ntop(AF_INET6, &rti->rti_prefix, + prefixbuf, sizeof(prefixbuf)), + rti->rti_prefixlen); fprintf(fp, "preference: %s, ", - rtpref_str[0xff & (rti->rtpref >> 3)]); - if (rti->ltime == ND6_INFINITE_LIFETIME) + rtpref_str[0xff & (rti->rti_rtpref >> 3)]); + if (rti->rti_ltime == ND6_INFINITE_LIFETIME) fprintf(fp, "lifetime: infinity"); else - fprintf(fp, "lifetime: %ld", (long)rti->ltime); + fprintf(fp, "lifetime: %ld", + (long)rti->rti_ltime); fprintf(fp, ")\n"); } #endif + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + struct rdnss_addr *rdna; + + if (rdn == TAILQ_FIRST(&rai->rai_rdnss)) + fprintf(fp, " Recursive DNS servers:\n" + " Lifetime\tServers\n"); + + fprintf(fp, " %8u\t", rdn->rd_ltime); + TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) { + inet_ntop(AF_INET6, &rdna->ra_dns, + prefixbuf, sizeof(prefixbuf)); + + if (rdna != TAILQ_FIRST(&rdn->rd_list)) + fprintf(fp, " \t"); + fprintf(fp, "%s\n", prefixbuf); + } + fprintf(fp, "\n"); + } + + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + struct dnssl_addr *dnsa; + char buf[NI_MAXHOST]; + + if (dns == TAILQ_FIRST(&rai->rai_dnssl)) + fprintf(fp, " DNS search list:\n" + " Lifetime\tDomains\n"); + + fprintf(fp, " %8u\t", dns->dn_ltime); + TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) { + dname_labeldec(buf, sizeof(buf), dnsa->da_dom); + if (dnsa != TAILQ_FIRST(&dns->dn_list)) + fprintf(fp, " \t"); + fprintf(fp, "%s(%d)\n", buf, dnsa->da_len); + } + fprintf(fp, "\n"); + } } } void -rtadvd_dump_file(dumpfile) - char *dumpfile; +rtadvd_dump_file(const char *dumpfile) { syslog(LOG_DEBUG, "<%s> dump current status to %s", __func__, dumpfile); @@ -250,3 +292,30 @@ fclose(fp); } + +/* 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++ = '.'; + syslog(LOG_DEBUG, "<%s> labellen = %zd", __func__, len); + memcpy(dst, src, len); + src += len; + dst += len; + } + *dst = '\0'; + + return (src - src_origin); +} Index: usr.sbin/rtadvd/rrenum.c =================================================================== --- usr.sbin/rtadvd/rrenum.c (revision 222972) +++ usr.sbin/rtadvd/rrenum.c (working copy) @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -74,7 +75,7 @@ /* * Check validity of a Prefix Control Operation(PCO). - * Return 0 on success, 1 on failure. + * return 0 on success, 1 on failure. */ static int rr_pco_check(int len, struct rr_pco_match *rpm) @@ -86,8 +87,8 @@ if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", - __func__, rpm->rpm_len); - return 1; + __func__, rpm->rpm_len); + return (1); } /* rpm->rpm_code must be valid value */ switch (rpm->rpm_code) { @@ -97,14 +98,14 @@ break; default: syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__, - rpm->rpm_code); - return 1; + rpm->rpm_code); + return (1); } /* rpm->rpm_matchlen must be 0 to 128 inclusive */ if (rpm->rpm_matchlen > 128) { syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", - __func__, rpm->rpm_matchlen); - return 1; + __func__, rpm->rpm_matchlen); + return (1); } /* @@ -126,23 +127,22 @@ */ if (checklen > 128) { syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" - " rpu_keeplen %d is %d(over 128)", - __func__, rpu->rpu_uselen, - rpu->rpu_keeplen, - rpu->rpu_uselen + rpu->rpu_keeplen); - return 1; + " rpu_keeplen %d is %d(over 128)", + __func__, rpu->rpu_uselen, rpu->rpu_keeplen, + rpu->rpu_uselen + rpu->rpu_keeplen); + return (1); } } - return 0; + return (0); } static void do_use_prefix(int len, struct rr_pco_match *rpm, - struct in6_rrenumreq *irr, int ifindex) + struct in6_rrenumreq *irr, int ifindex) { struct rr_pco_use *rpu, *rpulim; struct rainfo *rai; - struct prefix *pp; + struct prefix *pfx; rpu = (struct rr_pco_use *)(rpm + 1); rpulim = (struct rr_pco_use *)((char *)rpm + len); @@ -164,7 +164,7 @@ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, - strerror(errno)); + strerror(errno)); return; } @@ -176,19 +176,23 @@ irr->irr_u_uselen = rpu->rpu_uselen; irr->irr_u_keeplen = rpu->rpu_keeplen; irr->irr_raf_mask_onlink = - !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); irr->irr_raf_mask_auto = - !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); + !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); irr->irr_vltime = ntohl(rpu->rpu_vltime); irr->irr_pltime = ntohl(rpu->rpu_pltime); irr->irr_raf_onlink = - (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1; + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? + 0 : 1; irr->irr_raf_auto = - (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1; + (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? + 0 : 1; irr->irr_rrf_decrvalid = - (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1; + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? + 0 : 1; irr->irr_rrf_decrprefd = - (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1; + (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? + 0 : 1; irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); irr->irr_useprefix.sin6_family = AF_INET6; irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; @@ -196,7 +200,7 @@ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, - strerror(errno)); + strerror(errno)); /* very adhoc: should be rewritten */ if (rpm->rpm_code == RPM_PCO_CHANGE && @@ -206,28 +210,31 @@ if ((rai = if_indextorainfo(ifindex)) == NULL) continue; /* non-advertising IF */ - for (pp = rai->prefix.next; pp != &rai->prefix; - pp = pp->next) { + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { struct timeval now; - if (prefix_match(&pp->prefix, pp->prefixlen, - &rpm->rpm_prefix, - rpm->rpm_matchlen)) { + if (prefix_match(&pfx->pfx_prefix, + pfx->pfx_prefixlen, &rpm->rpm_prefix, + rpm->rpm_matchlen)) { /* change parameters */ - pp->validlifetime = ntohl(rpu->rpu_vltime); - pp->preflifetime = ntohl(rpu->rpu_pltime); + pfx->pfx_validlifetime = + ntohl(rpu->rpu_vltime); + pfx->pfx_preflifetime = + ntohl(rpu->rpu_pltime); if (irr->irr_rrf_decrvalid) { gettimeofday(&now, 0); - pp->vltimeexpire = - now.tv_sec + pp->validlifetime; + pfx->pfx_vltimeexpire = + now.tv_sec + + pfx->pfx_validlifetime; } else - pp->vltimeexpire = 0; + pfx->pfx_vltimeexpire = 0; if (irr->irr_rrf_decrprefd) { gettimeofday(&now, 0); - pp->pltimeexpire = - now.tv_sec + pp->preflifetime; + pfx->pfx_pltimeexpire = + now.tv_sec + + pfx->pfx_preflifetime; } else - pp->pltimeexpire = 0; + pfx->pfx_pltimeexpire = 0; } } } @@ -245,11 +252,11 @@ struct in6_rrenumreq irr; if ((rr_pco_check(len, rpm) != 0)) - return 1; + return (1); if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + strerror(errno)); exit(1); } @@ -264,8 +271,8 @@ while (if_indextoname(++ifindex, irr.irr_name)) { /* - * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off, - * the interface is not applied + * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and + * IFF_UP is off, the interface is not applied */ if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 && (iflist[ifindex]->ifm_flags & IFF_UP) == 0) @@ -274,13 +281,13 @@ do_use_prefix(len, rpm, &irr, ifindex); } if (errno == ENXIO) - return 0; + return (0); else if (errno) { syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__, - strerror(errno)); - return 1; + strerror(errno)); + return (1); } - return 0; + return (0); } /* @@ -305,11 +312,11 @@ int rpmlen; rpm = (struct rr_pco_match *)cp; - if (len < sizeof(struct rr_pco_match)) { + if ((size_t)len < sizeof(struct rr_pco_match)) { tooshort: syslog(LOG_ERR, "<%s> pkt too short. left len = %d. " - "gabage at end of pkt?", __func__, len); - return 1; + "gabage at end of pkt?", __func__, len); + return (1); } rpmlen = rpm->rpm_len << 3; if (len < rpmlen) @@ -325,7 +332,7 @@ len -= rpmlen; } - return 0; + return (0); } /* @@ -334,46 +341,45 @@ */ static int rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, - struct in6_addr *dst) + struct in6_addr *dst) { u_char ntopbuf[INET6_ADDRSTRLEN]; /* omit rr minimal length check. hope kernel have done it. */ /* rr_command length check */ - if (len < (sizeof(struct icmp6_router_renum) + - sizeof(struct rr_pco_match))) { + if ((size_t)len < (sizeof(struct icmp6_router_renum) + + sizeof(struct rr_pco_match))) { syslog(LOG_ERR, "<%s> rr_command len %d is too short", - __func__, len); - return 1; + __func__, len); + return (1); } /* destination check. only for multicast. omit unicast check. */ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && !IN6_IS_ADDR_MC_SITELOCAL(dst)) { syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", - __func__, - inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN)); - return 1; + __func__, + inet_ntop(AF_INET6, dst, ntopbuf, sizeof(ntopbuf))); + return (1); } /* seqnum and segnum check */ if (rro.rro_seqnum > rr->rr_seqnum) { syslog(LOG_WARNING, - "<%s> rcvd old seqnum %d from %s", - __func__, (u_int32_t)ntohl(rr->rr_seqnum), - inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN)); - return 1; + "<%s> rcvd old seqnum %d from %s", + __func__, (u_int32_t)ntohl(rr->rr_seqnum), + inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf))); + return (1); } if (rro.rro_seqnum == rr->rr_seqnum && (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) syslog(LOG_WARNING, - "<%s> rcvd duped segnum %d from %s", - __func__, rr->rr_segnum, - inet_ntop(AF_INET6, from, ntopbuf, - INET6_ADDRSTRLEN)); - return 0; + "<%s> rcvd duped segnum %d from %s", + __func__, rr->rr_segnum, inet_ntop(AF_INET6, from, + ntopbuf, sizeof(ntopbuf))); + return (0); } /* update seqnum */ @@ -382,16 +388,16 @@ /* init rro_segnum_bits */ memset(rro.rro_segnum_bits, 0, - sizeof(rro.rro_segnum_bits)); + sizeof(rro.rro_segnum_bits)); } rro.rro_seqnum = rr->rr_seqnum; - return 0; + return (0); } static void rr_command_input(int len, struct icmp6_router_renum *rr, - struct in6_addr *from, struct in6_addr *dst) + struct in6_addr *from, struct in6_addr *dst) { /* rr_command validity check */ if (rr_command_check(len, rr, from, dst)) @@ -401,9 +407,8 @@ return; /* do router renumbering */ - if (do_rr(len, rr)) { + if (do_rr(len, rr)) goto failed; - } /* update segnum */ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); @@ -417,27 +422,26 @@ void rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, - struct sockaddr_in6 *from, struct in6_addr *dst) + struct sockaddr_in6 *from, struct in6_addr *dst) { u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; syslog(LOG_DEBUG, - "<%s> RR received from %s to %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf[0], INET6_ADDRSTRLEN), - inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> RR received from %s to %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0] ,sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* packet validation based on Section 4.1 of RFC2894 */ - if (len < sizeof(struct icmp6_router_renum)) { + if ((size_t)len < sizeof(struct icmp6_router_renum)) { syslog(LOG_NOTICE, - "<%s>: RR short message (size %d) from %s to %s on %s", - __func__, len, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf[0], INET6_ADDRSTRLEN), - inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s>: RR short message (size %d) from %s to %s on %s", + __func__, len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0], + sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -449,16 +453,16 @@ * We rely on the kernel input routine for unicast addresses, and thus * check multicast destinations only. */ - if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && - !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) { + if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && !IN6_ARE_ADDR_EQUAL( + &sin6_sitelocal_allrouters.sin6_addr, &pi->ipi6_addr)) { syslog(LOG_NOTICE, - "<%s>: RR message with invalid destination (%s) " - "from %s on %s", - __func__, - inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN), - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf[1], INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s>: RR message with invalid destination (%s) " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &dst, ntopbuf[0], sizeof(ntopbuf[0])), + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1], + sizeof(ntopbuf[1])), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -477,7 +481,7 @@ break; default: syslog(LOG_ERR, "<%s> received unknown code %d", - __func__, rr->rr_code); + __func__, rr->rr_code); break; } Index: usr.sbin/rtadvd/rtadvd.8 =================================================================== --- usr.sbin/rtadvd/rtadvd.8 (revision 222972) +++ usr.sbin/rtadvd/rtadvd.8 (working copy) @@ -37,9 +37,10 @@ .Nd router advertisement daemon .Sh SYNOPSIS .Nm -.Op Fl dDfMRs +.Op Fl dDfRs .Op Fl c Ar configfile .Op Fl F Ar dumpfile +.Op Fl M Ar ifname .Op Fl p Ar pidfile .Ar interface ... .Sh DESCRIPTION @@ -103,7 +104,7 @@ above will be suppressed. .Pp Basically, hosts MUST NOT send Router Advertisement messages at any -time (RFC 2461, Section 6.2.3). +time (RFC 4861, Section 6.2.3). However, it would sometimes be useful to allow hosts to advertise some parameters such as prefix information and link MTU. Thus, @@ -168,6 +169,18 @@ .Fl F . .Pp Use +.Dv SIGHUP +to reload the configuration file +.Pa /etc/rtadvd.conf . +If an invalid parameter is found in the configuration file upon the reload, +the entry will be ignored and the old configuration will be used. +When parameters in an existing entry are updated, +.Nm +will send Router Advertisement messages with the old configuration but +zero router lifetime to the interface first, and then start to send a new +message. +.Pp +Use .Dv SIGTERM to kill .Nm @@ -176,7 +189,7 @@ .Nm will transmit router advertisement with router lifetime 0 to all the interfaces -.Pq in accordance with RFC2461 6.2.5 . +.Pq in accordance with RFC 4861 6.2.5 . .Sh FILES .Bl -tag -width Pa -compact .It Pa /etc/rtadvd.conf @@ -193,6 +206,34 @@ .Sh SEE ALSO .Xr rtadvd.conf 5 , .Xr rtsol 8 +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%A Hesham Soliman +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 4861 +.Re +.Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 2461 (obsoleted by RFC 4861) +.Re +.Rs +.%A Richard Draves +.%T Default Router Preferences and More-Specific Routes +.%R draft-ietf-ipngwg-router-selection-xx.txt +.Re +.Rs +.%A J. Jeong +.%A S. Park +.%A L. Beloeil +.%A S. Madanapalli +.%T IPv6 Router Advertisement Options for DNS Configuration +.%R RFC 6106 +.Re .Sh HISTORY The .Nm Index: usr.sbin/rtadvd/config.h =================================================================== --- usr.sbin/rtadvd/config.h (revision 222972) +++ usr.sbin/rtadvd/config.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -30,7 +30,9 @@ * SUCH DAMAGE. */ -extern void getconfig(char *); +extern int getconfig(int); +extern int rmconfig(int); +extern int loadconfig(char *[], const int); extern void delete_prefix(struct prefix *); extern void invalidate_prefix(struct prefix *); extern void update_prefix(struct prefix *); @@ -45,3 +47,5 @@ */ #define MAXPREFIX 100 #define MAXROUTE 100 +#define MAXRDNSSENT 100 +#define MAXDNSSLENT 100 Index: usr.sbin/rtadvd/dump.h =================================================================== --- usr.sbin/rtadvd/dump.h (revision 222972) +++ usr.sbin/rtadvd/dump.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -30,4 +30,4 @@ * SUCH DAMAGE. */ -extern void rtadvd_dump_file(char *); +extern void rtadvd_dump_file(const char *); Index: usr.sbin/rtadvd/rrenum.h =================================================================== --- usr.sbin/rtadvd/rrenum.h (revision 222972) +++ usr.sbin/rtadvd/rrenum.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE Index: usr.sbin/rtadvd/timer.c =================================================================== --- usr.sbin/rtadvd/timer.c (revision 222972) +++ usr.sbin/rtadvd/timer.c (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,6 +31,7 @@ */ #include +#include #include #include @@ -39,21 +40,19 @@ #include #include "timer.h" -static struct rtadvd_timer timer_head; - #define MILLION 1000000 -#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\ - (t1)->tv_usec == (t2)->tv_usec) -static struct timeval tm_max = {0x7fffffff, 0x7fffffff}; +struct rtadvd_timer_head_t ra_timer = + TAILQ_HEAD_INITIALIZER(ra_timer); +static struct timeval tm_limit = {0x7fffffff, 0x7fffffff}; +static struct timeval tm_max; void -rtadvd_timer_init() +rtadvd_timer_init(void) { - memset(&timer_head, 0, sizeof(timer_head)); - timer_head.next = timer_head.prev = &timer_head; - timer_head.tm = tm_max; + tm_max = tm_limit; + TAILQ_INIT(&ra_timer); } struct rtadvd_timer * @@ -61,54 +60,57 @@ void (*update)(void *, struct timeval *), void *timeodata, void *updatedata) { - struct rtadvd_timer *newtimer; + struct rtadvd_timer *rat; - if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + if (timeout == NULL) { syslog(LOG_ERR, - "<%s> can't allocate memory", __func__); + "<%s> timeout function unspecified", __func__); exit(1); } - memset(newtimer, 0, sizeof(*newtimer)); - - if (timeout == NULL) { + rat = malloc(sizeof(*rat)); + if (rat == NULL) { syslog(LOG_ERR, - "<%s> timeout function unspecified", __func__); + "<%s> can't allocate memory", __func__); exit(1); } - newtimer->expire = timeout; - newtimer->update = update; - newtimer->expire_data = timeodata; - newtimer->update_data = updatedata; - newtimer->tm = tm_max; + memset(rat, 0, sizeof(*rat)); + rat->rat_expire = timeout; + rat->rat_update = update; + rat->rat_expire_data = timeodata; + rat->rat_update_data = updatedata; + rat->rat_tm = tm_max; + /* link into chain */ - insque(newtimer, &timer_head); + TAILQ_INSERT_TAIL(&ra_timer, rat, rat_next); - return(newtimer); + return (rat); } void -rtadvd_remove_timer(struct rtadvd_timer **timer) +rtadvd_remove_timer(struct rtadvd_timer *rat) { - remque(*timer); - free(*timer); - *timer = NULL; + + if (rat == NULL) + return; + + TAILQ_REMOVE(&ra_timer, rat, rat_next); + free(rat); } void -rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer) +rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *rat) { struct timeval now; /* reset the timer */ gettimeofday(&now, NULL); + TIMEVAL_ADD(&now, tm, &rat->rat_tm); - TIMEVAL_ADD(&now, tm, &timer->tm); - /* update the next expiration time */ - if (TIMEVAL_LT(timer->tm, timer_head.tm)) - timer_head.tm = timer->tm; + if (TIMEVAL_LT(&rat->rat_tm, &tm_max)) + tm_max = rat->rat_tm; return; } @@ -119,58 +121,52 @@ * Return the next interval for select() call. */ struct timeval * -rtadvd_check_timer() +rtadvd_check_timer(void) { static struct timeval returnval; struct timeval now; - struct rtadvd_timer *tm = timer_head.next, *tm_next; + struct rtadvd_timer *rat; gettimeofday(&now, NULL); - - timer_head.tm = tm_max; - - for (tm = timer_head.next; tm != &timer_head; tm = tm_next) { - tm_next = tm->next; - - if (TIMEVAL_LEQ(tm->tm, now)) { - if (((*tm->expire)(tm->expire_data) == NULL)) + tm_max = tm_limit; + TAILQ_FOREACH(rat, &ra_timer, rat_next) { + if (TIMEVAL_LEQ(&rat->rat_tm, &now)) { + if (((*rat->rat_expire)(rat->rat_expire_data) == NULL)) continue; /* the timer was removed */ - if (tm->update) - (*tm->update)(tm->update_data, &tm->tm); - TIMEVAL_ADD(&tm->tm, &now, &tm->tm); + if (rat->rat_update) + (*rat->rat_update)(rat->rat_update_data, &rat->rat_tm); + TIMEVAL_ADD(&rat->rat_tm, &now, &rat->rat_tm); } - - if (TIMEVAL_LT(tm->tm, timer_head.tm)) - timer_head.tm = tm->tm; + if (TIMEVAL_LT(&rat->rat_tm, &tm_max)) + tm_max = rat->rat_tm; } - - if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) { + if (TIMEVAL_EQUAL(&tm_max, &tm_limit)) { /* no need to timeout */ - return(NULL); - } else if (TIMEVAL_LT(timer_head.tm, now)) { + return (NULL); + } else if (TIMEVAL_LT(&tm_max, &now)) { /* this may occur when the interval is too small */ returnval.tv_sec = returnval.tv_usec = 0; } else - TIMEVAL_SUB(&timer_head.tm, &now, &returnval); - return(&returnval); + TIMEVAL_SUB(&tm_max, &now, &returnval); + return (&returnval); } struct timeval * -rtadvd_timer_rest(struct rtadvd_timer *timer) +rtadvd_timer_rest(struct rtadvd_timer *rat) { static struct timeval returnval, now; gettimeofday(&now, NULL); - if (TIMEVAL_LEQ(timer->tm, now)) { + if (TIMEVAL_LEQ(&rat->rat_tm, &now)) { syslog(LOG_DEBUG, - "<%s> a timer must be expired, but not yet", - __func__); + "<%s> a timer must be expired, but not yet", + __func__); returnval.tv_sec = returnval.tv_usec = 0; } else - TIMEVAL_SUB(&timer->tm, &now, &returnval); + TIMEVAL_SUB(&rat->rat_tm, &now, &returnval); - return(&returnval); + return (&returnval); } /* result = a + b */ Index: usr.sbin/rtadvd/rtadvd.c =================================================================== --- usr.sbin/rtadvd/rtadvd.c (revision 222972) +++ usr.sbin/rtadvd/rtadvd.c (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,12 +31,15 @@ */ #include +#include #include #include #include #include +#include #include +#include #include #include #include @@ -46,12 +49,17 @@ #include +#include +#include +#include + #include #include #include #include #include #include +#include #include #include #include @@ -66,6 +74,7 @@ #include "if.h" #include "config.h" #include "dump.h" +#include "pathnames.h" struct msghdr rcvmhdr; static u_char *rcvcmsgbuf; @@ -74,30 +83,32 @@ static size_t sndcmsgbuflen; volatile sig_atomic_t do_dump; volatile sig_atomic_t do_die; +volatile sig_atomic_t do_reload; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; struct sockaddr_in6 rcvfrom; -struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; -struct in6_addr in6a_site_allrouters; -static char *dumpfilename = "/var/run/rtadvd.dump"; -static char *pidfilename = "/var/run/rtadvd.pid"; +static const char *dumpfilename = _PATH_RTADVDDUMP; +static const char *pidfilename = _PATH_RTADVDPID; +const char *conffile = _PATH_RTADVDCONF; static struct pidfh *pfh; static char *mcastif; int sock; int rtsock = -1; int accept_rr = 0; int dflag = 0, sflag = 0; +static int ifl_len; +static char **ifl_names; -u_char *conffile = NULL; +struct railist_head_t railist = + TAILQ_HEAD_INITIALIZER(railist); -struct rainfo *ralist = NULL; struct nd_optlist { - struct nd_optlist *next; - struct nd_opt_hdr *opt; + TAILQ_ENTRY(nd_optlist) nol_next; + struct nd_opt_hdr *nol_opt; }; -union nd_opts { - struct nd_opt_hdr *nd_opt_array[9]; +union nd_opt { + struct nd_opt_hdr *opt_array[9]; struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; @@ -105,51 +116,76 @@ struct nd_opt_prefix_info *pi; struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; - struct nd_optlist *list; + TAILQ_HEAD(, nd_optlist) opt_list; } nd_opt_each; }; -#define nd_opts_src_lladdr nd_opt_each.src_lladdr -#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr -#define nd_opts_pi nd_opt_each.pi -#define nd_opts_rh nd_opt_each.rh -#define nd_opts_mtu nd_opt_each.mtu -#define nd_opts_list nd_opt_each.list +#define opt_src_lladdr nd_opt_each.src_lladdr +#define opt_tgt_lladdr nd_opt_each.tgt_lladdr +#define opt_pi nd_opt_each.pi +#define opt_rh nd_opt_each.rh +#define opt_mtu nd_opt_each.mtu +#define opt_list nd_opt_each.opt_list -#define NDOPT_FLAG_SRCLINKADDR 0x1 -#define NDOPT_FLAG_TGTLINKADDR 0x2 -#define NDOPT_FLAG_PREFIXINFO 0x4 -#define NDOPT_FLAG_RDHDR 0x8 -#define NDOPT_FLAG_MTU 0x10 +#define NDOPT_FLAG_SRCLINKADDR (1 << 0) +#define NDOPT_FLAG_TGTLINKADDR (1 << 1) +#define NDOPT_FLAG_PREFIXINFO (1 << 2) +#define NDOPT_FLAG_RDHDR (1 << 3) +#define NDOPT_FLAG_MTU (1 << 4) +#define NDOPT_FLAG_RDNSS (1 << 5) +#define NDOPT_FLAG_DNSSL (1 << 6) u_int32_t ndopt_flags[] = { - 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR, - NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU, + [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, + [ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR, + [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, + [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, + [ND_OPT_MTU] = NDOPT_FLAG_MTU, + [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, + [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, }; -int main(int, char *[]); -static void set_die(int); -static void die(void); -static void sock_open(void); -static void rtsock_open(void); -static void rtadvd_input(void); -static void rs_input(int, struct nd_router_solicit *, - struct in6_pktinfo *, struct sockaddr_in6 *); -static void ra_input(int, struct nd_router_advert *, - struct in6_pktinfo *, struct sockaddr_in6 *); -static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, - struct sockaddr_in6 *); -static int nd6_options(struct nd_opt_hdr *, int, - union nd_opts *, u_int32_t); -static void free_ndopts(union nd_opts *); -static void ra_output(struct rainfo *); -static void rtmsg_input(void); -static void rtadvd_set_dump_file(int); -static void set_short_delay(struct rainfo *); +struct sockaddr_in6 sin6_linklocal_allnodes = { + .sin6_len = sizeof(sin6_linklocal_allnodes), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, +}; +struct sockaddr_in6 sin6_linklocal_allrouters = { + .sin6_len = sizeof(sin6_linklocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, +}; + +struct sockaddr_in6 sin6_sitelocal_allrouters = { + .sin6_len = sizeof(sin6_sitelocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT, +}; + +static void set_reload(int); +static void set_die(int); +static void die(void); +static void sock_open(void); +static void rtsock_open(void); +static void rtadvd_input(void); +static void rs_input(int, struct nd_router_solicit *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static void ra_input(int, struct nd_router_advert *, + struct in6_pktinfo *, struct sockaddr_in6 *); +static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, + struct sockaddr_in6 *); +static int nd6_options(struct nd_opt_hdr *, int, + union nd_opt *, u_int32_t); +static void free_ndopts(union nd_opt *); +static void rtmsg_input(void); +static void rtadvd_set_dump_file(int); +static void set_short_delay(struct rainfo *); +static int ifl_lookup(char *, char **, int); +static int check_accept_rtadv(int); +static int getinet6sysctl(int); + int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char *argv[]) { #ifdef HAVE_POLL_H struct pollfd set[2]; @@ -164,16 +200,16 @@ pid_t pid, otherpid; /* get command line options and arguments */ - while ((ch = getopt(argc, argv, "c:dDF:fMp:Rs")) != -1) { + while ((ch = getopt(argc, argv, "c:dDfF:M:p:Rs")) != -1) { switch (ch) { case 'c': conffile = optarg; break; case 'd': - dflag = 1; + dflag++; break; case 'D': - dflag = 2; + dflag += 2; break; case 'f': fflag = 1; @@ -202,8 +238,9 @@ argv += optind; if (argc == 0) { fprintf(stderr, - "usage: rtadvd [-dDfMRs] [-c conffile] " - "[-F dumpfile] [-p pidfile] interfaces...\n"); + "usage: rtadvd [-dDfRs] [-c conffile] " + "[-F dumpfile] [-M ifname] " + "[-p pidfile] interfaces...\n"); exit(1); } @@ -213,10 +250,12 @@ openlog("rtadvd", logopt, LOG_DAEMON); /* set log level */ - if (dflag == 0) + if (dflag > 1) + (void)setlogmask(LOG_UPTO(LOG_DEBUG)); + else if (dflag > 0) + (void)setlogmask(LOG_UPTO(LOG_INFO)); + else (void)setlogmask(LOG_UPTO(LOG_ERR)); - if (dflag == 1) - (void)setlogmask(LOG_UPTO(LOG_INFO)); /* timer initialization */ rtadvd_timer_init(); @@ -229,18 +268,13 @@ srandom((u_long)time(NULL)); #endif #endif - /* get iflist block from kernel */ init_iflist(); + ifl_names = argv; + ifl_len = argc; - while (argc--) - getconfig(*argv++); + loadconfig(argv, argc); - if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) { - fprintf(stderr, "fatal: inet_pton failed\n"); - exit(1); - } - pfh = pidfile_open(pidfilename, 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) @@ -292,15 +326,14 @@ if (rtsock >= 0) FD_SET(rtsock, fdsetp); #endif - signal(SIGTERM, set_die); signal(SIGUSR1, rtadvd_set_dump_file); + signal(SIGHUP, set_reload); while (1) { #ifndef HAVE_POLL_H memcpy(selectfdp, fdsetp, fdmasks); /* reinitialize */ #endif - if (do_dump) { /* SIGUSR1 */ do_dump = 0; rtadvd_dump_file(dumpfilename); @@ -311,6 +344,11 @@ /*NOTREACHED*/ } + if (do_reload) { + loadconfig(argv, argc); + do_reload = 0; + } + /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); @@ -325,7 +363,6 @@ "<%s> there's no timer. waiting for inputs", __func__); } - #ifdef HAVE_POLL_H if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM)) < 0) @@ -358,75 +395,95 @@ exit(0); /* NOTREACHED */ } +static int +ifl_lookup(char *ifn, char **names, int len) +{ + while (len--) + if (strncmp(names[len], ifn, IFNAMSIZ) == 0) + return (0); + return (-1); +} + static void -rtadvd_set_dump_file(sig) - int sig; +rtadvd_set_dump_file(int sig __unused) { + do_dump = 1; } static void -set_die(sig) - int sig; +set_reload(int sig __unused) { + + do_reload = 1; +} + +static void +set_die(int sig __unused) +{ + do_die = 1; } static void -die() +die(void) { - struct rainfo *ra; + struct rainfo *rai; + struct rdnss *rdn; + struct dnssl *dns; int i; const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; - if (dflag > 1) { - syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n", - __func__); - } + syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n", + __func__); - for (ra = ralist; ra; ra = ra->next) { - ra->lifetime = 0; - make_packet(ra); + TAILQ_FOREACH(rai, &railist, rai_next) { + rai->rai_lifetime = 0; + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) + dns->dn_ltime = 0; + make_packet(rai); } for (i = 0; i < retrans; i++) { - for (ra = ralist; ra; ra = ra->next) - ra_output(ra); + TAILQ_FOREACH(rai, &railist, rai_next) + ra_output(rai); sleep(MIN_DELAY_BETWEEN_RAS); } pidfile_remove(pfh); + exit(0); - /*NOTREACHED*/ } static void -rtmsg_input() +rtmsg_input(void) { int n, type, ifindex = 0, plen; size_t len; char msg[2048], *next, *lim; - u_char ifname[IF_NAMESIZE]; - struct prefix *prefix; + u_char ifname[IFNAMSIZ]; + struct if_announcemsghdr *ifan; + struct prefix *pfx; struct rainfo *rai; struct in6_addr *addr; char addrbuf[INET6_ADDRSTRLEN]; int prefixchange = 0; + int error; n = read(rtsock, msg, sizeof(msg)); - if (dflag > 1) { - syslog(LOG_DEBUG, "<%s> received a routing message " - "(type = %d, len = %d)", __func__, rtmsg_type(msg), n); - } + syslog(LOG_DEBUG, "<%s> received a routing message " + "(type = %d, len = %d)", __func__, rtmsg_type(msg), n); + if (n > rtmsg_len(msg)) { /* - * This usually won't happen for messages received on + * This usually won't happen for messages received on * a routing socket. */ - if (dflag > 1) - syslog(LOG_DEBUG, - "<%s> received data length is larger than " - "1st routing message len. multiple messages? " - "read %d bytes, but 1st msg len = %d", - __func__, n, rtmsg_len(msg)); + syslog(LOG_DEBUG, + "<%s> received data length is larger than " + "1st routing message len. multiple messages? " + "read %d bytes, but 1st msg len = %d", + __func__, n, rtmsg_len(msg)); #if 0 /* adjust length */ n = rtmsg_len(msg); @@ -438,11 +495,12 @@ int oldifflags; next = get_next_msg(next, lim, 0, &len, - RTADV_TYPE2BITMASK(RTM_ADD) | - RTADV_TYPE2BITMASK(RTM_DELETE) | - RTADV_TYPE2BITMASK(RTM_NEWADDR) | - RTADV_TYPE2BITMASK(RTM_DELADDR) | - RTADV_TYPE2BITMASK(RTM_IFINFO)); + RTADV_TYPE2BITMASK(RTM_ADD) | + RTADV_TYPE2BITMASK(RTM_DELETE) | + RTADV_TYPE2BITMASK(RTM_NEWADDR) | + RTADV_TYPE2BITMASK(RTM_DELADDR) | + RTADV_TYPE2BITMASK(RTM_IFINFO) | + RTADV_TYPE2BITMASK(RTM_IFANNOUNCE)); if (len == 0) break; type = rtmsg_type(next); @@ -458,25 +516,65 @@ case RTM_IFINFO: ifindex = get_ifm_ifindex(next); break; - default: - /* should not reach here */ - if (dflag > 1) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)next; + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + case IFAN_DEPARTURE: + break; + default: syslog(LOG_DEBUG, - "<%s:%d> unknown rtmsg %d on %s", - __func__, __LINE__, type, - if_indextoname(ifindex, ifname)); + "<%s:%d> unknown ifan msg (ifan_what=%d)", + __func__, __LINE__, ifan->ifan_what); + continue; } + + syslog(LOG_INFO, "<%s>: if_announcemsg (idx=%d:%d)", + __func__, ifan->ifan_index, ifan->ifan_what); + init_iflist(); + error = ifl_lookup(ifan->ifan_name, + ifl_names, ifl_len); + if (error) { + syslog(LOG_INFO, "<%s>: not a target " + "interface (idx=%d)", __func__, + ifan->ifan_index); + continue; + } + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + error = getconfig(ifan->ifan_index); + if (error) + syslog(LOG_ERR, + "<%s>: getconfig failed (idx=%d)" + " Ignored.", __func__, + ifan->ifan_index); + break; + case IFAN_DEPARTURE: + error = rmconfig(ifan->ifan_index); + if (error) + syslog(LOG_ERR, + "<%s>: rmconfig failed (idx=%d)" + " Ignored.", __func__, + ifan->ifan_index); + break; + } continue; + default: + /* should not reach here */ + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); + continue; } if ((rai = if_indextorainfo(ifindex)) == NULL) { - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s> route changed on " - "non advertising interface(%s)", - __func__, - if_indextoname(ifindex, ifname)); - } + syslog(LOG_DEBUG, + "<%s> route changed on " + "non advertising interface(%s)", + __func__, + if_indextoname(ifindex, ifname)); continue; } oldifflags = iflist[ifindex]->ifm_flags; @@ -500,25 +598,25 @@ __func__, plen); break; } - prefix = find_prefix(rai, addr, plen); - if (prefix) { - if (prefix->timer) { + pfx = find_prefix(rai, addr, plen); + if (pfx) { + if (pfx->pfx_timer) { /* * If the prefix has been invalidated, * make it available again. */ - update_prefix(prefix); + update_prefix(pfx); prefixchange = 1; - } else if (dflag > 1) { + } else syslog(LOG_DEBUG, "<%s> new prefix(%s/%d) " "added on %s, " "but it was already in list", __func__, inet_ntop(AF_INET6, addr, - (char *)addrbuf, INET6_ADDRSTRLEN), - plen, rai->ifname); - } + (char *)addrbuf, + sizeof(addrbuf)), + plen, rai->rai_ifname); break; } make_prefix(rai, ifindex, addr, plen); @@ -543,21 +641,17 @@ __func__, plen); break; } - prefix = find_prefix(rai, addr, plen); - if (prefix == NULL) { - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s> prefix(%s/%d) was " - "deleted on %s, " - "but it was not in list", - __func__, - inet_ntop(AF_INET6, addr, - (char *)addrbuf, INET6_ADDRSTRLEN), - plen, rai->ifname); - } + pfx = find_prefix(rai, addr, plen); + if (pfx == NULL) { + syslog(LOG_DEBUG, + "<%s> prefix(%s/%d) was deleted on %s, " + "but it was not in list", + __func__, inet_ntop(AF_INET6, addr, + (char *)addrbuf, sizeof(addrbuf)), + plen, rai->rai_ifname); break; } - invalidate_prefix(prefix); + invalidate_prefix(pfx); prefixchange = 1; break; case RTM_NEWADDR: @@ -571,12 +665,10 @@ break; default: /* should not reach here */ - if (dflag > 1) { - syslog(LOG_DEBUG, - "<%s:%d> unknown rtmsg %d on %s", - __func__, __LINE__, type, - if_indextoname(ifindex, ifname)); - } + syslog(LOG_DEBUG, + "<%s:%d> unknown rtmsg %d on %s", + __func__, __LINE__, type, + if_indextoname(ifindex, ifname)); return; } @@ -585,27 +677,29 @@ !(iflist[ifindex]->ifm_flags & IFF_UP)) { syslog(LOG_INFO, "<%s> interface %s becomes down. stop timer.", - __func__, rai->ifname); - rtadvd_remove_timer(&rai->timer); + __func__, rai->rai_ifname); + rtadvd_remove_timer(rai->rai_timer); + rai->rai_timer = NULL; } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */ - (iflist[ifindex]->ifm_flags & IFF_UP)) { + (iflist[ifindex]->ifm_flags & IFF_UP)) { syslog(LOG_INFO, "<%s> interface %s becomes up. restart timer.", - __func__, rai->ifname); + __func__, rai->rai_ifname); - rai->initcounter = 0; /* reset the counter */ - rai->waiting = 0; /* XXX */ - rai->timer = rtadvd_add_timer(ra_timeout, + rai->rai_initcounter = 0; /* reset the counter */ + rai->rai_waiting = 0; /* XXX */ + rai->rai_timer = rtadvd_add_timer(ra_timeout, ra_timer_update, rai, rai); - ra_timer_update((void *)rai, &rai->timer->tm); - rtadvd_set_timer(&rai->timer->tm, rai->timer); + ra_timer_update(rai, &rai->rai_timer->rat_tm); + rtadvd_set_timer(&rai->rai_timer->rat_tm, + rai->rai_timer); } else if (prefixchange && (iflist[ifindex]->ifm_flags & IFF_UP)) { /* * An advertised prefix has been added or invalidated. * Will notice the change in a short delay. */ - rai->initcounter = 0; + rai->rai_initcounter = 0; set_short_delay(rai); } } @@ -614,13 +708,13 @@ } void -rtadvd_input() +rtadvd_input(void) { - int i; + ssize_t i; int *hlimp = NULL; #ifdef OLDRAWSOCKET struct ip6_hdr *ip; -#endif +#endif struct icmp6_hdr *icp; int ifindex = 0; struct cmsghdr *cm; @@ -655,14 +749,14 @@ } if (ifindex == 0) { syslog(LOG_ERR, - "<%s> failed to get receiving interface", - __func__); + "<%s> failed to get receiving interface", + __func__); return; } if (hlimp == NULL) { syslog(LOG_ERR, - "<%s> failed to get receiving hop limit", - __func__); + "<%s> failed to get receiving hop limit", + __func__); return; } @@ -673,28 +767,28 @@ if (iflist[pi->ipi6_ifindex] == NULL || (iflist[pi->ipi6_ifindex]->ifm_flags & IFF_UP) == 0) { syslog(LOG_INFO, - "<%s> received data on a disabled interface (%s)", - __func__, - (iflist[pi->ipi6_ifindex] == NULL) ? "[gone]" : - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> received data on a disabled interface (%s)", + __func__, + (iflist[pi->ipi6_ifindex] == NULL) ? "[gone]" : + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } #ifdef OLDRAWSOCKET - if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { + if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { syslog(LOG_ERR, - "<%s> packet size(%d) is too short", - __func__, i); + "<%s> packet size(%d) is too short", + __func__, i); return; } ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ #else - if (i < sizeof(struct icmp6_hdr)) { + if ((size_t)i < sizeof(struct icmp6_hdr)) { syslog(LOG_ERR, - "<%s> packet size(%d) is too short", - __func__, i); + "<%s> packet size(%zd) is too short", + __func__, i); return; } @@ -704,7 +798,7 @@ switch (icp->icmp6_type) { case ND_ROUTER_SOLICIT: /* - * Message verification - RFC-2461 6.1.1 + * Message verification - RFC 4861 6.1.1 * XXX: these checks must be done in the kernel as well, * but we can't completely rely on them. */ @@ -714,7 +808,7 @@ "received from %s on %s", __func__, *hlimp, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -724,17 +818,17 @@ "received from %s on %s", __func__, icp->icmp6_code, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } - if (i < sizeof(struct nd_router_solicit)) { + if ((size_t)i < sizeof(struct nd_router_solicit)) { syslog(LOG_NOTICE, "<%s> RS from %s on %s does not have enough " - "length (len = %d)", + "length (len = %zd)", __func__, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } @@ -742,16 +836,25 @@ break; case ND_ROUTER_ADVERT: /* - * Message verification - RFC-2461 6.1.2 - * XXX: there's a same dilemma as above... + * Message verification - RFC 4861 6.1.2 + * XXX: there's the same dilemma as above... */ + if (!IN6_IS_ADDR_LINKLOCAL(&rcvfrom.sin6_addr)) { + syslog(LOG_NOTICE, + "<%s> RA witn non-linklocal source address " + "received from %s on %s", + __func__, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, + ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + return; + } if (*hlimp != 255) { syslog(LOG_NOTICE, "<%s> RA with invalid hop limit(%d) " "received from %s on %s", __func__, *hlimp, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } @@ -761,17 +864,17 @@ "received from %s on %s", __func__, icp->icmp6_code, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } - if (i < sizeof(struct nd_router_advert)) { + if ((size_t)i < sizeof(struct nd_router_advert)) { syslog(LOG_NOTICE, "<%s> RA from %s on %s does not have enough " - "length (len = %d)", + "length (len = %zd)", __func__, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, - INET6_ADDRSTRLEN), + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } @@ -785,7 +888,7 @@ break; } rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom, - &dst); + &dst); break; default: /* @@ -806,17 +909,17 @@ rs_input(int len, struct nd_router_solicit *rs, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - union nd_opts ndopts; - struct rainfo *ra; + u_char ntopbuf[INET6_ADDRSTRLEN]; + u_char ifnamebuf[IFNAMSIZ]; + union nd_opt ndopts; + struct rainfo *rai; struct soliciter *sol; syslog(LOG_DEBUG, - "<%s> RS received from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> RS received from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); @@ -824,36 +927,33 @@ len - sizeof(struct nd_router_solicit), &ndopts, NDOPT_FLAG_SRCLINKADDR)) { syslog(LOG_INFO, - "<%s> ND option check failed for an RS from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> ND option check failed for an RS from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* * If the IP source address is the unspecified address, there * must be no source link-layer address option in the message. - * (RFC-2461 6.1.1) + * (RFC 4861 6.1.1) */ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && - ndopts.nd_opts_src_lladdr) { + ndopts.opt_src_lladdr) { syslog(LOG_INFO, - "<%s> RS from unspecified src on %s has a link-layer" - " address option", - __func__, - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> RS from unspecified src on %s has a link-layer" + " address option", + __func__, if_indextoname(pi->ipi6_ifindex, ifnamebuf)); goto done; } - ra = ralist; - while (ra != NULL) { - if (pi->ipi6_ifindex == ra->ifindex) + TAILQ_FOREACH(rai, &railist, rai_next) + if (pi->ipi6_ifindex == (unsigned int)rai->rai_ifindex) break; - ra = ra->next; - } - if (ra == NULL) { + + if (rai == NULL) { syslog(LOG_INFO, "<%s> RS received on non advertising interface(%s)", __func__, @@ -861,7 +961,7 @@ goto done; } - ra->rsinput++; /* increment statistics */ + rai->rai_rsinput++; /* increment statistics */ /* * Decide whether to send RA according to the rate-limit @@ -871,21 +971,20 @@ /* record sockaddr waiting for RA, if possible */ sol = (struct soliciter *)malloc(sizeof(*sol)); if (sol) { - sol->addr = *from; - /* XXX RFC2553 need clarification on flowinfo */ - sol->addr.sin6_flowinfo = 0; - sol->next = ra->soliciter; - ra->soliciter = sol; + sol->sol_addr = *from; + /* XXX RFC 2553 need clarification on flowinfo */ + sol->sol_addr.sin6_flowinfo = 0; + TAILQ_INSERT_TAIL(&rai->rai_soliciter, sol, sol_next); } /* * If there is already a waiting RS packet, don't * update the timer. */ - if (ra->waiting++) + if (rai->rai_waiting++) goto done; - set_short_delay(ra); + set_short_delay(rai); done: free_ndopts(&ndopts); @@ -893,8 +992,7 @@ } static void -set_short_delay(rai) - struct rainfo *rai; +set_short_delay(struct rainfo *rai) { long delay; /* must not be greater than 1000000 */ struct timeval interval, now, min_delay, tm_tmp, *rest; @@ -904,7 +1002,7 @@ * corresponds to a time later than the time the next * multicast RA is scheduled to be sent, ignore the random * delay and send the advertisement at the - * already-scheduled time. RFC-2461 6.2.6 + * already-scheduled time. RFC 4861 6.2.6 */ #ifdef HAVE_ARC4RANDOM delay = arc4random_uniform(MAX_RA_DELAY_TIME); @@ -913,8 +1011,8 @@ #endif interval.tv_sec = 0; interval.tv_usec = delay; - rest = rtadvd_timer_rest(rai->timer); - if (TIMEVAL_LT(*rest, interval)) { + rest = rtadvd_timer_rest(rai->rai_timer); + if (TIMEVAL_LT(rest, &interval)) { syslog(LOG_DEBUG, "<%s> random delay is larger than " "the rest of the current timer", __func__); interval = *rest; @@ -928,170 +1026,207 @@ * previous advertisement was sent. */ gettimeofday(&now, NULL); - TIMEVAL_SUB(&now, &rai->lastsent, &tm_tmp); + TIMEVAL_SUB(&now, &rai->rai_lastsent, &tm_tmp); min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; min_delay.tv_usec = 0; - if (TIMEVAL_LT(tm_tmp, min_delay)) { + if (TIMEVAL_LT(&tm_tmp, &min_delay)) { TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay); TIMEVAL_ADD(&min_delay, &interval, &interval); } - rtadvd_set_timer(&interval, rai->timer); + rtadvd_set_timer(&interval, rai->rai_timer); } +static int +check_accept_rtadv(int idx) +{ + struct in6_ndireq nd; + u_char ifname[IFNAMSIZ]; + int s6; + int error; + + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> open socket failed for idx=%d.", + __func__, idx); + return (0); + } + if ((if_indextoname(idx, ifname)) == NULL) { + syslog(LOG_ERR, + "<%s> ifindex->ifname failed (idx=%d).", + __func__, idx); + close(s6); + return (0); + } + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifname, sizeof(nd.ifname)); + error = ioctl(s6, SIOCGIFINFO_IN6, &nd); + if (error) { + syslog(LOG_ERR, + "<%s> ioctl(SIOCGIFINFO_IN6) failed for idx=%d.", + __func__, idx); + nd.ndi.flags = 0; + } + close(s6); + + return (nd.ndi.flags & ND6_IFF_ACCEPT_RTADV); +} + +static int +getinet6sysctl(int code) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; + int value; + size_t size; + + mib[3] = code; + size = sizeof(value); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) + < 0) { + syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", + __func__, code, + strerror(errno)); + return (-1); + } + else + return (value); +} + static void -ra_input(int len, struct nd_router_advert *ra, +ra_input(int len, struct nd_router_advert *nra, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { struct rainfo *rai; - u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - union nd_opts ndopts; - char *on_off[] = {"OFF", "ON"}; + u_char ntopbuf[INET6_ADDRSTRLEN]; + u_char ifnamebuf[IFNAMSIZ]; + union nd_opt ndopts; + const char *on_off[] = {"OFF", "ON"}; u_int32_t reachabletime, retranstimer, mtu; int inconsistent = 0; + int error; - syslog(LOG_DEBUG, - "<%s> RA received from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); - + syslog(LOG_DEBUG, "<%s> RA received from %s on %s", __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), + if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); - if (nd6_options((struct nd_opt_hdr *)(ra + 1), - len - sizeof(struct nd_router_advert), - &ndopts, NDOPT_FLAG_SRCLINKADDR | - NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) { + error = nd6_options((struct nd_opt_hdr *)(nra + 1), + len - sizeof(struct nd_router_advert), &ndopts, + NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU | + NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL); + if (error) { syslog(LOG_INFO, - "<%s> ND option check failed for an RA from %s on %s", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> ND option check failed for an RA from %s on %s", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, + ifnamebuf)); return; } /* - * RA consistency check according to RFC-2461 6.2.7 + * RA consistency check according to RFC 4861 6.2.7 */ - if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) { + rai = if_indextorainfo(pi->ipi6_ifindex); + if (rai == NULL) { syslog(LOG_INFO, - "<%s> received RA from %s on non-advertising" - " interface(%s)", - __func__, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - if_indextoname(pi->ipi6_ifindex, ifnamebuf)); + "<%s> received RA from %s on non-advertising" + " interface(%s)", + __func__, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, + ifnamebuf)); goto done; } - rai->rainput++; /* increment statistics */ - + rai->rai_rainput++; /* increment statistics */ + /* Cur Hop Limit value */ - if (ra->nd_ra_curhoplimit && rai->hoplimit && - ra->nd_ra_curhoplimit != rai->hoplimit) { + if (nra->nd_ra_curhoplimit && rai->rai_hoplimit && + nra->nd_ra_curhoplimit != rai->rai_hoplimit) { syslog(LOG_INFO, - "<%s> CurHopLimit inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - ra->nd_ra_curhoplimit, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->hoplimit); + "<%s> CurHopLimit inconsistent on %s:" + " %d from %s, %d from us", + __func__, rai->rai_ifname, nra->nd_ra_curhoplimit, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_hoplimit); inconsistent++; } /* M flag */ - if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != - rai->managedflg) { + if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != + rai->rai_managedflg) { syslog(LOG_INFO, - "<%s> M flag inconsistent on %s:" - " %s from %s, %s from us", - __func__, - rai->ifname, - on_off[!rai->managedflg], - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - on_off[rai->managedflg]); + "<%s> M flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, rai->rai_ifname, on_off[!rai->rai_managedflg], + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), on_off[rai->rai_managedflg]); inconsistent++; } /* O flag */ - if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != - rai->otherflg) { + if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != + rai->rai_otherflg) { syslog(LOG_INFO, - "<%s> O flag inconsistent on %s:" - " %s from %s, %s from us", - __func__, - rai->ifname, - on_off[!rai->otherflg], - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - on_off[rai->otherflg]); + "<%s> O flag inconsistent on %s:" + " %s from %s, %s from us", + __func__, rai->rai_ifname, on_off[!rai->rai_otherflg], + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), on_off[rai->rai_otherflg]); inconsistent++; } /* Reachable Time */ - reachabletime = ntohl(ra->nd_ra_reachable); - if (reachabletime && rai->reachabletime && - reachabletime != rai->reachabletime) { + reachabletime = ntohl(nra->nd_ra_reachable); + if (reachabletime && rai->rai_reachabletime && + reachabletime != rai->rai_reachabletime) { syslog(LOG_INFO, - "<%s> ReachableTime inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - reachabletime, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->reachabletime); + "<%s> ReachableTime inconsistent on %s:" + " %d from %s, %d from us", + __func__, rai->rai_ifname, reachabletime, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_reachabletime); inconsistent++; } /* Retrans Timer */ - retranstimer = ntohl(ra->nd_ra_retransmit); - if (retranstimer && rai->retranstimer && - retranstimer != rai->retranstimer) { + retranstimer = ntohl(nra->nd_ra_retransmit); + if (retranstimer && rai->rai_retranstimer && + retranstimer != rai->rai_retranstimer) { syslog(LOG_INFO, - "<%s> RetranceTimer inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, - retranstimer, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->retranstimer); + "<%s> RetranceTimer inconsistent on %s:" + " %d from %s, %d from us", + __func__, rai->rai_ifname, retranstimer, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_retranstimer); inconsistent++; } /* Values in the MTU options */ - if (ndopts.nd_opts_mtu) { - mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); - if (mtu && rai->linkmtu && mtu != rai->linkmtu) { + if (ndopts.opt_mtu) { + mtu = ntohl(ndopts.opt_mtu->nd_opt_mtu_mtu); + if (mtu && rai->rai_linkmtu && mtu != rai->rai_linkmtu) { syslog(LOG_INFO, - "<%s> MTU option value inconsistent on %s:" - " %d from %s, %d from us", - __func__, - rai->ifname, mtu, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->linkmtu); + "<%s> MTU option value inconsistent on %s:" + " %d from %s, %d from us", + __func__, rai->rai_ifname, mtu, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_linkmtu); inconsistent++; } } /* Preferred and Valid Lifetimes for prefixes */ { - struct nd_optlist *optp = ndopts.nd_opts_list; + struct nd_optlist *nol; - if (ndopts.nd_opts_pi) { - if (prefix_check(ndopts.nd_opts_pi, rai, from)) + if (ndopts.opt_pi) + if (prefix_check(ndopts.opt_pi, rai, from)) inconsistent++; - } - while (optp) { - if (prefix_check((struct nd_opt_prefix_info *)optp->opt, - rai, from)) + + TAILQ_FOREACH(nol, &ndopts.opt_list, nol_next) + if (prefix_check((struct nd_opt_prefix_info *)nol->nol_opt, + rai, from)) inconsistent++; - optp = optp->next; - } } if (inconsistent) - rai->rainconsistent++; - + rai->rai_rainconsistent++; + done: free_ndopts(&ndopts); return; @@ -1100,51 +1235,49 @@ /* return a non-zero value if the received prefix is inconsitent with ours */ static int prefix_check(struct nd_opt_prefix_info *pinfo, - struct rainfo *rai, struct sockaddr_in6 *from) + struct rainfo *rai, struct sockaddr_in6 *from) { u_int32_t preferred_time, valid_time; - struct prefix *pp; + struct prefix *pfx; int inconsistent = 0; - u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; + u_char ntopbuf[INET6_ADDRSTRLEN]; + u_char prefixbuf[INET6_ADDRSTRLEN]; struct timeval now; #if 0 /* impossible */ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) - return(0); + return (0); #endif /* * log if the adveritsed prefix has link-local scope(sanity check?) */ - if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) { + if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) syslog(LOG_INFO, - "<%s> link-local prefix %s/%d is advertised " - "from %s on %s", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->ifname); - } + "<%s> link-local prefix %s/%d is advertised " + "from %s on %s", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_ifname); - if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, - pinfo->nd_opt_pi_prefix_len)) == NULL) { + if ((pfx = find_prefix(rai, &pinfo->nd_opt_pi_prefix, + pinfo->nd_opt_pi_prefix_len)) == NULL) { syslog(LOG_INFO, - "<%s> prefix %s/%d from %s on %s is not in our list", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - rai->ifname); - return(0); + "<%s> prefix %s/%d from %s on %s is not in our list", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), rai->rai_ifname); + return (0); } preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); - if (pp->pltimeexpire) { + if (pfx->pfx_pltimeexpire) { /* * The lifetime is decremented in real time, so we should * compare the expiration time. @@ -1155,134 +1288,135 @@ gettimeofday(&now, NULL); preferred_time += now.tv_sec; - if (!pp->timer && rai->clockskew && - abs(preferred_time - pp->pltimeexpire) > rai->clockskew) { + if (!pfx->pfx_timer && rai->rai_clockskew && + abs(preferred_time - pfx->pfx_pltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, - "<%s> preferred lifetime for %s/%d" - " (decr. in real time) inconsistent on %s:" - " %d from %s, %ld from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, preferred_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->pltimeexpire); + "<%s> preferred lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + rai->rai_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_pltimeexpire); inconsistent++; } - } else if (!pp->timer && preferred_time != pp->preflifetime) { + } else if (!pfx->pfx_timer && preferred_time != pfx->pfx_preflifetime) syslog(LOG_INFO, - "<%s> preferred lifetime for %s/%d" - " inconsistent on %s:" - " %d from %s, %d from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, preferred_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->preflifetime); - } + "<%s> preferred lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + rai->rai_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_preflifetime); valid_time = ntohl(pinfo->nd_opt_pi_valid_time); - if (pp->vltimeexpire) { + if (pfx->pfx_vltimeexpire) { gettimeofday(&now, NULL); valid_time += now.tv_sec; - if (!pp->timer && rai->clockskew && - abs(valid_time - pp->vltimeexpire) > rai->clockskew) { + if (!pfx->pfx_timer && rai->rai_clockskew && + abs(valid_time - pfx->pfx_vltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, - "<%s> valid lifetime for %s/%d" - " (decr. in real time) inconsistent on %s:" - " %d from %s, %ld from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, preferred_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->vltimeexpire); + "<%s> valid lifetime for %s/%d" + " (decr. in real time) inconsistent on %s:" + " %d from %s, %ld from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + rai->rai_ifname, preferred_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_vltimeexpire); inconsistent++; } - } else if (!pp->timer && valid_time != pp->validlifetime) { + } else if (!pfx->pfx_timer && valid_time != pfx->pfx_validlifetime) { syslog(LOG_INFO, - "<%s> valid lifetime for %s/%d" - " inconsistent on %s:" - " %d from %s, %d from us", - __func__, - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - prefixbuf, INET6_ADDRSTRLEN), - pinfo->nd_opt_pi_prefix_len, - rai->ifname, valid_time, - inet_ntop(AF_INET6, &from->sin6_addr, - ntopbuf, INET6_ADDRSTRLEN), - pp->validlifetime); + "<%s> valid lifetime for %s/%d" + " inconsistent on %s:" + " %d from %s, %d from us", + __func__, + inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, + sizeof(prefixbuf)), + pinfo->nd_opt_pi_prefix_len, + rai->rai_ifname, valid_time, + inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, + sizeof(ntopbuf)), pfx->pfx_validlifetime); inconsistent++; } - return(inconsistent); + return (inconsistent); } struct prefix * find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) { - struct prefix *pp; + struct prefix *pfx; int bytelen, bitlen; u_char bitmask; - for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) { - if (plen != pp->prefixlen) + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + if (plen != pfx->pfx_prefixlen) continue; + bytelen = plen / 8; bitlen = plen % 8; bitmask = 0xff << (8 - bitlen); - if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen)) + + if (memcmp((void *)prefix, (void *)&pfx->pfx_prefix, bytelen)) continue; + if (bitlen == 0 || - ((prefix->s6_addr[bytelen] & bitmask) == - (pp->prefix.s6_addr[bytelen] & bitmask))) { - return(pp); + ((prefix->s6_addr[bytelen] & bitmask) == + (pfx->pfx_prefix.s6_addr[bytelen] & bitmask))) { + return (pfx); } } - return(NULL); + return (NULL); } /* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */ int prefix_match(struct in6_addr *p0, int plen0, - struct in6_addr *p1, int plen1) + struct in6_addr *p1, int plen1) { int bytelen, bitlen; u_char bitmask; if (plen0 < plen1) - return(0); + return (0); + bytelen = plen1 / 8; bitlen = plen1 % 8; bitmask = 0xff << (8 - bitlen); + if (memcmp((void *)p0, (void *)p1, bytelen)) - return(0); + return (0); + if (bitlen == 0 || ((p0->s6_addr[bytelen] & bitmask) == (p1->s6_addr[bytelen] & bitmask))) { - return(1); + return (1); } - return(0); + return (0); } static int nd6_options(struct nd_opt_hdr *hdr, int limit, - union nd_opts *ndopts, u_int32_t optflags) + union nd_opt *ndopts, u_int32_t optflags) { int optlen = 0; for (; limit > 0; limit -= optlen) { - if (limit < sizeof(struct nd_opt_hdr)) { + if ((size_t)limit < sizeof(struct nd_opt_hdr)) { syslog(LOG_INFO, "<%s> short option header", __func__); goto bad; } @@ -1300,7 +1434,9 @@ goto bad; } - if (hdr->nd_opt_type > ND_OPT_MTU) { + if (hdr->nd_opt_type > ND_OPT_MTU && + hdr->nd_opt_type != ND_OPT_RDNSS && + hdr->nd_opt_type != ND_OPT_DNSSL) { syslog(LOG_INFO, "<%s> unknown ND option(type %d)", __func__, hdr->nd_opt_type); continue; @@ -1316,10 +1452,25 @@ * Option length check. Do it here for all fixed-length * options. */ - if ((hdr->nd_opt_type == ND_OPT_MTU && - (optlen != sizeof(struct nd_opt_mtu))) || - ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION && - optlen != sizeof(struct nd_opt_prefix_info)))) { + switch (hdr->nd_opt_type) { + case ND_OPT_MTU: + if (optlen == sizeof(struct nd_opt_mtu)) + break; + goto skip; + case ND_OPT_RDNSS: + if (optlen >= 24 && + (optlen - sizeof(struct nd_opt_rdnss)) % 16 == 0) + break; + goto skip; + case ND_OPT_DNSSL: + if (optlen >= 16 && + (optlen - sizeof(struct nd_opt_dnssl)) % 8 == 0) + break; + goto skip; + case ND_OPT_PREFIX_INFORMATION: + if (optlen == sizeof(struct nd_opt_prefix_info)) + break; +skip: syslog(LOG_INFO, "<%s> invalid option length", __func__); continue; @@ -1328,33 +1479,35 @@ switch (hdr->nd_opt_type) { case ND_OPT_TARGET_LINKADDR: case ND_OPT_REDIRECTED_HEADER: + case ND_OPT_RDNSS: + case ND_OPT_DNSSL: break; /* we don't care about these options */ case ND_OPT_SOURCE_LINKADDR: case ND_OPT_MTU: - if (ndopts->nd_opt_array[hdr->nd_opt_type]) { + if (ndopts->opt_array[hdr->nd_opt_type]) { syslog(LOG_INFO, "<%s> duplicated ND option (type = %d)", __func__, hdr->nd_opt_type); } - ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; + ndopts->opt_array[hdr->nd_opt_type] = hdr; break; case ND_OPT_PREFIX_INFORMATION: { - struct nd_optlist *pfxlist; + struct nd_optlist *nol; - if (ndopts->nd_opts_pi == 0) { - ndopts->nd_opts_pi = + if (ndopts->opt_pi == 0) { + ndopts->opt_pi = (struct nd_opt_prefix_info *)hdr; continue; } - if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) { + nol = malloc(sizeof(*nol)); + if (nol == NULL) { syslog(LOG_ERR, "<%s> can't allocate memory", __func__); goto bad; } - pfxlist->next = ndopts->nd_opts_list; - pfxlist->opt = hdr; - ndopts->nd_opts_list = pfxlist; + nol->nol_opt = hdr; + TAILQ_INSERT_TAIL(&(ndopts->opt_list), nol, nol_next); break; } @@ -1363,46 +1516,45 @@ } } - return(0); + return (0); bad: free_ndopts(ndopts); - return(-1); + return (-1); } static void -free_ndopts(union nd_opts *ndopts) +free_ndopts(union nd_opt *ndopts) { - struct nd_optlist *opt = ndopts->nd_opts_list, *next; + struct nd_optlist *nol; - while (opt) { - next = opt->next; - free(opt); - opt = next; + while ((nol = TAILQ_FIRST(&ndopts->opt_list)) != NULL) { + TAILQ_REMOVE(&ndopts->opt_list, nol, nol_next); + free(nol); } } void -sock_open() +sock_open(void) { struct icmp6_filter filt; struct ipv6_mreq mreq; - struct rainfo *ra = ralist; + struct rainfo *rai; int on; /* XXX: should be max MTU attached to the node */ static u_char answer[1500]; rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); + CMSG_SPACE(sizeof(int)); rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen); if (rcvcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); } - sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); + sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int)); sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen); if (sndcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); @@ -1410,113 +1562,84 @@ } if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { - syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); + syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } - /* specify to tell receiving interface */ on = 1; -#ifdef IPV6_RECVPKTINFO if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", - __func__, strerror(errno)); + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", __func__, + strerror(errno)); exit(1); } -#else /* old adv. API */ - if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s", - __func__, strerror(errno)); - exit(1); - } -#endif - on = 1; /* specify to tell value of hoplimit field of received IP6 hdr */ -#ifdef IPV6_RECVHOPLIMIT if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", - __func__, strerror(errno)); + sizeof(on)) < 0) { + syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", __func__, + strerror(errno)); exit(1); } -#else /* old adv. API */ - if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, - sizeof(on)) < 0) { - syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s", - __func__, strerror(errno)); - exit(1); - } -#endif - ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); if (accept_rr) ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); + if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, - sizeof(filt)) < 0) { + sizeof(filt)) < 0) { syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", - __func__, strerror(errno)); + __func__, strerror(errno)); exit(1); } /* * join all routers multicast address on each advertising interface. */ - if (inet_pton(AF_INET6, ALLROUTERS_LINK, - &mreq.ipv6mr_multiaddr.s6_addr) - != 1) { - syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", - __func__); - exit(1); - } - while (ra) { - mreq.ipv6mr_interface = ra->ifindex; + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_linklocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + TAILQ_FOREACH(rai, &railist, rai_next) { + mreq.ipv6mr_interface = rai->rai_ifindex; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, - sizeof(mreq)) < 0) { + sizeof(mreq)) < 0) { syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %s", - __func__, ra->ifname, strerror(errno)); + __func__, rai->rai_ifname, strerror(errno)); exit(1); } - ra = ra->next; } /* * When attending router renumbering, join all-routers site-local - * multicast group. + * multicast group. */ if (accept_rr) { - if (inet_pton(AF_INET6, ALLROUTERS_SITE, - &in6a_site_allrouters) != 1) { - syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", - __func__); - exit(1); - } - mreq.ipv6mr_multiaddr = in6a_site_allrouters; + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_sitelocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); if (mcastif) { if ((mreq.ipv6mr_interface = if_nametoindex(mcastif)) == 0) { syslog(LOG_ERR, - "<%s> invalid interface: %s", - __func__, mcastif); + "<%s> invalid interface: %s", + __func__, mcastif); exit(1); } } else - mreq.ipv6mr_interface = ralist->ifindex; + mreq.ipv6mr_interface = + TAILQ_FIRST(&railist)->rai_ifindex; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, - &mreq, sizeof(mreq)) < 0) { + &mreq, sizeof(mreq)) < 0) { syslog(LOG_ERR, - "<%s> IPV6_JOIN_GROUP(site) on %s: %s", - __func__, - mcastif ? mcastif : ralist->ifname, - strerror(errno)); + "<%s> IPV6_JOIN_GROUP(site) on %s: %s", __func__, + mcastif ? mcastif : + TAILQ_FIRST(&railist)->rai_ifname, + strerror(errno)); exit(1); } } - + /* initialize msghdr for receiving packets */ rcviov[0].iov_base = (caddr_t)answer; rcviov[0].iov_len = sizeof(answer); @@ -1533,17 +1656,17 @@ sndmhdr.msg_iovlen = 1; sndmhdr.msg_control = (caddr_t)sndcmsgbuf; sndmhdr.msg_controllen = sndcmsgbuflen; - + return; } /* open a routing socket to watch the routing table */ static void -rtsock_open() +rtsock_open(void) { if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { syslog(LOG_ERR, - "<%s> socket: %s", __func__, strerror(errno)); + "<%s> socket: %s", __func__, strerror(errno)); exit(1); } } @@ -1551,37 +1674,66 @@ struct rainfo * if_indextorainfo(int idx) { - struct rainfo *rai = ralist; + struct rainfo *rai; - for (rai = ralist; rai; rai = rai->next) { - if (rai->ifindex == idx) - return(rai); + TAILQ_FOREACH(rai, &railist, rai_next) { + syslog(LOG_DEBUG, "<%s> rai->rai_ifindex %d == idx %d?", + __func__, rai->rai_ifindex, idx); + if (rai->rai_ifindex == idx) + return (rai); } - return(NULL); /* search failed */ + return (NULL); /* search failed */ } -static void -ra_output(rainfo) -struct rainfo *rainfo; +void +ra_output(struct rainfo *rai) { int i; struct cmsghdr *cm; struct in6_pktinfo *pi; - struct soliciter *sol, *nextsol; + struct soliciter *sol; - if ((iflist[rainfo->ifindex]->ifm_flags & IFF_UP) == 0) { + if ((iflist[rai->rai_ifindex]->ifm_flags & IFF_UP) == 0) { syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA", - __func__, rainfo->ifname); + __func__, rai->rai_ifname); return; } - make_packet(rainfo); /* XXX: inefficient */ + /* + * Check lifetime, ACCEPT_RTADV flag, and ip6.forwarding. + * + * (lifetime == 0) = output + * (lifetime != 0 && !ip6.forwarding) = no output + * + * Basically, hosts MUST NOT send Router Advertisement + * messages at any time (RFC 4861, Section 6.2.3). However, it + * would sometimes be useful to allow hosts to advertise some + * parameters such as prefix information and link MTU. Thus, + * we allow hosts to invoke rtadvd only when router lifetime + * (on every advertising interface) is explicitly set + * zero. (see also the above section) + */ + syslog(LOG_DEBUG, + "<%s> check lifetime=%d, ACCEPT_RTADV=%d, ip6.forwarding=%d on %s", + __func__, rai->rai_lifetime, check_accept_rtadv(rai->rai_ifindex), + getinet6sysctl(IPV6CTL_FORWARDING), rai->rai_ifname); + if (rai->rai_lifetime != 0) { + if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) { + syslog(LOG_INFO, + "<%s> non-zero lifetime RA " + "is prepared for %s but ip6.forwarding=0." + " Ignored.", __func__, rai->rai_ifname); + return; + } + } - sndmhdr.msg_name = (caddr_t)&sin6_allnodes; - sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data; - sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen; + make_packet(rai); /* XXX: inefficient */ + sndmhdr.msg_name = (caddr_t)&sin6_linklocal_allnodes; + sndmhdr.msg_iov[0].iov_base = (caddr_t)rai->rai_ra_data; + sndmhdr.msg_iov[0].iov_len = rai->rai_ra_datalen; + cm = CMSG_FIRSTHDR(&sndmhdr); /* specify the outgoing interface */ cm->cmsg_level = IPPROTO_IPV6; @@ -1589,7 +1741,7 @@ 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 = rainfo->ifindex; + pi->ipi6_ifindex = rai->rai_ifindex; /* specify the hop limit of the packet */ { @@ -1603,80 +1755,79 @@ } syslog(LOG_DEBUG, - "<%s> send RA on %s, # of waitings = %d", - __func__, rainfo->ifname, rainfo->waiting); + "<%s> send RA on %s, # of waitings = %d", + __func__, rai->rai_ifname, rai->rai_waiting); i = sendmsg(sock, &sndmhdr, 0); - if (i < 0 || i != rainfo->ra_datalen) { + if (i < 0 || (size_t)i != rai->rai_ra_datalen) { if (i < 0) { syslog(LOG_ERR, "<%s> sendmsg on %s: %s", - __func__, rainfo->ifname, - strerror(errno)); + __func__, rai->rai_ifname, + strerror(errno)); } } /* update counter */ - if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) - rainfo->initcounter++; - rainfo->raoutput++; + if (rai->rai_initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) + rai->rai_initcounter++; + rai->rai_raoutput++; /* * unicast advertisements * XXX commented out. reason: though spec does not forbit it, unicast * advert does not really help */ - for (sol = rainfo->soliciter; sol; sol = nextsol) { - nextsol = sol->next; - - sol->next = NULL; + while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { + TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); free(sol); } - rainfo->soliciter = NULL; /* update timestamp */ - gettimeofday(&rainfo->lastsent, NULL); + gettimeofday(&rai->rai_lastsent, NULL); /* reset waiting conter */ - rainfo->waiting = 0; + rai->rai_waiting = 0; } /* process RA timer */ struct rtadvd_timer * -ra_timeout(void *data) +ra_timeout(void *arg) { - struct rainfo *rai = (struct rainfo *)data; + struct rainfo *rai; #ifdef notyet /* if necessary, reconstruct the packet. */ #endif + rai = (struct rainfo *)arg; + syslog(LOG_DEBUG, "<%s> RA timer on %s is expired", + __func__, rai->rai_ifname); - syslog(LOG_DEBUG, - "<%s> RA timer on %s is expired", - __func__, rai->ifname); - ra_output(rai); - return(rai->timer); + return (rai->rai_timer); } /* update RA timer */ void -ra_timer_update(void *data, struct timeval *tm) +ra_timer_update(void *arg, struct timeval *tm) { - struct rainfo *rai = (struct rainfo *)data; long interval; + struct rainfo *rai; + rai = (struct rainfo *)arg; /* * Whenever a multicast advertisement is sent from an interface, * the timer is reset to a uniformly-distributed random value * between the interface's configured MinRtrAdvInterval and * MaxRtrAdvInterval (RFC2461 6.2.4). */ - interval = rai->mininterval; + interval = rai->rai_mininterval; #ifdef HAVE_ARC4RANDOM - interval += arc4random_uniform(rai->maxinterval - rai->mininterval); + interval += arc4random_uniform(rai->rai_maxinterval - + rai->rai_mininterval); #else - interval += random() % (rai->maxinterval - rai->mininterval); + interval += random() % (rai->rai_maxinterval - + rai->rai_mininterval); #endif /* @@ -1684,9 +1835,9 @@ * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. - * (RFC-2461 6.2.4) + * (RFC 4861 6.2.4) */ - if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && + if (rai->rai_initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; @@ -1694,9 +1845,9 @@ tm->tv_usec = 0; syslog(LOG_DEBUG, - "<%s> RA timer on %s is set to %ld:%ld", - __func__, rai->ifname, - (long int)tm->tv_sec, (long int)tm->tv_usec); + "<%s> RA timer on %s is set to %ld:%ld", + __func__, rai->rai_ifname, + (long int)tm->tv_sec, (long int)tm->tv_usec); return; } Index: usr.sbin/rtadvd/timer.h =================================================================== --- usr.sbin/rtadvd/timer.h (revision 222972) +++ usr.sbin/rtadvd/timer.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,35 +31,42 @@ */ /* a < b */ -#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ - (((a).tv_sec == (b).tv_sec) && \ - ((a).tv_usec < (b).tv_usec))) +#define TIMEVAL_LT(a, b) \ + (((a)->tv_sec < (b)->tv_sec) || \ + (((a)->tv_sec == (b)->tv_sec) && \ + ((a)->tv_usec < (b)->tv_usec))) /* a <= b */ -#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ - (((a).tv_sec == (b).tv_sec) &&\ - ((a).tv_usec <= (b).tv_usec))) +#define TIMEVAL_LEQ(a, b) \ + (((a)->tv_sec < (b)->tv_sec) || \ + (((a)->tv_sec == (b)->tv_sec) && \ + ((a)->tv_usec <= (b)->tv_usec))) +#define TIMEVAL_EQUAL(a,b) \ + (((a)->tv_sec == (b)->tv_sec) && \ + ((a)->tv_usec == (b)->tv_usec)) + +extern TAILQ_HEAD(rtadvd_timer_head_t, rtadvd_timer) ra_timer; struct rtadvd_timer { - struct rtadvd_timer *next; - struct rtadvd_timer *prev; - struct rainfo *rai; - struct timeval tm; + TAILQ_ENTRY(rtadvd_timer) rat_next; - struct rtadvd_timer *(*expire)(void *); /* expiration function */ - void *expire_data; - void (*update)(void *, struct timeval *); /* update function */ - void *update_data; + struct rainfo *rat_rai; + struct timeval rat_tm; + struct rtadvd_timer *(*rat_expire)(void *); + void *rat_expire_data; + void (*rat_update)(void *, struct timeval *); + void *rat_update_data; }; -void rtadvd_timer_init(void); -struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *), - void (*)(void *, struct timeval *), void *, void *); -void rtadvd_set_timer(struct timeval *, struct rtadvd_timer *); -void rtadvd_remove_timer(struct rtadvd_timer **); -struct timeval * rtadvd_check_timer(void); -struct timeval * rtadvd_timer_rest(struct rtadvd_timer *); -void TIMEVAL_ADD(struct timeval *, struct timeval *, - struct timeval *); -void TIMEVAL_SUB(struct timeval *, struct timeval *, - struct timeval *); +void rtadvd_timer_init(void); +struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *), + void (*)(void *, struct timeval *), void *, void *); +void rtadvd_set_timer(struct timeval *, + struct rtadvd_timer *); +void rtadvd_remove_timer(struct rtadvd_timer *); +struct timeval *rtadvd_check_timer(void); +struct timeval *rtadvd_timer_rest(struct rtadvd_timer *); +void TIMEVAL_ADD(struct timeval *, struct timeval *, + struct timeval *); +void TIMEVAL_SUB(struct timeval *, struct timeval *, + struct timeval *); Index: usr.sbin/rtadvd/pathnames.h =================================================================== --- usr.sbin/rtadvd/pathnames.h (revision 222972) +++ usr.sbin/rtadvd/pathnames.h (working copy) @@ -1,4 +1,7 @@ /* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */ /* $FreeBSD$ */ -#define _PATH_RTADVDCONF "/etc/rtadvd.conf" +#define _PATH_RTADVDCONF "/etc/rtadvd.conf" +#define _PATH_RTADVDDUMP "/var/run/rtadvd.dump" +#define _PATH_RTADVDPID "/var/run/rtadvd.pid" + Index: usr.sbin/rtadvd/rtadvd.conf =================================================================== --- usr.sbin/rtadvd/rtadvd.conf (revision 222972) +++ usr.sbin/rtadvd/rtadvd.conf (working copy) @@ -18,4 +18,5 @@ # this part by hand, and then invoke rtadvd with the -s option. #ef0:\ -# :addr="3ffe:501:ffff:1000::":prefixlen#64: +# :addr="2001:db8:ffff:1000::":prefixlen#64:\ +# :rdnss="2001:db8:ffff:1000::1":dnssl="example.com": Index: usr.sbin/rtadvd/rtadvd.h =================================================================== --- usr.sbin/rtadvd/rtadvd.h (revision 222972) +++ usr.sbin/rtadvd/rtadvd.h (working copy) @@ -4,7 +4,7 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -30,12 +30,42 @@ * SUCH DAMAGE. */ -#define ALLNODES "ff02::1" -#define ALLROUTERS_LINK "ff02::2" -#define ALLROUTERS_SITE "ff05::2" -#define ANY "::" -#define RTSOLLEN 8 +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} + +#define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} + +extern struct sockaddr_in6 sin6_linklocal_allnodes; +extern struct sockaddr_in6 sin6_linklocal_allrouters; +extern struct sockaddr_in6 sin6_sitelocal_allrouters; + +/* + * 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 + /* protocol constants and default values */ #define DEF_MAXRTRADVINTERVAL 600 #define DEF_ADVLINKMTU 0 @@ -62,100 +92,151 @@ #define PREFIX_FROM_DYNAMIC 3 struct prefix { - struct prefix *next; /* forward link */ - struct prefix *prev; /* previous link */ + TAILQ_ENTRY(prefix) pfx_next; - struct rainfo *rainfo; /* back pointer to the interface */ + struct rainfo *pfx_rainfo; /* back pointer to the interface */ + /* + * Expiration timer. This is used when a prefix derived from + * the kernel is deleted. + */ + struct rtadvd_timer *pfx_timer; - struct rtadvd_timer *timer; /* expiration timer. used when a prefix - * derived from the kernel is deleted. - */ + u_int32_t pfx_validlifetime; /* AdvValidLifetime */ + long pfx_vltimeexpire; /* Expiration of vltime */ + u_int32_t pfx_preflifetime; /* AdvPreferredLifetime */ + long pfx_pltimeexpire; /* Expiration of pltime */ + u_int pfx_onlinkflg; /* bool: AdvOnLinkFlag */ + u_int pfx_autoconfflg; /* bool: AdvAutonomousFlag */ + int pfx_prefixlen; + int pfx_origin; /* From kernel or config */ - u_int32_t validlifetime; /* AdvValidLifetime */ - long vltimeexpire; /* expiration of vltime; decrement case only */ - u_int32_t preflifetime; /* AdvPreferredLifetime */ - long pltimeexpire; /* expiration of pltime; decrement case only */ - u_int onlinkflg; /* bool: AdvOnLinkFlag */ - u_int autoconfflg; /* bool: AdvAutonomousFlag */ - int prefixlen; - int origin; /* from kernel or config */ - struct in6_addr prefix; + struct in6_addr pfx_prefix; }; #ifdef ROUTEINFO struct rtinfo { - struct rtinfo *prev; /* previous link */ - struct rtinfo *next; /* forward link */ + TAILQ_ENTRY(rtinfo) rti_next; - u_int32_t ltime; /* route lifetime */ - u_int rtpref; /* route preference */ - int prefixlen; - struct in6_addr prefix; + u_int32_t rti_ltime; /* route lifetime */ + u_int rti_rtpref; /* route preference */ + int rti_prefixlen; + struct in6_addr rti_prefix; }; #endif +struct rdnss_addr { + TAILQ_ENTRY(rdnss_addr) ra_next; + + struct in6_addr ra_dns; /* DNS server entry */ +}; + +struct rdnss { + TAILQ_ENTRY(rdnss) rd_next; + + TAILQ_HEAD(, rdnss_addr) rd_list; /* list of DNS servers */ + int rd_cnt; /* number of DNS servers */ + u_int32_t rd_ltime; /* number of seconds valid */ +}; + +/* + * The maximum length of a domain name in a DNS search list is calculated + * by a domain name + length fields per 63 octets + a zero octet at + * the tail and adding 8 octet boundary padding. + */ +#define _DNAME_LABELENC_MAXLEN \ + (NI_MAXHOST + (NI_MAXHOST / 64 + 1) + 1) + +#define DNAME_LABELENC_MAXLEN \ + (_DNAME_LABELENC_MAXLEN + 8 - _DNAME_LABELENC_MAXLEN % 8) + +struct dnssl_addr { + TAILQ_ENTRY(dnssl_addr) da_next; + + int da_len; /* length of entry */ + char da_dom[DNAME_LABELENC_MAXLEN]; /* search domain name entry */ +}; + +struct dnssl { + TAILQ_ENTRY(dnssl) dn_next; + + TAILQ_HEAD(, dnssl_addr) dn_list; /* list of search domains */ + u_int32_t dn_ltime; /* number of seconds valid */ +}; + struct soliciter { - struct soliciter *next; - struct sockaddr_in6 addr; + TAILQ_ENTRY(soliciter) sol_next; + + struct sockaddr_in6 sol_addr; }; struct rainfo { /* pointer for list */ - struct rainfo *next; + TAILQ_ENTRY(rainfo) rai_next; /* timer related parameters */ - struct rtadvd_timer *timer; - int initcounter; /* counter for the first few advertisements */ - struct timeval lastsent; /* timestamp when the latest RA was sent */ - int waiting; /* number of RS waiting for RA */ + struct rtadvd_timer *rai_timer; + /* counter for the first few advertisements */ + int rai_initcounter; + /* timestamp when the latest RA was sent */ + struct timeval rai_lastsent; + /* number of RS waiting for RA */ + int rai_waiting; /* interface information */ - int ifindex; - int advlinkopt; /* bool: whether include link-layer addr opt */ - struct sockaddr_dl *sdl; - char ifname[16]; - int phymtu; /* mtu of the physical interface */ + int rai_ifindex; + int rai_advlinkopt; /* bool: whether include link-layer addr opt */ + int rai_advifprefix; /* bool: gather IF prefixes? */ + struct sockaddr_dl *rai_sdl; + char rai_ifname[IFNAMSIZ]; + u_int32_t rai_phymtu; /* mtu of the physical interface */ /* Router configuration variables */ - u_short lifetime; /* AdvDefaultLifetime */ - u_int maxinterval; /* MaxRtrAdvInterval */ - u_int mininterval; /* MinRtrAdvInterval */ - int managedflg; /* AdvManagedFlag */ - int otherflg; /* AdvOtherConfigFlag */ + u_short rai_lifetime; /* AdvDefaultLifetime */ + u_int rai_maxinterval; /* MaxRtrAdvInterval */ + u_int rai_mininterval; /* MinRtrAdvInterval */ + int rai_managedflg; /* AdvManagedFlag */ + int rai_otherflg; /* AdvOtherConfigFlag */ - int rtpref; /* router preference */ - u_int32_t linkmtu; /* AdvLinkMTU */ - u_int32_t reachabletime; /* AdvReachableTime */ - u_int32_t retranstimer; /* AdvRetransTimer */ - u_int hoplimit; /* AdvCurHopLimit */ - struct prefix prefix; /* AdvPrefixList(link head) */ - int pfxs; /* number of prefixes */ - long clockskew; /* used for consisitency check of lifetimes */ + int rai_rtpref; /* router preference */ + u_int32_t rai_linkmtu; /* AdvLinkMTU */ + u_int32_t rai_reachabletime; /* AdvReachableTime */ + u_int32_t rai_retranstimer; /* AdvRetransTimer */ + u_int rai_hoplimit; /* AdvCurHopLimit */ + TAILQ_HEAD(, prefix) rai_prefix;/* AdvPrefixList(link head) */ + int rai_pfxs; /* number of prefixes */ + + long rai_clockskew; /* used for consisitency check of lifetimes */ + + TAILQ_HEAD(, rdnss) rai_rdnss; /* DNS server list */ + TAILQ_HEAD(, dnssl) rai_dnssl; /* search domain list */ #ifdef ROUTEINFO - struct rtinfo route; /* route information option (link head) */ - int routes; /* number of route information options */ + TAILQ_HEAD(, rtinfo) rai_route; /* route information option (link head) */ + int rai_routes; /* number of route information options */ #endif - /* actual RA packet data and its length */ - size_t ra_datalen; - u_char *ra_data; + size_t rai_ra_datalen; + u_char *rai_ra_data; /* statistics */ - u_quad_t raoutput; /* number of RAs sent */ - u_quad_t rainput; /* number of RAs received */ - u_quad_t rainconsistent; /* number of RAs inconsistent with ours */ - u_quad_t rsinput; /* number of RSs received */ + u_quad_t rai_raoutput; /* # of RAs sent */ + u_quad_t rai_rainput; /* # of RAs received */ + u_quad_t rai_rainconsistent; /* # of RAs inconsistent with ours */ + u_quad_t rai_rsinput; /* # of RSs received */ /* info about soliciter */ - struct soliciter *soliciter; /* recent solication source */ + TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */ }; -struct rtadvd_timer *ra_timeout(void *); -void ra_timer_update(void *, struct timeval *); +/* Interface list including RA information */ +extern TAILQ_HEAD(railist_head_t, rainfo) railist; -int prefix_match(struct in6_addr *, int, struct in6_addr *, int); -struct rainfo *if_indextorainfo(int); -struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int); +struct rtadvd_timer *ra_timeout(void *); +void ra_timer_update(void *, struct timeval *); +void ra_output(struct rainfo *); -extern struct in6_addr in6a_site_allrouters; +int prefix_match(struct in6_addr *, int, + struct in6_addr *, int); +struct rainfo *if_indextorainfo(int); +struct prefix *find_prefix(struct rainfo *, + struct in6_addr *, int); Index: usr.sbin/rtadvd/rtadvd.conf.5 =================================================================== --- usr.sbin/rtadvd/rtadvd.conf.5 (revision 222972) +++ usr.sbin/rtadvd/rtadvd.conf.5 (working copy) @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 17, 1998 +.Dd June 4, 2011 .Dt RTADVD.CONF 5 .Os .Sh NAME @@ -179,10 +179,25 @@ and advertise the prefixes with the default parameters. Keywords other than .Cm clockskew +and +.Cm noifprefix can be augmented with a number, like .Dq Li prefix2 , to specify multiple prefixes. .Bl -tag -width indent +.It Cm \&noifprefix +(bool) Specifies no prefix on the network interfaces will be advertised. +By default +.Nm rtadvd +automatically gathers on-link prefixes from all of the network interfaces +and advertise them. +The +.Cm noifprefix +disables that behavior. +If this is specified and no +.Cm addr +keyword is specified, no prefix information option will be included in the +message. .It Cm \&clockskew (num) Time skew to adjust link propagation delays and clock skews between routers on the link @@ -355,6 +370,66 @@ .Dq Li rtr have basically been obsoleted, and should not be used any more. .Pp +The following items are for ICMPv6 Recursive DNS Server Option and +DNS Search List Option +.Pq RFC 6106 , +which will be attached to router advertisement header. +These items are optional. +.Bl -tag -width indent +.It Cm \&rdnss +(str) The IPv6 address of one or more recursive DNS servers. +The argument must be inside double quotes. +Multiple DNS servers can be specified in a comma-separated string. +If different lifetimes are needed for different servers, +separate entries can be given by using +.Cm rdnss , +.Cm rdnss0 , +.Cm rdnss1 , +.Cm rdnss2 ... +options with corresponding +.Cm rdnssltime , +.Cm rdnssltime0 , +.Cm rdnssltime1 , +.Cm rdnssltime2 ... +entries. +Note that the maximum number of servers depends on the receiver side. +See also +.Xr resolver 5 +manual page for resolver implementation in +.Fx . +.It Cm \&rdnssltime +The lifetime of the +.Cm rdnss +DNS server entries. +The default value is 3/2 of the interval time. +.It Cm \&dnssl +(str) One or more domain names in a comma-separated string. +These domain names will be used when making DNS queries on a +non-fully-qualified domain name. +If different lifetimes are needed for different domains, separate entries +can be given by using +.Cm dnssl , +.Cm dnssl0 , +.Cm dnssl1 , +.Cm dnssl2 ... +options with corresponding +.Cm dnsslltime , +.Cm dnsslltime0 , +.Cm dnsslltime1 , +.Cm dnsslltime2 ... +entries. +Note that the maximum number of names depends on the receiver side. +See also +.Xr resolver 5 +manual page for resolver implementation in +.Fx . +.It Cm \&dnsslltime +The lifetime of the +.Cm dnssl +DNS search list entries. +The default value is 3/2 of the interval time. +.El +.Pp You can also refer one line from another by using .Cm tc capability. @@ -388,9 +463,20 @@ .Xr rtadvd 8 . .Bd -literal -offset ef0:\\ - :addr="3ffe:501:ffff:1000::":prefixlen#64: + :addr="2001:db8:ffff:1000::":prefixlen#64: .Ed .Pp +The following example configures the +.Li wlan0 +interface and adds two DNS servers and a DNS domain search options +using the default option lifetime values. +.Bd -literal -offset +wlan0:\\ + :addr="2001:db8:ffff:1000::":prefixlen#64:\\ + :rdnss="2001:db8:ffff::10,2001:db8:ffff::2:43":\\ + :dnssl="example.com": +.Ed +.Pp The following example presents the default values in an explicit manner. The configuration is provided just for reference purposes; YOU DO NOT NEED TO HAVE IT AT ALL. @@ -399,24 +485,41 @@ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\ :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0: ef0:\\ - :addr="3ffe:501:ffff:1000::":prefixlen#64:tc=default: + :addr="2001:db8:ffff:1000::":prefixlen#64:tc=default: .Ed .Sh SEE ALSO .Xr termcap 5 , +.Xr resolver 5 , .Xr rtadvd 8 , .Xr rtsol 8 .Rs .%A Thomas Narten .%A Erik Nordmark .%A W. A. Simpson +.%A Hesham Soliman .%T Neighbor Discovery for IP version 6 (IPv6) -.%R RFC 2461 +.%R RFC 4861 .Re .Rs +.%A Thomas Narten +.%A Erik Nordmark +.%A W. A. Simpson +.%T Neighbor Discovery for IP version 6 (IPv6) +.%R RFC 2461 (obsoleted by RFC 4861) +.Re +.Rs .%A Richard Draves .%T Default Router Preferences and More-Specific Routes .%R draft-ietf-ipngwg-router-selection-xx.txt .Re +.Rs +.%A J. Jeong +.%A S. Park +.%A L. Beloeil +.%A S. Madanapalli +.%T IPv6 Router Advertisement Options for DNS Configuration +.%R RFC 6106 +.Re .Sh HISTORY The .Xr rtadvd 8 Index: usr.sbin/rtadvd/Makefile =================================================================== --- usr.sbin/rtadvd/Makefile (revision 222972) +++ usr.sbin/rtadvd/Makefile (working copy) @@ -18,8 +18,8 @@ MAN= rtadvd.conf.5 rtadvd.8 SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c -DPADD= ${LIBUTIL} -LDADD= -lutil +DPADD= ${LIBUTIL} +LDADD= -lutil CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO Property changes on: usr.sbin/rtsold ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/usr.sbin/rtsold:r222732,222743,222771,222820,222824,222848,222861,222972 Index: usr.sbin/rtsold/if.c =================================================================== --- usr.sbin/rtsold/if.c (revision 222972) +++ usr.sbin/rtsold/if.c (working copy) @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -78,45 +79,95 @@ interface_up(char *name) { struct ifreq ifr; + struct in6_ndireq nd; int llflag; + int s; + memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, name, sizeof(nd.ifname)); 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); + } + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { + warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s", + strerror(errno)); + close(s); + return (-1); + } warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name); + if (nd.ndi.flags & ND6_IFF_IFDISABLED) { + if (Fflag) { + nd.ndi.flags &= ~ND6_IFF_IFDISABLED; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { + warnmsg(LOG_WARNING, __func__, + "ioctl(SIOCSIFINFO_IN6): %s", + strerror(errno)); + close(s); + return (-1); + } + } else { + warnmsg(LOG_WARNING, __func__, + "%s is disabled.", name); + close(s); + return (-1); + } + } + if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) { + if (Fflag) { + nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) { + warnmsg(LOG_WARNING, __func__, + "ioctl(SIOCSIFINFO_IN6): %s", + strerror(errno)); + close(s); + return (-1); + } + } else + warnmsg(LOG_INFO, __func__, + "%s has no ACCEPT_RTADV but the flag is ignored " + "in 8.X.", name); + } + close(s); + llflag = get_llflag(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); } } @@ -133,16 +184,14 @@ 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; @@ -179,10 +228,10 @@ } inactive: - return(0); + return (0); active: - return(1); + return (1); } #define ROUNDUP(a, size) \ @@ -201,9 +250,9 @@ #ifdef IFT_IEEE80211 case IFT_IEEE80211: #endif - return(ROUNDUP8(ETHER_ADDR_LEN + 2)); + return (ROUNDUP8(ETHER_ADDR_LEN + 2)); default: - return(0); + return (0); } } @@ -248,7 +297,7 @@ return(NULL); if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { free(buf); - return(NULL); + return (NULL); } lim = buf + len; @@ -274,17 +323,17 @@ 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 @@ -297,9 +346,9 @@ 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 @@ -313,9 +362,9 @@ 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); } /*------------------------------------------------------------*/ @@ -361,12 +410,12 @@ freeifaddrs(ifap); close(s); - return ifr6.ifr_ifru.ifru_flags6; + return (ifr6.ifr_ifru.ifru_flags6); } freeifaddrs(ifap); close(s); - return -1; + return (-1); } Index: usr.sbin/rtsold/rtsol.c =================================================================== --- usr.sbin/rtsold/rtsol.c (revision 222972) +++ usr.sbin/rtsold/rtsol.c (working copy) @@ -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 #include +#define __BSD_VISIBLE 1 /* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */ #include +#undef __BSD_VISIBLE #include #include #include #include +#include #include #include #include @@ -61,8 +65,6 @@ #include #include "rtsold.h" -#define ALLROUTER "ff02::2" - static struct msghdr rcvmhdr; static struct msghdr sndmhdr; static struct iovec rcviov[2]; @@ -71,13 +73,41 @@ static int rcvcmsglen; int rssock; +struct ifinfo_head_t ifinfo_head = + TAILQ_HEAD_INITIALIZER(ifinfo_head); -static struct sockaddr_in6 sin6_allrouters = -{sizeof(sin6_allrouters), AF_INET6}; +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 *); +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 +#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) { @@ -91,63 +121,35 @@ 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); + 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); - } - 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 + /* specify to tell value of hoplimit field of received IP6 hdr */ on = 1; - /* specify to tell value of hoplimit field of received IP6 hdr */ -#ifdef IPV6_RECVHOPLIMIT 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); @@ -174,24 +176,24 @@ 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; int hoplimit = 255; - int i; + ssize_t i; 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 */ @@ -200,7 +202,7 @@ 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); @@ -211,37 +213,53 @@ 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 || 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, i, *hlimp = NULL; + 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; + struct rainfo *rai; + 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; + int newent_rai; + int newent_rao; /* get message. namelen and controllen must always be initialized. */ rcvmhdr.msg_namelen = sizeof(from); rcvmhdr.msg_controllen = rcvcmsglen; - if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { + if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } @@ -272,9 +290,9 @@ return; } - if (i < sizeof(struct nd_router_advert)) { + if ((size_t)msglen < sizeof(struct nd_router_advert)) { warnmsg(LOG_INFO, __func__, - "packet size(%d) is too short", i); + "packet size(%zd) is too short", msglen); return; } @@ -351,9 +369,179 @@ 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); } + gettimeofday(&now, NULL); + 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); + 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); + + 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(&lifetime, 0, sizeof(lifetime)); + lifetime.tv_sec = + ntohl(rdnss->nd_opt_rdnss_lifetime); + timeradd(&now, &lifetime, &rao->rao_expire); + + if (newent_rao) + TAILQ_INSERT_TAIL(&rai->rai_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); + + 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(&lifetime, 0, sizeof(lifetime)); + lifetime.tv_sec = + ntohl(dnssl->nd_opt_dnssl_lifetime); + timeradd(&now, &lifetime, &rao->rao_expire); + + if (newent_rao) + TAILQ_INSERT_TAIL(&rai->rai_ra_opt, + rao, rao_next); + p += len; + } + break; + default: + /* nothing to do for other options */ + break; + } + 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++; switch (ifi->state) { @@ -368,23 +556,209 @@ } } +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 rainfo *rai; + 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); + + /* + * 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); + 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; + } + 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 if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED || + ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) { + CALL_SCRIPT(RESDEL, NULL); + ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; + ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; + } + +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); + } + } + 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(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); @@ -392,38 +766,55 @@ 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; + } else { /* child */ + int nullfd; + char **_argv; - argv[0] = scriptpath; - argv[1] = ifname; - argv[2] = NULL; - if (safefile(scriptpath)) { warnmsg(LOG_ERR, __func__, "script \"%s\" cannot be executed safely", scriptpath); exit(1); } + nullfd = open("/dev/null", O_RDWR); + if (nullfd < 0) { + warnmsg(LOG_ERR, __func__, + "open /dev/null: %s", strerror(errno)); + exit(1); + } + if (fd[0] != -1) { /* Receive message from STDIN if any. */ + close(fd[1]); + if (fd[0] != STDIN_FILENO) { + /* Connect a pipe read-end to child's STDIN. */ + if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { + warnmsg(LOG_ERR, __func__, + "dup2 STDIN: %s", strerror(errno)); + exit(1); + } + close(fd[0]); + } + } else + dup2(nullfd, STDIN_FILENO); + + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + if (nullfd > STDERR_FILENO) + close(nullfd); - 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); + _argv = malloc(sizeof(*_argv) * argc); + if (_argv == NULL) { + warnmsg(LOG_ERR, __func__, + "malloc: %s", strerror(errno)); + exit(1); } - - execv(scriptpath, argv); - + 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; @@ -468,3 +859,37 @@ 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); +} Index: usr.sbin/rtsold/dump.c =================================================================== --- usr.sbin/rtsold/dump.c (revision 222972) +++ usr.sbin/rtsold/dump.c (working copy) @@ -34,10 +34,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -52,52 +54,78 @@ extern struct ifinfo *iflist; static void dump_interface_status(void); -static char *sec2str(time_t); -char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; +static const char * const ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; static void dump_interface_status(void) { - struct ifinfo *ifinfo; + struct ifinfo *ifi; + struct rainfo *rai; + struct ra_opt *rao; struct timeval now; + char ntopbuf[INET6_ADDRSTRLEN]; 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)); } - fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt); + 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"); + } } } void -rtsold_dump_file(char *dumpfile) +rtsold_dump_file(const char *dumpfile) { if ((fp = fopen(dumpfile, "w")) == NULL) { warnmsg(LOG_WARNING, __func__, "open a dump file(%s): %s", @@ -108,8 +136,8 @@ fclose(fp); } -static char * -sec2str(time_t total) +const char * +sec2str(const struct timeval *total) { static char result[256]; int days, hours, mins, secs; @@ -117,12 +145,20 @@ 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; n = snprintf(p, ep - p, "%dd", days); @@ -145,5 +181,6 @@ p += n; } snprintf(p, ep - p, "%ds", secs); - return(result); + + return (result); } Index: usr.sbin/rtsold/rtsock.c =================================================================== --- usr.sbin/rtsold/rtsock.c (revision 222972) +++ usr.sbin/rtsold/rtsock.c (working copy) @@ -83,7 +83,7 @@ rtsock_open(void) { - return socket(PF_ROUTE, SOCK_RAW, 0); + return (socket(PF_ROUTE, SOCK_RAW, 0)); } int @@ -94,9 +94,9 @@ char *lim, *next; struct rt_msghdr *rtm; int idx; - size_t len; + ssize_t len; int ret = 0; - const size_t lenlim = + const ssize_t lenlim = offsetof(struct rt_msghdr, rtm_msglen) + sizeof(rtm->rtm_msglen); n = read(s, msg, sizeof(msg)); @@ -130,19 +130,19 @@ } } - return ret; + return (ret); } #ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/ static int -rtsock_input_ifannounce(int s, struct rt_msghdr *rtm, char *lim) +rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim) { struct if_announcemsghdr *ifan; struct ifinfo *ifinfo; 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 @@ break; } - return 0; + return (0); } #endif Index: usr.sbin/rtsold/probe.c =================================================================== --- usr.sbin/rtsold/probe.c (revision 222972) +++ usr.sbin/rtsold/probe.c (working copy) @@ -72,18 +72,18 @@ 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 @@ sndmhdr.msg_iovlen = 1; sndmhdr.msg_control = (caddr_t)sndcmsgbuf; sndmhdr.msg_controllen = scmsglen; - return(0); + + return (0); } /* Index: usr.sbin/rtsold/rtsold.c =================================================================== --- usr.sbin/rtsold/rtsold.c (revision 222972) +++ usr.sbin/rtsold/rtsold.c (working copy) @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -63,16 +64,20 @@ #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; static int fflag = 0; -static int Fflag = 0; /* force setting sysctl parameters */ +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 +90,33 @@ */ #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 char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ +static const char *dumpfilename = RTSOL_DUMPFILE; #endif -#if 1 -static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ -#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, *opts; + const char *opts; #ifdef HAVE_POLL_H struct pollfd set[2]; #else @@ -122,20 +125,19 @@ int maxfd; #endif int rtsock; + char *argv0; - /* - * Initialization - */ +#ifndef SMALL + /* rtsold */ + opts = "adDfFm1O:P:R:"; +#else + /* rtsol */ + opts = "adDFO:P:R:"; + fflag = 1; + once = 1; +#endif argv0 = argv[0]; - /* get option */ - if (argv0 && argv0[strlen(argv0) - 1] != 'd') { - fflag = 1; - once = 1; - opts = "adDFO:"; - } else - opts = "adDfFm1O:"; - while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { case 'a': @@ -162,17 +164,23 @@ 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 */ @@ -195,7 +203,14 @@ 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)); @@ -229,7 +244,6 @@ if ((s = sockopen()) < 0) { warnmsg(LOG_ERR, __func__, "failed to open a socket"); exit(1); - /*NOTREACHED*/ } #ifdef HAVE_POLL_H set[0].fd = s; @@ -245,7 +259,6 @@ 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; @@ -258,12 +271,12 @@ #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 @@ -272,7 +285,6 @@ warnmsg(LOG_ERR, __func__, "failed to initialize interfaces"); exit(1); - /*NOTREACHED*/ } if (aflag) argv = autoifprobe(); @@ -281,7 +293,6 @@ warnmsg(LOG_ERR, __func__, "failed to initialize %s", *argv); exit(1); - /*NOTREACHED*/ } argv++; } @@ -294,7 +305,6 @@ /*NOTREACHED*/ } -#if 1 /* dump the current pid */ if (!once) { pid_t pid = getpid(); @@ -309,8 +319,6 @@ fclose(fp); } } -#endif - #ifndef HAVE_POLL_H memset(fdsetp, 0, fdmasks); FD_SET(s, fdsetp); @@ -340,7 +348,7 @@ 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; } @@ -376,102 +384,99 @@ } /* 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; + 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)); - strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->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; + ifi->state = IFS_DOWN; - rtsol_timer_update(ifinfo); + rtsol_timer_update(ifi); - /* link into chain */ - if (iflist) - ifinfo->next = iflist; - iflist = ifinfo; + TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next); + return (0); - 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; } } @@ -483,7 +488,7 @@ 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; @@ -497,43 +502,58 @@ free(ifi->rs_data); free(ifi->sdl); free(ifi); - return rv; + + return (rv); } #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) { 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; @@ -545,9 +565,9 @@ /* 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 * @@ -555,56 +575,68 @@ { static struct timeval returnval; struct timeval now, rtsol_timer; - struct ifinfo *ifinfo; + struct ifinfo *ifi; + struct rainfo *rai; + 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, <=)) { - if (dflag > 1) - warnmsg(LOG_DEBUG, __func__, - "timer expiration on %s, " - "state = %d", ifinfo->ifname, - ifinfo->state); + TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { + if (timercmp(&ifi->expire, &now, <=)) { + warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, " + "state = %d", ifi->ifname, ifi->state); - switch (ifinfo->state) { + 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: 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; } /* @@ -612,77 +644,104 @@ * 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. */ + int expire = 0; + + TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { + TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { + warnmsg(LOG_DEBUG, __func__, + "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) + ra_opt_handler(ifi); } - - if (timercmp(&ifinfo->expire, &rtsol_timer, <)) - rtsol_timer = ifinfo->expire; + 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; 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); + 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 @@ -690,12 +749,12 @@ #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, @@ -704,30 +763,29 @@ * 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); + 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 @@ -738,35 +796,30 @@ #ifndef SMALL static void -rtsold_set_dump_file(int sig) +rtsold_set_dump_file(int sig __unused) { do_dump = 1; } #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]; @@ -794,7 +847,7 @@ static char **argv = NULL; static int n = 0; char **a; - int s, i, found; + int s = 0, i, found; struct ifaddrs *ifap, *ifa, *target; struct in6_ndireq nd; @@ -808,11 +861,11 @@ 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; @@ -848,43 +901,53 @@ 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; if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) - continue; + warnmsg(LOG_INFO, __func__, + "ACCEPT_RTADV flag is ignored in 8.X"); } /* 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); } Index: usr.sbin/rtsold/Makefile =================================================================== --- usr.sbin/rtsold/Makefile (revision 222972) +++ usr.sbin/rtsold/Makefile (working copy) @@ -20,7 +20,6 @@ SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H - DPADD= ${LIBKVM} LDADD= -lkvm Index: usr.sbin/rtsold/rtsold.8 =================================================================== --- usr.sbin/rtsold/rtsold.8 (revision 222972) +++ usr.sbin/rtsold/rtsold.8 (working copy) @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 17, 1998 +.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 @@ -186,7 +192,9 @@ warning messages will be generated, but Router Solicitations will still be sent. The settings may be changed manually with -.Xr sysctl 8 . +.Xr sysctl 8 +and +.Xr ifconfig 8 . .It Fl m Enable mobility support. If this option is specified, @@ -219,10 +227,25 @@ 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 EXIT STATUS -.Ex -std -.\" .Sh FILES .Bl -tag -width /var/run/rtsold.dump -compact .It Pa /var/run/rtsold.pid @@ -232,7 +255,11 @@ dumps internal state on. .El .\" +.Sh EXIT STATUS +.Ex -std +.\" .Sh SEE ALSO +.Xr resolvconf 8 , .Xr rtadvd 8 , .Xr sysctl 8 .\" Index: usr.sbin/rtsold/rtsold.h =================================================================== --- usr.sbin/rtsold/rtsold.h (revision 222972) +++ usr.sbin/rtsold/rtsold.h (working copy) @@ -31,8 +31,33 @@ * $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; +}; + +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 { - 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 */ @@ -49,8 +74,13 @@ 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; @@ -63,18 +93,50 @@ #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 char *otherconf_script; +extern int Fflag; +extern const char *otherconf_script; +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))); extern char **autoifprobe(void); +extern int ra_opt_handler(struct ifinfo *); /* if.c */ extern int ifinit(void); @@ -96,7 +158,8 @@ extern void defrouter_probe(struct ifinfo *); /* dump.c */ -extern void rtsold_dump_file(char *); +extern void rtsold_dump_file(const char *); +extern const char *sec2str(const struct timeval *); /* rtsock.c */ extern int rtsock_open(void);