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