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