Hello again tech@, Here's also the updated version of a patch I wrote approx. one year ago to support RFC 6106 in rtadvd(8). J.R. Oldroyd told me there was a bug in the generation of the DNS search list and that the format of the packets generated was not valid.
I fixed that, so here is the patch. Regards, -- Stephane A. Sezer Index: sys/netinet/icmp6.h =================================================================== RCS file: /cvs/src/sys/netinet/icmp6.h,v retrieving revision 1.33 diff -u -r1.33 icmp6.h --- sys/netinet/icmp6.h 22 Mar 2010 12:23:32 -0000 1.33 +++ sys/netinet/icmp6.h 26 Jan 2012 17:11:40 -0000 @@ -282,6 +282,8 @@ #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#define ND_OPT_RDNSS 25 +#define ND_OPT_DNSSL 31 struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; @@ -310,6 +312,22 @@ u_int8_t nd_opt_mtu_len; u_int16_t nd_opt_mtu_reserved; u_int32_t nd_opt_mtu_mtu; +} __packed; + +struct nd_opt_rdnss { /* RDNSS option */ + 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 */ + 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; /* Index: usr.sbin/rtadvd/config.c =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/config.c,v retrieving revision 1.26 diff -u -r1.26 config.c --- usr.sbin/rtadvd/config.c 23 Apr 2008 10:17:50 -0000 1.26 +++ usr.sbin/rtadvd/config.c 26 Jan 2012 17:11:42 -0000 @@ -109,6 +109,8 @@ fatal("malloc"); TAILQ_INIT(&tmp->prefixes); + TAILQ_INIT(&tmp->rdnsss); + TAILQ_INIT(&tmp->dnssls); SLIST_INIT(&tmp->soliciters); /* check if we are allowed to forward packets (if not determined) */ @@ -323,6 +325,106 @@ if (tmp->pfxs == 0) get_prefix(tmp); + tmp->rdnsscnt = 0; + for (i = -1; i < MAXRDNSS; ++i) { + struct rdnss *rds; + char entbuf[256]; + char *tmpaddr; + + makeentry(entbuf, sizeof(entbuf), i, "rdnss"); + addr = agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + /* servers are separated by commas in the config file */ + val = 1; + tmpaddr = addr; + while (*tmpaddr++) + if (*tmpaddr == ',') + ++val; + + rds = malloc(sizeof(struct rdnss) + val * sizeof(struct in6_addr)); + if (rds == NULL) + fatal("malloc"); + + TAILQ_INSERT_TAIL(&tmp->rdnsss, rds, entry); + tmp->rdnsscnt++; + + rds->servercnt = val; + + makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); + MAYHAVE(val, entbuf, (tmp->maxinterval * 3) / 2); + if (val < tmp->maxinterval || val > tmp->maxinterval * 2) { + log_warnx("%s (%ld) on %s is invalid " + "(should be between %d and %d)", + entbuf, val, intface, tmp->maxinterval, + tmp->maxinterval * 2); + } + rds->lifetime = val; + + val = 0; + while ((tmpaddr = strsep(&addr, ","))) { + if (inet_pton(AF_INET6, tmpaddr, &rds->servers[val]) != 1) { + log_warn("inet_pton failed for %s", tmpaddr); + exit(1); + } + val++; + } + } + + tmp->dnsslcnt = 0; + for (i = -1; i < MAXDNSSL; ++i) { + struct dnssl *dsl; + char entbuf[256]; + char *tmpsl; + + makeentry(entbuf, sizeof(entbuf), i, "dnssl"); + addr = agetstr(entbuf, &bp); + if (addr == NULL) + continue; + + dsl = malloc(sizeof(struct dnssl)); + if (dsl == NULL) + fatal("malloc"); + + TAILQ_INIT(&dsl->dnssldoms); + + while ((tmpsl = strsep(&addr, ","))) { + struct dnssldom *dnsd; + ssize_t len; + + len = strlen(tmpsl); + + /* if the domain is not "dot-terminated", add it */ + if (tmpsl[len - 1] != '.') + len += 1; + + dnsd = malloc(sizeof(struct dnssldom) + len + 1); + if (dnsd == NULL) + fatal("malloc"); + + dnsd->length = len; + strlcpy(dnsd->domain, tmpsl, len + 1); + dnsd->domain[len - 1] = '.'; + dnsd->domain[len] = '\0'; + + TAILQ_INSERT_TAIL(&dsl->dnssldoms, dnsd, entry); + } + + TAILQ_INSERT_TAIL(&tmp->dnssls, dsl, entry); + tmp->dnsslcnt++; + + makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); + MAYHAVE(val, entbuf, (tmp->maxinterval * 3) / 2); + if (val < tmp->maxinterval || val > tmp->maxinterval * 2) { + log_warnx("%s (%ld) on %s is invalid " + "(should be between %d and %d)", + entbuf, val, intface, tmp->maxinterval, + tmp->maxinterval * 2); + } + dsl->lifetime = val; + } + MAYHAVE(val, "mtu", 0); if (val < 0 || val > 0xffffffff) { log_warnx("mtu (%ld) on %s out of range", val, intface); @@ -596,7 +698,12 @@ struct nd_router_advert *ra; struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; + struct nd_opt_rdnss *ndopt_rdnss; + struct nd_opt_dnssl *ndopt_dnssl; struct prefix *pfx; + struct rdnss *rds; + struct dnssl *dsl; + struct dnssldom *dnsd; /* calculate total length */ packlen = sizeof(struct nd_router_advert); @@ -613,6 +720,20 @@ packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; if (rainfo->linkmtu) packlen += sizeof(struct nd_opt_mtu); + TAILQ_FOREACH(rds, &rainfo->rdnsss, entry) + packlen += sizeof(struct nd_opt_rdnss) + 16 * rds->servercnt; + TAILQ_FOREACH(dsl, &rainfo->dnssls, entry) { + size_t domains_size = 0; + + packlen += sizeof(struct nd_opt_dnssl); + + TAILQ_FOREACH(dnsd, &dsl->dnssldoms, entry) + domains_size += dnsd->length; + + domains_size = (domains_size + 7) & ~7; + + packlen += domains_size; + } /* allocate memory for the packet */ if ((buf = malloc(packlen)) == NULL) @@ -705,6 +826,62 @@ ndopt_pi->nd_opt_pi_prefix = pfx->prefix; buf += sizeof(struct nd_opt_prefix_info); + } + + TAILQ_FOREACH(rds, &rainfo->rdnsss, entry) { + ndopt_rdnss = (struct nd_opt_rdnss *)buf; + ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; + ndopt_rdnss->nd_opt_rdnss_len = 1 + rds->servercnt * 2; + ndopt_rdnss->nd_opt_rdnss_reserved = 0; + ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rds->lifetime); + + buf += sizeof(struct nd_opt_rdnss); + + memcpy(buf, rds->servers, rds->servercnt * 16); + buf += rds->servercnt * 16; + } + + TAILQ_FOREACH(dsl, &rainfo->dnssls, entry) { + u_int32_t size; + char *curlabel_begin; + char *curlabel_end; + + ndopt_dnssl = (struct nd_opt_dnssl *)buf; + ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL; + ndopt_dnssl->nd_opt_dnssl_reserved = 0; + ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dsl->lifetime); + + size = 0; + TAILQ_FOREACH(dnsd, &dsl->dnssldoms, entry) + size += dnsd->length; + /* align size on the next 8 byte boundary */ + size = (size + 7) & ~7; + ndopt_dnssl->nd_opt_dnssl_len = 1 + size / 8; + + buf += sizeof(struct nd_opt_dnssl); + + TAILQ_FOREACH(dnsd, &dsl->dnssldoms, entry) { + curlabel_begin = dnsd->domain; + while ((curlabel_end = strchr(curlabel_begin, '.')) && + (curlabel_end - curlabel_begin) > 1) + { + size_t curlabel_size; + + curlabel_size = curlabel_end - curlabel_begin; + *buf = curlabel_size; + ++buf; + strncpy(buf, curlabel_begin, curlabel_size); + buf += curlabel_size; + curlabel_begin = curlabel_end + 1; + } + + /* null-terminate the current domain */ + *buf++ = '\0'; + } + + /* zero out the end of the current option */ + while ((int)buf % 8 != 0) + *buf++ = '\0'; } return; Index: usr.sbin/rtadvd/config.h =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/config.h,v retrieving revision 1.6 diff -u -r1.6 config.h --- usr.sbin/rtadvd/config.h 18 Jun 2003 02:26:58 -0000 1.6 +++ usr.sbin/rtadvd/config.h 26 Jan 2012 17:11:42 -0000 @@ -38,7 +38,9 @@ /* - * it is highly unlikely to have 100 prefix information options, + * it is highly unlikely to have 100 prefix, rdnss or dnssl information options, * so it should be okay to limit it */ #define MAXPREFIX 100 +#define MAXRDNSS 100 +#define MAXDNSSL 100 Index: usr.sbin/rtadvd/dump.c =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/dump.c,v retrieving revision 1.10 diff -u -r1.10 dump.c --- usr.sbin/rtadvd/dump.c 21 Jul 2008 19:14:15 -0000 1.10 +++ usr.sbin/rtadvd/dump.c 26 Jan 2012 17:11:42 -0000 @@ -103,6 +103,9 @@ { struct rainfo *rai; struct prefix *pfx; + struct rdnss *rds; + struct dnssl *dsl; + struct dnssldom *dnsd; char prefixbuf[INET6_ADDRSTRLEN]; int first; struct timeval now; @@ -212,6 +215,29 @@ free(vltime); free(pltime); free(flags); + } + + if (!TAILQ_EMPTY(&rai->rdnsss)) + log_info(" Recursive DNS servers:"); + TAILQ_FOREACH(rds, &rai->rdnsss, entry) { + log_info(" Servers:"); + for (first = 0; first < rds->servercnt; ++first) { + inet_ntop(AF_INET6, &rds->servers[first], + prefixbuf, sizeof(prefixbuf)); + log_info(" %s", prefixbuf); + } + log_info(" Lifetime: %u", rds->lifetime); + } + + if (!TAILQ_EMPTY(&rai->dnssls)) + log_info(" DNS search lists:"); + TAILQ_FOREACH(dsl, &rai->dnssls, entry) { + log_info(" Domains:"); + + TAILQ_FOREACH(dnsd, &dsl->dnssldoms, entry) + log_info(" %s", dnsd->domain); + + log_info(" Lifetime: %u", dsl->lifetime); } } } Index: usr.sbin/rtadvd/rtadvd.c =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/rtadvd.c,v retrieving revision 1.39 diff -u -r1.39 rtadvd.c --- usr.sbin/rtadvd/rtadvd.c 2 Mar 2011 17:30:48 -0000 1.39 +++ usr.sbin/rtadvd/rtadvd.c 26 Jan 2012 17:11:42 -0000 @@ -114,15 +114,22 @@ #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) +#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 *[]); @@ -804,8 +811,8 @@ SLIST_INIT(&ndopts.nd_opts_list); 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)) { + &ndopts, NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO + | NDOPT_FLAG_MTU | NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL)) { log_warnx("ND option check failed for an RA from %s on %s", inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, INET6_ADDRSTRLEN), @@ -1104,7 +1111,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) { log_info("unknown ND option(type %d)", hdr->nd_opt_type); @@ -1121,7 +1130,10 @@ * Option length check. Do it here for all fixed-length * options. */ - if ((hdr->nd_opt_type == ND_OPT_MTU && + if ((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) || + (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)))) { @@ -1133,6 +1145,8 @@ case ND_OPT_SOURCE_LINKADDR: 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_MTU: if (ndopts->nd_opt_array[hdr->nd_opt_type]) { @@ -1154,7 +1168,7 @@ log_warn("malloc"); goto bad; } - + pfx->opt = hdr; SLIST_INSERT_HEAD(&ndopts->nd_opts_list, pfx, entry); Index: usr.sbin/rtadvd/rtadvd.conf =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/rtadvd.conf,v retrieving revision 1.6 diff -u -r1.6 rtadvd.conf --- usr.sbin/rtadvd/rtadvd.conf 19 Jul 2008 10:35:31 -0000 1.6 +++ usr.sbin/rtadvd/rtadvd.conf 26 Jan 2012 17:11:42 -0000 @@ -18,4 +18,5 @@ # this part by hand, and then invoke rtadvd with the -s option. #ef0:\ -# :addr="2001:db8: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.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/rtadvd.conf.5,v retrieving revision 1.25 diff -u -r1.25 rtadvd.conf.5 --- usr.sbin/rtadvd/rtadvd.conf.5 19 Sep 2010 21:59:23 -0000 1.25 +++ usr.sbin/rtadvd/rtadvd.conf.5 26 Jan 2012 17:11:42 -0000 @@ -216,6 +216,30 @@ will be set to the interface MTU automatically. .El .Pp +The following items are for ICMPv6 RDNSS option, used to give a list of +recursive DNS servers to hosts. If this item is omitted, no information +about DNS servers will be advertised. +.Bl -tag -width indent +.It Cm \&rdnss +(str) The list of advertised recursive DNS servers, separated by commas. +.It Cm \&rdnssltime +(num) Validity of the list of DNS servers +.Pq unit: seconds . +The default value is 1.5 * the value of maxinterval. +.El +.Pp +The following items are used for ICMPv6 DNSSL option which specifies a +list of DNS suffixes advertised to hosts. If this option is not +specified, not DNS suffix will be sent to hosts. +.Bl -tag -width indent +.It Cm \&dnssl +(str) The list of advertised DNS suffixes, separated by commas. +.It Cm \&dnsslltime +(num) Validity of the list of DNS suffixes +.Pq unit: seconds . +The default value is 1.5 * the value of maxinterval. +.El +.Pp The following item controls ICMPv6 source link-layer address option, which will be attached to router advertisement header. As noted above, you can just omit the item, then @@ -272,6 +296,18 @@ .Bd -literal -offset indent ef0:\e :addr="2001:db8:ffff:1000::":prefixlen#64: +.Ed +.Pp +The following example configures two recursive DNS servers for the +.Li em0 +interface and sets the DNS search suffix to +.Do +example.com +.Dc . +.Bd -literal -offset indent +em0:\e + :rdnss="2001:db8:ffff:1000::1,2001:db8:ffff:1000::2":\e + :dnssl="example.com": .Ed .Pp The following example presents the default values in an explicit manner. Index: usr.sbin/rtadvd/rtadvd.h =================================================================== RCS file: /cvs/src/usr.sbin/rtadvd/rtadvd.h,v retrieving revision 1.11 diff -u -r1.11 rtadvd.h --- usr.sbin/rtadvd/rtadvd.h 9 Jun 2008 22:53:24 -0000 1.11 +++ usr.sbin/rtadvd/rtadvd.h 26 Jan 2012 17:11:42 -0000 @@ -82,6 +82,27 @@ struct in6_addr prefix; }; +struct rdnss { + TAILQ_ENTRY(rdnss) entry; + + u_int32_t lifetime; + int servercnt; + struct in6_addr servers[]; +}; + +struct dnssldom { + TAILQ_ENTRY(dnssldom) entry; + + u_int32_t length; + char domain[]; +}; + +struct dnssl { + TAILQ_ENTRY(dnssl) entry; + + u_int32_t lifetime; + TAILQ_HEAD(dnssldomlist, dnssldom) dnssldoms; +}; struct soliciter { SLIST_ENTRY(soliciter) entry; @@ -118,6 +139,10 @@ u_int hoplimit; /* AdvCurHopLimit */ TAILQ_HEAD(prefixlist, prefix) prefixes; /* AdvPrefixList(link head) */ int pfxs; /* number of prefixes */ + TAILQ_HEAD(rdnsslist, rdnss) rdnsss; /* advertised recursive dns servers */ + int rdnsscnt; /* number of rdnss entries */ + TAILQ_HEAD(dnssllist, dnssl) dnssls; + int dnsslcnt; long clockskew; /* used for consisitency check of lifetimes */