Support for MAX_INITIAL_RTR_ADVERTISEMENTS, MAX_FINAL_RTR_ADVERTISEMENTS,
MinRtrAdvInterval and MaxRtrAdvInterval

Change-Id: Ic853d621dbb5bad8eaafb0dfaf8da090db643740
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 8ed36a3..cf804b7 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -604,6 +604,7 @@
   union mysockaddr addr;
   int used;
   bool is_ra_adv_dis;
+  unsigned int final_ra_sent_count;
   struct iname *next;
 };
 
@@ -930,7 +931,7 @@
   char *name;
   char *mtu_name;
   int interval, lifetime, prio, mtu;
-  unsigned int reachable_time, retrans_time;
+  unsigned int reachable_time, retrans_time, min_ra_adv_interval, max_ra_adv_interval, ra_sent_count;
   unsigned char hop_limit;
   struct ra_interface *next;
 };
@@ -1663,9 +1664,20 @@
 
 /* radv.c */
 #ifdef HAVE_DHCP6
+// All RTR_ADV_INTERVALs in seconds
+#define DEFAULT_RTR_ADV_INTERVAL 600
+#define MAX_RTR_ADV_INTERVAL_UPPER_BOUND 1800
+#define MAX_RTR_ADV_INTERVAL_LOWER_BOUND 4
+#define MIN_RTR_ADV_INTERVAL 3
 // MAX_RA_DELAY_TIME in milliseconds
 #define MAX_RA_DELAY_TIME               500
 #define random_num_in_range(range) (rand() % (range))
+// MAX_INITIAL_RTR_ADVERT_INTERVAL in seconds
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16
+// MAX_INITIAL_RTR_ADVERTISEMENTS in number of transmissions
+#define MAX_INITIAL_RTR_ADVERTISEMENTS  3
+// MAX_FINAL_RTR_ADVERTISEMENTS in number of transmissions
+#define MAX_FINAL_RTR_ADVERTISEMENTS    3
 void ra_init(time_t now);
 void icmp6_packet(time_t now);
 time_t periodic_ra(time_t now);
