gateway: add helper function gw_default_calc_threshold
[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 olsr_linkcost gw_default_calc_threshold(olsr_linkcost path_cost) {
48   olsr_linkcost 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 = ((long long) path_cost * (long long) olsr_cnf->smart_gw_thresh + 50LL) / 100LL;
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   struct gateway_entry *inet_ipv4 = NULL;
65   struct gateway_entry *inet_ipv6 = NULL;
66   olsr_linkcost cost_ipv4 = ROUTE_COST_BROKEN;
67   olsr_linkcost cost_ipv6 = ROUTE_COST_BROKEN;
68   struct gateway_entry *gw;
69   bool dual;
70
71   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
72     olsr_linkcost path_cost_times_threshold;
73     struct tc_entry *tc = olsr_lookup_tc_entry(&gw->originator);
74
75     if (!tc) {
76           /* gateways should not exist without tc entry */
77       continue;
78     }
79
80     if (tc->path_cost == ROUTE_COST_BROKEN) {
81       /* do not consider nodes with an infinite ETX */
82       continue;
83     }
84
85     if (!gw->uplink || !gw->downlink) {
86       /* do not consider nodes without bandwidth or with a uni-directional link */
87       continue;
88     }
89
90     /* determine the path costs threshold */
91     path_cost_times_threshold = gw_default_calc_threshold(tc->path_cost);
92
93     if (!gw_def_finished_ipv4 && gw->ipv4 && gw->ipv4nat == olsr_cnf->smart_gw_allow_nat && path_cost_times_threshold < cost_ipv4) {
94       inet_ipv4 = gw;
95       cost_ipv4 = path_cost_times_threshold;
96     }
97     if (!gw_def_finished_ipv6 && gw->ipv6 && path_cost_times_threshold < cost_ipv6) {
98       inet_ipv6 = gw;
99       cost_ipv6 = path_cost_times_threshold;
100     }
101   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
102
103   /* determine if we found an IPv4 and IPv6 gateway */
104   gw_def_finished_ipv4 |= (inet_ipv4 != NULL);
105   gw_def_finished_ipv6 |= (inet_ipv6 != NULL);
106
107   /* determine if we are dealing with a dual stack gateway */
108   dual = (inet_ipv4 == inet_ipv6) && (inet_ipv4 != NULL);
109
110   if (inet_ipv4) {
111         /* we are dealing with an IPv4 or dual stack gateway */
112     olsr_set_inet_gateway(&inet_ipv4->originator, true, dual, false);
113   }
114   if (inet_ipv6 && !dual) {
115     /* we are dealing with an IPv6-only gateway */
116     olsr_set_inet_gateway(&inet_ipv6->originator, false, true, false);
117   }
118
119   if ((olsr_cnf->smart_gw_thresh == 0) && gw_def_finished_ipv4 && gw_def_finished_ipv6) {
120     /* stop looking for a better gateway */
121     olsr_stop_timer(gw_def_timer);
122     gw_def_timer = NULL;
123   }
124 }
125
126 /**
127  * Timer callback for lazy gateway selection
128  *
129  * @param unused unused
130  */
131 static void gw_default_timer(void *unused __attribute__ ((unused))) {
132   /* accept a 10% increase/decrease in the number of gateway nodes without triggering a stablecount reset */
133   if (((tc_tree.count * 10) <= (gw_def_nodecount * 11)) ||
134       ((tc_tree.count * 10) >= (gw_def_nodecount *  9))) {
135     gw_def_nodecount = tc_tree.count;
136   }
137
138   if (tc_tree.count == gw_def_nodecount) {
139     /* the number of gateway nodes is 'stable' */
140     gw_def_stablecount++;
141   }
142   else {
143     /* there was a significant change in the number of gateway nodes */
144     gw_def_nodecount = tc_tree.count;
145     gw_def_stablecount = 0;
146   }
147
148   if (gw_def_stablecount >= olsr_cnf->smart_gw_stablecount) {
149     /* the number of gateway nodes is stable enough, so we should select a new gateway now */
150     gw_default_choose_gateway();
151   }
152 }
153
154 /**
155  * Lookup a new gateway
156  *
157  * @param ipv4 lookup new v4 gateway
158  * @param ipv6 lookup new v6 gateway
159  */
160 static void olsr_gw_default_lookup_gateway(bool ipv4, bool ipv6) {
161   if (ipv4) {
162     /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
163     gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
164   }
165   if (ipv6) {
166     /* get a new IPv6 gateway if we use OLSRv6 */
167     gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6);
168   }
169
170   if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) {
171     gw_default_choose_gateway();
172   }
173 }
174
175 /*
176  * Exported functions
177  */
178
179 /**
180  * initialization of default gateway handler
181  */
182 void olsr_gw_default_init(void) {
183   /* initialize values */
184   gw_def_nodecount = 0;
185   gw_def_stablecount = 0;
186   gw_def_finished_ipv4 = false;
187   gw_def_finished_ipv6 = false;
188   gw_def_timer = NULL;
189
190   /* setup default handler */
191   olsr_set_inetgw_handler(&gw_def_handler);
192 }
193
194 /*
195  * Handler functions
196  */
197
198 /**
199  * Handle gateway startup
200  */
201 static void gw_default_startup_handler(void) {
202   /* reset node count */
203   gw_def_nodecount = tc_tree.count;
204   gw_def_stablecount = 0;
205
206   /* get a new IPv4 gateway if we use OLSRv4 or NIIT */
207   gw_def_finished_ipv4 = !(olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit);
208
209   /* get a new IPv6 gateway if we use OLSRv6 */
210   gw_def_finished_ipv6 = !(olsr_cnf->ip_version == AF_INET6);
211
212   /* keep in mind we might be a gateway ourself */
213   gw_def_finished_ipv4 |= olsr_cnf->has_ipv4_gateway;
214   gw_def_finished_ipv6 |= olsr_cnf->has_ipv6_gateway;
215
216   /* (re)start gateway lazy selection timer */
217   olsr_set_timer(&gw_def_timer, olsr_cnf->smart_gw_period, 0, true, &gw_default_timer, NULL, 0);
218 }
219
220 /**
221  * Choose a new gateway
222  *
223  * @param ipv4 lookup new v4 gateway
224  * @param ipv6 lookup new v6 gateway
225  */
226 static void gw_default_choosegw_handler(bool ipv4, bool ipv6) {
227   olsr_gw_default_lookup_gateway(ipv4, ipv6);
228
229   if (!(gw_def_finished_ipv4 && gw_def_finished_ipv6)) {
230     gw_default_startup_handler();
231   }
232 }
233
234 /**
235  * Update a gateway entry
236  *
237  * @param gw the gateway entry
238  */
239 static void gw_default_update_handler(struct gateway_entry *gw) {
240   bool v4changed = gw && (gw == olsr_get_ipv4_inet_gateway(NULL))
241       && (!gw->ipv4 || (gw->ipv4nat && !olsr_cnf->smart_gw_allow_nat));
242   bool v6changed = gw && (gw == olsr_get_ipv6_inet_gateway(NULL)) && !gw->ipv6;
243
244   if (v4changed || v6changed) {
245     olsr_gw_default_lookup_gateway(v4changed, v6changed);
246   }
247 }
248
249 /**
250  * Remove a gateway entry
251  *
252  * @param gw the gateway entry
253  */
254 static void gw_default_delete_handler(struct gateway_entry *gw) {
255   bool isv4 = gw && (gw == olsr_get_ipv4_inet_gateway(NULL));
256   bool isv6 = gw && (gw == olsr_get_ipv6_inet_gateway(NULL));
257
258   if (isv4 || isv6) {
259     olsr_gw_default_lookup_gateway(isv4, isv6);
260   }
261 }
262 #endif /* linux */