4 * Created on: 05.01.2010
10 #include "common/avl.h"
15 #include "olsr_cookie.h"
16 #include "scheduler.h"
17 #include "kernel_routes.h"
18 #include "kernel_tunnel.h"
20 #include "duplicate_set.h"
22 #include "gateway_default_handler.h"
23 #include "gateway_list.h"
25 #include "egressTypes.h"
26 #include "egressFile.h"
32 * Defines for the multi-gateway script
35 #define SCRIPT_MODE_GENERIC "generic"
36 #define SCRIPT_MODE_OLSRIF "olsrif"
37 #define SCRIPT_MODE_SGWSRVTUN "sgwsrvtun"
38 #define SCRIPT_MODE_EGRESSIF "egressif"
39 #define SCRIPT_MODE_SGWTUN "sgwtun"
41 /** structure that holds an interface name, mark and a pointer to the gateway that uses it */
42 struct interfaceName {
43 char name[IFNAMSIZ]; /**< interface name */
44 uint8_t tableNr; /**< routing table number */
45 uint8_t ruleNr; /**< IP rule number */
46 uint8_t bypassRuleNr; /**< bypass IP rule number */
47 struct gateway_entry *gw; /**< gateway that uses this interface name */
50 /** the gateway tree */
51 struct avl_tree gateway_tree;
54 static struct olsr_cookie_info *gateway_entry_mem_cookie = NULL;
56 /** gateway container cookie */
57 static struct olsr_cookie_info *gw_container_entry_mem_cookie = NULL;
59 /** the gateway netmask for the HNA */
60 static uint8_t smart_gateway_netmask[sizeof(union olsr_ip_addr)];
62 /** the gateway handler/plugin */
63 static struct olsr_gw_handler *gw_handler;
65 /** the IPv4 gateway list */
66 struct gw_list gw_list_ipv4;
68 /** the IPv6 gateway list */
69 struct gw_list gw_list_ipv6;
71 /** the current IPv4 gateway */
72 static struct gw_container_entry *current_ipv4_gw;
74 /** the current IPv6 gateway */
75 static struct gw_container_entry *current_ipv6_gw;
77 /** interface names for smart gateway tunnel interfaces, IPv4 */
78 struct interfaceName * sgwTunnel4InterfaceNames;
80 /** interface names for smart gateway tunnel interfaces, IPv6 */
81 struct interfaceName * sgwTunnel6InterfaceNames;
83 /** the timer for proactive takedown */
84 static struct timer_entry *gw_takedown_timer;
86 struct BestOverallLink {
90 struct sgw_egress_if * egress;
91 struct gateway_entry * olsr;
93 int olsrTunnelIfIndex;
96 static struct sgw_egress_if * bestEgressLinkPrevious = NULL;
97 static struct sgw_egress_if * bestEgressLink = NULL;
99 static struct BestOverallLink bestOverallLinkPrevious;
100 static struct BestOverallLink bestOverallLink;
103 * Forward Declarations
106 static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t prefixlen, bool immediate);
113 * @return the gateway 'server' tunnel name to use
115 static inline const char * server_tunnel_name(void) {
116 return (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6);
120 * Convert the netmask of the HNA (in the form of an IP address) to a HNA
123 * @param mask the netmask of the HNA (in the form of an IP address)
124 * @param prefixlen the prefix length
125 * @return a pointer to the HNA
127 static inline uint8_t * hna_mask_to_hna_pointer(union olsr_ip_addr *mask, int prefixlen) {
128 return (((uint8_t *)mask) + ((prefixlen+7)/8));
132 * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent)
133 * to an uplink/downlink speed value
135 * @param value the encoded 1 byte transport value
136 * @return the uplink/downlink speed value (in kbit/s)
138 static uint32_t deserialize_gw_speed(uint8_t value) {
143 /* 0 and 1 alias onto 0 during serialisation. We take 0 here to mean 0 and
144 * not 1 (since a bandwidth of 1 is no bandwidth at all really) */
148 if (value == UINT8_MAX) {
149 /* maximum value: also return maximum value */
153 speed = (value >> 3) + 1;
163 * Convert an uplink/downlink speed value into an encoded 1 byte transport
164 * value (5 bits mantissa, 3 bits exponent)
166 * @param speed the uplink/downlink speed value (in kbit/s)
167 * @return value the encoded 1 byte transport value
169 static uint8_t serialize_gw_speed(uint32_t speed) {
176 if (speed > 320000000) {
180 while ((speed > 32 || (speed % 10) == 0) && exp < 7) {
184 return ((speed - 1) << 3) | exp;
188 * Find an interfaceName struct corresponding to a certain gateway
189 * (when gw != NULL) or to an empty interfaceName struct (when gw == NULL).
191 * @param gw the gateway to find (when not NULL), or the empty struct to find (when NULL)
192 * @return a pointer to the struct, or NULL when not found
194 static struct interfaceName * find_interfaceName(struct gateway_entry *gw) {
195 struct interfaceName * sgwTunnelInterfaceNames;
198 if (!multi_gateway_mode()) {
202 assert(sgwTunnel4InterfaceNames);
203 assert(sgwTunnel6InterfaceNames);
205 sgwTunnelInterfaceNames = (olsr_cnf->ip_version == AF_INET) ? sgwTunnel4InterfaceNames : sgwTunnel6InterfaceNames;
206 while (i < olsr_cnf->smart_gw_use_count) {
207 struct interfaceName * ifn = &sgwTunnelInterfaceNames[i];
218 * Get an unused olsr ipip tunnel name for a certain gateway and store it in name.
220 * @param gw pointer to the gateway
221 * @param name pointer to output buffer (length IFNAMSIZ)
222 * @param interfaceName a pointer to the location where to store a pointer to the interfaceName struct
224 static void get_unused_iptunnel_name(struct gateway_entry *gw, char * name, struct interfaceName ** interfaceName) {
225 static uint32_t counter = 0;
229 assert(interfaceName);
231 memset(name, 0, IFNAMSIZ);
233 if (multi_gateway_mode()) {
234 struct interfaceName * ifn = find_interfaceName(NULL);
237 strncpy(&name[0], &ifn->name[0], sizeof(ifn->name));
238 *interfaceName = ifn;
243 /* do not return, fall-through to classic naming as fallback */
246 snprintf(name, IFNAMSIZ, "tnl_%08x", (olsr_cnf->ip_version == AF_INET) ? gw->originator.v4.s_addr : ++counter);
247 *interfaceName = NULL;
251 * Set an olsr ipip tunnel name that is used by a certain gateway as unused
253 * @param gw pointer to the gateway
255 static void set_unused_iptunnel_name(struct gateway_entry *gw) {
256 struct interfaceName * ifn;
258 if (!multi_gateway_mode()) {
264 ifn = find_interfaceName(gw);
272 * Run the multi-gateway script/
274 * @param mode the mode (see SCRIPT_MODE_* defines)
275 * @param addMode true to add policy routing, false to remove it
276 * @param ifname the interface name (optional)
277 * @param tableNr the routing table number (optional)
278 * @param ruleNr the IP rule number/priority (optional)
279 * @param bypassRuleNr the bypass IP rule number/priority (optional)
280 * @return true when successful
282 static bool multiGwRunScript(const char * mode, bool addMode, const char * ifName, uint32_t tableNr, uint32_t ruleNr, uint32_t bypassRuleNr) {
286 assert(!strcmp(mode, SCRIPT_MODE_GENERIC) //
287 || !strcmp(mode, SCRIPT_MODE_OLSRIF)//
288 || !strcmp(mode, SCRIPT_MODE_SGWSRVTUN)//
289 || !strcmp(mode, SCRIPT_MODE_EGRESSIF)//
290 || !strcmp(mode, SCRIPT_MODE_SGWTUN)//
293 assert(strcmp(mode, SCRIPT_MODE_GENERIC) //
294 || (!strcmp(mode, SCRIPT_MODE_GENERIC) && !ifName && !tableNr && !ruleNr && !bypassRuleNr));
296 assert(strcmp(mode, SCRIPT_MODE_OLSRIF) //
297 || (!strcmp(mode, SCRIPT_MODE_OLSRIF) && ifName && !tableNr && !ruleNr && bypassRuleNr));
299 assert(strcmp(mode, SCRIPT_MODE_SGWSRVTUN) //
300 || (!strcmp(mode, SCRIPT_MODE_SGWSRVTUN) && ifName && tableNr&& ruleNr && !bypassRuleNr));
302 assert(strcmp(mode, SCRIPT_MODE_EGRESSIF) //
303 || (!strcmp(mode, SCRIPT_MODE_EGRESSIF) && ifName && tableNr && ruleNr && bypassRuleNr));
305 assert(strcmp(mode, SCRIPT_MODE_SGWTUN) //
306 || (!strcmp(mode, SCRIPT_MODE_SGWTUN) && ifName && tableNr && ruleNr && !bypassRuleNr));
308 abuf_init(&buf, 1024);
310 abuf_appendf(&buf, "\"%s\"", olsr_cnf->smart_gw_policyrouting_script);
312 abuf_appendf(&buf, " \"%s\"", (olsr_cnf->ip_version == AF_INET) ? "ipv4" : "ipv6");
314 abuf_appendf(&buf, " \"%s\"", mode);
316 abuf_appendf(&buf, " \"%s\"", addMode ? "add" : "del");
319 abuf_appendf(&buf, " \"%s\"", ifName);
323 abuf_appendf(&buf, " \"%u\"", tableNr);
327 abuf_appendf(&buf, " \"%u\"", ruleNr);
331 abuf_appendf(&buf, " \"%u\"", bypassRuleNr);
342 * Setup generic multi-gateway iptables and ip rules
344 * @param add true to add policy routing, false to remove it
345 * @return true when successful
347 static bool multiGwRulesGeneric(bool add) {
348 return multiGwRunScript(SCRIPT_MODE_GENERIC, add, NULL, 0, 0, 0);
352 * Setup multi-gateway iptables and ip rules for all OLSR interfaces.
354 * @param add true to add policy routing, false to remove it
355 * @return true when successful
357 static bool multiGwRulesOlsrInterfaces(bool add) {
359 struct olsr_if * ifn;
362 for (ifn = olsr_cnf->interfaces; ifn; ifn = ifn->next, i++) {
363 if (!multiGwRunScript( //
364 SCRIPT_MODE_OLSRIF,//
369 olsr_cnf->smart_gw_offset_rules + olsr_cnf->smart_gw_egress_interfaces_count + i //
382 * Setup multi-gateway iptables and ip rules for the smart gateway server tunnel.
384 * @param add true to add policy routing, false to remove it
385 * @return true when successful
387 static bool multiGwRulesSgwServerTunnel(bool add) {
388 return multiGwRunScript( //
389 SCRIPT_MODE_SGWSRVTUN,//
391 server_tunnel_name(), //
392 olsr_cnf->smart_gw_offset_tables, //
393 olsr_cnf->smart_gw_offset_rules + olsr_cnf->smart_gw_egress_interfaces_count + getNrOfOlsrInterfaces(olsr_cnf), //
399 * Setup multi-gateway iptables and ip rules for all egress interfaces.
401 * @param add true to add policy routing, false to remove it
402 * @return true when successful
404 static bool multiGwRulesEgressInterfaces(bool add) {
408 struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
410 if (!multiGwRunScript(SCRIPT_MODE_EGRESSIF, add, egress_if->name, egress_if->tableNr, egress_if->ruleNr, egress_if->bypassRuleNr)) {
417 egress_if = egress_if->next;
424 * Setup multi-gateway iptables and ip rules for the smart gateway client tunnels.
426 * @param add true to add policy routing, false to remove it
427 * @return true when successful
429 static bool multiGwRulesSgwTunnels(bool add) {
433 while (i < olsr_cnf->smart_gw_use_count) {
434 struct interfaceName * ifn = (olsr_cnf->ip_version == AF_INET) ? &sgwTunnel4InterfaceNames[i] : &sgwTunnel6InterfaceNames[i];
435 if (!multiGwRunScript(SCRIPT_MODE_SGWTUN, add, ifn->name, ifn->tableNr, ifn->ruleNr, ifn->bypassRuleNr)) {
449 * Process interface up/down events for non-olsr interfaces, which are egress
452 * @param if_index the index of the interface
453 * @param flag the up/down event
455 static void doEgressInterface(int if_index, enum olsr_ifchg_flag flag) {
458 char ifname[IF_NAMESIZE];
459 struct sgw_egress_if * egress_if;
462 * we need to get the name of the interface first because the interface
463 * might be hot-plugged _after_ olsrd has started
465 if (!if_indextoname(if_index, ifname)) {
466 /* not a known OS interface */
470 egress_if = findEgressInterface(ifname);
472 /* not a known egress interface */
476 egress_if->if_index = if_index;
478 if (egress_if->upCurrent) {
479 /* interface is already up: no change */
483 egress_if->upPrevious = egress_if->upCurrent;
484 egress_if->upCurrent = true;
485 egress_if->upChanged = true;
487 egress_if->bwCostsChanged = egressBwCalculateCosts(&egress_if->bwCurrent, egress_if->upCurrent);
491 case IFCHG_IF_REMOVE: {
493 * we need to find the egress interface by if_index because we might
494 * be too late; the kernel could already have removed the interface
495 * in which case we'd get a NULL ifname here if we'd try to call
498 struct sgw_egress_if * egress_if = findEgressInterfaceByIndex(if_index);
500 /* not a known egress interface */
504 if (!egress_if->upCurrent) {
505 /* interface is already down: no change */
509 egress_if->upPrevious = egress_if->upCurrent;
510 egress_if->upCurrent = false;
511 egress_if->upChanged = true;
513 egress_if->bwCostsChanged = egressBwCalculateCosts(&egress_if->bwCurrent, egress_if->upCurrent);
517 case IFCHG_IF_UPDATE:
522 doRoutesMultiGw(true, false, GW_MULTI_CHANGE_PHASE_RUNTIME);
530 * Callback for tunnel interface monitoring which will set the route into the tunnel
531 * when the interface comes up again.
533 * @param if_index the interface index
534 * @param ifh the interface (NULL when not an olsr interface)
535 * @param flag interface change flags
537 static void smartgw_tunnel_monitor(int if_index, struct interface *ifh, enum olsr_ifchg_flag flag) {
538 if (!ifh && multi_gateway_mode()) {
539 /* non-olsr interface in multi-sgw mode */
540 doEgressInterface(if_index, flag);
547 * Timer callback to remove and cleanup a gateway entry
551 static void cleanup_gateway_handler(void *ptr) {
552 struct gateway_entry *gw = ptr;
554 if (gw->ipv4 || gw->ipv6) {
555 /* do not clean it up when it is in use */
559 /* remove gateway entry */
560 avl_delete(&gateway_tree, &gw->node);
561 olsr_cookie_free(gateway_entry_mem_cookie, gw);
565 * Remove a gateway from a gateway list.
567 * @param gw_list a pointer to the gateway list
568 * @param ipv4 true when dealing with an IPv4 gateway / gateway list
569 * @param gw a pointer to the gateway to remove from the list
571 static void removeGatewayFromList(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * gw) {
573 struct interfaceName * ifn = find_interfaceName(gw->gw);
575 olsr_os_inetgw_tunnel_route(gw->tunnel->if_index, ipv4, false, ifn->tableNr);
577 olsr_os_del_ipip_tunnel(gw->tunnel);
578 set_unused_iptunnel_name(gw->gw);
582 olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(gw_list, gw));
586 * Remove expensive gateways from the gateway list.
587 * It uses the smart_gw_takedown_percentage configuration parameter
589 * @param gw_list a pointer to the gateway list
590 * @param ipv4 true when dealing with an IPv4 gateway / gateway list
591 * @param current_gw the current gateway
593 static void takeDownExpensiveGateways(struct gw_list * gw_list, bool ipv4, struct gw_container_entry * current_gw) {
594 int64_t current_gw_cost_boundary;
597 * exit immediately when takedown is disabled, there is no current gateway, or
598 * when there is only a single gateway
600 if ((olsr_cnf->smart_gw_takedown_percentage == 0) || (current_gw == NULL ) || (gw_list->count <= 1)) {
604 /* get the cost boundary */
605 current_gw_cost_boundary = current_gw->gw->path_cost;
606 if (olsr_cnf->smart_gw_takedown_percentage < 100) {
607 if (current_gw_cost_boundary <= (INT64_MAX / 100)) {
608 current_gw_cost_boundary = ((current_gw_cost_boundary * 100) / olsr_cnf->smart_gw_takedown_percentage);
610 /* perform scaling because otherwise the percentage calculation can overflow */
611 current_gw_cost_boundary = (((current_gw_cost_boundary ) / olsr_cnf->smart_gw_takedown_percentage) * 100) + 99;
615 /* loop while we still have gateways */
616 while (gw_list->count > 1) {
617 /* get the worst gateway */
618 struct gw_container_entry * worst_gw = olsr_gw_list_get_worst_entry(gw_list);
620 /* exit when it's the current gateway */
621 if (worst_gw == current_gw) {
626 * exit when it (and further ones; the list is sorted on costs) has lower
627 * costs than the boundary costs
629 if (worst_gw->gw->path_cost < current_gw_cost_boundary) {
633 /* it's too expensive: take it down */
634 removeGatewayFromList(gw_list, ipv4, worst_gw);
639 * Timer callback for proactive gateway takedown
641 * @param unused unused
643 static void gw_takedown_timer_callback(void *unused __attribute__ ((unused))) {
644 takeDownExpensiveGateways(&gw_list_ipv4, true, current_ipv4_gw);
645 takeDownExpensiveGateways(&gw_list_ipv6, false, current_ipv6_gw);
653 * Initialize gateway system
655 int olsr_init_gateways(void) {
658 gateway_entry_mem_cookie = olsr_alloc_cookie("gateway_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY);
659 olsr_cookie_set_memory_size(gateway_entry_mem_cookie, sizeof(struct gateway_entry));
661 gw_container_entry_mem_cookie = olsr_alloc_cookie("gw_container_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY);
662 olsr_cookie_set_memory_size(gw_container_entry_mem_cookie, sizeof(struct gw_container_entry));
664 avl_init(&gateway_tree, avl_comp_default);
666 olsr_gw_list_init(&gw_list_ipv4, olsr_cnf->smart_gw_use_count);
667 olsr_gw_list_init(&gw_list_ipv6, olsr_cnf->smart_gw_use_count);
669 sgwTunnel4InterfaceNames = NULL;
670 sgwTunnel6InterfaceNames = NULL;
671 memset(&bestOverallLinkPrevious, 0, sizeof(bestOverallLinkPrevious));
672 memset(&bestOverallLink, 0, sizeof(bestOverallLink));
674 if (multi_gateway_mode()) {
676 struct sgw_egress_if * egressif;
677 unsigned int nrOlsrIfs = getNrOfOlsrInterfaces(olsr_cnf);
679 /* setup the egress interface name/mark pairs */
681 egressif = olsr_cnf->smart_gw_egress_interfaces;
683 egressif->tableNr = olsr_cnf->smart_gw_offset_tables + 1 + i;
684 egressif->ruleNr = olsr_cnf->smart_gw_offset_rules + olsr_cnf->smart_gw_egress_interfaces_count + nrOlsrIfs + 1 + i;
685 egressif->bypassRuleNr = olsr_cnf->smart_gw_offset_rules + i;
687 egressif = egressif->next;
690 assert(i == olsr_cnf->smart_gw_egress_interfaces_count);
692 /* setup the SGW tunnel name/mark pairs */
693 sgwTunnel4InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel4InterfaceNames");
694 sgwTunnel6InterfaceNames = olsr_malloc(sizeof(struct interfaceName) * olsr_cnf->smart_gw_use_count, "sgwTunnel6InterfaceNames");
695 for (i = 0; i < olsr_cnf->smart_gw_use_count; i++) {
696 struct interfaceName * ifn = &sgwTunnel4InterfaceNames[i];
697 uint32_t tableNr = olsr_cnf->smart_gw_offset_tables + 1 + olsr_cnf->smart_gw_egress_interfaces_count + i;
698 uint32_t ruleNr = olsr_cnf->smart_gw_offset_rules + olsr_cnf->smart_gw_egress_interfaces_count + nrOlsrIfs + 1 + olsr_cnf->smart_gw_egress_interfaces_count + i;
701 ifn->tableNr = tableNr;
702 ifn->ruleNr = ruleNr;
703 ifn->bypassRuleNr = 0;
704 snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_4%03u", ifn->tableNr);
706 ifn = &sgwTunnel6InterfaceNames[i];
708 ifn->tableNr = tableNr;
709 ifn->ruleNr = ruleNr;
710 ifn->bypassRuleNr = 0;
711 snprintf(&ifn->name[0], sizeof(ifn->name), "tnl_6%03u", ifn->tableNr);
715 current_ipv4_gw = NULL;
716 current_ipv6_gw = NULL;
720 refresh_smartgw_netmask();
722 /* initialize default gateway handler */
723 gw_handler = &gw_def_handler;
728 * There appears to be a kernel bug in some kernels (at least in the 3.0
729 * Debian Squeeze kernel, but not in the Fedora 17 kernels) around
730 * initialising the IPIP server tunnel (loading the IPIP module), so we retry
731 * a few times before giving up
733 while (retries-- > 0) {
734 if (!olsr_os_init_iptunnel(server_tunnel_name())) {
739 olsr_printf(0, "Could not initialise the IPIP server tunnel, retrying %d more times\n", retries);
749 * Startup gateway system
751 int olsr_startup_gateways(void) {
754 if (!multi_gateway_mode()) {
755 olsr_add_ifchange_handler(smartgw_tunnel_monitor);
759 /* Initialise the egress interfaces */
761 struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
763 egress_if->if_index = if_nametoindex(egress_if->name);
765 egress_if->upPrevious = egress_if->upCurrent = olsr_if_isup(egress_if->name);
766 egress_if->upChanged = (egress_if->upPrevious != egress_if->upCurrent);
768 egressBwClear(&egress_if->bwPrevious, egress_if->upPrevious);
769 egressBwClear(&egress_if->bwCurrent, egress_if->upCurrent);
770 egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
771 egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
772 egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
773 egress_if->bwChanged = egressBwChanged(egress_if);
775 egress_if->inEgressFile = false;
777 egress_if = egress_if->next;
781 ok = ok && multiGwRulesGeneric(true);
782 ok = ok && multiGwRulesSgwServerTunnel(true);
783 ok = ok && multiGwRulesOlsrInterfaces(true);
784 ok = ok && multiGwRulesEgressInterfaces(true);
785 ok = ok && multiGwRulesSgwTunnels(true);
787 olsr_printf(0, "Could not setup multi-gateway iptables and ip rules\n");
788 olsr_shutdown_gateways();
793 doRoutesMultiGw(true, false, GW_MULTI_CHANGE_PHASE_STARTUP);
795 olsr_add_ifchange_handler(smartgw_tunnel_monitor);
797 /* Check egress interfaces up status to compensate for a race: the interfaces
798 * can change status between initialising their data structures and
799 * registering the tunnel monitor */
801 struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
803 bool upCurrent = olsr_if_isup(egress_if->name);
805 if (upCurrent != egress_if->upCurrent) {
806 int index = upCurrent ? (int) if_nametoindex(egress_if->name) : egress_if->if_index;
807 enum olsr_ifchg_flag flag = upCurrent ? IFCHG_IF_ADD : IFCHG_IF_REMOVE;
808 smartgw_tunnel_monitor(index, NULL, flag);
811 egress_if = egress_if->next;
815 if (olsr_cnf->smart_gw_takedown_percentage > 0) {
816 /* start gateway takedown timer */
817 olsr_set_timer(&gw_takedown_timer, olsr_cnf->smart_gw_period, 0, true, &gw_takedown_timer_callback, NULL, 0);
824 * Shutdown gateway tunnel system
826 void olsr_shutdown_gateways(void) {
827 if (!multi_gateway_mode()) {
828 olsr_remove_ifchange_handler(smartgw_tunnel_monitor);
832 if (olsr_cnf->smart_gw_takedown_percentage > 0) {
833 /* stop gateway takedown timer */
834 olsr_stop_timer(gw_takedown_timer);
835 gw_takedown_timer = NULL;
838 olsr_remove_ifchange_handler(smartgw_tunnel_monitor);
842 struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
844 egress_if->upPrevious = egress_if->upCurrent;
845 egress_if->upCurrent = false;
846 egress_if->upChanged = (egress_if->upPrevious != egress_if->upCurrent);
848 egress_if->bwPrevious = egress_if->bwCurrent;
849 egressBwClear(&egress_if->bwCurrent, egress_if->upCurrent);
850 egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
851 egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
852 egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
853 egress_if->bwChanged = egressBwChanged(egress_if);
855 egress_if->inEgressFile = false;
857 egress_if = egress_if->next;
860 doRoutesMultiGw(true, false, GW_MULTI_CHANGE_PHASE_SHUTDOWN);
862 (void)multiGwRulesSgwTunnels(false);
863 (void)multiGwRulesEgressInterfaces(false);
864 (void)multiGwRulesOlsrInterfaces(false);
865 (void)multiGwRulesSgwServerTunnel(false);
866 (void)multiGwRulesGeneric(false);
870 * Cleanup gateway tunnel system
872 void olsr_cleanup_gateways(void) {
873 struct gateway_entry * tree_gw;
874 struct gw_container_entry * gw;
876 /* remove all gateways in the gateway tree that are not the active gateway */
877 OLSR_FOR_ALL_GATEWAY_ENTRIES(tree_gw) {
878 if ((tree_gw != olsr_get_inet_gateway(false)) && (tree_gw != olsr_get_inet_gateway(true))) {
879 olsr_delete_gateway_tree_entry(tree_gw, FORCE_DELETE_GW_ENTRY, true);
881 } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(tree_gw)
883 /* remove all active IPv4 gateways (should be at most 1 now) */
884 OLSR_FOR_ALL_GWS(&gw_list_ipv4.head, gw) {
886 olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true);
889 OLSR_FOR_ALL_GWS_END(gw);
891 /* remove all active IPv6 gateways (should be at most 1 now) */
892 OLSR_FOR_ALL_GWS(&gw_list_ipv6.head, gw) {
894 olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true);
897 OLSR_FOR_ALL_GWS_END(gw);
899 /* there should be no more gateways */
900 assert(!avl_walk_first(&gateway_tree));
901 assert(!current_ipv4_gw);
902 assert(!current_ipv6_gw);
904 olsr_os_cleanup_iptunnel(server_tunnel_name());
907 gw_handler->cleanup();
910 if (sgwTunnel4InterfaceNames) {
911 free(sgwTunnel4InterfaceNames);
912 sgwTunnel4InterfaceNames = NULL;
914 if (sgwTunnel6InterfaceNames) {
915 free(sgwTunnel6InterfaceNames);
916 sgwTunnel6InterfaceNames = NULL;
919 olsr_gw_list_cleanup(&gw_list_ipv6);
920 olsr_gw_list_cleanup(&gw_list_ipv4);
921 olsr_free_cookie(gw_container_entry_mem_cookie);
922 olsr_free_cookie(gateway_entry_mem_cookie);
926 * Triggers the first lookup of a gateway.
928 void olsr_trigger_inetgw_startup(void) {
930 gw_handler->startup();
934 * Print debug information about gateway entries
937 void olsr_print_gateway_entries(void) {
938 struct ipaddr_str buf;
939 struct gateway_entry *gw;
940 const int addrsize = olsr_cnf->ip_version == AF_INET ? (INET_ADDRSTRLEN - 1) : (INET6_ADDRSTRLEN - 1);
942 OLSR_PRINTF(0, "\n--- %s ---------------------------------------------------- GATEWAYS\n\n", olsr_wallclock_string());
943 OLSR_PRINTF(0, "%-*s %-6s %-9s %-9s %s\n",
944 addrsize, "IP address", "Type", "Uplink", "Downlink", olsr_cnf->ip_version == AF_INET ? "" : "External Prefix");
946 OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
947 OLSR_PRINTF(0, "%-*s %s%c%s%c%c %-9u %-9u %s\n",
949 olsr_ip_to_string(&buf, &gw->originator),
950 gw->ipv4nat ? "" : " ",
951 gw->ipv4 ? '4' : ' ',
952 gw->ipv4nat ? "(N)" : "",
953 (gw->ipv4 && gw->ipv6) ? ',' : ' ',
954 gw->ipv6 ? '6' : ' ',
957 gw->external_prefix.prefix_len == 0 ? "" : olsr_ip_prefix_to_string(&gw->external_prefix));
958 } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
967 * Apply the smart gateway modifications to an outgoing HNA
969 * @param mask pointer to netmask of the HNA
970 * @param prefixlen of the HNA
972 void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
973 uint8_t *ptr = hna_mask_to_hna_pointer(mask, prefixlen);
975 /* copy the current settings for uplink/downlink into the mask */
976 memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen / 8);
977 if (olsr_cnf->has_ipv4_gateway) {
978 ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4;
980 if (olsr_cnf->smart_gw_uplink_nat) {
981 ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4_NAT;
984 if (olsr_cnf->has_ipv6_gateway) {
985 ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6;
987 if (!olsr_cnf->has_ipv6_gateway || prefixlen != ipv6_internet_route.prefix_len) {
988 ptr[GW_HNA_FLAGS] &= ~GW_HNA_FLAG_IPV6PREFIX;
993 * SgwDynSpeed Plugin Interface
997 * Setup the gateway netmask
999 void refresh_smartgw_netmask(void) {
1002 /* clear the mask */
1003 memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask));
1005 if (olsr_cnf->smart_gw_active) {
1006 ip = (uint8_t *) &smart_gateway_netmask;
1008 ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
1009 ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
1010 ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
1012 if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) {
1013 ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX;
1014 ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len;
1015 memcpy(&ip[GW_HNA_V6PREFIX], &olsr_cnf->smart_gw_prefix.prefix, 8);
1021 * TC/SPF/HNA Interface
1025 * Checks if a HNA prefix/netmask combination is a smart gateway
1029 * @return true if is a valid smart gateway HNA, false otherwise
1031 bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *mask) {
1034 if (!is_prefix_inetgw(prefix)) {
1038 ptr = hna_mask_to_hna_pointer(mask, prefix->prefix_len);
1039 return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0;
1043 * Update a gateway_entry based on a HNA
1045 * @param originator ip of the source of the HNA
1046 * @param mask netmask of the HNA
1047 * @param prefixlen of the HNA
1048 * @param seqno the sequence number of the HNA
1050 void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_addr *mask, int prefixlen, uint16_t seqno) {
1051 struct gw_container_entry * new_gw_in_list;
1053 int64_t prev_path_cost = 0;
1054 struct gateway_entry *gw = node2gateway(avl_find(&gateway_tree, originator));
1057 gw = olsr_cookie_malloc(gateway_entry_mem_cookie);
1058 gw->originator = *originator;
1059 gw->node.key = &gw->originator;
1061 avl_insert(&gateway_tree, &gw->node, AVL_DUP_NO);
1062 } else if (olsr_seqno_diff(seqno, gw->seqno) <= 0) {
1063 /* ignore older HNAs */
1067 /* keep new HNA seqno */
1070 ptr = hna_mask_to_hna_pointer(mask, prefixlen);
1071 if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) {
1072 gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]);
1073 gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]);
1079 gw->ipv4 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4) != 0;
1080 gw->ipv4nat = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4_NAT) != 0;
1083 if (olsr_cnf->ip_version == AF_INET6) {
1084 gw->ipv6 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6) != 0;
1086 /* do not reset prefixlength for ::ffff:0:0 HNAs */
1087 if (prefixlen == ipv6_internet_route.prefix_len) {
1088 memset(&gw->external_prefix, 0, sizeof(gw->external_prefix));
1090 if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6PREFIX) != 0
1091 && memcmp(mask->v6.s6_addr, &ipv6_internet_route.prefix, olsr_cnf->ipsize) == 0) {
1092 /* this is the right prefix (2000::/3), so we can copy the prefix */
1093 gw->external_prefix.prefix_len = ptr[GW_HNA_V6PREFIXLEN];
1094 memcpy(&gw->external_prefix.prefix, &ptr[GW_HNA_V6PREFIX], 8);
1099 /* stop cleanup timer if necessary */
1100 if (gw->cleanup_timer) {
1101 olsr_stop_timer(gw->cleanup_timer);
1102 gw->cleanup_timer = NULL;
1106 prev_path_cost = gw->path_cost;
1107 gw->path_cost = gw_handler->getcosts(gw);
1109 if (prev_path_cost != gw->path_cost) {
1110 /* re-sort the gateway list when costs have changed and when it is an active gateway */
1111 new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw);
1112 if (new_gw_in_list) {
1113 new_gw_in_list = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list);
1114 assert(new_gw_in_list);
1117 new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw);
1118 if (new_gw_in_list) {
1119 new_gw_in_list = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list);
1120 assert(new_gw_in_list);
1123 if (multi_gateway_mode() && //
1124 ((!gw->ipv6 && current_ipv4_gw && current_ipv4_gw->gw == gw) || //
1125 (gw->ipv6 && current_ipv6_gw && current_ipv6_gw->gw == gw)) //
1127 /* the active gw has changed its costs: re-evaluate egress routes */
1128 doRoutesMultiGw(false, true, GW_MULTI_CHANGE_PHASE_RUNTIME);
1132 /* call update handler */
1134 gw_handler->update(gw);
1138 * Delete a gateway based on the originator IP and the prefixlength of a HNA.
1139 * Should only be called if prefix is a smart_gw prefix or if node is removed
1144 * @param immediate when set to true then the gateway is removed from the
1145 * gateway tree immediately, else it is removed on a delayed schedule.
1147 void olsr_delete_gateway_entry(union olsr_ip_addr *originator, uint8_t prefixlen, bool immediate) {
1148 olsr_delete_gateway_tree_entry(node2gateway(avl_find(&gateway_tree, originator)), prefixlen, immediate);
1152 * Delete a gateway entry .
1154 * @param gw a gateway entry from the gateway tree
1156 * @param immediate when set to true then the gateway is removed from the
1157 * gateway tree immediately, else it is removed on a delayed schedule.
1159 static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t prefixlen, bool immediate) {
1160 bool change = false;
1166 if (immediate && gw->cleanup_timer) {
1167 /* stop timer if we have to remove immediately */
1168 olsr_stop_timer(gw->cleanup_timer);
1169 gw->cleanup_timer = NULL;
1172 if (gw->cleanup_timer == NULL || gw->ipv4 || gw->ipv6) {
1173 /* the gw is not scheduled for deletion */
1175 if (olsr_cnf->ip_version == AF_INET && prefixlen == 0) {
1178 gw->ipv4nat = false;
1179 } else if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_internet_route.prefix_len) {
1182 } else if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_mappedv4_route.prefix_len) {
1185 gw->ipv4nat = false;
1188 if (prefixlen == FORCE_DELETE_GW_ENTRY || !(gw->ipv4 || gw->ipv6)) {
1189 struct gw_container_entry * gw_in_list;
1191 /* prevent this gateway from being chosen as the new gateway */
1193 gw->ipv4nat = false;
1196 /* handle gateway loss */
1198 gw_handler->delete(gw);
1200 /* cleanup gateway if necessary */
1201 gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw);
1203 if (current_ipv4_gw && current_ipv4_gw->gw == gw) {
1204 olsr_os_inetgw_tunnel_route(current_ipv4_gw->tunnel->if_index, true, false, olsr_cnf->rt_table_tunnel);
1205 current_ipv4_gw = NULL;
1208 if (gw_in_list->tunnel) {
1209 struct interfaceName * ifn = find_interfaceName(gw_in_list->gw);
1211 olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, true, false, ifn->tableNr);
1213 olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
1214 set_unused_iptunnel_name(gw_in_list->gw);
1215 gw_in_list->tunnel = NULL;
1218 gw_in_list->gw = NULL;
1219 gw_in_list = olsr_gw_list_remove(&gw_list_ipv4, gw_in_list);
1220 olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list);
1223 gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw);
1225 if (current_ipv6_gw && current_ipv6_gw->gw == gw) {
1226 olsr_os_inetgw_tunnel_route(current_ipv6_gw->tunnel->if_index, false, false, olsr_cnf->rt_table_tunnel);
1227 current_ipv6_gw = NULL;
1230 if (gw_in_list->tunnel) {
1231 struct interfaceName * ifn = find_interfaceName(gw_in_list->gw);
1233 olsr_os_inetgw_tunnel_route(gw_in_list->tunnel->if_index, false, false, ifn->tableNr);
1235 olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
1236 set_unused_iptunnel_name(gw_in_list->gw);
1237 gw_in_list->tunnel = NULL;
1240 gw_in_list->gw = NULL;
1241 gw_in_list = olsr_gw_list_remove(&gw_list_ipv6, gw_in_list);
1242 olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list);
1246 /* remove gateway entry on a delayed schedule */
1247 olsr_set_timer(&gw->cleanup_timer, GW_CLEANUP_INTERVAL, 0, false, cleanup_gateway_handler, gw, NULL);
1249 cleanup_gateway_handler(gw);
1252 /* when the current gateway was deleted, then immediately choose a new gateway */
1253 if (!current_ipv4_gw || !current_ipv6_gw) {
1255 gw_handler->choose(!current_ipv4_gw, !current_ipv6_gw);
1258 } else if (change) {
1260 gw_handler->update(gw);
1266 * Triggers a check if the one of the gateways have been lost or has an
1269 void olsr_trigger_gatewayloss_check(void) {
1273 if (current_ipv4_gw && current_ipv4_gw->gw) {
1274 struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv4_gw->gw->originator);
1275 ipv4 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN);
1277 if (current_ipv6_gw && current_ipv6_gw->gw) {
1278 struct tc_entry *tc = olsr_lookup_tc_entry(¤t_ipv6_gw->gw->originator);
1279 ipv6 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN);
1284 gw_handler->choose(ipv4, ipv6);
1289 * Gateway Plugin Functions
1293 * Sets a new internet gateway.
1295 * @param the chosen gateway
1296 * @param ipv4 set ipv4 gateway
1297 * @param ipv6 set ipv6 gateway
1298 * @return true if an error happened, false otherwise
1300 bool olsr_set_inet_gateway(struct gateway_entry * chosen_gw, bool ipv4, bool ipv6) {
1301 struct gateway_entry *new_gw;
1303 ipv4 = ipv4 && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
1304 ipv6 = ipv6 && (olsr_cnf->ip_version == AF_INET6);
1305 if (!ipv4 && !ipv6) {
1309 new_gw = node2gateway(avl_find(&gateway_tree, &chosen_gw->originator));
1311 /* the originator is not in the gateway tree, we can't set it as gateway */
1318 (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) &&
1319 (!current_ipv4_gw || current_ipv4_gw->gw != new_gw)) {
1320 /* new gw is different than the current gw */
1322 struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, new_gw);
1323 if (new_gw_in_list) {
1324 /* new gw is already in the gw list */
1325 assert(new_gw_in_list->tunnel);
1326 olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
1327 current_ipv4_gw = new_gw_in_list;
1329 if (multi_gateway_mode()) {
1330 doRoutesMultiGw(false, true, GW_MULTI_CHANGE_PHASE_RUNTIME);
1333 /* new gw is not yet in the gw list */
1334 char name[IFNAMSIZ];
1335 struct olsr_iptunnel_entry *new_v4gw_tunnel;
1336 struct interfaceName * interfaceName;
1338 if (olsr_gw_list_full(&gw_list_ipv4)) {
1339 /* the list is full: remove the worst active gateway */
1340 struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv4);
1343 removeGatewayFromList(&gw_list_ipv4, true, worst);
1346 get_unused_iptunnel_name(new_gw, name, &interfaceName);
1347 new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true, name);
1348 if (new_v4gw_tunnel) {
1349 if (interfaceName) {
1350 olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, interfaceName->tableNr);
1352 olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
1354 new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
1355 new_gw_in_list->gw = new_gw;
1356 new_gw_in_list->tunnel = new_v4gw_tunnel;
1357 current_ipv4_gw = olsr_gw_list_add(&gw_list_ipv4, new_gw_in_list);
1359 if (multi_gateway_mode()) {
1360 doRoutesMultiGw(false, true, GW_MULTI_CHANGE_PHASE_RUNTIME);
1363 /* adding the tunnel failed, we try again in the next cycle */
1364 set_unused_iptunnel_name(new_gw);
1373 (!current_ipv6_gw || current_ipv6_gw->gw != new_gw)) {
1374 /* new gw is different than the current gw */
1376 struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, new_gw);
1377 if (new_gw_in_list) {
1378 /* new gw is already in the gw list */
1379 assert(new_gw_in_list->tunnel);
1380 olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, olsr_cnf->rt_table_tunnel);
1381 current_ipv6_gw = new_gw_in_list;
1383 if (multi_gateway_mode()) {
1384 doRoutesMultiGw(false, true, GW_MULTI_CHANGE_PHASE_RUNTIME);
1387 /* new gw is not yet in the gw list */
1388 char name[IFNAMSIZ];
1389 struct olsr_iptunnel_entry *new_v6gw_tunnel;
1390 struct interfaceName * interfaceName;
1392 if (olsr_gw_list_full(&gw_list_ipv6)) {
1393 /* the list is full: remove the worst active gateway */
1394 struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv6);
1397 removeGatewayFromList(&gw_list_ipv6, false, worst);
1400 get_unused_iptunnel_name(new_gw, name, &interfaceName);
1401 new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false, name);
1402 if (new_v6gw_tunnel) {
1403 if (interfaceName) {
1404 olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, interfaceName->tableNr);
1406 olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, olsr_cnf->rt_table_tunnel);
1408 new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
1409 new_gw_in_list->gw = new_gw;
1410 new_gw_in_list->tunnel = new_v6gw_tunnel;
1411 current_ipv6_gw = olsr_gw_list_add(&gw_list_ipv6, new_gw_in_list);
1413 if (multi_gateway_mode()) {
1414 doRoutesMultiGw(false, true, GW_MULTI_CHANGE_PHASE_RUNTIME);
1417 /* adding the tunnel failed, we try again in the next cycle */
1418 set_unused_iptunnel_name(new_gw);
1424 return !ipv4 && !ipv6;
1428 * @param ipv6 if set to true then the IPv6 gateway is returned, otherwise the IPv4
1429 * gateway is returned
1430 * @return a pointer to the gateway_entry of the current ipv4 internet gw or
1433 struct gateway_entry *olsr_get_inet_gateway(bool ipv6) {
1435 return current_ipv6_gw ? current_ipv6_gw->gw : NULL;
1438 return current_ipv4_gw ? current_ipv4_gw->gw : NULL;
1442 * Process Egress Changes
1445 #define MSGW_ROUTE_ADD_ALLOWED(phase) ((phase == GW_MULTI_CHANGE_PHASE_STARTUP ) || (phase == GW_MULTI_CHANGE_PHASE_RUNTIME ))
1446 #define MSGW_ROUTE_ADD_FORCED(phase) ( phase == GW_MULTI_CHANGE_PHASE_STARTUP )
1447 #define MSGW_ROUTE_DEL_ALLOWED(phase) ((phase == GW_MULTI_CHANGE_PHASE_RUNTIME ) || (phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN))
1448 #define MSGW_ROUTE_DEL_FORCED(phase) ( phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN)
1449 #define MSGW_ROUTE_FORCED(phase) ((phase == GW_MULTI_CHANGE_PHASE_STARTUP ) || (phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN))
1452 * Determine best egress link.
1453 * The list of egress interface is ordered on priority (the declaration order),
1454 * so the function will - for multiple egress links with the same costs - set the
1455 * best egress interface to the first declared one of those.
1456 * When there is no best egress interface (that is up) then the function will
1457 * set the best egress interface to NULL.
1459 * @param phase the phase of the change (startup/runtime/shutdown)
1460 * @return true when the best egress link changed or when any of its relevant
1461 * parameters has changed
1463 static bool determineBestEgressLink(enum sgw_multi_change_phase phase) {
1464 struct sgw_egress_if * bestEgress = olsr_cnf->smart_gw_egress_interfaces;
1466 if (phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN) {
1469 struct sgw_egress_if * egress_if = bestEgress;
1471 egress_if = egress_if->next;
1474 if (egress_if->upCurrent && (egress_if->bwCurrent.costs < bestEgress->bwCurrent.costs)) {
1475 bestEgress = egress_if;
1478 egress_if = egress_if->next;
1481 if (bestEgress && (!bestEgress->upCurrent || (bestEgress->bwCurrent.costs == INT64_MAX))) {
1486 bestEgressLinkPrevious = bestEgressLink;
1487 bestEgressLink = bestEgress;
1489 return ((bestEgressLinkPrevious != bestEgressLink) || //
1490 (bestEgressLink && (bestEgressLink->upChanged || bestEgressLink->bwChanged)));
1494 * Determine best overall link (choose egress interface over olsrd).
1496 * When there is no best overall link, the best overall link will be set to a
1497 * NULL egress interface.
1499 * @param phase the phase of the change (startup/runtime/shutdown)
1500 * @return true when the best egress link changed or when any of its relevant
1501 * parameters has changed
1503 static bool determineBestOverallLink(enum sgw_multi_change_phase phase) {
1504 struct gw_container_entry * gwContainer = (olsr_cnf->ip_version == AF_INET) ? current_ipv4_gw : current_ipv6_gw;
1505 struct gateway_entry * olsrGw = !gwContainer ? NULL : gwContainer->gw;
1507 int64_t egressCosts = !bestEgressLink ? INT64_MAX : bestEgressLink->bwCurrent.costs;
1508 int64_t olsrCosts = !olsrGw ? INT64_MAX : olsrGw->path_cost;
1509 int64_t bestOverallCosts = MIN(egressCosts, olsrCosts);
1511 bestOverallLinkPrevious = bestOverallLink;
1512 if ((bestOverallCosts == INT64_MAX) || (phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN)) {
1513 bestOverallLink.valid = false;
1514 bestOverallLink.isOlsr = false;
1515 bestOverallLink.link.egress = NULL;
1516 bestOverallLink.olsrTunnelIfIndex = 0;
1517 } else if (egressCosts <= olsrCosts) {
1518 bestOverallLink.valid = bestEgressLink;
1519 bestOverallLink.isOlsr = false;
1520 bestOverallLink.link.egress = bestEgressLink;
1521 bestOverallLink.olsrTunnelIfIndex = 0;
1523 struct olsr_iptunnel_entry * tunnel = !gwContainer ? NULL : gwContainer->tunnel;
1525 bestOverallLink.valid = olsrGw;
1526 bestOverallLink.isOlsr = true;
1527 bestOverallLink.link.olsr = olsrGw;
1528 bestOverallLink.olsrTunnelIfIndex = !tunnel ? 0 : tunnel->if_index;
1531 return memcmp(&bestOverallLink, &bestOverallLinkPrevious, sizeof(bestOverallLink));
1535 * Process changes that are relevant to egress interface: changes to the
1536 * egress interfaces themselves and to the smart gateway that is chosen by olsrd
1538 * @param egressChanged true when an egress interface changed
1539 * @param olsrChanged true when the smart gateway changed
1540 * @param phase the phase of the change (startup/runtime/shutdown)
1542 void doRoutesMultiGw(bool egressChanged, bool olsrChanged, enum sgw_multi_change_phase phase) {
1543 bool bestEgressChanged = false;
1544 bool bestOverallChanged = false;
1545 bool force = MSGW_ROUTE_FORCED(phase);
1548 (phase == GW_MULTI_CHANGE_PHASE_STARTUP) || //
1549 (phase == GW_MULTI_CHANGE_PHASE_RUNTIME) || //
1550 (phase == GW_MULTI_CHANGE_PHASE_SHUTDOWN));
1552 if (!egressChanged && !olsrChanged && !force) {
1556 assert(multi_gateway_mode());
1558 if (egressChanged || force) {
1559 bestEgressChanged = determineBestEgressLink(phase);
1562 if (olsrChanged || bestEgressChanged || force) {
1563 bestOverallChanged = determineBestOverallLink(phase);
1566 if (!bestEgressChanged && !bestOverallChanged && !force) {
1570 // FIXME program routes
1572 out: if (egressChanged) {
1573 /* clear the 'changed' flags of egress interfaces */
1574 struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
1576 egress_if->upChanged = false;
1578 egress_if->bwCostsChanged = false;
1579 egress_if->bwNetworkChanged = false;
1580 egress_if->bwGatewayChanged = false;
1581 egress_if->bwChanged = false;
1583 egress_if = egress_if->next;
1588 #endif /* __linux__ */