diff options
-rw-r--r-- | sys/netinet/icmp6.h | 18 | ||||
-rw-r--r-- | usr.sbin/rtadvd/Makefile | 2 | ||||
-rw-r--r-- | usr.sbin/rtadvd/config.c | 232 | ||||
-rw-r--r-- | usr.sbin/rtadvd/config.h | 2 | ||||
-rw-r--r-- | usr.sbin/rtadvd/dump.c | 64 | ||||
-rw-r--r-- | usr.sbin/rtadvd/if.c | 1 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rrenum.c | 1 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.c | 69 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.conf | 3 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.conf.5 | 85 | ||||
-rw-r--r-- | usr.sbin/rtadvd/rtadvd.h | 42 | ||||
-rw-r--r-- | usr.sbin/rtsold/rtsol.c | 304 | ||||
-rw-r--r-- | usr.sbin/rtsold/rtsold.8 | 18 | ||||
-rw-r--r-- | usr.sbin/rtsold/rtsold.c | 8 | ||||
-rw-r--r-- | usr.sbin/rtsold/rtsold.h | 1 |
15 files changed, 802 insertions, 48 deletions
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 5faae7c..5a91956 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -297,6 +297,8 @@ struct nd_opt_hdr { /* Neighbor discovery option header */ #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#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 */ @@ -338,6 +340,22 @@ struct nd_opt_route_info { /* route info */ /* 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 */ diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile index 9dbfc99..29c26fe 100644 --- a/usr.sbin/rtadvd/Makefile +++ b/usr.sbin/rtadvd/Makefile @@ -21,7 +21,7 @@ SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c DPADD= ${LIBUTIL} LDADD= -lutil -CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO +CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO -DRDNSS WARNS?= 1 diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index 5eadcc5..086a23c 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -53,6 +53,7 @@ #include <stdio.h> #include <syslog.h> #include <errno.h> +#include <netdb.h> #include <string.h> #include <search.h> #include <stdlib.h> @@ -65,6 +66,11 @@ #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; @@ -72,6 +78,32 @@ extern struct rainfo *ralist; static struct rtadvd_timer *prefix_timeout(void *); static void makeentry(char *, size_t, int, char *); static int getinet6sysctl(int); +static size_t dname_labelenc(char *, const char *); + +/* Encode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labelenc(char *dst, const char *src) +{ + char *dst_origin; + size_t len; + + 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 ((len = strlen(src)) != 0) { + /* Put a length field with 63 octet limitation first. */ + *dst++ = len = MIN(63, len + 1); + memcpy(dst, src, len); + dst += len; + src += len; + } + syslog(LOG_DEBUG, "<%s> labellen = %d", __func__, dst - dst_origin); + return (dst - dst_origin); +} void getconfig(intface) @@ -123,6 +155,10 @@ getconfig(intface) #ifdef ROUTEINFO tmp->route.next = tmp->route.prev = &tmp->route; #endif +#ifdef RDNSS + TAILQ_INIT(&tmp->rdnss); + TAILQ_INIT(&tmp->dnssl); +#endif /* check if we are allowed to forward packets (if not determined) */ if (forwarding < 0) { @@ -276,7 +312,6 @@ getconfig(intface) tmp->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); @@ -442,7 +477,6 @@ getconfig(intface) tmp->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); @@ -585,6 +619,118 @@ getconfig(intface) } #endif +#ifdef RDNSS + /* DNS server and DNS search list information */ + for (i = -1; i < MAXRDNSSENT ; i++) { + struct rdnss *rdn; + struct rdnss_addr *rdna; + char *ap; + int c; + + makeentry(entbuf, sizeof(entbuf), i, "rdnss"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + rdn = malloc(sizeof(*rdn)); + if (rdn == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for rdnss entry", + __func__); + exit(1); + } + memset(rdn, 0, sizeof(*rdn)); + TAILQ_INIT(&rdn->rd_list); + + for (ap = addr; ap - addr < strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + rdna = malloc(sizeof(*rdna)); + if (rdna == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for " + "rdnss_addr entry", + __func__); + exit(1); + } + memset(rdna, 0, sizeof(*rdna)); + if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) { + syslog(LOG_ERR, "<%s> inet_pton failed for %s", + __func__, abuf); + exit(1); + } + TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next); + } + + makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); + MAYHAVE(val, entbuf, (tmp->maxinterval * 3 / 2)); + if (val < tmp->maxinterval || val > tmp->maxinterval * 2) { + syslog(LOG_ERR, "%s (%ld) on %s is invalid " + "(must be between %d and %d)", + entbuf, val, intface, tmp->maxinterval, + tmp->maxinterval * 2); + exit(1); + } + rdn->rd_ltime = val; + + /* link into chain */ + insque(rdn, &tmp->rdnss); + } + + for (i = -1; i < MAXDNSSLENT ; i++) { + struct dnssl *dns; + struct dnssl_addr *dnsa; + char *ap; + int c; + char *p, *q; + + makeentry(entbuf, sizeof(entbuf), i, "dnssl"); + addr = (char *)agetstr(entbuf, &bp); + if (addr == NULL) + break; + dns = malloc(sizeof(*dns)); + if (dns == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for dnssl entry", + __func__); + exit(1); + } + memset(dns, 0, sizeof(*dns)); + TAILQ_INIT(&dns->dn_list); + + for (ap = addr; ap - addr < strlen(addr); ap += c+1) { + c = strcspn(ap, ","); + strncpy(abuf, ap, c); + abuf[c] = '\0'; + dnsa = malloc(sizeof(struct dnssl_addr)); + if (dnsa == NULL) { + syslog(LOG_ERR, + "<%s> can't get allocate buffer for " + "dnssl_addr entry", __func__); + exit(1); + } + memset(dnsa, 0, sizeof(*dnsa)); + 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, (tmp->maxinterval * 3 / 2)); + if (val < tmp->maxinterval || val > tmp->maxinterval * 2) { + syslog(LOG_ERR, "%s (%ld) on %s is invalid " + "(must be between %d and %d)", + entbuf, val, intface, tmp->maxinterval, + tmp->maxinterval * 2); + exit(1); + } + dns->dn_ltime = val; + + /* link into chain */ + insque(dns, &tmp->dnssl); + } +#endif /* okey */ tmp->next = ralist; ralist = tmp; @@ -913,6 +1059,13 @@ make_packet(struct rainfo *rainfo) struct nd_opt_route_info *ndopt_rti; struct rtinfo *rti; #endif +#ifdef RDNSS + struct nd_opt_rdnss *ndopt_rdnss; + struct rdnss *rdn; + struct nd_opt_dnssl *ndopt_dnssl; + struct dnssl *dns; + size_t len; +#endif struct prefix *pfx; /* calculate total length */ @@ -936,6 +1089,29 @@ make_packet(struct rainfo *rainfo) packlen += sizeof(struct nd_opt_route_info) + ((rti->prefixlen + 0x3f) >> 6) * 8; #endif +#ifdef RDNSS + TAILQ_FOREACH(rdn, &rainfo->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, &rainfo->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 += 8 - (len % 8); + + packlen += len; + } +#endif /* allocate memory for the packet */ if ((buf = malloc(packlen)) == NULL) { @@ -944,6 +1120,7 @@ make_packet(struct rainfo *rainfo) __func__); exit(1); } + memset(buf, 0, packlen); if (rainfo->ra_data) { /* free the previous packet */ free(rainfo->ra_data); @@ -1056,6 +1233,57 @@ make_packet(struct rainfo *rainfo) } #endif +#ifdef RDNSS + TAILQ_FOREACH(rdn, &rainfo->rdnss, rd_next) { + struct rdnss_addr *rdna; + + 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); + + 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; + + syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__, + ndopt_rdnss->nd_opt_rdnss_len); + } + TAILQ_FOREACH(dns, &rainfo->dnssl, dn_next) { + struct dnssl_addr *dnsa; + size_t len = 0; + + 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 += 8 - (len % 8); + + /* 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); + } +#endif return; } diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index 2d02b8a..3fa14f9 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -45,3 +45,5 @@ extern void get_prefix(struct rainfo *); */ #define MAXPREFIX 100 #define MAXROUTE 100 +#define MAXRDNSSENT 100 +#define MAXDNSSLENT 100 diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c index d37f5db..a94a62e 100644 --- a/usr.sbin/rtadvd/dump.c +++ b/usr.sbin/rtadvd/dump.c @@ -45,6 +45,7 @@ #include <arpa/inet.h> +#include <netdb.h> #include <time.h> #include <stdio.h> #include <stdarg.h> @@ -63,6 +64,7 @@ extern struct rainfo *ralist; static char *ether_str(struct sockaddr_dl *); static void if_dump(void); +static size_t dname_labeldec(char *, const char *); static char *rtpref_str[] = { "medium", /* 00 */ @@ -96,6 +98,10 @@ if_dump() #ifdef ROUTEINFO struct rtinfo *rti; #endif +#ifdef RDNSS + struct rdnss *rdn; + struct dnssl *dns; +#endif char prefixbuf[INET6_ADDRSTRLEN]; int first; struct timeval now; @@ -230,6 +236,44 @@ if_dump() fprintf(fp, ")\n"); } #endif +#ifdef RDNSS + TAILQ_FOREACH(rdn, &rai->rdnss, rd_next) { + struct rdnss_addr *rdna; + + if (rdn == TAILQ_FIRST(&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->dnssl, dn_next) { + struct dnssl_addr *dnsa; + char buf[NI_MAXHOST + 1]; + + if (dns == TAILQ_FIRST(&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, 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"); + } +#endif } } @@ -250,3 +294,23 @@ rtadvd_dump_file(dumpfile) fclose(fp); } + +/* Decode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labeldec(char *dst, const char *src) +{ + size_t len; + const char *src_origin; + + src_origin = src; + while (*src && (len = (uint8_t)(*src++) & 0x3f) != 0) { + syslog(LOG_DEBUG, "<%s> labellen = %d", __func__, len); + memcpy(dst, src, len); + src += len; + dst += len; + if (*(dst - 1) == '\0') + break; + } + + return (src - src_origin); +} diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c index d8ed088..6cd8dd8 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -44,6 +44,7 @@ #include <netinet/icmp6.h> #include <unistd.h> #include <errno.h> +#include <netdb.h> #include <stdlib.h> #include <string.h> #include <syslog.h> diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c index aafa0f9..b2ea902 100644 --- a/usr.sbin/rtadvd/rrenum.c +++ b/usr.sbin/rtadvd/rrenum.c @@ -45,6 +45,7 @@ #include <arpa/inet.h> #include <errno.h> +#include <netdb.h> #include <string.h> #include <stdlib.h> #include <syslog.h> diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 02e3dc7..3652e20 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -37,6 +37,7 @@ #include <sys/queue.h> #include <net/if.h> +#include <net/if_media.h> #include <net/route.h> #include <net/if_dl.h> #include <netinet/in.h> @@ -52,6 +53,7 @@ #include <err.h> #include <errno.h> #include <libutil.h> +#include <netdb.h> #include <string.h> #include <stdlib.h> #include <syslog.h> @@ -115,15 +117,26 @@ union nd_opts { #define nd_opts_mtu nd_opt_each.mtu #define nd_opts_list nd_opt_each.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) +#ifdef RDNSS +#define NDOPT_FLAG_RDNSS (1 << 5) +#define NDOPT_FLAG_DNSSL (1 << 6) +#endif 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, +#ifdef RDNSS + [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, + [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, +#endif }; int main(int, char *[]); @@ -376,6 +389,10 @@ static void die() { struct rainfo *ra; +#ifdef RDNSS + struct rdnss *rdn; + struct dnssl *dns; +#endif int i; const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; @@ -386,6 +403,12 @@ die() for (ra = ralist; ra; ra = ra->next) { ra->lifetime = 0; +#ifdef RDNSS + TAILQ_FOREACH(rdn, &ra->rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &ra->dnssl, dn_next) + dns->dn_ltime = 0; +#endif make_packet(ra); } for (i = 0; i < retrans; i++) { @@ -961,7 +984,11 @@ ra_input(int len, struct nd_router_advert *ra, 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)) { + NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU +#ifdef RDNSS + | NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL +#endif + )) { syslog(LOG_INFO, "<%s> ND option check failed for an RA from %s on %s", __func__, @@ -1300,7 +1327,12 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, goto bad; } - if (hdr->nd_opt_type > ND_OPT_MTU) { + if (hdr->nd_opt_type > ND_OPT_MTU +#ifdef RDNSS + && hdr->nd_opt_type != ND_OPT_RDNSS && + hdr->nd_opt_type != ND_OPT_DNSSL +#endif + ) { syslog(LOG_INFO, "<%s> unknown ND option(type %d)", __func__, hdr->nd_opt_type); continue; @@ -1317,9 +1349,18 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, * 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)))) { + optlen != sizeof(struct nd_opt_mtu)) || +#ifdef RDNSS + (hdr->nd_opt_type == ND_OPT_RDNSS && + (optlen < 24 || + (optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0)) || + (hdr->nd_opt_type == ND_OPT_DNSSL && + (optlen < 16 || + (optlen - sizeof(struct nd_opt_dnssl)) % 8 != 0)) || +#endif + (hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION && + optlen != sizeof(struct nd_opt_prefix_info)) + ) { syslog(LOG_INFO, "<%s> invalid option length", __func__); continue; @@ -1328,6 +1369,10 @@ nd6_options(struct nd_opt_hdr *hdr, int limit, switch (hdr->nd_opt_type) { case ND_OPT_TARGET_LINKADDR: case ND_OPT_REDIRECTED_HEADER: +#ifdef RDNSS + case ND_OPT_RDNSS: + case ND_OPT_DNSSL: +#endif break; /* we don't care about these options */ case ND_OPT_SOURCE_LINKADDR: case ND_OPT_MTU: diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf index 33ab7f3..6213c4e 100644 --- a/usr.sbin/rtadvd/rtadvd.conf +++ b/usr.sbin/rtadvd/rtadvd.conf @@ -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:\ +# :rddns="2001:db8:ffff:1000::1":dnssl="foo.com": diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5 index 81ffa70..f6003cd 100644 --- a/usr.sbin/rtadvd/rtadvd.conf.5 +++ b/usr.sbin/rtadvd/rtadvd.conf.5 @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 17, 1998 +.Dd May 28, 2011 .Dt RTADVD.CONF 5 .Os .Sh NAME @@ -355,6 +355,65 @@ However, keywords that start with .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,7 +447,18 @@ option to .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="foo.com": .Ed .Pp The following example presents the default values in an explicit manner. @@ -399,10 +469,11 @@ default:\\ :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 @@ -417,6 +488,14 @@ ef0:\\ .%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 diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 828fec6..e346a1a 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -94,6 +94,44 @@ struct rtinfo { }; #endif +#ifdef RDNSS +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 */ +}; +#endif + struct soliciter { struct soliciter *next; struct sockaddr_in6 addr; @@ -130,6 +168,10 @@ struct rainfo { u_int hoplimit; /* AdvCurHopLimit */ struct prefix prefix; /* AdvPrefixList(link head) */ int pfxs; /* number of prefixes */ +#ifdef RDNSS + TAILQ_HEAD(, rdnss) rdnss; /* DNS server list */ + TAILQ_HEAD(, dnssl) dnssl; /* search domain list */ +#endif long clockskew; /* used for consisitency check of lifetimes */ #ifdef ROUTEINFO diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c index be2a9b8..2200fe8 100644 --- a/usr.sbin/rtsold/rtsol.c +++ b/usr.sbin/rtsold/rtsol.c @@ -50,6 +50,7 @@ #include <arpa/inet.h> +#include <netdb.h> #include <time.h> #include <fcntl.h> #include <unistd.h> @@ -77,9 +78,24 @@ static struct sockaddr_in6 sin6_allrouters = { .sin6_family = AF_INET6, }; -static void call_script(char *, char *); +struct script_msg { + TAILQ_ENTRY(script_msg) sm_next; + + char *sm_msg; +}; + +static void call_script(char **, void *); +static size_t dname_labeldec(char *, const char *); static int safefile(const char *); +#define _ARGS_OTHER otherconf_script, ifi->ifname +#define _ARGS_RESCONF resolvconf_script, "-a", ifi->ifname +#define CALL_SCRIPT(name, sm_head) \ + do { \ + char *sarg[] = { _ARGS_##name, NULL }; \ + call_script(sarg, sm_head); \ + } while(0); + int sockopen(void) { @@ -234,17 +250,34 @@ rtsol_input(int s) { u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; int ifindex = 0, *hlimp = NULL; - ssize_t i; + ssize_t msglen; struct in6_pktinfo *pi = NULL; struct ifinfo *ifi = NULL; struct icmp6_hdr *icp; struct nd_router_advert *nd_ra; struct cmsghdr *cm; + char *raoptp; + char *p; + struct in6_addr *addr; + struct nd_opt_hdr *ndo; + struct nd_opt_rdnss *rdnss; + struct nd_opt_dnssl *dnssl; + size_t len; + struct script_msg *smp; + TAILQ_HEAD(, script_msg) sm_ns_head = + TAILQ_HEAD_INITIALIZER(sm_ns_head); + char nsbuf[11 + INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 1]; + /* 11 = sizeof("nameserver "), 1+1 = \n\0 termination */ + TAILQ_HEAD(, script_msg) sm_sl_head = + TAILQ_HEAD_INITIALIZER(sm_sl_head); + char slbuf[7 + NI_MAXHOST + 1 + 1]; + /* 7 = sizeof("search "), 1+1 = \n\0 termination */ + char dname[NI_MAXHOST + 1]; /* get message. namelen and controllen must always be initialized. */ rcvmhdr.msg_namelen = sizeof(from); rcvmhdr.msg_controllen = rcvcmsglen; - if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { + if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } @@ -275,9 +308,9 @@ rtsol_input(int s) return; } - if ((size_t)i < sizeof(struct nd_router_advert)) { + if ((size_t)msglen < sizeof(struct nd_router_advert)) { warnmsg(LOG_INFO, __func__, - "packet size(%zd) is too short", i); + "packet size(%zd) is too short", msglen); return; } @@ -354,9 +387,166 @@ rtsol_input(int s) warnmsg(LOG_DEBUG, __func__, "OtherConfigFlag on %s is turned on", ifi->ifname); ifi->otherconfig = 1; - call_script(otherconf_script, ifi->ifname); + CALL_SCRIPT(OTHER, NULL); } +#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ + (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) + raoptp = (char *)icp + sizeof(struct nd_router_advert); + + warnmsg(LOG_DEBUG, __func__, "Processing RA"); + /* Process RA options. */ + 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: + if (resolvconf_script == NULL) + break; + rdnss = (struct nd_opt_rdnss *)raoptp; + /* XXX: no lifetime handling now */ + + 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)); + + continue; + } + if (IN6_IS_ADDR_LINKLOCAL(addr)) + /* XXX: % has to be escaped here */ + sprintf(nsbuf, "nameserver " + "%s%c%c%c%c%s\n", + ntopbuf, + SCOPE_DELIMITER, + SCOPE_DELIMITER, + SCOPE_DELIMITER, + SCOPE_DELIMITER, + ifi->ifname); + else + sprintf(nsbuf, "nameserver %s\n", + ntopbuf); + warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", + nsbuf); + + smp = malloc(sizeof(*smp)); + if (smp == NULL) { + warnmsg(LOG_ERR, __func__, + "malloc failed: %s", + strerror(errno)); + continue; + } + memset(smp, 0, sizeof(*smp)); + smp->sm_msg = strdup(nsbuf); + if (smp->sm_msg == NULL) { + warnmsg(LOG_ERR, __func__, + "strdup failed: %s", + strerror(errno)); + free(smp); + continue; + } + TAILQ_INSERT_TAIL(&sm_ns_head, smp, sm_next); + addr++; + } + break; + case ND_OPT_DNSSL: + if (resolvconf_script == NULL) + break; + dnssl = (struct nd_opt_dnssl *)raoptp; + /* XXX: no lifetime handling now */ + + if (TAILQ_EMPTY(&sm_sl_head)) { + smp = malloc(sizeof(*smp)); + if (smp == NULL) { + warnmsg(LOG_ERR, __func__, + "malloc failed: %s", + strerror(errno)); + break; + } + smp = malloc(sizeof(*smp)); + smp->sm_msg = strdup("search "); + if (smp->sm_msg == NULL) { + warnmsg(LOG_ERR, __func__, + "strdup failed: %s", + strerror(errno)); + free(smp); + break; + } + TAILQ_INSERT_TAIL(&sm_sl_head, smp, sm_next); + } + + p = raoptp + sizeof(*dnssl); + while (0 < (len = dname_labeldec(dname, p))) { + sprintf(slbuf, "%s ", dname); + warnmsg(LOG_DEBUG, __func__, "slbuf = %s", + slbuf); + + smp = malloc(sizeof(*smp)); + if (smp == NULL) { + warnmsg(LOG_ERR, __func__, + "malloc failed: %s", + strerror(errno)); + break; + } + memset(smp, 0, sizeof(*smp)); + smp->sm_msg = strdup(slbuf); + if (smp->sm_msg == NULL) { + warnmsg(LOG_ERR, __func__, + "strdup failed: %s", + strerror(errno)); + free(smp); + break; + } + TAILQ_INSERT_TAIL(&sm_sl_head, smp, sm_next); + p += len; + } + break; + default: + /* nothing to do for other options */ + break; + } + raoptp = (char *)RA_OPT_NEXT_HDR(raoptp); + } + if (!TAILQ_EMPTY(&sm_sl_head)) { + smp = malloc(sizeof(*smp)); + if (smp == NULL) { + warnmsg(LOG_ERR, __func__, "malloc failed: %s", + strerror(errno)); + return; + } + smp = malloc(sizeof(*smp)); + smp->sm_msg = strdup("\n"); + if (smp->sm_msg == NULL) { + warnmsg(LOG_ERR, __func__, "strdup failed: %s", + strerror(errno)); + free(smp); + return; + } + } + TAILQ_CONCAT(&sm_ns_head, &sm_sl_head, sm_next); + if (!TAILQ_EMPTY(&sm_ns_head) || !TAILQ_EMPTY(&sm_sl_head)) { + struct script_msg *sm_tmp; + + CALL_SCRIPT(RESCONF, &sm_ns_head); + + /* Clear script message queue. */ + smp = TAILQ_FIRST(&sm_ns_head); + while(smp != NULL) { + sm_tmp = TAILQ_NEXT(smp, sm_next); + free(smp); + smp = sm_tmp; + } + } ifi->racnt++; switch (ifi->state) { @@ -372,22 +562,56 @@ rtsol_input(int s) } static void -call_script(char *scriptpath, char *ifname) +call_script(char *argv[], void *head) { + char *scriptpath; + int fd[2]; + int error; pid_t pid, wpid; + TAILQ_HEAD(, script_msg) *sm_head = NULL; - if (scriptpath == NULL) + sm_head = head; + fd[0] = fd[1] = -1; + if ((scriptpath = argv[0]) == NULL) return; + 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(%d)", + 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); @@ -399,13 +623,8 @@ call_script(char *scriptpath, char *ifname) warnmsg(LOG_DEBUG, __func__, "script \"%s\" terminated", scriptpath); } - } else { - char *argv[3]; - int fd; - - argv[0] = scriptpath; - argv[1] = ifname; - argv[2] = NULL; + } else { /* child */ + int nullfd; if (safefile(scriptpath)) { warnmsg(LOG_ERR, __func__, @@ -413,20 +632,35 @@ call_script(char *scriptpath, char *ifname) scriptpath); exit(1); } - - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); + nullfd = open("/dev/null", O_RDWR); + if (nullfd < 0) { + warnmsg(LOG_ERR, __func__, + "open /dev/null: %s", strerror(errno)); + exit(1); } + 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); execv(scriptpath, argv); - warnmsg(LOG_ERR, __func__, "child: exec failed: %s", strerror(errno)); - exit(0); + exit(1); } return; @@ -471,3 +705,23 @@ safefile(const char *path) return (0); } + +/* Decode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labeldec(char *dst, const char *src) +{ + size_t len; + const char *src_origin; + + src_origin = src; + while (*src && (len = (uint8_t)(*src++) & 0x3f) != 0) { + warnmsg(LOG_DEBUG, __func__, "labellen = %d", len); + memcpy(dst, src, len); + src += len; + dst += len; + if (*(dst - 1) == '\0') + break; + } + + return (src - src_origin); +} diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8 index 9ce0bb0..9af262e 100644 --- a/usr.sbin/rtsold/rtsold.8 +++ b/usr.sbin/rtsold/rtsold.8 @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 2, 2009 +.Dd May 28, 2011 .Dt RTSOLD 8 .Os .\" @@ -41,18 +41,22 @@ .Nm .Op Fl dDfFm1 .Op Fl O Ar script-name +.Op Fl R Ar script-name .Ar interface ... .Nm .Op Fl dDfFm1 .Op Fl O Ar script-name +.Op Fl R Ar script-name .Fl a .Nm rtsol -.Op Fl dDF +.Op Fl dD .Op Fl O Ar script-name +.Op Fl R Ar script-name .Ar interface ... .Nm rtsol .Op Fl dD .Op Fl O Ar script-name +.Op Fl R Ar script-name .Fl a .\" .Sh DESCRIPTION @@ -221,6 +225,15 @@ configuration. must be the absolute path from root to the script file, be a regular file, and be created by the same owner who runs .Nm . +.It Fl R Ar script-name +Specifies a script to run when router advertisment options +.Dv RDNSS Pq Recursive DNS Server +or +.Dv DNSSL Pq DNS Search List +are encountered. The information of DNS servers and DNS search domains +will be sent to standard input of this script. The +.Xr resolvconf 8 +script is used by default. .El .Sh FILES .Bl -tag -width /var/run/rtsold.dump -compact @@ -235,6 +248,7 @@ dumps internal state on. .Ex -std .\" .Sh SEE ALSO +.Xr resolvconf 8 , .Xr rtadvd 8 , .Xr sysctl 8 .\" diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c index 0db050c..cbe84f5 100644 --- a/usr.sbin/rtsold/rtsold.c +++ b/usr.sbin/rtsold/rtsold.c @@ -73,6 +73,7 @@ int aflag = 0; int dflag = 0; char *otherconf_script; +char *resolvconf_script = "/sbin/resolvconf"; /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ @@ -133,9 +134,9 @@ main(int argc, char **argv) if (argv0 && argv0[strlen(argv0) - 1] != 'd') { fflag = 1; once = 1; - opts = "adDFO:"; + opts = "adDFO:R:"; } else - opts = "adDfFm1O:"; + opts = "adDfFm1O:R:"; while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -163,6 +164,9 @@ main(int argc, char **argv) case 'O': otherconf_script = optarg; break; + case 'R': + resolvconf_script = optarg; + break; default: usage(argv0); /*NOTREACHED*/ diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h index 8aef490..f2882b8 100644 --- a/usr.sbin/rtsold/rtsold.h +++ b/usr.sbin/rtsold/rtsold.h @@ -69,6 +69,7 @@ extern int dflag; extern int aflag; extern int Fflag; extern char *otherconf_script; +extern char *resolvconf_script; extern int ifconfig(char *); extern void iflist_init(void); struct ifinfo *find_ifinfo(int); |