Merge branch 'release-0.6.6'
[olsrd.git] / src / gateway_default_handler.c
1 /*
2  * gateway_default_handler.c
3  *
4  *  Created on: Jan 29, 2010
5  *      Author: rogge
6  */
7 #ifdef __linux__
8
9 #include "gateway_default_handler.h"
10
11 #include "gateway_costs.h"
12 #include "defs.h"
13 #include "gateway.h"
14 #include "lq_plugin.h"
15
16 static uint32_t gw_def_nodecount;
17 static uint32_t gw_def_stablecount;
18 static bool gw_def_choose_new_ipv4_gw;
19 static bool gw_def_choose_new_ipv6_gw;
20 static struct timer_entry *gw_def_timer;
21 static struct costs_weights gw_costs_weights;
22
23 /* forward declarations */
24 static void gw_default_init(void);
25 static void gw_default_cleanup(void);
26 static void gw_default_startup_handler(void);
27 static uint64_t gw_default_getcosts(struct gateway_entry *gw);
28 static void gw_default_choosegw_handler(bool ipv4, bool ipv6);
29 static void gw_default_update_handler(struct gateway_entry *);
30 static void gw_default_delete_handler(struct gateway_entry *);
31
32 /**
33  * Callback list for the gateway (default) handler
34  */
35 struct olsr_gw_handler gw_def_handler = {
36     &gw_default_init,
37     &gw_default_cleanup,
38     &gw_default_startup_handler,
39     &gw_default_getcosts,
40     &gw_default_choosegw_handler,
41     &gw_default_update_handler,
42     &gw_default_delete_handler
43 };
44
45 /*
46  * Helper functions
47  */
48
49 /**
50  * Calculate the threshold path cost.
51  *
52  * @param path_cost the path cost
53  * @return the threshold path cost
54  */
55 static inline uint64_t gw_default_calc_threshold(uint64_t path_cost) {
56   if (olsr_cnf->smart_gw_thresh == 0) {
57     return path_cost;
58   }
59
60   return ((path_cost * (uint64_t) olsr_cnf->smart_gw_thresh) + (uint64_t) 50) / (uint64_t) 100;
61 }
62
63 /**
64  * Look through the gateway list and select the best gateway
65  * depending on the distance to this router
66  */
67 static void gw_default_choose_gateway(void) {
68   uint64_t cost_ipv4_threshold = UINT64_MAX;
69   uint64_t cost_ipv6_threshold = UINT64_MAX;
70   bool cost_ipv4_threshold_valid = false;
71   bool cost_ipv6_threshold_valid = false;
72   struct gateway_entry *chosen_gw_ipv4 = NULL;
73   struct gateway_entry *chosen_gw_ipv6 = NULL;
74   uint64_t chosen_gw_ipv4_costs = UINT64_MAX;
75   uint64_t chosen_gw_ipv6_costs = UINT64_MAX;
76   struct gateway_entry *gw;
77   bool dual = false;
78
79   if (olsr_cnf->smart_gw_thresh) {
80     /* determine the path cost thresholds */
81
82     uint64_t cost = gw_default_getcosts(olsr_get_inet_gateway(false));
83     if (cost != UINT64_MAX) {
84       cost_ipv4_threshold = gw_default_calc_threshold(cost);
85       cost_ipv4_threshold_valid = true;
86     }
87
88     cost = gw_default_getcosts(olsr_get_inet_gateway(true));
89     if (cost != UINT64_MAX) {
90       cost_ipv6_threshold = gw_default_calc_threshold(cost);
91       cost_ipv6_threshold_valid = true;
92     }
93   }
94
95   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
96     uint64_t gw_cost = gw_default_getcosts(gw);
97
98     if (gw_cost == UINT64_MAX) {
99       /* never select a node with infinite costs */
100       continue;
101     }
102
103     if (gw_def_choose_new_ipv4_gw) {
104       bool gw_eligible_v4 = gw->ipv4
105           /* && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit) *//* contained in gw_def_choose_new_ipv4_gw */
106           && (olsr_cnf->smart_gw_allow_nat || !gw->ipv4nat);
107       if (gw_eligible_v4 && gw_cost < chosen_gw_ipv4_costs
108           && (!cost_ipv4_threshold_valid || (gw_cost < cost_ipv4_threshold))) {
109         chosen_gw_ipv4 = gw;
110         chosen_gw_ipv4_costs = gw_cost;
111       }
112     }
113
114     if (gw_def_choose_new_ipv6_gw) {
115       bool gw_eligible_v6 = gw->ipv6
116           /* && olsr_cnf->ip_version == AF_INET6 *//* contained in gw_def_choose_new_ipv6_gw */;
117       if (gw_eligible_v6 && gw_cost < chosen_gw_ipv6_costs
118           && (!cost_ipv6_threshold_valid || (gw_cost < cost_ipv6_threshold))) {
119         chosen_gw_ipv6 = gw;
120         chosen_gw_ipv6_costs = gw_cost;
121       }
122     }
123   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
124
125   /* determine if we should keep looking for IPv4 and/or IPv6 gateways */
126   gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && (chosen_gw_ipv4 == NULL);
127   gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && (chosen_gw_ipv6 == NULL);
128
129   /* determine if we are dealing with a dual stack gateway */
130   dual = chosen_gw_ipv4 && (chosen_gw_ipv4 == chosen_gw_ipv6);
131
132   if (chosen_gw_ipv4) {
133     /* we are dealing with an IPv4 or dual stack gateway */
134     olsr_set_inet_gateway(&chosen_gw_ipv4->originator, chosen_gw_ipv4_costs, true, dual);
135   }
136   if (chosen_gw_ipv6 && !dual) {
137     /* we are dealing with an IPv6-only gateway */
138     olsr_set_inet_gateway(&chosen_gw_ipv6->originator, chosen_gw_ipv6_costs, false, true);
139   }
140
141   if ((olsr_cnf->smart_gw_thresh == 0) && !gw_def_choose_new_ipv4_gw && !gw_def_choose_new_ipv6_gw) {
142     /* stop looking for a better gateway */
143     olsr_stop_timer(gw_def_timer);
144     gw_def_timer = NULL;
145   }
146 }
147
148 /**
149  * Timer callback for lazy gateway selection
150  *
151  * @param unused unused
152  */
153 static void gw_default_timer(void *unused __attribute__ ((unused))) {
154   /* accept a 10% increase/decrease in the number of gateway nodes without triggering a stablecount reset */
155   unsigned int tree100percent = tc_tree.count * 10;
156   uint32_t nodecount090percent = gw_def_nodecount * 9;
157   uint32_t nodecount110percent = gw_def_nodecount * 11;
158   if ((tree100percent >= nodecount090percent) && (tree100percent <= nodecount110percent)) {
159     gw_def_nodecount = tc_tree.count;
160   }
161
162   if (tc_tree.count == gw_def_nodecount) {
163     /* the number of gateway nodes is 'stable' */
164     gw_def_stablecount++;
165   } else {
166     /* there was a significant change in the number of gateway nodes */
167     gw_def_nodecount = tc_tree.count;
168     gw_def_stablecount = 0;
169   }
170
171   if (gw_def_stablecount >= olsr_cnf->smart_gw_stablecount) {
172     /* the number of gateway nodes is stable enough, so we should select a new gateway now */
173     gw_default_choose_gateway();
174   }
175 }
176
177 /**
178  * Lookup a new gateway
179  *
180  * @param ipv4 lookup new v4 gateway
181  * @param ipv6 lookup new v6 gateway
182  */
183 static void gw_default_lookup_gateway(bool ipv4, bool ipv6) {
184   if (ipv4) {
185     /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
186     gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit;
187   }
188   if (ipv6) {
189     /* get a new IPv6 gateway if we use OLSRv6 */
190     gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6;
191   }
192
193   if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) {
194     gw_default_choose_gateway();
195   }
196 }
197
198 /*
199  * Exported functions
200  */
201
202 /*
203  * Handler functions
204  */
205
206 /**
207  * initialization of default gateway handler
208  */
209 static void gw_default_init(void) {
210   /* initialize values */
211   gw_def_nodecount = 0;
212   gw_def_stablecount = 0;
213   gw_def_choose_new_ipv4_gw = true;
214   gw_def_choose_new_ipv6_gw = true;
215   gw_def_timer = NULL;
216
217   gw_costs_weights.WexitU = olsr_cnf->smart_gw_weight_exitlink_up;
218   gw_costs_weights.WexitD = olsr_cnf->smart_gw_weight_exitlink_down;
219   gw_costs_weights.Wetx = olsr_cnf->smart_gw_weight_etx;
220   gw_costs_weights.Detx = olsr_cnf->smart_gw_divider_etx;
221 }
222
223 /**
224  * Cleanup default gateway handler
225  */
226 static void gw_default_cleanup(void) {
227 }
228
229 /**
230  * Handle gateway startup
231  */
232 static void gw_default_startup_handler(void) {
233   /* reset node count */
234   gw_def_nodecount = tc_tree.count;
235   gw_def_stablecount = 0;
236
237   /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
238   gw_def_choose_new_ipv4_gw = (olsr_cnf->ip_version == AF_INET) || olsr_cnf->use_niit;
239
240   /* get a new IPv6 gateway if we use OLSRv6 */
241   gw_def_choose_new_ipv6_gw = olsr_cnf->ip_version == AF_INET6;
242
243   /* keep in mind we might be a gateway ourself */
244   gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && !olsr_cnf->has_ipv4_gateway;
245   gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && !olsr_cnf->has_ipv6_gateway;
246
247   /* (re)start gateway lazy selection timer */
248   olsr_set_timer(&gw_def_timer, olsr_cnf->smart_gw_period, 0, true, &gw_default_timer, NULL, 0);
249 }
250
251 /**
252  * Called when the costs of a gateway must be determined.
253  *
254  * @param gw the gateway
255  * @return the costs, or UINT64_MAX in case the gateway is null or has inifinite costs
256  */
257 static uint64_t gw_default_getcosts(struct gateway_entry *gw) {
258   struct tc_entry* tc;
259
260   if (!gw) {
261     return UINT64_MAX;
262   }
263
264   tc = olsr_lookup_tc_entry(&gw->originator);
265
266   if (!tc || (tc->path_cost == ROUTE_COST_BROKEN) || (!gw->uplink || !gw->downlink)) {
267     /* gateways should not exist without tc entry */
268     /* do not consider nodes with an infinite ETX */
269     /* do not consider nodes without bandwidth or with a uni-directional link */
270     return UINT64_MAX;
271   }
272
273   /* determine the path cost */
274   return gw_costs_weigh(gw_costs_weights, tc->path_cost, gw->uplink, gw->downlink);
275 }
276
277 /**
278  * Choose a new gateway
279  *
280  * @param ipv4 lookup new v4 gateway
281  * @param ipv6 lookup new v6 gateway
282  */
283 static void gw_default_choosegw_handler(bool ipv4, bool ipv6) {
284   gw_default_lookup_gateway(ipv4, ipv6);
285
286   if (gw_def_choose_new_ipv4_gw || gw_def_choose_new_ipv6_gw) {
287     gw_default_startup_handler();
288   }
289 }
290
291 /**
292  * Update a gateway entry
293  *
294  * @param gw the gateway entry
295  */
296 static void gw_default_update_handler(struct gateway_entry *gw) {
297   if (olsr_cnf->smart_gw_thresh == 0) {
298     /* classical behaviour: stick with the chosen gateway unless it changes */
299     bool v4changed = gw && (gw == olsr_get_inet_gateway(false))
300         && (!gw->ipv4 || (gw->ipv4nat && !olsr_cnf->smart_gw_allow_nat));
301     bool v6changed = gw && (gw == olsr_get_inet_gateway(true)) && !gw->ipv6;
302
303     if (v4changed || v6changed) {
304       gw_default_lookup_gateway(v4changed, v6changed);
305     }
306   } else {
307     /* new behaviour: always pick a new gateway */
308     gw_default_lookup_gateway(true, true);
309   }
310 }
311
312 /**
313  * Remove a gateway entry
314  *
315  * @param gw the gateway entry
316  */
317 static void gw_default_delete_handler(struct gateway_entry *gw) {
318   bool isv4 = gw && (gw == olsr_get_inet_gateway(false));
319   bool isv6 = gw && (gw == olsr_get_inet_gateway(true));
320
321   if (isv4 || isv6) {
322     gw_default_lookup_gateway(isv4, isv6);
323   }
324 }
325 #endif /* __linux__ */