gateway: add helper function gw_default_calc_threshold
[olsrd.git] / src / gateway_default_handler.c
index a18dba8..d8a5adb 100644 (file)
@@ -4,28 +4,29 @@
  *  Created on: Jan 29, 2010
  *      Author: rogge
  */
+#ifdef linux
+
+#include "gateway_default_handler.h"
 
 #include "defs.h"
 #include "gateway.h"
-#include "gateway_default_handler.h"
-#include "scheduler.h"
-#include "tc_set.h"
-#include "log.h"
 #include "lq_plugin.h"
 
-#include "assert.h"
-
-#ifdef LINUX_NETLINK_ROUTING
-static uint32_t gw_def_nodecount, gw_def_stablecount;
-static bool gw_def_finished_ipv4, gw_def_finished_ipv6;
-
+static uint32_t gw_def_nodecount;
+static uint32_t gw_def_stablecount;
+static bool gw_def_finished_ipv4;
+static bool gw_def_finished_ipv6;
 static struct timer_entry *gw_def_timer;
 
+/* forward declarations */
 static void gw_default_startup_handler(void);
 static void gw_default_choosegw_handler(bool ipv4, bool ipv6);
 static void gw_default_update_handler(struct gateway_entry *);
 static void gw_default_delete_handler(struct gateway_entry *);
 
