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