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);