gateway: fix overlap in success/fail IPIP tunnel init
[olsrd.git] / src / gateway.c
index 3c457d8..1699c4c 100644 (file)
  * Defines for the multi-gateway script
  */
 
-#define SCRIPT_IPVERSION      ((olsr_cnf->ip_version == AF_INET) ? "ipv4" : "ipv6")
-
 #define SCRIPT_MODE_GENERIC   "generic"
 #define SCRIPT_MODE_OLSRIF    "olsrif"
 #define SCRIPT_MODE_SGWSRVTUN "sgwsrvtun"
 #define SCRIPT_MODE_EGRESSIF  "egressif"
 #define SCRIPT_MODE_SGWTUN    "sgwtun"
 
-#define SCRIPT_ADDMODE(add)   (add ? "add" : "del")
-
 /** structure that holds an interface name, mark and a pointer to the gateway that uses it */
 struct interfaceName {
   char name[IFNAMSIZ]; /**< interface name */
@@ -93,9 +89,31 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr
  * Helper Functions
  */
 
-#define TUNNEL_NAME (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6)
+/**
+ * @return the gateway 'server' tunnel name to use
+ */
+static inline const char * server_tunnel_name(void) {
+  return (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6);
+}
+
+/**
+ * Convert the netmask of the HNA (in the form of an IP address) to a HNA
+ * pointer.
+ *
+ * @param mask the netmask of the HNA (in the form of an IP address)
+ * @param prefixlen the prefix length
+ * @return a pointer to the HNA
+ */
+static inline uint8_t * hna_mask_to_hna_pointer(union olsr_ip_addr *mask, int prefixlen) {
+  return (((uint8_t *)mask) + ((prefixlen+7)/8));
+}
 
-#define OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen) (((uint8_t *)mask) + ((prefixlen+7)/8))
+/**
+ * @return true if multi-gateway mode is enabled
+ */
+static inline bool multi_gateway_mode(void) {
+  return (olsr_cnf->smart_gw_use_count > 1);
+}
 
 /**
  * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent)
@@ -159,6 +177,10 @@ static struct interfaceName * find_interfaceName(struct gateway_entry *gw) {
   struct interfaceName * sgwTunnelInterfaceNames;
   uint8_t i = 0;
 
+  if (!multi_gateway_mode()) {
+    return NULL;
+  }
+
   assert(sgwTunnel4InterfaceNames);
   assert(sgwTunnel6InterfaceNames);
 
@@ -188,21 +210,21 @@ static void get_unused_iptunnel_name(struct gateway_entry *gw, char * name, stru
   assert(name);
   assert(interfaceName);
 
-  if (olsr_cnf->smart_gw_use_count > 1) {
+  memset(name, 0, IFNAMSIZ);
+
+  if (multi_gateway_mode()) {
     struct interfaceName * ifn = find_interfaceName(NULL);
 
     if (ifn) {
-      ifn->gw = gw;
       strncpy(&name[0], &ifn->name[0], sizeof(ifn->name));
       *interfaceName = ifn;
+      ifn->gw = gw;
       return;
     }
 
-    assert(ifn);
     /* do not return, fall-through to classic naming as fallback */
   }
 
-  memset(name, 0, IFNAMSIZ);
   snprintf(name, IFNAMSIZ, "tnl_%08x", (olsr_cnf->ip_version == AF_INET) ? gw->originator.v4.s_addr : ++counter);
   *interfaceName = NULL;
 }
