[fd00::} and [fe80::] special addresses in DHCPv6 options.
diff --git a/src/radv.c b/src/radv.c
index 84c1aab..76027cd 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -31,8 +31,8 @@
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
- struct in6_addr link_local, link_global;
- unsigned int pref_time, adv_interval;
+ struct in6_addr link_local, link_global, ula;
+ unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
};
struct search_param {
@@ -206,6 +206,7 @@
struct dhcp_opt *opt_cfg;
struct ra_interface *ra_param = find_iface_param(iface_name);
int done_dns = 0, old_prefix = 0;
+ unsigned int min_pref_time;
#ifdef HAVE_LINUX_NETWORK
FILE *f;
#endif
@@ -228,7 +229,7 @@
parm.if_name = iface_name;
parm.first = 1;
parm.now = now;
- parm.pref_time = 0;
+ parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
parm.adv_interval = calc_interval(ra_param);
/* set tag with name == interface */
@@ -245,6 +246,18 @@
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
return;
+ /* Find smallest preferred time within address classes,
+ to use as lifetime for options. This is a rather arbitrary choice. */
+ min_pref_time = 0xffffffff;
+ if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
+ min_pref_time = parm.glob_pref_time;
+
+ if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
+ min_pref_time = parm.ula_pref_time;
+
+ if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
+ min_pref_time = parm.link_pref_time;
+
/* Look for constructed contexts associated with addresses which have gone,
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
@@ -340,22 +353,48 @@
if (opt_cfg->opt == OPTION6_DNS_SERVER)
{
- struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
-
+ struct in6_addr *a;
+ int len;
+
done_dns = 1;
- if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time == 0))
+
+ 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(parm.pref_time);
- /* zero means "self" */
- for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
- if (IN6_IS_ADDR_UNSPECIFIED(a))
- put_opt6(&parm.link_global, IN6ADDRSZ);
- else
- put_opt6(a, IN6ADDRSZ);
+ /* reduce len for any addresses we can't substitute */
+ for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0;
+ i < opt_cfg->len; i += IN6ADDRSZ, a++)
+ if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
+ (IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
+ (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
+ len -= IN6ADDRSZ;
+
+ if (len != 0)
+ {
+ put_opt6_char(ICMP6_OPT_RDNSS);
+ put_opt6_char((len/8) + 1);
+ put_opt6_short(0);
+ put_opt6_long(min_pref_time);
+
+ for (a = (struct in6_addr *)opt_cfg->val, i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
+ if (IN6_IS_ADDR_UNSPECIFIED(a))
+ {
+ if (parm.glob_pref_time != 0)
+ put_opt6(&parm.link_global, IN6ADDRSZ);
+ }
+ else if (IN6_IS_ADDR_ULA_ZERO(a))
+ {
+ if (parm.ula_pref_time != 0)
+ put_opt6(&parm.ula, IN6ADDRSZ);
+ }
+ else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
+ {
+ if (parm.link_pref_time != 0)
+ put_opt6(&parm.link_local, IN6ADDRSZ);
+ }
+ else
+ put_opt6(a, IN6ADDRSZ);
+ }
}
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
@@ -365,7 +404,7 @@
put_opt6_char(ICMP6_OPT_DNSSL);
put_opt6_char(len + 1);
put_opt6_short(0);
- put_opt6_long(parm.pref_time);
+ put_opt6_long(min_pref_time);
put_opt6(opt_cfg->val, opt_cfg->len);
/* pad */
@@ -374,13 +413,13 @@
}
}
- if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
+ if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
{
/* default == us, as long as we are supplying DNS service. */
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
- put_opt6_long(parm.pref_time);
+ put_opt6_long(min_pref_time);
put_opt6(&parm.link_local, IN6ADDRSZ);
}
@@ -426,7 +465,16 @@
if (if_index == param->ind)
{
if (IN6_IS_ADDR_LINKLOCAL(local))
- param->link_local = *local;
+ {
+ /* Can there be more than one LL address?
+ Select the one with the longest preferred time
+ if there is. */
+ if (preferred > param->link_pref_time)
+ {
+ param->link_pref_time = preferred;
+ param->link_local = *local;
+ }
+ }
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
@@ -516,11 +564,22 @@
/* configured time is ceiling */
if (!constructed || preferred > time)
preferred = time;
-
- if (preferred > param->pref_time)
+
+ if (IN6_IS_ADDR_ULA(local))
{
- param->pref_time = preferred;
- param->link_global = *local;
+ if (preferred > param->ula_pref_time)
+ {
+ param->ula_pref_time = preferred;
+ param->ula = *local;
+ }
+ }
+ else
+ {
+ if (preferred > param->glob_pref_time)
+ {
+ param->glob_pref_time = preferred;
+ param->link_global = *local;
+ }
}
if (real_prefix != 0)
@@ -546,7 +605,6 @@
if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
}
-
}
}
}