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