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