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