From 18f0fb050b6c3a042095a2306c3674328fc75880 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 31 Mar 2012 21:18:55 +0100 Subject: [PATCH] RDNSS and DNSSL data in router advertisements. --- CHANGELOG | 5 ++- man/dnsmasq.8 | 4 ++ src/dhcp6-protocol.h | 1 + src/option.c | 38 ++++++++++++------- src/radv-protocol.h | 1 + src/radv.c | 88 +++++++++++++++++++++++++++++++++++++++----- 6 files changed, 113 insertions(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fb773e1..dca25d5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -69,9 +69,12 @@ version 2.61 Fix bug in address6_available() which caused DHCPv6 lease aquistion to fail of more than one dhcp-range in use. + + Provide RDNSS and DNSSL data in router advertisements, + using the settings provided for DHCP options + option6:domain-search and option6:dns-server. - version 2.60 Fix compilation problem in Mac OS X Lion. Thanks to Olaf Flebbe for the patch. diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index acc1920..4189d45 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1343,6 +1343,10 @@ the machine running dnsmasq. By default, he "managed address" bits are set, and the "use SLAAC" bit is reset. This can be changed for individual subnets with the mode keywords described in .B --dhcp-range. +RFC6106 DNS parameters are included in the advertisements. By default, +the relevant link-local address of the machine running dnsmasq is sent +as recursive DNS server. If provided, the DHCPv6 options dns-server and +domain-search are used for RDNSS and DNSSL. .TP .B --enable-tftp[=] Enable the TFTP server function. This is deliberately limited to that diff --git a/src/dhcp6-protocol.h b/src/dhcp6-protocol.h index 7910920..166fc90 100644 --- a/src/dhcp6-protocol.h +++ b/src/dhcp6-protocol.h @@ -54,6 +54,7 @@ #define OPTION6_RECONFIGURE_MSG 19 #define OPTION6_RECONF_ACCEPT 20 #define OPTION6_DNS_SERVER 23 +#define OPTION6_DOMAIN_SEARCH 24 #define OPTION6_REMOTE_ID 37 #define OPTION6_SUBSCRIBER_ID 38 #define OPTION6_FQDN 39 diff --git a/src/option.c b/src/option.c index 6f5fe62..611b599 100644 --- a/src/option.c +++ b/src/option.c @@ -1000,6 +1000,7 @@ static char *parse_dhcp_opt(char *arg, int flags) while (arg && *arg) { u16 len = strlen(arg); + unhide_metas(arg); PUTSHORT(len, p); memcpy(p, arg, len); p += len; @@ -1013,29 +1014,40 @@ static char *parse_dhcp_opt(char *arg, int flags) } else if (comma && (opt_len & OT_RFC1035_NAME)) { - int i, commas = 1; - unsigned char *p, *newp; - - for (i = 0; comma[i]; i++) - if (comma[i] == ',') - commas++; - - newp = opt_malloc(strlen(comma)+(2*commas)); - p = newp; + unsigned char *p = NULL, *newp, *end; + int len = 0; arg = comma; comma = split(arg); while (arg && *arg) { - p = do_rfc1035_name(p, arg); - *p++ = 0; + char *dom = canonicalise_opt(arg); + if (!dom) + { + problem = _("bad domain in dhcp-option"); + break; + } + newp = opt_malloc(len + strlen(dom) + 2); + + if (p) + { + memcpy(newp, p, len); + free(p); + } + + p = newp; + end = do_rfc1035_name(p + len, dom); + *end++ = 0; + len = end - p; + free(dom); + arg = comma; comma = split(arg); } - new->val = newp; - new->len = p - newp; + new->val = p; + new->len = len; } #endif else diff --git a/src/radv-protocol.h b/src/radv-protocol.h index 8e83169..1f95395 100644 --- a/src/radv-protocol.h +++ b/src/radv-protocol.h @@ -43,6 +43,7 @@ struct prefix_opt { #define ICMP6_OPT_PREFIX 3 #define ICMP6_OPT_MTU 5 #define ICMP6_OPT_RDNSS 25 +#define ICMP6_OPT_DNSSL 31 diff --git a/src/radv.c b/src/radv.c index 3998792..af8c943 100644 --- a/src/radv.c +++ b/src/radv.c @@ -29,6 +29,7 @@ struct ra_param { int ind, managed, other, found_context, first; char *if_name; + struct dhcp_netid *tags; struct in6_addr link_local; }; @@ -189,7 +190,10 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest) struct ifreq ifr; struct sockaddr_in6 addr; struct dhcp_context *context; - + struct dhcp_netid iface_id; + struct dhcp_opt *opt_cfg; + int done_dns = 0; + save_counter(0); ra = expand(sizeof(struct ra_packet)); @@ -208,9 +212,17 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest) parm.if_name = iface_name; parm.first = 1; - for (context = daemon->ra_contexts; context; context = context->next) - context->flags &= ~CONTEXT_RA_DONE; + /* set tag with name == interface */ + iface_id.net = iface_name; + iface_id.next = NULL; + parm.tags = &iface_id; + for (context = daemon->ra_contexts; context; context = context->next) + { + context->flags &= ~CONTEXT_RA_DONE; + context->netid.next = &context->netid; + } + if (!iface_enumerate(AF_INET6, &parm, add_prefixes) || !parm.found_context) return; @@ -226,14 +238,63 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest) } iface_enumerate(AF_LOCAL, &iface, add_lla); + + /* RDNSS, RFC 6106, use relevant DHCP6 options */ + (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6); - /* RDNSS, RFC 6106 */ - put_opt6_char(ICMP6_OPT_RDNSS); - put_opt6_char(3); - put_opt6_short(0); - put_opt6_long(1800); /* lifetime - twice RA retransmit */ - put_opt6(&parm.link_local, IN6ADDRSZ); + for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) + { + int i; + + /* netids match and not encapsulated? */ + if (!(opt_cfg->flags & DHOPT_TAGOK)) + continue; + + if (opt_cfg->opt == OPTION6_DNS_SERVER) + { + struct in6_addr *a = (struct in6_addr *)opt_cfg->val; + done_dns = 1; + if (opt_cfg->len == 0) + continue; + + put_opt6_char(ICMP6_OPT_RDNSS); + put_opt6_char((opt_cfg->len/8) + 1); + put_opt6_short(0); + put_opt6_long(1800); /* lifetime - twice RA retransmit */ + /* zero means "self" */ + for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++) + if (IN6_IS_ADDR_UNSPECIFIED(a)) + put_opt6(&parm.link_local, IN6ADDRSZ); + else + put_opt6(a, IN6ADDRSZ); + } + + if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0) + { + int len = ((opt_cfg->len+7)/8); + + put_opt6_char(ICMP6_OPT_DNSSL); + put_opt6_char(len + 1); + put_opt6_short(0); + put_opt6_long(1800); /* lifetime - twice RA retransmit */ + put_opt6(opt_cfg->val, opt_cfg->len); + + /* pad */ + for (i = opt_cfg->len; i < len * 8; i++) + put_opt6_char(0); + } + } + + if (!done_dns) + { + /* default == us. */ + put_opt6_char(ICMP6_OPT_RDNSS); + put_opt6_char(3); + put_opt6_short(0); + put_opt6_long(1800); /* lifetime - twice RA retransmit */ + put_opt6(&parm.link_local, IN6ADDRSZ); + } /* set managed bits unless we're providing only RA on this link */ if (parm.managed) @@ -323,7 +384,14 @@ static int add_prefixes(struct in6_addr *local, int prefix, context->ra_time = 0; param->first = 0; param->found_context = 1; - + + /* collect dhcp-range tags */ + if (context->netid.next == &context->netid && context->netid.net) + { + context->netid.next = param->tags; + param->tags = &context->netid; + } + if (!(context->flags & CONTEXT_RA_DONE)) { context->flags |= CONTEXT_RA_DONE;