sgw: send sgw HNAs with zero bandwidth after local gw has gone away
authorFerry Huberts <ferry.huberts@pelagic.nl>
Fri, 8 May 2015 08:00:00 +0000 (10:00 +0200)
committerFerry Huberts <ferry.huberts@pelagic.nl>
Fri, 8 May 2015 12:45:01 +0000 (14:45 +0200)
This is to improve sgw convergence speed in the mesh.

sgw HNAs with zero bandwidth are sent after the local default gateway
has gone away (it's bandwidth has become zero on either the uplink,
downlink or both) or its interface has gone down or disappeared.

These HNAs are only sent during the next 'HNA period' after which the
sending of these HNAs stops; this is the 'unannounce' window.

Each of the zero bandwidth HNAs has an adjusted vtime so that the actual
timeout of the original HNA on the receiver side doesn't change (within
the bound of the normal jittering of sending the HNAs).

Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
src/build_msg.c
src/interfaces.h

index 972c981..43d1ea6 100644 (file)
@@ -80,7 +80,7 @@ static bool serialize_tc4(struct tc_message *, struct interface_olsr *);
 
 static bool serialize_mid4(struct interface_olsr *);
 
-static bool serialize_hna4(struct interface_olsr *);
+static bool serialize_hna4(struct ip_prefix_list *h, struct interface_olsr *, bool is_zero_bw);
 
 /* IPv6 */
 
@@ -90,7 +90,7 @@ static bool serialize_tc6(struct tc_message *, struct interface_olsr *);
 
 static bool serialize_mid6(struct interface_olsr *);
 
-static bool serialize_hna6(struct interface_olsr *);
+static bool serialize_hna6(struct ip_prefix_list *h, struct interface_olsr *, bool is_zero_bw);
 
 /**
  * Set the timer that controls the generation of
@@ -215,10 +215,10 @@ queue_hna(struct interface_olsr * ifp)
 
   switch (olsr_cnf->ip_version) {
   case (AF_INET6):
-    return serialize_hna6(ifp);
+    return serialize_hna6(olsr_cnf->hna_entries, ifp, false);
   case (AF_INET):
   default:
-    return serialize_hna4(ifp);
+    return serialize_hna4(olsr_cnf->hna_entries, ifp, false);
   }
   return false;
 }
@@ -975,9 +975,16 @@ static void appendHNAEntry(struct interface_olsr *ifp, struct ip_prefix_list *h,
     union olsr_message *m, struct hnapair **pair, bool zero
 #ifndef __linux__
 __attribute__((unused))
+#endif
+  , bool * sgw_set
+#ifndef __linux__
+__attribute__((unused))
 #endif
   ) {
   union olsr_ip_addr ip_addr;
+#ifdef __linux__
+  bool is_def_route = is_prefix_inetgw(&h->net);
+#endif
 
   if ((*curr_size + (2 * olsr_cnf->ipsize)) > *remainsize) {
     /* Only add HNA message if it contains data */
@@ -1001,9 +1008,10 @@ __attribute__((unused))
 
   olsr_prefix_to_netmask(&ip_addr, h->net.prefix_len);
 #ifdef __linux__