@@ -215,21 +237,17 @@ static void get_unused_iptunnel_name(struct gateway_entry *gw, char * name, stru
 static void set_unused_iptunnel_name(struct gateway_entry *gw) {
   struct interfaceName * ifn;
 
-  if (olsr_cnf->smart_gw_use_count <= 1) {
+  if (!multi_gateway_mode()) {
     return;
   }
 
   assert(gw);
-  assert(sgwTunnel4InterfaceNames);
-  assert(sgwTunnel6InterfaceNames);
 
   ifn = find_interfaceName(gw);
   if (ifn) {
     ifn->gw = NULL;
     return;
   }
-
-  assert(ifn);
 }
 
 /**
@@ -249,14 +267,14 @@ static bool multiGwRunScript(const char * mode, bool add, const char * ifname, u
 
   abuf_appendf(&buf, "\"%s\"", olsr_cnf->smart_gw_policyrouting_script);
 
-  abuf_appendf(&buf, " \"%s\"", SCRIPT_IPVERSION);
+  abuf_appendf(&buf, " \"%s\"", (olsr_cnf->ip_version == AF_INET) ? "ipv4" : "ipv6");
 
   assert(!strcmp(mode, SCRIPT_MODE_GENERIC) || !strcmp(mode, SCRIPT_MODE_OLSRIF) ||
       !strcmp(mode, SCRIPT_MODE_SGWSRVTUN) || !strcmp(mode, SCRIPT_MODE_EGRESSIF) ||
       !strcmp(mode, SCRIPT_MODE_SGWTUN));
   abuf_appendf(&buf, " \"%s\"", mode);
 
-  abuf_appendf(&buf, " \"%s\"", SCRIPT_ADDMODE(add));
+  abuf_appendf(&buf, " \"%s\"", add ? "add" : "del");
 
   if (ifname) {
     assert(!strcmp(mode, SCRIPT_MODE_OLSRIF) || !strcmp(mode, SCRIPT_MODE_SGWSRVTUN) ||
@@ -284,7 +302,7 @@ static bool multiGwRunScript(const char * mode, bool add, const char * ifname, u
 /**
  * Setup generic multi-gateway iptables and ip rules
  *
- * - generic (on olsrd up/down)
+ * - generic (on olsrd start/stop)
  * iptablesExecutable -t mangle -A OUTPUT -j CONNMARK --restore-mark
  *
  * @param add true to add policy routing, false to remove it
@@ -297,7 +315,7 @@ static bool multiGwRulesGeneric(bool add) {
 /**
  * Setup multi-gateway iptables and ip rules for all OLSR interfaces.
  *
- * - olsr interfaces (on olsrd up/down)
+ * - olsr interfaces (on olsrd start/stop)
  * iptablesExecutable -t mangle -A PREROUTING -i ${olsrInterface} -j CONNMARK --restore-mark
  *
  * @param add true to add policy routing, false to remove it
@@ -322,20 +340,20 @@ static bool multiGwRulesOlsrInterfaces(bool add) {
 /**
  * Setup multi-gateway iptables and ip rules for the smart gateway server tunnel.
  *
- * - sgw server tunnel interface (on olsrd up/down)
+ * - sgw server tunnel interface (on olsrd start/stop)
  * iptablesExecutable -t mangle -A PREROUTING  -i tunl0 -j CONNMARK --restore-mark
  *
  * @param add true to add policy routing, false to remove it
  * @return true when successful
  */
 static bool multiGwRulesSgwServerTunnel(bool add) {
-  return multiGwRunScript(SCRIPT_MODE_SGWSRVTUN, add, TUNNEL_NAME, NULL);
+  return multiGwRunScript(SCRIPT_MODE_SGWSRVTUN, add, server_tunnel_name(), NULL);
 }
 
 /**
  * Setup multi-gateway iptables and ip rules for all egress interfaces.
  *
- * - egress interfaces (on interface up/down)
+ * - egress interfaces (on interface start/stop)
  * iptablesExecutable -t mangle -A POSTROUTING -m conntrack --ctstate NEW -o ${egressInterface} -j CONNMARK --set-mark ${egressInterfaceMark}
  * iptablesExecutable -t mangle -A INPUT       -m conntrack --ctstate NEW -i ${egressInterface} -j CONNMARK --set-mark ${egressInterfaceMark}
  * ip rule add fwmark ${egressInterfaceMark} table ${egressInterfaceMark} pref ${egressInterfaceMark}
@@ -351,7 +369,7 @@ static bool multiGwRulesEgressInterfaces(bool add) {
   bool ok = true;
   unsigned int i = 0;
 
-  for (i = 0; i < olsr_cnf->smart_gw_use_count; i++) {
+  for (i = 0; i < olsr_cnf->smart_gw_egress_interfaces_count; i++) {
     struct interfaceName * ifn = &sgwEgressInterfaceNames[i];
     if (!multiGwRunScript(SCRIPT_MODE_EGRESSIF, add, ifn->name, &ifn->mark)) {
       ok = false;
@@ -367,7 +385,7 @@ static bool multiGwRulesEgressInterfaces(bool add) {
 /**
  * Setup multi-gateway iptables and ip rules for the smart gateway client tunnels.
  *
- * - sgw tunnels (on sgw tunnel up/down)
+ * - sgw tunnels (on sgw tunnel start/stop)
  * iptablesExecutable -t mangle -A POSTROUTING -m conntrack --ctstate NEW -o ${sgwTunnelInterface} -j CONNMARK --set-mark ${sgwTunnelInterfaceMark}
  * ip rule add fwmark ${sgwTunnelInterfaceMark} table ${sgwTunnelInterfaceMark} pref ${sgwTunnelInterfaceMark}
  *
@@ -443,6 +461,8 @@ static void cleanup_gateway_handler(void *ptr) {
  * Initialize gateway system
  */
 int olsr_init_gateways(void) {
+  int retries = 5;
+
   gateway_entry_mem_cookie = olsr_alloc_cookie("gateway_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY);
   olsr_cookie_set_memory_size(gateway_entry_mem_cookie, sizeof(struct gateway_entry));
 
@@ -454,21 +474,29 @@ int olsr_init_gateways(void) {
   olsr_gw_list_init(&gw_list_ipv4, olsr_cnf->smart_gw_use_count);
   olsr_gw_list_init(&gw_list_ipv6, olsr_cnf->smart_gw_use_count);
 
-  if (olsr_cnf->smart_gw_use_count <= 1) {
+  if (!multi_gateway_mode()) {
     sgwEgressInterfaceNames = NULL;
     sgwTunnel4InterfaceNames = NULL;
     sgwTunnel6InterfaceNames = NULL;
   } else {
     uint8_t i;
+    struct sgw_egress_if * egressif;
 
     /* setup the egress interface name/mark pairs */
     sgwEgressInterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_egress_interfaces_count, "sgwEgressInterfaceNames");
-    for (i = 0; i < olsr_cnf->smart_gw_egress_interfaces_count; i++) {
+    i = 0;
+    egressif = olsr_cnf->smart_gw_egress_interfaces;
+    while (egressif) {
       struct interfaceName * ifn = &sgwEgressInterfaceNames[i];
       ifn->gw = NULL;
       ifn->mark = i + olsr_cnf->smart_gw_mark_offset_egress;
-      snprintf(&ifn->name[0], sizeof(ifn->name), "egress_%03u", ifn->mark);
+      egressif->mark = ifn->mark;
+      snprintf(&ifn->name[0], sizeof(ifn->name), egressif->name, egressif->mark);
+
+      egressif = egressif->next;
+      i++;
     }
+    assert(i == olsr_cnf->smart_gw_egress_interfaces_count);
 
     /* setup the SGW tunnel name/mark pairs */
     sgwTunnel4InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel4InterfaceNames");
@@ -477,12 +505,12 @@ int olsr_init_gateways(void) {
       struct interfaceName * ifn = &sgwTunnel4InterfaceNames[i];
       ifn->gw = NULL;
       ifn->mark = i + olsr_cnf->smart_gw_mark_offset_tunnels;
-      snprintf(&ifn->name[0], sizeof(ifn->name), "tnl4_%03u", ifn->mark);
+      snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_4%03u", ifn->mark);
 
       ifn = &sgwTunnel6InterfaceNames[i];
       ifn->gw = NULL;
       ifn->mark = i + olsr_cnf->smart_gw_mark_offset_tunnels;
-      snprintf(&ifn->name[0], sizeof(ifn->name), "tnl6_%03u", ifn->mark);
+      snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_6%03u", ifn->mark);
     }
   }
 
@@ -497,7 +525,22 @@ int olsr_init_gateways(void) {
   gw_handler = &gw_def_handler;
   gw_handler->init();
 
-  if (olsr_os_init_iptunnel(TUNNEL_NAME)) {
+
+  /*
+   * There appears to be a kernel bug in some kernels (at least in the 3.0
+   * Debian Squeeze kernel, but not in the Fedora 17 kernels) around
+   * initialising the IPIP server tunnel (loading the IPIP module), so we retry
+   * a few times before giving up
+   */
+  while (retries-- > 0) {
+    if (!olsr_os_init_iptunnel(server_tunnel_name())) {
+      retries = 5;
+      break;
+    }
+
+    olsr_printf(0, "Could not initialise the IPIP server tunnel, retrying %d more times\n", retries);
+  }
+  if (retries <= 0) {
     return 1;
   }
 
@@ -512,7 +555,7 @@ int olsr_init_gateways(void) {
 int olsr_startup_gateways(void) {
   bool ok = true;
 
-  if (olsr_cnf->smart_gw_use_count <= 1) {
+  if (!multi_gateway_mode()) {
     return 0;
   }
 
@@ -534,7 +577,7 @@ int olsr_startup_gateways(void) {
  * Shutdown gateway tunnel system
  */
 void olsr_shutdown_gateways(void) {
-  if (olsr_cnf->smart_gw_use_count <= 1) {
+  if (!multi_gateway_mode()) {
     return;
   }
 
@@ -549,20 +592,17 @@ void olsr_shutdown_gateways(void) {
  * Cleanup gateway tunnel system
  */
 void olsr_cleanup_gateways(void) {
-  struct avl_node * avlnode = NULL;
+  struct gateway_entry * tree_gw;
   struct gw_container_entry * gw;
 
   olsr_remove_ifchange_handler(smartgw_tunnel_monitor);
 
-  olsr_os_cleanup_iptunnel(TUNNEL_NAME);
-
   /* remove all gateways in the gateway tree that are not the active gateway */
-  while ((avlnode = avl_walk_first(&gateway_tree))) {
-    struct gateway_entry* tree_gw = node2gateway(avlnode);
+  OLSR_FOR_ALL_GATEWAY_ENTRIES(tree_gw) {
     if ((tree_gw != olsr_get_inet_gateway(false)) && (tree_gw != olsr_get_inet_gateway(true))) {
       olsr_delete_gateway_tree_entry(tree_gw, FORCE_DELETE_GW_ENTRY, true);
     }
-  }
+  } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
 
   /* remove all active IPv4 gateways (should be at most 1 now) */
   OLSR_FOR_ALL_GWS(&gw_list_ipv4.head, gw) {
@@ -585,6 +625,8 @@ void olsr_cleanup_gateways(void) {
   assert(!current_ipv4_gw);
   assert(!current_ipv6_gw);
 
+  olsr_os_cleanup_iptunnel(server_tunnel_name());
+
   assert(gw_handler);
   gw_handler->cleanup();
   gw_handler = NULL;
@@ -656,8 +698,9 @@ void olsr_print_gateway_entries(void) {
  * @param prefixlen of the HNA
  */
 void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
-  uint8_t *ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen);
+  uint8_t *ptr = hna_mask_to_hna_pointer(mask, prefixlen);
 
+  /* copy the current settings for uplink/downlink into the mask */
   memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen / 8);
   if (olsr_cnf->has_ipv4_gateway) {
     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4;
@@ -683,17 +726,17 @@ void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
  */
 void refresh_smartgw_netmask(void) {
   uint8_t *ip;
+
+  /* clear the mask */
   memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask));
 
   if (olsr_cnf->smart_gw_active) {
     ip = (uint8_t *) &smart_gateway_netmask;
 
-    if (olsr_cnf->smart_gw_uplink > 0 && olsr_cnf->smart_gw_downlink > 0) {
-      /* the link is bi-directional with a non-zero bandwidth */
-      ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
-      ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
-      ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
-    }
+    ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
+    ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
+    ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
+
     if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) {
       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX;
       ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len;
@@ -720,7 +763,7 @@ bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *ma
     return false;
   }
 
-  ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefix->prefix_len);
+  ptr = hna_mask_to_hna_pointer(mask, prefix->prefix_len);
   return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0;
 }
 
@@ -751,7 +794,7 @@ void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_add
   /* keep new HNA seqno */
   gw->seqno = seqno;
 
-  ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen);
+  ptr = hna_mask_to_hna_pointer(mask, prefixlen);
   if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) {
     gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]);
     gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]);
@@ -790,6 +833,14 @@ void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_add
   if (new_gw_in_list) {
     assert(gw_handler);
     new_gw_in_list = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, gw_handler->getcosts(new_gw_in_list->gw));
+    assert(new_gw_in_list);
+  }
+
+  new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw);
+  if (new_gw_in_list) {
+    assert(gw_handler);
+    new_gw_in_list = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list, gw_handler->getcosts(new_gw_in_list->gw));
+    assert(new_gw_in_list);
   }
 
   /* call update handler */
@@ -864,14 +915,14 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr
       gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw);
       if (gw_in_list) {
         if (current_ipv4_gw && current_ipv4_gw->gw == gw) {
-          olsr_os_inetgw_tunnel_route(current_ipv4_gw->tunnel->if_index, true, false, NULL);
+          olsr_os_inetgw_tunnel_route(current_ipv4_gw->tunnel->if_index, true, false, olsr_cnf->rt_table_tunnel);
           current_ipv4_gw = NULL;
         }
 
         if (gw_in_list->tunnel) {
           struct interfaceName * ifn = find_interfaceName(gw_in_list->gw);
           if (ifn) {
-            olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, true, false, &ifn->mark);
+            olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, true, false, ifn->mark);
           }
           olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
           set_unused_iptunnel_name(gw_in_list->gw);
@@ -886,14 +937,14 @@ static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t pr
       gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw);
       if (gw_in_list) {
         if (current_ipv6_gw && current_ipv6_gw->gw == gw) {
-          olsr_os_inetgw_tunnel_route(current_ipv6_gw->tunnel->if_index, false, false, NULL);
+          olsr_os_inetgw_tunnel_route(current_ipv6_gw->tunnel->if_index, false, false, olsr_cnf->rt_table_tunnel);
           current_ipv6_gw = NULL;
         }
 
         if (gw_in_list->tunnel) {
           struct interfaceName * ifn = find_interfaceName(gw_in_list->gw);
           if (ifn) {
-            olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, false, false, &ifn->mark);
+            olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, false, false, ifn->mark);
           }
           olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
           set_unused_iptunnel_name(gw_in_list->gw);
@@ -977,14 +1028,17 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
   }
 
   /* handle IPv4 */
-  if (ipv4 && new_gw->ipv4 && (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) && (!current_ipv4_gw || current_ipv4_gw->gw != new_gw)) {
-    /* new gw is different than the current gw */
+  if (ipv4 &&
+      new_gw->ipv4 &&
+      (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) &&
+      (!current_ipv4_gw || current_ipv4_gw->gw != new_gw || current_ipv4_gw->path_cost != path_cost)) {
+    /* new gw is different than the current gw, or costs have changed */
 
     struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, new_gw);
     if (new_gw_in_list) {
       /* new gw is already in the gw list */
       assert(new_gw_in_list->tunnel);
-      olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, NULL);
+      olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
       current_ipv4_gw = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, path_cost);
     } else {
       /* new gw is not yet in the gw list */
@@ -1000,7 +1054,7 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
         if (worst->tunnel) {
           struct interfaceName * ifn = find_interfaceName(worst->gw);
           if (ifn) {
-            olsr_os_inetgw_tunnel_route(worst->tunnel->if_index, true, false, &ifn->mark);
+            olsr_os_inetgw_tunnel_route(worst->tunnel->if_index, true, false, ifn->mark);
           }
           olsr_os_del_ipip_tunnel(worst->tunnel);
           set_unused_iptunnel_name(worst->gw);
@@ -1014,9 +1068,9 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
       new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true, name);
       if (new_v4gw_tunnel) {
         if (interfaceName) {
-          olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, &interfaceName->mark);
+          olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, interfaceName->mark);
         }
-        olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, NULL);
+        olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
 
         new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
         new_gw_in_list->gw = new_gw;
@@ -1032,14 +1086,16 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
   }
 
   /* handle IPv6 */
-  if (ipv6 && new_gw->ipv6 && (!current_ipv6_gw || current_ipv6_gw->gw != new_gw)) {
-    /* new gw is different than the current gw */
+  if (ipv6 &&
+      new_gw->ipv6 &&
+      (!current_ipv6_gw || current_ipv6_gw->gw != new_gw || current_ipv6_gw->path_cost != path_cost)) {
+    /* new gw is different than the current gw, or costs have changed */
 
        struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, new_gw);
     if (new_gw_in_list) {
       /* new gw is already in the gw list */
       assert(new_gw_in_list->tunnel);
-      olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, NULL);
+      olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
       current_ipv6_gw = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list, path_cost);
     } else {
       /* new gw is not yet in the gw list */
@@ -1055,7 +1111,7 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
         if (worst->tunnel) {
           struct interfaceName * ifn = find_interfaceName(worst->gw);
           if (ifn) {
-            olsr_os_inetgw_tunnel_route(worst->tunnel->if_index, false, false, &ifn->mark);
+            olsr_os_inetgw_tunnel_route(worst->tunnel->if_index, false, false, ifn->mark);
           }
           olsr_os_del_ipip_tunnel(worst->tunnel);
           set_unused_iptunnel_name(worst->gw);
@@ -1069,9 +1125,9 @@ bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, b
       new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false, name);
       if (new_v6gw_tunnel) {
         if (interfaceName) {
-          olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, &interfaceName->mark);
+          olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, interfaceName->mark);
         }
-        olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, NULL);
+        olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, olsr_cnf->rt_table_tunnel);
 
         new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
         new_gw_in_list->gw = new_gw;