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