+/**
+ * Callback list for the gateway (default) handler
+ */
 static struct olsr_gw_handler gw_def_handler = {
   &gw_default_startup_handler,
   &gw_default_choosegw_handler,
@@ -33,119 +34,195 @@ static struct olsr_gw_handler gw_def_handler = {
   &gw_default_delete_handler
 };
 
+/*
+ * Helper functions
+ */
+
+/**
+ * Calculate the threshold path cost.
+ *
+ * @param path_cost the path cost
+ * @return the threshold path cost
+ */
+static inline olsr_linkcost gw_default_calc_threshold(olsr_linkcost path_cost) {
+  olsr_linkcost path_cost_times_threshold;
+
+  if (olsr_cnf->smart_gw_thresh == 0) {
+    path_cost_times_threshold = path_cost;
+  } else {
+    path_cost_times_threshold = ((long long) path_cost * (long long) olsr_cnf->smart_gw_thresh + 50LL) / 100LL;
+  }
+
+  return path_cost_times_threshold;
+}
+
 /**
  * Look through the gateway list and select the best gateway
  * depending on the distance to this router
  */
 static void gw_default_choose_gateway(void) {
-  struct tc_entry *tc;
-  struct gateway_entry *inet_ipv4, *inet_ipv6;
-  olsr_linkcost cost_ipv4, cost_ipv6;
+  struct gateway_entry *inet_ipv4 = NULL;
+  struct gateway_entry *inet_ipv6 = NULL;
+  olsr_linkcost cost_ipv4 = ROUTE_COST_BROKEN;
+  olsr_linkcost cost_ipv6 = ROUTE_COST_BROKEN;
   struct gateway_entry *gw;
   bool dual;
 
-  cost_ipv4 = ROUTE_COST_BROKEN;
-  cost_ipv6 = ROUTE_COST_BROKEN;
+  OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
+    olsr_linkcost path_cost_times_threshold;
+    struct tc_entry *tc = olsr_lookup_tc_entry(&gw->originator);
 
-  inet_ipv4 = NULL;
-  inet_ipv6 = NULL;
+    if (!tc) {
+         /* gateways should not exist without tc entry */
+      continue;
+    }
 
-  OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
-    /* gateways should not exist without tc entry */
-    if ((tc = olsr_lookup_tc_entry(&gw->originator)) == NULL) {
+    if (tc->path_cost == ROUTE_COST_BROKEN) {
+      /* do not consider nodes with an infinite ETX */
       continue;
     }
 
-    if (!gw_def_finished_ipv4 && gw->ipv4 && gw->ipv4nat == olsr_cnf->smart_gw_allow_nat && tc->path_cost < cost_ipv4) {
+    if (!gw->uplink || !gw->downlink) {
+      /* do not consider nodes without bandwidth or with a uni-directional link */
+      continue;
+    }
+
+    /* determine the path costs threshold */
+    path_cost_times_threshold = gw_default_calc_threshold(tc->path_cost);
+
+    if (!gw_def_finished_ipv4 && gw->ipv4 && gw->ipv4nat == olsr_cnf->smart_gw_allow_nat && path_cost_times_threshold < cost_ipv4) {
       inet_ipv4 = gw;
-      cost_ipv4 = tc->path_cost;
+      cost_ipv4 = path_cost_times_threshold;
     }
-    if (!gw_def_finished_ipv6 && gw->ipv6 && tc->path_cost < cost_ipv6) {
+    if (!gw_def_finished_ipv6 && gw->ipv6 && path_cost_times_threshold < cost_ipv6) {
       inet_ipv6 = gw;
-      cost_ipv6 = tc->path_cost;
+      cost_ipv6 = path_cost_times_threshold;
     }
   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
 
-  /* found an IPv4 gateway ? */
-  gw_def_finished_ipv4 |= inet_ipv4 != NULL;
-  gw_def_finished_ipv6 |= inet_ipv6 != NULL;
-  dual = inet_ipv4 == inet_ipv6;
+  /* determine if we found an IPv4 and IPv6 gateway */
+  gw_def_finished_ipv4 |= (inet_ipv4 != NULL);
+  gw_def_finished_ipv6 |= (inet_ipv6 != NULL);
+
+  /* determine if we are dealing with a dual stack gateway */
+  dual = (inet_ipv4 == inet_ipv6) && (inet_ipv4 != NULL);
+
   if (inet_ipv4) {
+       /* we are dealing with an IPv4 or dual stack gateway */
     olsr_set_inet_gateway(&inet_ipv4->originator, true, dual, false);
   }
   if (inet_ipv6 && !dual) {
+    /* we are dealing with an IPv6-only gateway */
     olsr_set_inet_gateway(&inet_ipv6->originator, false, true, false);
   }
 
-  /* finished ? */
-  if (gw_def_finished_ipv4 && gw_def_finished_ipv6) {
+  if ((olsr_cnf->smart_gw_thresh == 0) && gw_def_finished_ipv4 && gw_def_finished_ipv6) {
+    /* stop looking for a better gateway */
     olsr_stop_timer(gw_def_timer);
     gw_def_timer = NULL;
   }
 }
 
-/* timer for lazy gateway selection */
+/**
+ * Timer callback for lazy gateway selection
+ *
+ * @param unused unused
+ */
 static void gw_default_timer(void *unused __attribute__ ((unused))) {
-  /* accept a 10% increase without trigger a stablecount reset */
-  if (tc_tree.count * 10 <= gw_def_nodecount * 11) {
+  /* accept a 10% increase/decrease in the number of gateway nodes without triggering a stablecount reset */
+  if (((tc_tree.count * 10) <= (gw_def_nodecount * 11)) ||
+      ((tc_tree.count * 10) >= (gw_def_nodecount *  9))) {
     gw_def_nodecount = tc_tree.count;
   }
-  if (tc_tree.count <= gw_def_nodecount) {
+
+  if (tc_tree.count == gw_def_nodecount) {
+    /* the number of gateway nodes is 'stable' */
     gw_def_stablecount++;
   }
   else {
+    /* there was a significant change in the number of gateway nodes */
     gw_def_nodecount = tc_tree.count;
     gw_def_stablecount = 0;
   }
 
-  if (gw_def_stablecount >= GW_DEFAULT_STABLE_COUNT) {
+  if (gw_def_stablecount >= olsr_cnf->smart_gw_stablecount) {
+    /* the number of gateway nodes is stable enough, so we should select a new gateway now */
     gw_default_choose_gateway();
   }
 }
 
-/* gateway handler callbacks */
-static void gw_default_startup_handler(void) {
-  /* reset node count */
-  gw_def_nodecount = tc_tree.count;
-  gw_def_stablecount = 0;
+/**
+ * Lookup a new gateway
+ *
+ * @param ipv4 lookup new v4 gateway
+ * @param ipv6 lookup new v6 gateway
+ */
+static void olsr_gw_default_lookup_gateway(bool ipv4, bool ipv6) {
+  if (ipv4) {
+    /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
+    gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
+  }
+  if (ipv6) {
+    /* get a new IPv6 gateway if we use OLSRv6 */
+    gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6);
+  }
 
-  /* get new ipv4 GW if we use OLSRv4 or NIIT */
-  gw_def_finished_ipv4 = olsr_cnf->ip_version == AF_INET6 && !olsr_cnf->use_niit;
+  if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) {
+    gw_default_choose_gateway();
+  }
+}
 
-  /* get new ipv6 GW if we use OLSRv6 */
-  gw_def_finished_ipv6 = olsr_cnf->ip_version == AF_INET;
+/*
+ * Exported functions
+ */
 
-  /* keep in mind we might be a gateway ourself */
-  gw_def_finished_ipv4 |= olsr_cnf->has_ipv4_gateway;
-  gw_def_finished_ipv6 |= olsr_cnf->has_ipv6_gateway;
+/**
+ * initialization of default gateway handler
+ */
+void olsr_gw_default_init(void) {
+  /* initialize values */
+  gw_def_nodecount = 0;
+  gw_def_stablecount = 0;
+  gw_def_finished_ipv4 = false;
+  gw_def_finished_ipv6 = false;
+  gw_def_timer = NULL;
 
-  /* start gateway selection timer */
-  olsr_set_timer(&gw_def_timer, GW_DEFAULT_TIMER_INTERVAL, 0, true, &gw_default_timer, NULL, 0);
+  /* setup default handler */
+  olsr_set_inetgw_handler(&gw_def_handler);
 }
 
-static void gw_default_update_handler(struct gateway_entry *gw) {
-  bool v4changed, v6changed;
+/*
+ * Handler functions
+ */
 
-  v4changed = (gw == olsr_get_ipv4_inet_gateway(NULL))
-      && (!gw->ipv4 || (gw->ipv4nat && !olsr_cnf->smart_gw_allow_nat));
-  v6changed = (gw == olsr_get_ipv6_inet_gateway(NULL)) && !gw->ipv6;
+/**
+ * Handle gateway startup
+ */
+static void gw_default_startup_handler(void) {
+  /* reset node count */
+  gw_def_nodecount = tc_tree.count;
+  gw_def_stablecount = 0;
 
-  if (v4changed || v6changed) {
-    olsr_gw_default_lookup_gateway(v4changed, v6changed);
-  }
-}
+  /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
+  gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
 
-static void gw_default_delete_handler(struct gateway_entry *gw) {
-  bool isv4, isv6;
+  /* get a new IPv6 gateway if we use OLSRv6 */
+  gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6);
 
-  isv4 = gw == olsr_get_ipv4_inet_gateway(NULL);
-  isv6 = gw == olsr_get_ipv6_inet_gateway(NULL);
+  /* keep in mind we might be a gateway ourself */
+  gw_def_finished_ipv4 |= olsr_cnf->has_ipv4_gateway;
+  gw_def_finished_ipv6 |= olsr_cnf->has_ipv6_gateway;
 
-  if (gw != NULL && (isv4 || isv6)) {
-    olsr_gw_default_lookup_gateway(isv4, isv6);
-  }
+  /* (re)start gateway lazy selection timer */
+  olsr_set_timer(&gw_def_timer, olsr_cnf->smart_gw_period, 0, true, &gw_default_timer, NULL, 0);
 }
 
+/**
+ * Choose a new gateway
+ *
+ * @param ipv4 lookup new v4 gateway
+ * @param ipv6 lookup new v6 gateway
+ */
 static void gw_default_choosegw_handler(bool ipv4, bool ipv6) {
   olsr_gw_default_lookup_gateway(ipv4, ipv6);
 
@@ -155,39 +232,31 @@ static void gw_default_choosegw_handler(bool ipv4, bool ipv6) {
 }
 
 /**
- * initialization of default gateway handler
+ * Update a gateway entry
+ *
+ * @param gw the gateway entry
  */
-void olsr_gw_default_init(void) {
-  /* initialize values */
-  gw_def_timer = NULL;
-  gw_def_finished_ipv4 = false;
-  gw_def_finished_ipv6 = false;
-  gw_def_nodecount = 0;
-  gw_def_stablecount = 0;
-  gw_def_timer = NULL;
+static void gw_default_update_handler(struct gateway_entry *gw) {
+  bool v4changed = gw && (gw == olsr_get_ipv4_inet_gateway(NULL))
+      && (!gw->ipv4 || (gw->ipv4nat && !olsr_cnf->smart_gw_allow_nat));
+  bool v6changed = gw && (gw == olsr_get_ipv6_inet_gateway(NULL)) && !gw->ipv6;
 
-  /* setup default handler */
-  olsr_set_inetgw_handler(&gw_def_handler);
+  if (v4changed || v6changed) {
+    olsr_gw_default_lookup_gateway(v4changed, v6changed);
+  }
 }
 
 /**
- * Lookup a new gateway based on distance metric
+ * Remove a gateway entry
  *
- * @param ipv4 lookup new v4 gateway
- * @param ipv6 lookup new v6 gateway
+ * @param gw the gateway entry
  */
-void olsr_gw_default_lookup_gateway(bool ipv4, bool ipv6) {
-  if (ipv4) {
-    /* get new ipv4 GW if we use OLSRv4 or NIIT */
-    gw_def_finished_ipv4 = olsr_cnf->ip_version == AF_INET6 && !olsr_cnf->use_niit;
-  }
-  if (ipv6) {
-    /* get new ipv6 GW if we use OLSRv6 */
-    gw_def_finished_ipv6 = olsr_cnf->ip_version == AF_INET;
-  }
+static void gw_default_delete_handler(struct gateway_entry *gw) {
+  bool isv4 = gw && (gw == olsr_get_ipv4_inet_gateway(NULL));
+  bool isv6 = gw && (gw == olsr_get_ipv6_inet_gateway(NULL));
 
-  if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) {
-    gw_default_choose_gateway();
+  if (isv4 || isv6) {
+    olsr_gw_default_lookup_gateway(isv4, isv6);
   }
 }
-#endif
+#endif /* linux */