Index: sys/netinet/icmp6.h =================================================================== --- sys/netinet/icmp6.h (revision 222277) +++ sys/netinet/icmp6.h (working copy) @@ -297,6 +297,8 @@ #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 @@ /* 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 */ Index: usr.sbin/rtadvd/if.c =================================================================== --- usr.sbin/rtadvd/if.c (revision 222402) +++ usr.sbin/rtadvd/if.c (working copy) @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include Index: usr.sbin/rtadvd/config.c =================================================================== --- usr.sbin/rtadvd/config.c (revision 222402) +++ usr.sbin/rtadvd/config.c (working copy) @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -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,7 +78,33 @@ 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) char *intface; @@ -123,6 +155,10 @@ #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 @@ 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 @@ 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 @@ } #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 @@ 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,7 +1089,30 @@ 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) { syslog(LOG_ERR, @@ -944,6 +1120,7 @@ __func__); exit(1); } + memset(buf, 0, packlen); if (rainfo->ra_data) { /* free the previous packet */ free(rainfo->ra_data); @@ -1056,6 +1233,57 @@ } #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; } Index: usr.sbin/rtadvd/dump.c =================================================================== --- usr.sbin/rtadvd/dump.c (revision 222402) +++ usr.sbin/rtadvd/dump.c (working copy) @@ -45,6 +45,7 @@ #include +#include #include #include #include @@ -63,6 +64,7 @@ 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 @@ #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 @@ 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 @@ 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); +} Index: usr.sbin/rtadvd/rrenum.c =================================================================== --- usr.sbin/rtadvd/rrenum.c (revision 222402) +++ usr.sbin/rtadvd/rrenum.c (working copy) @@ -45,6 +45,7 @@ #include #include +#include #include #include #include Index: usr.sbin/rtadvd/config.h =================================================================== --- usr.sbin/rtadvd/config.h (revision 222402) +++ usr.sbin/rtadvd/config.h (working copy) @@ -45,3 +45,5 @@ */ #define MAXPREFIX 100 #define MAXROUTE 100 +#define MAXRDNSSENT 100 +#define MAXDNSSLENT 100 Index: usr.sbin/rtadvd/rtadvd.c =================================================================== --- usr.sbin/rtadvd/rtadvd.c (revision 222402) +++ usr.sbin/rtadvd/rtadvd.c (working copy) @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -115,15 +117,26 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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: Index: usr.sbin/rtadvd/rtadvd.conf =================================================================== --- usr.sbin/rtadvd/rtadvd.conf (revision 222402) +++ 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:\ +# :rddns="2001:db8:ffff:1000::1":dnssl="foo.com": Index: usr.sbin/rtadvd/rtadvd.h =================================================================== --- usr.sbin/rtadvd/rtadvd.h (revision 222402) +++ usr.sbin/rtadvd/rtadvd.h (working copy) @@ -94,6 +94,44 @@ }; #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 @@ 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 Index: usr.sbin/rtadvd/rtadvd.conf.5 =================================================================== --- usr.sbin/rtadvd/rtadvd.conf.5 (revision 222402) +++ usr.sbin/rtadvd/rtadvd.conf.5 (working copy) @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 17, 1998 +.Dd May 28, 2011 .Dt RTADVD.CONF 5 .Os .Sh NAME @@ -355,6 +355,65 @@ .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 +447,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="foo.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,10 +469,11 @@ :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 @@ .%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 222402) +++ usr.sbin/rtadvd/Makefile (working copy) @@ -21,7 +21,7 @@ DPADD= ${LIBUTIL} LDADD= -lutil -CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO +CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO -DRDNSS WARNS?= 1 Index: usr.sbin/rtsold/rtsol.c =================================================================== --- usr.sbin/rtsold/rtsol.c (revision 222402) +++ usr.sbin/rtsold/rtsol.c (working copy) @@ -50,6 +50,7 @@ #include +#include #include #include #include @@ -77,9 +78,24 @@ .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 @@ { 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 @@ 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 @@ 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 @@ } 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,34 +623,44 @@ warnmsg(LOG_DEBUG, __func__, "script \"%s\" terminated", scriptpath); } - } else { - char *argv[3]; - int fd; + } else { /* child */ + int nullfd; - 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); } - - 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 @@ 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); +} Index: usr.sbin/rtsold/rtsold.c =================================================================== --- usr.sbin/rtsold/rtsold.c (revision 222402) +++ usr.sbin/rtsold/rtsold.c (working copy) @@ -73,6 +73,7 @@ int dflag = 0; char *otherconf_script; +char *resolvconf_script = "/sbin/resolvconf"; /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ @@ -133,9 +134,9 @@ 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 @@ case 'O': otherconf_script = optarg; break; + case 'R': + resolvconf_script = optarg; + break; default: usage(argv0); /*NOTREACHED*/ Index: usr.sbin/rtsold/rtsold.8 =================================================================== --- usr.sbin/rtsold/rtsold.8 (revision 222402) +++ usr.sbin/rtsold/rtsold.8 (working copy) @@ -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 @@ 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 @@ .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 222402) +++ usr.sbin/rtsold/rtsold.h (working copy) @@ -69,6 +69,7 @@ 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);