Merge branch 'release-0.9.7'
[olsrd.git] / src / gateway_default_handler.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45 #ifdef __linux__
46
47 #include "gateway_default_handler.h"
48
49 #include "gateway_costs.h"
50 #include "defs.h"
51 #include "gateway.h"
52 #include "lq_plugin.h"
53
54 static uint32_t gw_def_nodecount;
55 static uint32_t gw_def_stablecount;
56 static bool gw_def_choose_new_ipv4_gw;
57 static bool gw_def_choose_new_ipv6_gw;
58 static struct timer_entry *gw_def_timer;
59
60 /* forward declarations */
61 static void gw_default_init(void);
62 static void gw_default_cleanup(void);
63 static void gw_default_startup_handler(void);
64 static int64_t gw_default_getcosts(struct gateway_entry *gw);
65 static void gw_default_choosegw_handler(bool ipv4, bool ipv6);
66 static void gw_default_update_handler(struct gateway_entry *);
67 static void gw_default_delete_handler(struct gateway_entry *);
68
69 /**
70  * Callback list for the gateway (default) handler
71  */
72 struct olsr_gw_handler gw_def_handler = {
73     &gw_default_init,
74     &gw_default_cleanup,
75     &gw_default_startup_handler,
76     &gw_default_getcosts,
77     &gw_default_choosegw_handler,
78     &gw_default_update_handler,
79     &gw_default_delete_handler
80 };
81
82 /*
83  * Helper functions
84  */
85
86 /**
87  * Calculate the threshold path cost.
88  *
89  * @param path_cost the path cost
90  * @return the threshold path cost
91  */
92 static INLINE int64_t gw_default_calc_threshold(int64_t path_cost) {
93   if (olsr_cnf->smart_gw_thresh == 0) {
94     return path_cost;
95   }
96
97   return ((path_cost * olsr_cnf->smart_gw_thresh) + 50) / 100;
98 }
99
100 /**
101  * Look through the gateway list and select the best gateway
102  * depending on the costs
103  */
104 static void gw_default_choose_gateway(void) {
105   int64_t cost_ipv4_threshold = INT64_MAX;
106   int64_t cost_ipv6_threshold = INT64_MAX;
107   bool cost_ipv4_threshold_valid = false;
108   bool cost_ipv6_threshold_valid = false;
109   struct gateway_entry *chosen_gw_ipv4 = NULL;
110   struct gateway_entry *chosen_gw_ipv6 = NULL;
111   struct gateway_entry *gw;
112   bool dual = false;
113
114   if (olsr_cnf->smart_gw_thresh) {
115     /* determine the path cost thresholds */
116
117     struct gateway_entry * current_gw = olsr_get_inet_gateway(false);
118     if (current_gw) {
119       cost_ipv4_threshold = gw_default_calc_threshold(current_gw->path_cost);
120       cost_ipv4_threshold_valid = true;
121     }
122
123     current_gw = olsr_get_inet_gateway(true);
124     if (current_gw) {
125       cost_ipv6_threshold = gw_default_calc_threshold(current_gw->path_cost);
126       cost_ipv6_threshold_valid = true;
127     }
128   }
129
130   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
131     int64_t gw_cost = gw->path_cost;
132
133     if (gw_cost == INT64_MAX) {
134       /* never select a node with infinite costs */
135       continue;
136     }
137
138     if (gw_def_choose_new_ipv4_gw) {
139       bool gw_eligible_v4 = isGwSelectable(gw, false) ;
140       if (gw_eligible_v4 && gw_cost < (chosen_gw_ipv4 ? chosen_gw_ipv4->path_cost : INT64_MAX)
141           && (!cost_ipv4_threshold_valid || (gw_cost < cost_ipv4_threshold))) {
142         chosen_gw_ipv4 = gw;
143       }
144     }
145
146     if (gw_def_choose_new_ipv6_gw) {
147       bool gw_eligible_v6 = isGwSelectable(gw, true);
148       if (gw_eligible_v6 && gw_cost < (chosen_gw_ipv6 ? chosen_gw_ipv6->path_cost : INT64_MAX)
149           && (!cost_ipv6_threshold_valid || (gw_cost < cost_ipv6_threshold))) {
150         chosen_gw_ipv6 = gw;
151       }
152     }
153   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
154
155   /* determine if we should keep looking for IPv4 and/or IPv6 gateways */
156   gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && (chosen_gw_ipv4 == NULL);
157   gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && (chosen_gw_ipv6 == NULL);
158
159   /* determine if we are dealing with a dual stack gateway */
160   dual = chosen_gw_ipv4 && (chosen_gw_ipv4 == chosen_gw_ipv6);
161
162   if (chosen_gw_ipv4) {
163     /* we are dealing with an IPv4 or dual stack gateway */
164     olsr_set_inet_gateway(chosen_gw_ipv4, true, dual);
165   }
166   if (chosen_gw_ipv6 && !dual) {
167     /* we are dealing with an IPv6-only gateway */
168     olsr_set_inet_gateway(chosen_gw_ipv6, false, true);
169   }
170
171   if ((olsr_cnf->smart_gw_thresh == 0) && !gw_def_choose_new_ipv4_gw && !gw_def_choose_new_ipv6_gw) {
172     /* stop looking for a better gateway */
173     olsr_stop_timer(gw_def_timer);
174     gw_def_timer = NULL;
175   }
176 }
177
178 /**
179  * Timer callback for lazy gateway selection
180  *
181  * @param unused unused
182  */
183 static void gw_default_timer(void *unused __attribute__ ((unused))) {
184   /* accept a 10% increase/decrease in the number of gateway nodes without triggering a stablecount reset */
185   unsigned int tree100percent = tc_tree.count * 10;
186   uint32_t nodecount090percent = gw_def_nodecount * 9;
187   uint32_t nodecount110percent = gw_def_nodecount * 11;
188   if ((tree100percent >= nodecount090percent) && (tree100percent <= nodecount110percent)) {
189     gw_def_nodecount = tc_tree.count;
190   }
191
192   if (tc_tree.count == gw_def_nodecount) {
193     /* the number of gateway nodes is 'stable' */
194     gw_def_stablecount++;
195   } else {
196     /* there was a significant change in the number of gateway nodes */
197     gw_def_nodecount = tc_tree.count;
198     gw_def_stablecount = 0;
199   }
200
201   if (gw_def_stablecount >= olsr_cnf->smart_gw_stablecount) {
202     /* the number of gateway nodes is stable enough, so we should select a new gateway now */
203     gw_default_choose_gateway();
204   }
205 }
206
207 /**
208  * Lookup a new gateway
209  *
210  * @param ipv4 lookup new v4 gateway
211  * @param ipv6 lookup new v6 gateway
212  */
213 static void gw_default_lookup_gateway(bool ipv4, bool ipv6) {
214   if (ipv4) {
215     /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
216     gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit;
217   }
218   if (ipv6) {
219     /* get a new IPv6 gateway if we use OLSRv6 */
220     gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6;
221   }
222
223   if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) {
224     gw_default_choose_gateway();
225   }
226 }
227
228 /*
229  * Exported functions
230  */
231
232 /*
233  * Handler functions
234  */
235
236 /**
237  * initialization of default gateway handler
238  */
239 static void gw_default_init(void) {
240   /* initialize values */
241   gw_def_nodecount = 0;
242   gw_def_stablecount = 0;
243   gw_def_choose_new_ipv4_gw = true;
244   gw_def_choose_new_ipv6_gw = true;
245   gw_def_timer = NULL;
246 }
247
248 /**
249  * Cleanup default gateway handler
250  */
251 static void gw_default_cleanup(void) {
252 }
253
254 /**
255  * Handle gateway startup
256  */
257 static void gw_default_startup_handler(void) {
258   /* reset node count */
259   gw_def_nodecount = tc_tree.count;
260   gw_def_stablecount = 0;
261
262   /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
263   gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit;
264
265   /* get a new IPv6 gateway if we use OLSRv6 */
266   gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6;
267
268   /* keep in mind we might be a gateway ourself */
269   gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && !olsr_cnf->has_ipv4_gateway;
270   gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && !olsr_cnf->has_ipv6_gateway;
271
272   /* (re)start gateway lazy selection timer */
273   olsr_set_timer(&gw_def_timer, olsr_cnf->smart_gw_period, 0, true, &gw_default_timer, NULL, 0);
274 }
275
276 /**
277  * Called when the costs of a gateway must be determined.
278  *
279  * @param gw the gateway
280  * @return the costs, or INT64_MAX in case the gateway is null or has infinite costs
281  */
282 static int64_t gw_default_getcosts(struct gateway_entry *gw) {
283   struct tc_entry* tc;
284
285   if (!gw) {
286     return INT64_MAX;
287   }
288
289   tc = olsr_lookup_tc_entry(&gw->originator);
290
291   if (!tc || (tc->path_cost >= ROUTE_COST_BROKEN) || (!gw->uplink || !gw->downlink)) {
292     /* gateways should not exist without tc entry */
293     /* do not consider nodes with an infinite ETX */
294     /* do not consider nodes without bandwidth or with a uni-directional link */
295     return INT64_MAX;
296   }
297
298   /* determine the path cost */
299   return gw_costs_weigh(true, tc->path_cost, gw->uplink, gw->downlink);
300 }
301
302 /**
303  * Choose a new gateway
304  *
305  * @param ipv4 lookup new v4 gateway
306  * @param ipv6 lookup new v6 gateway
307  */
308 static void gw_default_choosegw_handler(bool ipv4, bool ipv6) {
309   gw_default_lookup_gateway(ipv4, ipv6);
310
311   if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) {
312     gw_default_startup_handler();
313   }
314 }
315
316 /**
317  * Update a gateway entry
318  *
319  * @param gw the gateway entry
320  */
321 static void gw_default_update_handler(struct gateway_entry *gw) {
322   if (olsr_cnf->smart_gw_thresh == 0) {
323     /* classical behaviour: stick with the chosen gateway unless it changes */
324     bool v4changed = gw && (gw == olsr_get_inet_gateway(false))
325         && (!gw->ipv4 || (gw->ipv4nat && !olsr_cnf->smart_gw_allow_nat));
326     bool v6changed = gw && (gw == olsr_get_inet_gateway(true)) && !gw->ipv6;
327
328     if (v4changed || v6changed) {
329       gw_default_lookup_gateway(v4changed, v6changed);
330     }
331   } else {
332     /* new behaviour: always pick a new gateway */
333     gw_default_lookup_gateway(true, true);
334   }
335 }
336
337 /**
338  * Remove a gateway entry
339  *
340  * @param gw the gateway entry
341  */
342 static void gw_default_delete_handler(struct gateway_entry *gw) {
343   bool isv4 = gw && (gw == olsr_get_inet_gateway(false));
344   bool isv6 = gw && (gw == olsr_get_inet_gateway(true));
345
346   if (isv4 || isv6) {
347     gw_default_lookup_gateway(isv4, isv6);
348   }
349 }
350 #endif /* __linux__ */