-  if (olsr_cnf->smart_gw_active && is_prefix_inetgw(&h->net)) {
+  if (olsr_cnf->smart_gw_active && is_def_route) {
     /* this is the default route, overwrite it with the smart gateway */
     olsr_modifiy_inetgw_netmask(&ip_addr, h->net.prefix_len, zero);
+    *sgw_set = true;
   }
 #endif /* __linux__ */
   (*pair)->addr = h->net.prefix.v4.s_addr;
@@ -1019,23 +1027,24 @@ __attribute__((unused))
  *@return nada
  */
 static bool
-serialize_hna4(struct interface_olsr *ifp)
+serialize_hna4(struct ip_prefix_list *h, struct interface_olsr *ifp, bool is_zero_bw)
 {
   uint16_t remainsize, curr_size, needsize;
   /* preserve existing data in output buffer */
   union olsr_message *m;
   struct hnapair *pair;
-  struct ip_prefix_list *h;
+  bool h_empty = !h;
+  bool sgw_set = false;
 
-  /* No hna nets */
   if (ifp == NULL) {
     return false;
   }
+
   if (olsr_cnf->ip_version != AF_INET) {
     return false;
   }
-  h = olsr_cnf->hna_entries;
-  if (h == NULL) {
+
+  if (h_empty && !ifp->sgw_sgw_zero_bw_timeout) {
     return false;
   }
 
@@ -1043,43 +1052,75 @@ serialize_hna4(struct interface_olsr *ifp)
 
   curr_size = OLSR_HNA_IPV4_HDRSIZE;
 
-  /* calculate size needed for HNA */
   needsize = curr_size;
-  while (h) {
-    needsize += olsr_cnf->ipsize*2;
-    h = h->next;
-  }
 
-  h = olsr_cnf->hna_entries;
+  if (!h_empty) {
+    /* calculate size needed for HNA */
+    struct ip_prefix_list *h_tmp = h;
+    while (h_tmp) {
+      needsize += olsr_cnf->ipsize*2;
+      h_tmp = h_tmp->next;
+    }
 
-  /* Send pending packet if not room in buffer */
-  if (needsize > remainsize) {
-    net_output(ifp);
-    remainsize = net_outbuffer_bytes_left(ifp);
-  }
-  check_buffspace(curr_size, remainsize, "HNA");
+    /* Send pending packet if not room in buffer */
+    if (needsize > remainsize) {
+      net_output(ifp);
+      remainsize = net_outbuffer_bytes_left(ifp);
+    }
+    check_buffspace(curr_size, remainsize, "HNA");
 
-  m = (union olsr_message *)msg_buffer;
+    m = (union olsr_message *)msg_buffer;
 
-  /* Fill header */
-  m->v4.olsr_msgtype = HNA_MESSAGE;
-  m->v4.olsr_vtime = ifp->valtimes.hna;
-  // olsr_msgsize
-  m->v4.originator = olsr_cnf->main_addr.v4.s_addr;
-  m->v4.ttl = MAX_TTL;
-  m->v4.hopcnt = 0;
-  // seqno
+    /* Fill header */
+    m->v4.olsr_msgtype = HNA_MESSAGE;
+    m->v4.olsr_vtime = is_zero_bw ? reltime_to_me(ifp->sgw_sgw_zero_bw_timeout) : ifp->valtimes.hna;
+    // olsr_msgsize
+    m->v4.originator = olsr_cnf->main_addr.v4.s_addr;
+    m->v4.ttl = MAX_TTL;
+    m->v4.hopcnt = 0;
+    // seqno
 
-  pair = m->v4.message.hna.hna_net;
+    pair = m->v4.message.hna.hna_net;
 
-  for (; h != NULL; h = h->next) {
-    appendHNAEntry(ifp, h, &remainsize, &curr_size, m, &pair, false);
-  }
+    for (; h != NULL; h = h->next) {
+      appendHNAEntry(ifp, h, &remainsize, &curr_size, m, &pair, is_zero_bw, &sgw_set);
+    }
 
-  m->v4.olsr_msgsize = htons(curr_size);
-  m->v4.seqno = htons(get_msg_seqno());
+    m->v4.olsr_msgsize = htons(curr_size);
+    m->v4.seqno = htons(get_msg_seqno());
 
-  net_outbuffer_push(ifp, msg_buffer, curr_size);
+    net_outbuffer_push(ifp, msg_buffer, curr_size);
+
+#ifdef __linux__
+    if (sgw_set && !is_zero_bw) {
+      /* (re)set zero bandwidth sgw HNAs timeout */
+      ifp->sgw_sgw_zero_bw_timeout = ifp->valtimes.hna_reltime;
+    }
+#endif
+  }
+
+#ifdef __linux__
+  if (olsr_cnf->smart_gw_active /* sgw is active */
+      && (h_empty || !sgw_set) /* there are no HNAs at all or no sgw HNA */
+      && ifp->sgw_sgw_zero_bw_timeout /* the zero bandwidth sgw HNA window is still valid */
+      && !is_zero_bw /* prevent infinite recursion */
+      ) {
+    struct ip_prefix_list h_zero;
+
+    memset(&h_zero, 0, sizeof(h_zero));
+    serialize_hna4(&h_zero, ifp, true);
+
+    /* decrement the window validity time */
+    {
+      unsigned int hna_period = ifp->hna_gen_timer->timer_period;
+      if (ifp->sgw_sgw_zero_bw_timeout <= hna_period) {
+        ifp->sgw_sgw_zero_bw_timeout = 0;
+      } else {
+        ifp->sgw_sgw_zero_bw_timeout -= hna_period;
+      }
+    }
+  }
+#endif /* __linux__ */
 
   //printf("Sending HNA (%d bytes)...\n", outputsize);
   return false;
@@ -1089,9 +1130,16 @@ static void appendHNA6Entry(struct interface_olsr *ifp, struct ip_prefix_list *h
     union olsr_message *m, struct hnapair6 **pair, bool zero
 #ifndef __linux__
 __attribute__((unused))
+#endif
+  , bool * sgw_set
+#ifndef __linux__
+__attribute__((unused))
 #endif
   ) {
   union olsr_ip_addr ip_addr;
+#ifdef __linux__
+  bool is_def_route = is_prefix_inetgw(&h->net);
+#endif
 
   if ((*curr_size + (2 * olsr_cnf->ipsize)) > *remainsize) {
     /* Only add HNA message if it contains data */
@@ -1115,9 +1163,10 @@ __attribute__((unused))
 
   olsr_prefix_to_netmask(&ip_addr, h->net.prefix_len);
 #ifdef __linux__
-  if (olsr_cnf->smart_gw_active && is_prefix_inetgw(&h->net)) {
-    /* this is the default gateway, so overwrite it with the smart one */
+  if (olsr_cnf->smart_gw_active && is_def_route) {
+    /* this is the default route, overwrite it with the smart gateway */
     olsr_modifiy_inetgw_netmask(&ip_addr, h->net.prefix_len, zero);
+    *sgw_set = true;
   }
 #endif /* __linux__ */
   (*pair)->addr = h->net.prefix.v6;
@@ -1133,59 +1182,102 @@ __attribute__((unused))
  *@return nada
  */
 static bool
-serialize_hna6(struct interface_olsr *ifp)
+serialize_hna6(struct ip_prefix_list *h, struct interface_olsr *ifp, bool is_zero_bw)
 {
   uint16_t remainsize, curr_size, needsize;
   /* preserve existing data in output buffer */
   union olsr_message *m;
-  struct hnapair6 *pair6;
-  struct ip_prefix_list *h = olsr_cnf->hna_entries;
+  struct hnapair6 *pair;
+  bool h_empty = !h;
+  bool sgw_set = false;
+
+  if (ifp == NULL) {
+    return false;
+  }
 
-  /* No hna nets */
-  if ((olsr_cnf->ip_version != AF_INET6) || (!ifp) || h == NULL)
+  if (olsr_cnf->ip_version != AF_INET6) {
     return false;
+  }
+
+  if (h_empty && !ifp->sgw_sgw_zero_bw_timeout) {
+    return false;
+  }
 
   remainsize = net_outbuffer_bytes_left(ifp);
 
   curr_size = OLSR_HNA_IPV6_HDRSIZE;
 
-  /* calculate size needed for HNA */
   needsize = curr_size;
-  while (h) {
-    needsize += olsr_cnf->ipsize*2;
-    h = h->next;
-  }
 
-  h = olsr_cnf->hna_entries;
+  if (!h_empty) {
+    /* calculate size needed for HNA */
+    struct ip_prefix_list *h_tmp = h;
+    while (h_tmp) {
+      needsize += olsr_cnf->ipsize*2;
+      h_tmp = h_tmp->next;
+    }
 
-  /* Send pending packet if not room in buffer */
-  if (needsize > remainsize) {
-    net_output(ifp);
-    remainsize = net_outbuffer_bytes_left(ifp);
-  }
-  check_buffspace(curr_size, remainsize, "HNA");
+    /* Send pending packet if not room in buffer */
+    if (needsize > remainsize) {
+      net_output(ifp);
+      remainsize = net_outbuffer_bytes_left(ifp);
+    }
+    check_buffspace(curr_size, remainsize, "HNA");
 
-  m = (union olsr_message *)msg_buffer;
+    m = (union olsr_message *)msg_buffer;
 
-  /* Fill header */
-  m->v6.olsr_msgtype = HNA_MESSAGE;
-  m->v6.olsr_vtime = ifp->valtimes.hna;
-  // olsr_msgsize
-  m->v6.originator = olsr_cnf->main_addr.v6;
-  m->v6.ttl = MAX_TTL;
-  m->v6.hopcnt = 0;
-  // seqno
+    /* Fill header */
+    m->v6.olsr_msgtype = HNA_MESSAGE;
+    m->v6.olsr_vtime = is_zero_bw ? reltime_to_me(ifp->sgw_sgw_zero_bw_timeout) : ifp->valtimes.hna;
+    // olsr_msgsize
+    m->v6.originator = olsr_cnf->main_addr.v6;
+    m->v6.ttl = MAX_TTL;
+    m->v6.hopcnt = 0;
+    // seqno
+
+    pair = m->v6.message.hna.hna_net;
+
+    for (; h != NULL; h = h->next) {
+      appendHNA6Entry(ifp, h, &remainsize, &curr_size, m, &pair, is_zero_bw, &sgw_set);
+    }
+
+    m->v6.olsr_msgsize = htons(curr_size);
+    m->v6.seqno = htons(get_msg_seqno());
 
-  pair6 = m->v6.message.hna.hna_net;
+    net_outbuffer_push(ifp, msg_buffer, curr_size);
 
-  for (; h != NULL; h = h->next) {
-    appendHNA6Entry(ifp, h, &remainsize, &curr_size, m, &pair6, false);
+#ifdef __linux__
+    if (sgw_set && !is_zero_bw) {
+      /* (re)set zero bandwidth sgw HNAs timeout */
+      ifp->sgw_sgw_zero_bw_timeout = ifp->valtimes.hna_reltime;
+    }
+#endif
   }
 
-  m->v6.olsr_msgsize = htons(curr_size);
-  m->v6.seqno = htons(get_msg_seqno());
+#ifdef __linux__
+  if (olsr_cnf->smart_gw_active /* sgw is active */
+      && (h_empty || !sgw_set) /* there are no HNAs at all or no sgw HNA */
+      && ifp->sgw_sgw_zero_bw_timeout /* the zero bandwidth sgw HNA window is still valid */
+      && !is_zero_bw /* prevent infinite recursion */
+      ) {
+    struct ip_prefix_list h_zero;
+
+    memset(&h_zero, 0, sizeof(h_zero));
+    serialize_hna6(&h_zero, ifp, true);
+
+    /* decrement the window validity time */
+    {
+      unsigned int hna_period = ifp->hna_gen_timer->timer_period;
+      if (ifp->sgw_sgw_zero_bw_timeout <= hna_period) {
+        ifp->sgw_sgw_zero_bw_timeout = 0;
+      } else {
+        ifp->sgw_sgw_zero_bw_timeout -= hna_period;
+      }
+    }
+  }
+#endif /* __linux__ */
 
-  net_outbuffer_push(ifp, msg_buffer, curr_size);
+  //printf("Sending HNA (%d bytes)...\n", outputsize);
   return false;
 }
 
index 90472c3..66db628 100644 (file)
@@ -165,6 +165,9 @@ struct interface_olsr {
   /* Timeout for OLSR forwarding on this if */
   uint32_t fwdtimer;
 
+  /* Timeout for OLSR to keep sending zero bandwidth sgw HNAs */
+  uint32_t sgw_sgw_zero_bw_timeout;
+
   /* the buffer to construct the packet data */
   struct olsr_netbuf netbuf;