Add default gateway selection algorithm and plugin callback
[olsrd.git] / src / gateway.c
1 /*
2  * gateway.c
3  *
4  *  Created on: 05.01.2010
5  *      Author: henning
6  */
7
8 #include "common/avl.h"
9 #include "defs.h"
10 #include "ipcalc.h"
11 #include "olsr.h"
12 #include "olsr_cfg.h"
13 #include "olsr_cookie.h"
14 #include "scheduler.h"
15 #include "gateway_default_handler.h"
16 #include "gateway.h"
17
18 #include "assert.h"
19
20 struct avl_tree gateway_tree;
21
22 static struct olsr_cookie_info *gw_mem_cookie = NULL;
23 static uint8_t smart_gateway_netmask[sizeof(union olsr_ip_addr)];
24 static struct gateway_entry *current_ipv4_gw, *current_ipv6_gw;
25 static struct olsr_gw_handler *gw_handler;
26
27 /**
28  * Reconstructs an uplink/downlink speed value from the encoded
29  * 1 byte transport value (3 bit mantissa, 5 bit exponent)
30  * @param value
31  * @return
32  */
33 static uint32_t
34 deserialize_gw_speed(uint8_t value) {
35   uint32_t speed, exp;
36
37   speed = (value >> 3)+1;
38   exp = value & 7;
39   while (exp-- > 0) {
40     speed *= 10;
41   }
42   return speed;
43 }
44
45 /**
46  * Convert an uplink/downlink speed into an exponential encoded
47  * transport value (1 byte, 3 bit mantissa and 5 bit exponent)
48  * @param value uplink/downlink speed in kbit/s
49  * @return encoded byte value
50  */
51 static uint8_t
52 serialize_gw_speed(uint32_t speed) {
53   uint8_t exp = 0;
54
55   if (speed == 0 || speed > 320000000) {
56     return 0;
57   }
58
59   while (speed > 32 || (speed % 10) == 0) {
60     speed /= 10;
61     exp ++;
62   }
63   return ((speed-1) << 3) | exp;
64 }
65
66 /**
67  * Initialize gateway system
68  */
69 void
70 olsr_init_gateways(void) {
71   uint8_t *ip;
72   gw_mem_cookie = olsr_alloc_cookie("Gateway cookie", OLSR_COOKIE_TYPE_MEMORY);
73   olsr_cookie_set_memory_size(gw_mem_cookie, sizeof(struct gateway_entry));
74
75   avl_init(&gateway_tree, avl_comp_default);
76   current_ipv4_gw = NULL;
77   current_ipv6_gw = NULL;
78
79   memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask));
80
81   if (olsr_cnf->smart_gw_active) {
82     union olsr_ip_addr gw_net;
83 #ifdef MAXIMUM_GATEWAY_PREFIX_LENGTH
84     int prefix;
85 #endif
86
87     memset(&gw_net, 0, sizeof(gw_net));
88
89 #ifdef MAXIMUM_GATEWAY_PREFIX_LENGTH
90     for (prefix = 1; prefix <= MAXIMUM_GATEWAY_PREFIX_LENGTH; prefix++) {
91       while (ip_prefix_list_remove(&olsr_cnf->hna_entries, &gw_net, prefix));
92     }
93 #endif
94     ip = (uint8_t *) &smart_gateway_netmask;
95
96     if (olsr_cnf->smart_gw_uplink > 0 || olsr_cnf->smart_gw_downlink > 0) {
97       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
98       ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
99       ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
100     }
101     if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) {
102       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX;
103       ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len;
104       memcpy(&ip[GW_HNA_V6PREFIX], &olsr_cnf->smart_gw_prefix.prefix, 8);
105     }
106   }
107
108   /*
109    * initialize default gateway handler,
110    * can be overwritten with olsr_set_inetgw_handler
111    */
112   olsr_gw_default_init();
113 }
114
115 /**
116  * Triggers the first lookup of an gateway. This lookup might
117  * take some time to wait for routing/gateway information.
118  */
119 void
120 olsr_trigger_inetgw_startup(void) {
121   gw_handler->handle_startup();
122 }
123
124 /**
125  * Triggers an instant gateway selection based on the current data
126  * @param ipv4 trigger a ipv4 gateway lookup
127  * @param ipv6 trigger a ipv6 gateway lookup
128  * @return 0 if successful, -1 otherwise
129  */
130 int
131 olsr_trigger_inetgw_selection(bool ipv4, bool ipv6) {
132   gw_handler->select_gateway(ipv4, ipv6);
133   return ((ipv4 && current_ipv4_gw == NULL) || (ipv6 && current_ipv6_gw == NULL)) ? -1 : 0;
134 }
135
136 /**
137  * Set a new gateway handler. Do only call this once during startup from
138  * a plugin to overwrite the default handler.
139  * @param h pointer to gateway handler struct
140  */
141 void
142 olsr_set_inetgw_handler(struct olsr_gw_handler *h) {
143   gw_handler = h;
144 }
145
146 /**
147  * Sets a new internet gateway.
148  * An external set command might trigger an internal one if
149  * address is not a legal gateway.
150  *
151  * @param originator ip address of the node with the new gateway
152  * @param ipv4 set ipv4 gateway
153  * @param ipv6 set ipv6 gateway
154  * @param external true if change was triggered directly by an user,
155  *   false if triggered by automatic lookup.
156  * @return 0 if successful, -1 otherwise
157  */
158 int
159 olsr_set_inet_gateway(union olsr_ip_addr *originator, bool ipv4, bool ipv6, bool external) {
160   struct gateway_entry *entry;
161
162   ipv4 = ipv4 && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
163   ipv6 = ipv6 && (olsr_cnf->ip_version == AF_INET6);
164
165   entry = olsr_find_gateway_entry(originator);
166   if (entry != NULL) {
167     if (ipv4 && entry->ipv4 && (!entry->ipv4nat || olsr_cnf->smart_gw_allow_nat)) {
168       /* valid ipv4 gateway */
169       current_ipv4_gw = entry;
170       ipv4 = false;
171     }
172     if (ipv6 && entry->ipv6) {
173       /* valid ipv6 gateway */
174       current_ipv6_gw = entry;
175       ipv6 = false;
176     }
177   }
178
179   /* still gateway missing ? */
180   if (ipv4 || ipv6) {
181     return external ? olsr_trigger_inetgw_selection(ipv4, ipv6) : -1;
182   }
183   return 0;
184 }
185
186 /**
187  * returns the gateway_entry of the current internet gw.
188  * @param ipv6 true to lookup ipv6 gateway, false to lookup ipv4
189  * @return pointer to gateway_entry or NULL if not set
190  */
191 struct gateway_entry *olsr_get_inet_gateway(bool ipv6) {
192   return ipv6 ? current_ipv6_gw : current_ipv4_gw;
193 }
194
195 /**
196  * @param originator
197  * @return gateway_entry for corresponding router
198  */
199 struct gateway_entry *
200 olsr_find_gateway_entry(union olsr_ip_addr *originator) {
201   struct avl_node *node = avl_find(&gateway_tree, originator);
202
203   return node == NULL ? NULL : node2gateway(node);
204 }
205
206 /**
207  * update a gateway_entry based on data received from a HNA
208  * @param originator ip of the source of the HNA
209  * @param mask netmask of the HNA
210  * @param prefixlen of the HNA
211  */
212 void
213 olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_addr *mask, int prefixlen) {
214   struct gateway_entry *gw;
215   uint8_t *ptr;
216
217   ptr = ((uint8_t *)mask) + ((prefixlen+7)/8);
218
219   gw = olsr_find_gateway_entry(originator);
220   if (!gw) {
221     gw = olsr_cookie_malloc(gw_mem_cookie);
222
223     gw->originator = *originator;
224     gw->node.key = &gw->originator;
225
226     avl_insert(&gateway_tree, &gw->node, AVL_DUP_NO);
227   }
228
229   if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) {
230     gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]);
231     gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]);
232   }
233   else {
234     gw->uplink = 1;
235     gw->downlink = 1;
236   }
237
238   gw->ipv4 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4) != 0;
239   gw->ipv4nat = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4_NAT) != 0;
240
241   if (olsr_cnf->ip_version == AF_INET6) {
242     gw->ipv6 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6) != 0;
243
244     /* do not reset prefixlength for ::ffff:0:0 HNAs */
245     if (prefixlen == ipv6_internet_route.prefix_len) {
246       memset(&gw->external_prefix, 0, sizeof(gw->external_prefix));
247
248       if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6PREFIX) != 0
249           && memcmp(mask->v6.s6_addr, &ipv6_internet_route.prefix, olsr_cnf->ipsize) == 0) {
250         /* this is the right prefix (2000::/3), so we can copy the prefix */
251         gw->external_prefix.prefix_len = ptr[GW_HNA_V6PREFIXLEN];
252         memcpy(&gw->external_prefix.prefix, &ptr[GW_HNA_V6PREFIX], 8);
253       }
254     }
255   }
256
257   gw_handler->handle_update_gw(gw);
258 }
259
260 /**
261  * Delete a gateway based on the originator IP and the prefixlength of a HNA.
262  * Should only be called if prefix is a smart_gw prefix or if node is removed
263  * from TC set.
264  * @param originator
265  * @param prefixlen
266  */
267 void
268 olsr_delete_gateway_entry(union olsr_ip_addr *originator, uint8_t prefixlen) {
269   struct gateway_entry *gw;
270   bool change = false;
271
272   gw = olsr_find_gateway_entry(originator);
273   if (gw) {
274     if (olsr_cnf->ip_version == AF_INET && prefixlen == 0) {
275       gw->ipv4 = false;
276       gw->ipv4nat = false;
277       change = true;
278     }
279     if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_internet_route.prefix_len) {
280       gw->ipv6 = false;
281       change = true;
282     }
283     if (olsr_cnf->ip_version == AF_INET6 && prefixlen == mapped_v4_gw.prefix_len) {
284       gw->ipv4 = false;
285       gw->ipv4nat = false;
286       change = true;
287     }
288
289     if (prefixlen == FORCE_DELETE_GW_ENTRY || !(gw->ipv4 || gw->ipv6)) {
290       /* prevent this gateway from being choosen as the new gateway */
291       gw->ipv4 = false;
292       gw->ipv6 = false;
293
294       /* handle gateway loss */
295       gw_handler->handle_delete_gw(gw);
296
297       /* cleanup gateway pointer if necessary */
298       if (current_ipv4_gw == gw) {
299         current_ipv4_gw = NULL;
300       }
301       if (current_ipv6_gw == gw) {
302         current_ipv6_gw = NULL;
303       }
304
305       /* remove gateway entry */
306       avl_delete(&gateway_tree, &gw->node);
307       olsr_cookie_free(gw_mem_cookie, gw);
308     }
309     else if (change) {
310       gw_handler->handle_update_gw(gw);
311     }
312   }
313 }
314
315 /**
316  * Checks if a prefix/netmask combination is a smart gateway
317  * @param prefix
318  * @param mask
319  * @return true if is a valid smart gateway HNA, false otherwise
320  */
321 bool
322 olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *mask) {
323   uint8_t *ptr;
324
325   if (!ip_is_inetgw_prefix(prefix)) {
326     return false;
327   }
328
329   ptr = ((uint8_t *)mask) + ((prefix->prefix_len+7)/8);
330   return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0;
331 }
332
333 /**
334  * Apply the smart gateway modifications to an outgoing HNA
335  * @param mask pointer to netmask of the HNA
336  * @param prefixlen of the HNA
337  */
338 void
339 olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
340   uint8_t *ptr = ((uint8_t *)mask) + ((prefixlen+7)/8);
341
342   memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen/8);
343   if (olsr_cnf->has_ipv4_gateway) {
344     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4;
345
346     if (olsr_cnf->smart_gw_uplink_nat) {
347       ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4_NAT;
348     }
349   }
350   if (olsr_cnf->has_ipv6_gateway) {
351     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6;
352   }
353   if (!olsr_cnf->has_ipv6_gateway || prefixlen != ipv6_internet_route.prefix_len){
354     ptr[GW_HNA_FLAGS] &= ~GW_HNA_FLAG_IPV6PREFIX;
355   }
356 }
357
358 /**
359  * Print debug information about gateway entries
360  */
361 void
362 olsr_print_gateway_entries(void) {
363 #ifndef NODEBUG
364   struct ipaddr_str buf;
365   struct gateway_entry *gw;
366   const int addrsize = olsr_cnf->ip_version == AF_INET ? 15 : 39;
367
368   OLSR_PRINTF(0, "\n--- %s ---------------------------------------------------- GATEWAYS\n\n",
369       olsr_wallclock_string());
370   OLSR_PRINTF(0, "%-*s %-6s %-9s %-9s %s\n", addrsize, "IP address", "Type", "Uplink", "Downlink",
371       olsr_cnf->ip_version == AF_INET ? "" : "External Prefix");
372
373   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
374     OLSR_PRINTF(0, "%-*s %s%c%s%c%c %-9u %-9u %s\n", addrsize, olsr_ip_to_string(&buf, &gw->originator),
375         gw->ipv4nat ? "" : "   ",
376         gw->ipv4 ? '4' : ' ',
377         gw->ipv4nat ? "(N)" : "",
378         (gw->ipv4 && gw->ipv6) ? ',' : ' ',
379         gw->ipv6 ? '6' : ' ',
380         gw->uplink, gw->downlink,
381         gw->external_prefix.prefix_len == 0 ? "" : olsr_ip_prefix_to_string(&gw->external_prefix));
382   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
383 #endif
384 }