#include <assert.h>
#include <net/if.h>
+/*
+ * Defines for the multi-gateway script
+ */
+
+#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"
+
+/** structure that holds an interface name, mark and a pointer to the gateway that uses it */
+struct interfaceName {
+ char name[IFNAMSIZ]; /**< interface name */
+ uint8_t mark; /**< marking */
+ struct gateway_entry *gw; /**< gateway that uses this interface name */
+};
+
/** the gateway tree */
struct avl_tree gateway_tree;
static struct olsr_gw_handler *gw_handler;
/** the IPv4 gateway list */
-static struct gw_list gw_list_ipv4;
+struct gw_list gw_list_ipv4;
/** the IPv6 gateway list */
-static struct gw_list gw_list_ipv6;
+struct gw_list gw_list_ipv6;
/** the current IPv4 gateway */
static struct gw_container_entry *current_ipv4_gw;
/** the current IPv6 gateway */
static struct gw_container_entry *current_ipv6_gw;
+/** interface names for smart gateway egress interfaces */
+struct interfaceName * sgwEgressInterfaceNames;
+
+/** interface names for smart gateway tunnel interfaces, IPv4 */
+struct interfaceName * sgwTunnel4InterfaceNames;
+
+/** interface names for smart gateway tunnel interfaces, IPv6 */
+struct interfaceName * sgwTunnel6InterfaceNames;
+
+/** the timer for proactive takedown */
+static struct timer_entry *gw_takedown_timer;
+
/*
* Forward Declarations
*/
* 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);
+}
-#define OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen) (((uint8_t *)mask) + ((prefixlen+7)/8))
+/**
+ * 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));
+}
+
+/**
+ * @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)
}
/**
- * Dummy for generating an interface name for an olsr ipip tunnel
- * @param target IP destination of the tunnel
+ * Find an interfaceName struct corresponding to a certain gateway
+ * (when gw != NULL) or to an empty interfaceName struct (when gw == NULL).
+ *
+ * @param gw the gateway to find (when not NULL), or the empty struct to find (when NULL)
+ * @return a pointer to the struct, or NULL when not found
+ */
+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);
+
+ sgwTunnelInterfaceNames = (olsr_cnf->ip_version == AF_INET) ? sgwTunnel4InterfaceNames : sgwTunnel6InterfaceNames;
+ while (i < olsr_cnf->smart_gw_use_count) {
+ struct interfaceName * ifn = &sgwTunnelInterfaceNames[i];
+ if (ifn->gw == gw) {
+ return ifn;
+ }
+ i++;
+ }
+
+ return NULL;
+}
+
+/**
+ * Get an unused olsr ipip tunnel name for a certain gateway and store it in name.
+ *
+ * @param gw pointer to the gateway
* @param name pointer to output buffer (length IFNAMSIZ)
+ * @param interfaceName a pointer to the location where to store a pointer to the interfaceName struct
*/
-static void generate_iptunnel_name(union olsr_ip_addr *target, char * name) {
+static void get_unused_iptunnel_name(struct gateway_entry *gw, char * name, struct interfaceName ** interfaceName) {
static uint32_t counter = 0;
+ assert(gw);
+ assert(name);
+ assert(interfaceName);
+
memset(name, 0, IFNAMSIZ);
- snprintf(name, IFNAMSIZ, "tnl_%08x", olsr_cnf->ip_version == AF_INET ? target->v4.s_addr : ++counter);
+
+ if (multi_gateway_mode()) {
+ struct interfaceName * ifn = find_interfaceName(NULL);
+
+ if (ifn) {
+ strncpy(&name[0], &ifn->name[0], sizeof(ifn->name));
+ *interfaceName = ifn;
+ ifn->gw = gw;
+ return;
+ }
+
+ /* do not return, fall-through to classic naming as fallback */
+ }
+
+ snprintf(name, IFNAMSIZ, "tnl_%08x", (olsr_cnf->ip_version == AF_INET) ? gw->originator.v4.s_addr : ++counter);
+ *interfaceName = NULL;
+}
+
+/**
+ * Set an olsr ipip tunnel name that is used by a certain gateway as unused
+ *
+ * @param gw pointer to the gateway
+ */
+static void set_unused_iptunnel_name(struct gateway_entry *gw) {
+ struct interfaceName * ifn;
+
+ if (!multi_gateway_mode()) {
+ return;
+ }
+
+ assert(gw);
+
+ ifn = find_interfaceName(gw);
+ if (ifn) {
+ ifn->gw = NULL;
+ return;
+ }
+}
+
+/**
+ * Run the multi-gateway script/
+ *
+ * @param mode the mode (see SCRIPT_MODE_* defines)
+ * @param add true to add policy routing, false to remove it
+ * @param ifname the interface name (optional)
+ * @param ifmark the interface mark (optional
+ * @return true when successful
+ */
+static bool multiGwRunScript(const char * mode, bool add, const char * ifname, uint8_t * ifmark) {
+ struct autobuf buf;
+ int r;
+
+ abuf_init(&buf, 1024);
+
+ abuf_appendf(&buf, "\"%s\"", olsr_cnf->smart_gw_policyrouting_script);
+
+ 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\"", add ? "add" : "del");
+
+ if (ifname) {
+ assert(!strcmp(mode, SCRIPT_MODE_OLSRIF) || !strcmp(mode, SCRIPT_MODE_SGWSRVTUN) ||
+ !strcmp(mode, SCRIPT_MODE_EGRESSIF) || !strcmp(mode, SCRIPT_MODE_SGWTUN));
+ abuf_appendf(&buf, " \"%s\"", ifname);
+ } else {
+ assert(!strcmp(mode, SCRIPT_MODE_GENERIC));
+ }
+ if (ifmark) {
+ assert(!strcmp(mode, SCRIPT_MODE_EGRESSIF) || !strcmp(mode, SCRIPT_MODE_SGWTUN));
+ assert(ifname);
+ abuf_appendf(&buf, " \"%u\"", *ifmark);
+ } else {
+ assert(!strcmp(mode, SCRIPT_MODE_GENERIC) || !strcmp(mode, SCRIPT_MODE_OLSRIF) ||
+ !strcmp(mode, SCRIPT_MODE_SGWSRVTUN));
+ }
+
+ r = system(buf.buf);
+
+ abuf_free(&buf);
+
+ return (r == 0);
+}
+
+/**
+ * Setup generic multi-gateway iptables and ip rules
+ *
+ * - 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
+ * @return true when successful
+ */
+static bool multiGwRulesGeneric(bool add) {
+ return multiGwRunScript(SCRIPT_MODE_GENERIC, add, NULL, NULL);
+}
+
+/**
+ * Setup multi-gateway iptables and ip rules for all OLSR interfaces.
+ *
+ * - 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
+ * @return true when successful
+ */
+static bool multiGwRulesOlsrInterfaces(bool add) {
+ bool ok = true;
+ struct interface * ifn;
+
+ for (ifn = ifnet; ifn; ifn = ifn->int_next) {
+ if (!multiGwRunScript(SCRIPT_MODE_OLSRIF, add, ifn->int_name, NULL)) {
+ ok = false;
+ if (add) {
+ return ok;
+ }
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * Setup multi-gateway iptables and ip rules for the smart gateway server tunnel.
+ *
+ * - 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, server_tunnel_name(), NULL);
+}
+
+/**
+ * Setup multi-gateway iptables and ip rules for all egress interfaces.
+ *
+ * - 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}
+ *
+ * like table:
+ * ppp0 91
+ * eth0 92
+ *
+ * @param add true to add policy routing, false to remove it
+ * @return true when successful
+ */
+static bool multiGwRulesEgressInterfaces(bool add) {
+ bool ok = true;
+ unsigned int i = 0;
+
+ 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;
+ if (add) {
+ return ok;
+ }
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * Setup multi-gateway iptables and ip rules for the smart gateway client tunnels.
+ *
+ * - 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}
+ *
+ * like table:
+ * tnl_101 101
+ * tnl_102 102
+ * tnl_103 103
+ * tnl_104 104
+ * tnl_105 105
+ * tnl_106 106
+ * tnl_107 107
+ * tnl_108 108
+ */
+static bool multiGwRulesSgwTunnels(bool add) {
+ bool ok = true;
+ unsigned int i = 0;
+
+ while (i < olsr_cnf->smart_gw_use_count) {
+ struct interfaceName * ifn = (olsr_cnf->ip_version == AF_INET) ? &sgwTunnel4InterfaceNames[i] : &sgwTunnel6InterfaceNames[i];
+ if (!multiGwRunScript(SCRIPT_MODE_SGWTUN, add, ifn->name, &ifn->mark)) {
+ ok = false;
+ if (add) {
+ return ok;
+ }
+ }
+
+ i++;
+ }
+
+ return ok;
}
/*
olsr_cookie_free(gateway_entry_mem_cookie, gw);
}
+/**
+ * Remove a gateway from a gateway list.
+ *
+ * @param gw_list a pointer to the gateway list
+ * @param ipv4 true when dealing with an IPv4 gateway / gateway list
+ * @param gw a pointer to the gateway to remove from the list
+ */
+static void removeGatewayFromList(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * gw) {
+ if (gw->tunnel) {
+ struct interfaceName * ifn = find_interfaceName(gw->gw);
+ if (ifn) {
+ olsr_os_inetgw_tunnel_route(gw->tunnel->if_index, ipv4, false, ifn->mark);
+ }
+ olsr_os_del_ipip_tunnel(gw->tunnel);
+ set_unused_iptunnel_name(gw->gw);
+ gw->tunnel = NULL;
+ }
+ gw->gw = NULL;
+ olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(gw_list, gw));
+}
+
+/**
+ * Remove expensive gateways from the gateway list.
+ * It uses the smart_gw_takedown_percentage configuration parameter
+ *
+ * @param gw_list a pointer to the gateway list
+ * @param ipv4 true when dealing with an IPv4 gateway / gateway list
+ * @param current_gw the current gateway
+ */
+static void takeDownExpensiveGateways(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * current_gw) {
+ uint64_t current_gw_cost_boundary;
+
+ /*
+ * exit immediately when takedown is disabled, there is no current gateway, or
+ * when there is only a single gateway
+ */
+ if ((olsr_cnf->smart_gw_takedown_percentage == 0) || (current_gw == NULL ) || (gw_list->count <= 1)) {
+ return;
+ }
+
+ /* get the cost boundary */
+
+ /* scale down because otherwise the percentage calculation can overflow */
+ current_gw_cost_boundary = (current_gw->path_cost >> 2);
+
+ if (olsr_cnf->smart_gw_takedown_percentage < 100) {
+ current_gw_cost_boundary = (current_gw_cost_boundary * 100) / olsr_cnf->smart_gw_takedown_percentage;
+ }
+
+ /* loop while we still have gateways */
+ while (gw_list->count > 1) {
+ /* get the worst gateway */
+ struct gw_container_entry * worst_gw = olsr_gw_list_get_worst_entry(gw_list);
+
+ /* exit when it's the current gateway */
+ if (worst_gw == current_gw) {
+ return;
+ }
+
+ /*
+ * exit when it (and further ones; the list is sorted on costs) has lower
+ * costs than the boundary costs
+ */
+ if ((worst_gw->path_cost >> 2) < current_gw_cost_boundary) {
+ return;
+ }
+
+ /* it's is too expensive: take it down */
+ removeGatewayFromList(gw_list, ipv4, worst_gw);
+ }
+}
+
+/**
+ * Timer callback for proactive gateway takedown
+ *
+ * @param unused unused
+ */
+static void gw_takedown_timer_callback(void *unused __attribute__ ((unused))) {
+ takeDownExpensiveGateways(&gw_list_ipv4, true, current_ipv4_gw);
+ takeDownExpensiveGateways(&gw_list_ipv6, false, current_ipv6_gw);
+}
+
/*
* Main Interface
*/
* 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));
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 (!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");
+ 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;
+ 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");
+ sgwTunnel6InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel6InterfaceNames");
+ for (i = 0; i < olsr_cnf->smart_gw_use_count; i++) {
+ 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), "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), "tnl_6%03u", ifn->mark);
+ }
+ }
+
current_ipv4_gw = NULL;
current_ipv6_gw = NULL;
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;
}
return 0;
}
+/**
+ * Startup gateway system
+ */
+int olsr_startup_gateways(void) {
+ bool ok = true;
+
+ if (!multi_gateway_mode()) {
+ return 0;
+ }
+
+ ok = ok && multiGwRulesGeneric(true);
+ ok = ok && multiGwRulesSgwServerTunnel(true);
+ ok = ok && multiGwRulesOlsrInterfaces(true);
+ ok = ok && multiGwRulesEgressInterfaces(true);
+ ok = ok && multiGwRulesSgwTunnels(true);
+ if (!ok) {
+ olsr_printf(0, "Could not setup multi-gateway iptables and ip rules\n");
+ olsr_shutdown_gateways();
+ return 1;
+ }
+
+ if (olsr_cnf->smart_gw_takedown_percentage > 0) {
+ /* start gateway takedown timer */
+ olsr_set_timer(&gw_takedown_timer, olsr_cnf->smart_gw_period, 0, true, &gw_takedown_timer_callback, NULL, 0);
+ }
+
+ return 0;
+}
+
+/**
+ * Shutdown gateway tunnel system
+ */
+void olsr_shutdown_gateways(void) {
+ if (!multi_gateway_mode()) {
+ return;
+ }
+
+ if (olsr_cnf->smart_gw_takedown_percentage > 0) {
+ /* stop gateway takedown timer */
+ olsr_stop_timer(gw_takedown_timer);
+ gw_takedown_timer = NULL;
+ }
+
+ (void)multiGwRulesSgwTunnels(false);
+ (void)multiGwRulesEgressInterfaces(false);
+ (void)multiGwRulesOlsrInterfaces(false);
+ (void)multiGwRulesSgwServerTunnel(false);
+ (void)multiGwRulesGeneric(false);
+}
+
/**
* 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) {
assert(!current_ipv4_gw);
assert(!current_ipv6_gw);
+ olsr_os_cleanup_iptunnel(server_tunnel_name());
+
assert(gw_handler);
gw_handler->cleanup();
gw_handler = NULL;
+ if (sgwEgressInterfaceNames) {
+ free(sgwEgressInterfaceNames);
+ sgwEgressInterfaceNames = NULL;
+ }
+ if (sgwTunnel4InterfaceNames) {
+ free(sgwTunnel4InterfaceNames);
+ sgwTunnel4InterfaceNames = NULL;
+ }
+ if (sgwTunnel6InterfaceNames) {
+ free(sgwTunnel6InterfaceNames);
+ sgwTunnel6InterfaceNames = NULL;
+ }
+
olsr_gw_list_cleanup(&gw_list_ipv6);
olsr_gw_list_cleanup(&gw_list_ipv4);
olsr_free_cookie(gw_container_entry_mem_cookie);
* @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;
*/
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;
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;
}
/* 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]);
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 */
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_del_ipip_tunnel(gw_in_list->tunnel);
+ set_unused_iptunnel_name(gw_in_list->gw);
gw_in_list->tunnel = NULL;
}
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_del_ipip_tunnel(gw_in_list->tunnel);
+ set_unused_iptunnel_name(gw_in_list->gw);
gw_in_list->tunnel = NULL;
}
}
/* 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 */
char name[IFNAMSIZ];
struct olsr_iptunnel_entry *new_v4gw_tunnel;
+ struct interfaceName * interfaceName;
if (olsr_gw_list_full(&gw_list_ipv4)) {
/* the list is full: remove the worst active gateway */
struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv4);
assert(worst);
- if (worst->tunnel) {
- olsr_os_del_ipip_tunnel(worst->tunnel);
- worst->tunnel = NULL;
- }
- worst->gw = NULL;
- olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(&gw_list_ipv4, worst));
+ removeGatewayFromList(&gw_list_ipv4, true, worst);
}
- generate_iptunnel_name(&new_gw->originator, name);
+ get_unused_iptunnel_name(new_gw, name, &interfaceName);
new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true, name);
if (new_v4gw_tunnel) {
- olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, NULL);
+ 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, olsr_cnf->rt_table_tunnel);
new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
new_gw_in_list->gw = new_gw;
current_ipv4_gw = olsr_gw_list_add(&gw_list_ipv4, new_gw_in_list);
} else {
/* adding the tunnel failed, we try again in the next cycle */
+ set_unused_iptunnel_name(new_gw);
ipv4 = false;
}
}
}
/* 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 */
char name[IFNAMSIZ];
struct olsr_iptunnel_entry *new_v6gw_tunnel;
+ struct interfaceName * interfaceName;
if (olsr_gw_list_full(&gw_list_ipv6)) {
/* the list is full: remove the worst active gateway */
struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv6);
assert(worst);
- if (worst->tunnel) {
- olsr_os_del_ipip_tunnel(worst->tunnel);
- worst->tunnel = NULL;
- }
- worst->gw = NULL;
- olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(&gw_list_ipv6, worst));
+ removeGatewayFromList(&gw_list_ipv6, false, worst);
}
- generate_iptunnel_name(&new_gw->originator, name);
+ get_unused_iptunnel_name(new_gw, name, &interfaceName);
new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false, name);
if (new_v6gw_tunnel) {
- olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, NULL);
+ 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, olsr_cnf->rt_table_tunnel);
new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
new_gw_in_list->gw = new_gw;
current_ipv6_gw = olsr_gw_list_add(&gw_list_ipv6, new_gw_in_list);
} else {
/* adding the tunnel failed, we try again in the next cycle */
+ set_unused_iptunnel_name(new_gw);
ipv6 = false;
}
}