diff --git a/src/option.c b/src/option.c
index da94de8..63a40f3 100644
--- a/src/option.c
+++ b/src/option.c
@@ -528,7 +528,7 @@
   { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
   { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
   { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
-  { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>][,<reachable_time>][,<hop_limit>][,<retrans_time>]", gettext_noop("Set MTU, priority, resend-interval, router-lifetime, reachable_time, hop_limit and retrans_time"), NULL },
+  { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>][,<reachable_time>][,<hop_limit>][,<retrans_time>][,<min_ra_adv_interval>][,<max_ra_adv_interval>]", gettext_noop("Set MTU, priority, resend-interval, router-lifetime, reachable_time, hop_limit, retrans_time, min_ra_adv_interval and max_ra_adv_interval"), NULL },
   { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
   { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
   { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
@@ -2466,6 +2466,7 @@
 	new->name = opt_string_alloc(arg);
 	new->used = 0;
 	new->is_ra_adv_dis = false;
+	new->final_ra_sent_count = 0;
 	arg = comma;
       } while (arg);
       break;
@@ -2478,6 +2479,7 @@
 	{
 	  if (strcmp(tmp->name, arg) == 0) {
 	    tmp->is_ra_adv_dis = true;
+		tmp->final_ra_sent_count = 0;
 	  }
 	}
 	arg = comma;
@@ -4103,6 +4105,9 @@
           new->reachable_time = 0;
           new->hop_limit = 0;
           new->retrans_time = 0;
+          new->ra_sent_count = 0;
+          new->max_ra_adv_interval = DEFAULT_RTR_ADV_INTERVAL;
+          new->min_ra_adv_interval = new->max_ra_adv_interval/3;
           if (strcasestr(comma, "mtu:") == comma)
             {
               arg = comma + 4;
@@ -4133,8 +4138,14 @@
            if (comma && !atoi_check(comma, (int *)&new->reachable_time))
              goto err;
            comma = split(arg);
-           if ((arg && !atoi_check(arg, (int *)&new->hop_limit)) ||
-               (comma && (!atoi_check(comma, (int *)&new->retrans_time))))
+           if (arg && (!atoi_check(arg, (int *)&new->hop_limit)))
+             goto err;
+           arg = split(comma);
+           if (comma && !atoi_check(comma, (int *)&new->retrans_time))
+             goto err;
+           comma = split(arg);
+           if ((arg && !atoi_check(arg, (int *)&new->min_ra_adv_interval)) ||
+               (comma && (!atoi_check(comma, (int *)&new->max_ra_adv_interval))))
              {
 err:
                free(new->name);
diff --git a/src/radv.c b/src/radv.c
index c65aca1..fdc674b 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -265,13 +265,6 @@
 #endif
 
   struct iname *if_tmp;
-  for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
-  {
-    if ((strcmp(if_tmp->name, iface_name) == 0) && (if_tmp->is_ra_adv_dis == true)) {
-     return;
-    }
-  }
-  
   parm.ind = iface;
   parm.managed = 0;
   parm.other = 0;
@@ -302,6 +295,20 @@
   iface_id.next = NULL;
   parm.tags = &iface_id; 
   
+  for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+  {
+    if ((strcmp(if_tmp->name, iface_name) == 0) && (if_tmp->is_ra_adv_dis == true)) {
+      if (if_tmp->final_ra_sent_count < MAX_FINAL_RTR_ADVERTISEMENTS) {
+        ra->lifetime = htons(0);
+        if_tmp->final_ra_sent_count++;
+        break;
+      }
+      else {
+        return;
+      }
+    }
+  }
+
   for (context = daemon->dhcp6; context; context = context->next)
     {
       context->flags &= ~CONTEXT_RA_DONE;
@@ -565,7 +572,7 @@
   while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base, 
 			   save_counter(-1), 0, (struct sockaddr *)&addr, 
 			   sizeof(addr))));
-  
+  ra_param->ra_sent_count++;
 }
 
 static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
@@ -587,7 +594,12 @@
   for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
   {
     if ((strcmp(if_tmp->name, param->if_name) == 0) && (if_tmp->is_ra_adv_dis == true)) {
-      return 1;
+      if (if_tmp->final_ra_sent_count < MAX_FINAL_RTR_ADVERTISEMENTS) {
+        break;
+      }
+      else {
+        return 1;
+      }
     }
   }
   
@@ -848,7 +860,9 @@
               struct iname *if_tmp;
               for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
               {
-                if ((strcmp(if_tmp->name, param.name) == 0) && (if_tmp->is_ra_adv_dis == false)) {
+                if ((strcmp(if_tmp->name, param.name) == 0) &&
+                    ((if_tmp->is_ra_adv_dis == false) ||
+                     (if_tmp->final_ra_sent_count < MAX_FINAL_RTR_ADVERTISEMENTS))) {
                   send_ra(now, param.iface, param.name, NULL);
 
                 /* Also send on all interfaces that are aliases of this
@@ -1005,17 +1019,23 @@
 
 static unsigned int calc_interval(struct ra_interface *ra)
 {
-  int interval = 600;
+  int interval = DEFAULT_RTR_ADV_INTERVAL;
+  unsigned int min_rtr_interval = ra->min_ra_adv_interval;
+  unsigned int max_rtr_interval = ra->max_ra_adv_interval;
+
+  if (max_rtr_interval > MAX_RTR_ADV_INTERVAL_UPPER_BOUND)
+    max_rtr_interval = MAX_RTR_ADV_INTERVAL_UPPER_BOUND;
+  else if (max_rtr_interval < MAX_RTR_ADV_INTERVAL_LOWER_BOUND)
+    max_rtr_interval = MAX_RTR_ADV_INTERVAL_LOWER_BOUND;
+
+  if (min_rtr_interval < MIN_RTR_ADV_INTERVAL)
+    min_rtr_interval = MIN_RTR_ADV_INTERVAL;
+
+  interval = (rand() % (max_rtr_interval - min_rtr_interval + 1)) + min_rtr_interval;
   
-  if (ra && ra->interval != 0)
-    {
-      interval = ra->interval;
-      if (interval > 1800)
-	interval = 1800;
-      else if (interval < 4)
-	interval = 4;
-    }
-  
+  if (((ra->ra_sent_count + 1) < MAX_INITIAL_RTR_ADVERTISEMENTS) &&
+       interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
+    interval = random_num_in_range(MAX_INITIAL_RTR_ADVERT_INTERVAL);
   return (unsigned int)interval;
 }