gateway: let the gateway code determine the tunnel name
[olsrd.git] / src / gateway.c
1 /*
2  * gateway.c
3  *
4  *  Created on: 05.01.2010
5  *      Author: henning
6  */
7
8 #ifdef __linux__
9
10 #include "common/avl.h"
11 #include "defs.h"
12 #include "ipcalc.h"
13 #include "olsr.h"
14 #include "olsr_cfg.h"
15 #include "olsr_cookie.h"
16 #include "scheduler.h"
17 #include "kernel_routes.h"
18 #include "kernel_tunnel.h"
19 #include "net_os.h"
20 #include "duplicate_set.h"
21 #include "log.h"
22 #include "gateway_default_handler.h"
23 #include "gateway_list.h"
24 #include "gateway.h"
25
26 #include <assert.h>
27 #include <net/if.h>
28
29 /** the gateway tree */
30 struct avl_tree gateway_tree;
31
32 /** gateway cookie */
33 static struct olsr_cookie_info *gateway_entry_mem_cookie = NULL;
34
35 /** gateway container cookie */
36 static struct olsr_cookie_info *gw_container_entry_mem_cookie = NULL;
37
38 /** the gateway netmask for the HNA */
39 static uint8_t smart_gateway_netmask[sizeof(union olsr_ip_addr)];
40
41 /** the gateway handler/plugin */
42 static struct olsr_gw_handler *gw_handler;
43
44 /** the IPv4 gateway list */
45 static struct gw_list gw_list_ipv4;
46
47 /** the IPv6 gateway list */
48 static struct gw_list gw_list_ipv6;
49
50 /** the current IPv4 gateway */
51 static struct gw_container_entry *current_ipv4_gw;
52
53 /** the current IPv6 gateway */
54 static struct gw_container_entry *current_ipv6_gw;
55
56 /*
57  * Forward Declarations
58  */
59
60 static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t prefixlen, bool immediate);
61
62 /*
63  * Helper Functions
64  */
65
66 #define TUNNEL_NAME (olsr_cnf->ip_version == AF_INET ? TUNNEL_ENDPOINT_IF : TUNNEL_ENDPOINT_IF6)
67
68 #define OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen) (((uint8_t *)mask) + ((prefixlen+7)/8))
69
70 /**
71  * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent)
72  * to an uplink/downlink speed value
73  *
74  * @param value the encoded 1 byte transport value
75  * @return the uplink/downlink speed value (in kbit/s)
76  */
77 static uint32_t deserialize_gw_speed(uint8_t value) {
78   uint32_t speed;
79   uint32_t exp;
80
81   if (!value) {
82     /* 0 and 1 alias onto 0 during serialisation. We take 0 here to mean 0 and
83      * not 1 (since a bandwidth of 1 is no bandwidth at all really) */
84     return 0;
85   }
86
87   speed = (value >> 3) + 1;
88   exp = value & 7;
89
90   while (exp-- > 0) {
91     speed *= 10;
92   }
93   return speed;
94 }
95
96 /**
97  * Convert an uplink/downlink speed value into an encoded 1 byte transport
98  * value (5 bits mantissa, 3 bits exponent)
99  *
100  * @param speed the uplink/downlink speed value (in kbit/s)
101  * @return value the encoded 1 byte transport value
102  */
103 static uint8_t serialize_gw_speed(uint32_t speed) {
104   uint8_t exp = 0;
105
106   if (speed == 0) {
107     return 0;
108   }
109
110   if (speed > 320000000) {
111     return 0xff;
112   }
113
114   while ((speed > 32 || (speed % 10) == 0) && exp < 7) {
115     speed /= 10;
116     exp++;
117   }
118   return ((speed - 1) << 3) | exp;
119 }
120
121 /**
122  * Dummy for generating an interface name for an olsr ipip tunnel
123  * @param target IP destination of the tunnel
124  * @param name pointer to output buffer (length IFNAMSIZ)
125  */
126 static void generate_iptunnel_name(union olsr_ip_addr *target, char * name) {
127   static uint32_t counter = 0;
128
129   memset(name, 0, IFNAMSIZ);
130   snprintf(name, IFNAMSIZ, "tnl_%08x", olsr_cnf->ip_version == AF_INET ? target->v4.s_addr : ++counter);
131 }
132
133 /*
134  * Callback Functions
135  */
136
137 /**
138  * Callback for tunnel interface monitoring which will set the route into the tunnel
139  * when the interface comes up again.
140  *
141  * @param if_index the interface index
142  * @param ifh the interface
143  * @param flag interface change flags
144  */
145 static void smartgw_tunnel_monitor(int if_index __attribute__ ((unused)),
146     struct interface *ifh __attribute__ ((unused)), enum olsr_ifchg_flag flag __attribute__ ((unused))) {
147   return;
148 }
149
150 /**
151  * Timer callback to remove and cleanup a gateway entry
152  *
153  * @param ptr
154  */
155 static void cleanup_gateway_handler(void *ptr) {
156   struct gateway_entry *gw = ptr;
157
158   if (gw->ipv4 || gw->ipv6) {
159     /* do not clean it up when it is in use */
160     return;
161   }
162
163   /* remove gateway entry */
164   avl_delete(&gateway_tree, &gw->node);
165   olsr_cookie_free(gateway_entry_mem_cookie, gw);
166 }
167
168 /*
169  * Main Interface
170  */
171
172 /**
173  * Initialize gateway system
174  */
175 int olsr_init_gateways(void) {
176   gateway_entry_mem_cookie = olsr_alloc_cookie("gateway_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY);
177   olsr_cookie_set_memory_size(gateway_entry_mem_cookie, sizeof(struct gateway_entry));
178
179   gw_container_entry_mem_cookie = olsr_alloc_cookie("gw_container_entry_mem_cookie", OLSR_COOKIE_TYPE_MEMORY);
180   olsr_cookie_set_memory_size(gw_container_entry_mem_cookie, sizeof(struct gw_container_entry));
181
182   avl_init(&gateway_tree, avl_comp_default);
183
184   olsr_gw_list_init(&gw_list_ipv4, 1);
185   olsr_gw_list_init(&gw_list_ipv6, 1);
186
187   current_ipv4_gw = NULL;
188   current_ipv6_gw = NULL;
189
190   gw_handler = NULL;
191
192   refresh_smartgw_netmask();
193
194   /* initialize default gateway handler */
195   gw_handler = &gw_def_handler;
196   gw_handler->init();
197
198   if (olsr_os_init_iptunnel(TUNNEL_NAME)) {
199     return 1;
200   }
201
202   olsr_add_ifchange_handler(smartgw_tunnel_monitor);
203
204   return 0;
205 }
206
207 /**
208  * Cleanup gateway tunnel system
209  */
210 void olsr_cleanup_gateways(void) {
211   struct avl_node * avlnode = NULL;
212   struct gw_container_entry * gw;
213
214   olsr_remove_ifchange_handler(smartgw_tunnel_monitor);
215
216   olsr_os_cleanup_iptunnel(TUNNEL_NAME);
217
218   /* remove all gateways in the gateway tree that are not the active gateway */
219   while ((avlnode = avl_walk_first(&gateway_tree))) {
220     struct gateway_entry* tree_gw = node2gateway(avlnode);
221     if ((tree_gw != olsr_get_inet_gateway(false)) && (tree_gw != olsr_get_inet_gateway(true))) {
222       olsr_delete_gateway_tree_entry(tree_gw, FORCE_DELETE_GW_ENTRY, true);
223     }
224   }
225
226   /* remove all active IPv4 gateways (should be at most 1 now) */
227   OLSR_FOR_ALL_GWS(&gw_list_ipv4.head, gw) {
228     if (gw && gw->gw) {
229       olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true);
230     }
231   }
232   OLSR_FOR_ALL_GWS_END(gw);
233
234   /* remove all active IPv6 gateways (should be at most 1 now) */
235   OLSR_FOR_ALL_GWS(&gw_list_ipv6.head, gw) {
236     if (gw && gw->gw) {
237       olsr_delete_gateway_entry(&gw->gw->originator, FORCE_DELETE_GW_ENTRY, true);
238     }
239   }
240   OLSR_FOR_ALL_GWS_END(gw);
241
242   /* there should be no more gateways */
243   assert(!avl_walk_first(&gateway_tree));
244   assert(!current_ipv4_gw);
245   assert(!current_ipv6_gw);
246
247   assert(gw_handler);
248   gw_handler->cleanup();
249   gw_handler = NULL;
250
251   olsr_gw_list_cleanup(&gw_list_ipv6);
252   olsr_gw_list_cleanup(&gw_list_ipv4);
253   olsr_free_cookie(gw_container_entry_mem_cookie);
254   olsr_free_cookie(gateway_entry_mem_cookie);
255 }
256
257 /**
258  * Triggers the first lookup of a gateway.
259  */
260 void olsr_trigger_inetgw_startup(void) {
261   assert(gw_handler);
262   gw_handler->startup();
263 }
264
265 /**
266  * Print debug information about gateway entries
267  */
268 #ifndef NODEBUG
269 void olsr_print_gateway_entries(void) {
270   struct ipaddr_str buf;
271   struct gateway_entry *gw;
272   const int addrsize = olsr_cnf->ip_version == AF_INET ? (INET_ADDRSTRLEN - 1) : (INET6_ADDRSTRLEN - 1);
273
274   OLSR_PRINTF(0, "\n--- %s ---------------------------------------------------- GATEWAYS\n\n", olsr_wallclock_string());
275   OLSR_PRINTF(0, "%-*s %-6s %-9s %-9s %s\n",
276       addrsize, "IP address", "Type", "Uplink", "Downlink", olsr_cnf->ip_version == AF_INET ? "" : "External Prefix");
277
278   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
279     OLSR_PRINTF(0, "%-*s %s%c%s%c%c %-9u %-9u %s\n",
280         addrsize,
281         olsr_ip_to_string(&buf, &gw->originator),
282         gw->ipv4nat ? "" : "   ",
283         gw->ipv4 ? '4' : ' ',
284         gw->ipv4nat ? "(N)" : "",
285         (gw->ipv4 && gw->ipv6) ? ',' : ' ',
286         gw->ipv6 ? '6' : ' ',
287         gw->uplink,
288         gw->downlink,
289         gw->external_prefix.prefix_len == 0 ? "" : olsr_ip_prefix_to_string(&gw->external_prefix));
290   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
291 }
292 #endif /* NODEBUG */
293
294 /*
295  * Tx Path Interface
296  */
297
298 /**
299  * Apply the smart gateway modifications to an outgoing HNA
300  *
301  * @param mask pointer to netmask of the HNA
302  * @param prefixlen of the HNA
303  */
304 void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
305   uint8_t *ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen);
306
307   memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen / 8);
308   if (olsr_cnf->has_ipv4_gateway) {
309     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4;
310
311     if (olsr_cnf->smart_gw_uplink_nat) {
312       ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4_NAT;
313     }
314   }
315   if (olsr_cnf->has_ipv6_gateway) {
316     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6;
317   }
318   if (!olsr_cnf->has_ipv6_gateway || prefixlen != ipv6_internet_route.prefix_len) {
319     ptr[GW_HNA_FLAGS] &= ~GW_HNA_FLAG_IPV6PREFIX;
320   }
321 }
322
323 /*
324  * SgwDynSpeed Plugin Interface
325  */
326
327 /**
328  * Setup the gateway netmask
329  */
330 void refresh_smartgw_netmask(void) {
331   uint8_t *ip;
332   memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask));
333
334   if (olsr_cnf->smart_gw_active) {
335     ip = (uint8_t *) &smart_gateway_netmask;
336
337     if (olsr_cnf->smart_gw_uplink > 0 && olsr_cnf->smart_gw_downlink > 0) {
338       /* the link is bi-directional with a non-zero bandwidth */
339       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
340       ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
341       ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
342     }
343     if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) {
344       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX;
345       ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len;
346       memcpy(&ip[GW_HNA_V6PREFIX], &olsr_cnf->smart_gw_prefix.prefix, 8);
347     }
348   }
349 }
350
351 /*
352  * TC/SPF/HNA Interface
353  */
354
355 /**
356  * Checks if a HNA prefix/netmask combination is a smart gateway
357  *
358  * @param prefix
359  * @param mask
360  * @return true if is a valid smart gateway HNA, false otherwise
361  */
362 bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *mask) {
363   uint8_t *ptr;
364
365   if (!is_prefix_inetgw(prefix)) {
366     return false;
367   }
368
369   ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefix->prefix_len);
370   return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0;
371 }
372
373 /**
374  * Update a gateway_entry based on a HNA
375  *
376  * @param originator ip of the source of the HNA
377  * @param mask netmask of the HNA
378  * @param prefixlen of the HNA
379  * @param seqno the sequence number of the HNA
380  */
381 void olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_addr *mask, int prefixlen, uint16_t seqno) {
382   struct gw_container_entry * new_gw_in_list;
383   uint8_t *ptr;
384   struct gateway_entry *gw = node2gateway(avl_find(&gateway_tree, originator));
385
386   if (!gw) {
387     gw = olsr_cookie_malloc(gateway_entry_mem_cookie);
388     gw->originator = *originator;
389     gw->node.key = &gw->originator;
390
391     avl_insert(&gateway_tree, &gw->node, AVL_DUP_NO);
392   } else if (olsr_seqno_diff(seqno, gw->seqno) <= 0) {
393     /* ignore older HNAs */
394     return;
395   }
396
397   /* keep new HNA seqno */
398   gw->seqno = seqno;
399
400   ptr = OLSR_IP_ADDR_2_HNA_PTR(mask, prefixlen);
401   if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) {
402     gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]);
403     gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]);
404   } else {
405     gw->uplink = 0;
406     gw->downlink = 0;
407   }
408
409   gw->ipv4 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4) != 0;
410   gw->ipv4nat = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4_NAT) != 0;
411
412   if (olsr_cnf->ip_version == AF_INET6) {
413     gw->ipv6 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6) != 0;
414
415     /* do not reset prefixlength for ::ffff:0:0 HNAs */
416     if (prefixlen == ipv6_internet_route.prefix_len) {
417       memset(&gw->external_prefix, 0, sizeof(gw->external_prefix));
418
419       if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6PREFIX) != 0
420           && memcmp(mask->v6.s6_addr, &ipv6_internet_route.prefix, olsr_cnf->ipsize) == 0) {
421         /* this is the right prefix (2000::/3), so we can copy the prefix */
422         gw->external_prefix.prefix_len = ptr[GW_HNA_V6PREFIXLEN];
423         memcpy(&gw->external_prefix.prefix, &ptr[GW_HNA_V6PREFIX], 8);
424       }
425     }
426   }
427
428   /* stop cleanup timer if necessary */
429   if (gw->cleanup_timer) {
430     olsr_stop_timer(gw->cleanup_timer);
431     gw->cleanup_timer = NULL;
432   }
433
434   /* update the costs of the gateway when it is an active gateway */
435   new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw);
436   if (new_gw_in_list) {
437     assert(gw_handler);
438     new_gw_in_list = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, gw_handler->getcosts(new_gw_in_list->gw));
439   }
440
441   /* call update handler */
442   assert(gw_handler);
443   gw_handler->update(gw);
444 }
445
446 /**
447  * Delete a gateway based on the originator IP and the prefixlength of a HNA.
448  * Should only be called if prefix is a smart_gw prefix or if node is removed
449  * from TC set.
450  *
451  * @param originator
452  * @param prefixlen
453  * @param immediate when set to true then the gateway is removed from the
454  * gateway tree immediately, else it is removed on a delayed schedule.
455  */
456 void olsr_delete_gateway_entry(union olsr_ip_addr *originator, uint8_t prefixlen, bool immediate) {
457   olsr_delete_gateway_tree_entry(node2gateway(avl_find(&gateway_tree, originator)), prefixlen, immediate);
458 }
459
460 /**
461  * Delete a gateway entry .
462  *
463  * @param gw a gateway entry from the gateway tree
464  * @param prefixlen
465  * @param immediate when set to true then the gateway is removed from the
466  * gateway tree immediately, else it is removed on a delayed schedule.
467  */
468 static void olsr_delete_gateway_tree_entry(struct gateway_entry * gw, uint8_t prefixlen, bool immediate) {
469   bool change = false;
470
471   if (!gw) {
472     return;
473   }
474
475   if (immediate && gw->cleanup_timer) {
476     /* stop timer if we have to remove immediately */
477     olsr_stop_timer(gw->cleanup_timer);
478     gw->cleanup_timer = NULL;
479   }
480
481   if (gw->cleanup_timer == NULL || gw->ipv4 || gw->ipv6) {
482     /* the gw  is not scheduled for deletion */
483
484     if (olsr_cnf->ip_version == AF_INET && prefixlen == 0) {
485       change = gw->ipv4;
486       gw->ipv4 = false;
487       gw->ipv4nat = false;
488     } else if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_internet_route.prefix_len) {
489       change = gw->ipv6;
490       gw->ipv6 = false;
491     } else if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_mappedv4_route.prefix_len) {
492       change = gw->ipv4;
493       gw->ipv4 = false;
494       gw->ipv4nat = false;
495     }
496
497     if (prefixlen == FORCE_DELETE_GW_ENTRY || !(gw->ipv4 || gw->ipv6)) {
498       struct gw_container_entry * gw_in_list;
499
500       /* prevent this gateway from being chosen as the new gateway */
501       gw->ipv4 = false;
502       gw->ipv4nat = false;
503       gw->ipv6 = false;
504
505       /* handle gateway loss */
506       assert(gw_handler);
507       gw_handler->delete(gw);
508
509       /* cleanup gateway if necessary */
510       gw_in_list = olsr_gw_list_find(&gw_list_ipv4, gw);
511       if (gw_in_list) {
512         if (current_ipv4_gw && current_ipv4_gw->gw == gw) {
513           olsr_os_inetgw_tunnel_route(current_ipv4_gw->tunnel->if_index, true, false, NULL);
514           current_ipv4_gw = NULL;
515         }
516
517         if (gw_in_list->tunnel) {
518           olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
519           gw_in_list->tunnel = NULL;
520         }
521
522         gw_in_list->gw = NULL;
523         gw_in_list = olsr_gw_list_remove(&gw_list_ipv4, gw_in_list);
524         olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list);
525       }
526
527       gw_in_list = olsr_gw_list_find(&gw_list_ipv6, gw);
528       if (gw_in_list) {
529         if (current_ipv6_gw && current_ipv6_gw->gw == gw) {
530           olsr_os_inetgw_tunnel_route(current_ipv6_gw->tunnel->if_index, false, false, NULL);
531           current_ipv6_gw = NULL;
532         }
533
534         if (gw_in_list->tunnel) {
535           olsr_os_del_ipip_tunnel(gw_in_list->tunnel);
536           gw_in_list->tunnel = NULL;
537         }
538
539         gw_in_list->gw = NULL;
540         gw_in_list = olsr_gw_list_remove(&gw_list_ipv6, gw_in_list);
541         olsr_cookie_free(gw_container_entry_mem_cookie, gw_in_list);
542       }
543
544       if (!immediate) {
545         /* remove gateway entry on a delayed schedule */
546         olsr_set_timer(&gw->cleanup_timer, GW_CLEANUP_INTERVAL, 0, false, cleanup_gateway_handler, gw, NULL);
547       } else {
548         cleanup_gateway_handler(gw);
549       }
550
551       /* when the current gateway was deleted, then immediately choose a new gateway */
552       if (!current_ipv4_gw || !current_ipv6_gw) {
553         assert(gw_handler);
554         gw_handler->choose(!current_ipv4_gw, !current_ipv6_gw);
555       }
556
557     } else if (change) {
558       assert(gw_handler);
559       gw_handler->update(gw);
560     }
561   }
562 }
563
564 /**
565  * Triggers a check if the one of the gateways have been lost or has an
566  * ETX = infinity
567  */
568 void olsr_trigger_gatewayloss_check(void) {
569   bool ipv4 = false;
570   bool ipv6 = false;
571
572   if (current_ipv4_gw && current_ipv4_gw->gw) {
573     struct tc_entry *tc = olsr_lookup_tc_entry(&current_ipv4_gw->gw->originator);
574     ipv4 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN);
575   }
576   if (current_ipv6_gw && current_ipv6_gw->gw) {
577     struct tc_entry *tc = olsr_lookup_tc_entry(&current_ipv6_gw->gw->originator);
578     ipv6 = (tc == NULL || tc->path_cost == ROUTE_COST_BROKEN);
579   }
580
581   if (ipv4 || ipv6) {
582     assert(gw_handler);
583     gw_handler->choose(ipv4, ipv6);
584   }
585 }
586
587 /*
588  * Gateway Plugin Functions
589  */
590
591 /**
592  * Sets a new internet gateway.
593  *
594  * @param originator ip address of the node with the new gateway
595  * @param path_cost the path cost
596  * @param ipv4 set ipv4 gateway
597  * @param ipv6 set ipv6 gateway
598  * @return true if an error happened, false otherwise
599  */
600 bool olsr_set_inet_gateway(union olsr_ip_addr *originator, uint64_t path_cost, bool ipv4, bool ipv6) {
601   struct gateway_entry *new_gw;
602
603   ipv4 = ipv4 && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
604   ipv6 = ipv6 && (olsr_cnf->ip_version == AF_INET6);
605   if (!ipv4 && !ipv6) {
606     return true;
607   }
608
609   new_gw = node2gateway(avl_find(&gateway_tree, originator));
610   if (!new_gw) {
611     /* the originator is not in the gateway tree, we can't set it as gateway */
612     return true;
613   }
614
615   /* handle IPv4 */
616   if (ipv4 && new_gw->ipv4 && (!new_gw->ipv4nat || olsr_cnf->smart_gw_allow_nat) && (!current_ipv4_gw || current_ipv4_gw->gw != new_gw)) {
617     /* new gw is different than the current gw */
618
619     struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv4, new_gw);
620     if (new_gw_in_list) {
621       /* new gw is already in the gw list */
622       assert(new_gw_in_list->tunnel);
623       olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, NULL);
624       current_ipv4_gw = olsr_gw_list_update(&gw_list_ipv4, new_gw_in_list, path_cost);
625     } else {
626       /* new gw is not yet in the gw list */
627       char name[IFNAMSIZ];
628       struct olsr_iptunnel_entry *new_v4gw_tunnel;
629
630       generate_iptunnel_name(&new_gw->originator, name);
631       new_v4gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, true, name);
632       if (new_v4gw_tunnel) {
633         olsr_os_inetgw_tunnel_route(new_v4gw_tunnel->if_index, true, true, NULL);
634
635         if (olsr_gw_list_full(&gw_list_ipv4)) {
636           /* the list is full: remove the worst active gateway */
637           struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv4);
638           assert(worst);
639
640           worst->gw = NULL;
641           if (worst->tunnel) {
642             olsr_os_del_ipip_tunnel(worst->tunnel);
643             worst->tunnel = NULL;
644           }
645           olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(&gw_list_ipv4, worst));
646         }
647
648         new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
649         new_gw_in_list->gw = new_gw;
650         new_gw_in_list->tunnel = new_v4gw_tunnel;
651         new_gw_in_list->path_cost = path_cost;
652         current_ipv4_gw = olsr_gw_list_add(&gw_list_ipv4, new_gw_in_list);
653       } else {
654         /* adding the tunnel failed, we try again in the next cycle */
655         ipv4 = false;
656       }
657     }
658   }
659
660   /* handle IPv6 */
661   if (ipv6 && new_gw->ipv6 && (!current_ipv6_gw || current_ipv6_gw->gw != new_gw)) {
662     /* new gw is different than the current gw */
663
664         struct gw_container_entry * new_gw_in_list = olsr_gw_list_find(&gw_list_ipv6, new_gw);
665     if (new_gw_in_list) {
666       /* new gw is already in the gw list */
667       assert(new_gw_in_list->tunnel);
668       olsr_os_inetgw_tunnel_route(new_gw_in_list->tunnel->if_index, true, true, NULL);
669       current_ipv6_gw = olsr_gw_list_update(&gw_list_ipv6, new_gw_in_list, path_cost);
670     } else {
671       /* new gw is not yet in the gw list */
672       char name[IFNAMSIZ];
673       struct olsr_iptunnel_entry *new_v6gw_tunnel;
674
675       generate_iptunnel_name(&new_gw->originator, name);
676       new_v6gw_tunnel = olsr_os_add_ipip_tunnel(&new_gw->originator, false, name);
677       if (new_v6gw_tunnel) {
678         olsr_os_inetgw_tunnel_route(new_v6gw_tunnel->if_index, false, true, NULL);
679
680         if (olsr_gw_list_full(&gw_list_ipv6)) {
681           /* the list is full: remove the worst active gateway */
682           struct gw_container_entry* worst = olsr_gw_list_get_worst_entry(&gw_list_ipv6);
683           assert(worst);
684
685           worst->gw = NULL;
686           if (worst->tunnel) {
687             olsr_os_del_ipip_tunnel(worst->tunnel);
688             worst->tunnel = NULL;
689           }
690           olsr_cookie_free(gw_container_entry_mem_cookie, olsr_gw_list_remove(&gw_list_ipv6, worst));
691         }
692
693         new_gw_in_list = olsr_cookie_malloc(gw_container_entry_mem_cookie);
694         new_gw_in_list->gw = new_gw;
695         new_gw_in_list->tunnel = new_v6gw_tunnel;
696         new_gw_in_list->path_cost = path_cost;
697         current_ipv6_gw = olsr_gw_list_add(&gw_list_ipv6, new_gw_in_list);
698       } else {
699         /* adding the tunnel failed, we try again in the next cycle */
700         ipv6 = false;
701       }
702     }
703   }
704
705   return !ipv4 && !ipv6;
706 }
707
708 /**
709  * @param ipv6 if set to true then the IPv6 gateway is returned, otherwise the IPv4
710  * gateway is returned
711  * @return a pointer to the gateway_entry of the current ipv4 internet gw or
712  * NULL if not set.
713  */
714 struct gateway_entry *olsr_get_inet_gateway(bool ipv6) {
715         if (ipv6) {
716                 return current_ipv6_gw ? current_ipv6_gw->gw : NULL;
717         }
718
719         return current_ipv4_gw ? current_ipv4_gw->gw : NULL;
720 }
721
722 #endif /* __linux__ */