]>
Commit | Line | Data |
---|---|---|
c6c8fea2 | 1 | /* |
64afe353 | 2 | * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors: |
c6c8fea2 SE |
3 | * |
4 | * Marek Lindner | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of version 2 of the GNU General Public | |
8 | * License as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
18 | * 02110-1301, USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include "main.h" | |
2265c141 | 23 | #include "bat_sysfs.h" |
c6c8fea2 SE |
24 | #include "gateway_client.h" |
25 | #include "gateway_common.h" | |
26 | #include "hard-interface.h" | |
57f0c07c | 27 | #include "originator.h" |
c6c8fea2 SE |
28 | #include <linux/ip.h> |
29 | #include <linux/ipv6.h> | |
30 | #include <linux/udp.h> | |
31 | #include <linux/if_vlan.h> | |
32 | ||
25b6d3c1 | 33 | static void gw_node_free_ref(struct gw_node *gw_node) |
c6c8fea2 | 34 | { |
25b6d3c1 | 35 | if (atomic_dec_and_test(&gw_node->refcount)) |
eb340b2f | 36 | kfree_rcu(gw_node, rcu); |
c6c8fea2 SE |
37 | } |
38 | ||
c4aac1ab | 39 | static struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv) |
c6c8fea2 | 40 | { |
c4aac1ab | 41 | struct gw_node *gw_node; |
c6c8fea2 | 42 | |
5d02b3cd | 43 | rcu_read_lock(); |
c4aac1ab ML |
44 | gw_node = rcu_dereference(bat_priv->curr_gw); |
45 | if (!gw_node) | |
7b36e8ee | 46 | goto out; |
c6c8fea2 | 47 | |
c4aac1ab ML |
48 | if (!atomic_inc_not_zero(&gw_node->refcount)) |
49 | gw_node = NULL; | |
43c70ad5 | 50 | |
5d02b3cd LL |
51 | out: |
52 | rcu_read_unlock(); | |
c4aac1ab | 53 | return gw_node; |
c6c8fea2 SE |
54 | } |
55 | ||
c4aac1ab | 56 | struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv) |
c6c8fea2 | 57 | { |
5d02b3cd | 58 | struct gw_node *gw_node; |
c4aac1ab | 59 | struct orig_node *orig_node = NULL; |
c6c8fea2 | 60 | |
c4aac1ab ML |
61 | gw_node = gw_get_selected_gw_node(bat_priv); |
62 | if (!gw_node) | |
63 | goto out; | |
64 | ||
65 | rcu_read_lock(); | |
66 | orig_node = gw_node->orig_node; | |
67 | if (!orig_node) | |
68 | goto unlock; | |
69 | ||
70 | if (!atomic_inc_not_zero(&orig_node->refcount)) | |
71 | orig_node = NULL; | |
c6c8fea2 | 72 | |
c4aac1ab ML |
73 | unlock: |
74 | rcu_read_unlock(); | |
75 | out: | |
c6c8fea2 | 76 | if (gw_node) |
25b6d3c1 | 77 | gw_node_free_ref(gw_node); |
c4aac1ab | 78 | return orig_node; |
c6c8fea2 SE |
79 | } |
80 | ||
25b6d3c1 | 81 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) |
c6c8fea2 | 82 | { |
5d02b3cd | 83 | struct gw_node *curr_gw_node; |
c6c8fea2 | 84 | |
c4aac1ab ML |
85 | spin_lock_bh(&bat_priv->gw_list_lock); |
86 | ||
25b6d3c1 ML |
87 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) |
88 | new_gw_node = NULL; | |
c6c8fea2 | 89 | |
728cbc6a | 90 | curr_gw_node = rcu_dereference_protected(bat_priv->curr_gw, 1); |
5d02b3cd | 91 | rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); |
25b6d3c1 ML |
92 | |
93 | if (curr_gw_node) | |
94 | gw_node_free_ref(curr_gw_node); | |
c4aac1ab ML |
95 | |
96 | spin_unlock_bh(&bat_priv->gw_list_lock); | |
97 | } | |
98 | ||
99 | void gw_deselect(struct bat_priv *bat_priv) | |
100 | { | |
2265c141 | 101 | atomic_set(&bat_priv->gw_reselect, 1); |
c6c8fea2 SE |
102 | } |
103 | ||
2265c141 | 104 | static struct gw_node *gw_get_best_gw_node(struct bat_priv *bat_priv) |
c6c8fea2 | 105 | { |
e1a5382f | 106 | struct neigh_node *router; |
2265c141 AQ |
107 | struct hlist_node *node; |
108 | struct gw_node *gw_node, *curr_gw = NULL; | |
c6c8fea2 | 109 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
2265c141 | 110 | uint8_t max_tq = 0; |
c6c8fea2 SE |
111 | int down, up; |
112 | ||
c4aac1ab | 113 | rcu_read_lock(); |
c6c8fea2 | 114 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { |
e1a5382f | 115 | if (gw_node->deleted) |
c6c8fea2 SE |
116 | continue; |
117 | ||
e1a5382f LL |
118 | router = orig_node_get_router(gw_node->orig_node); |
119 | if (!router) | |
c6c8fea2 SE |
120 | continue; |
121 | ||
2265c141 AQ |
122 | if (!atomic_inc_not_zero(&gw_node->refcount)) |
123 | goto next; | |
124 | ||
c6c8fea2 SE |
125 | switch (atomic_read(&bat_priv->gw_sel_class)) { |
126 | case 1: /* fast connection */ | |
127 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, | |
128 | &down, &up); | |
129 | ||
e1a5382f | 130 | tmp_gw_factor = (router->tq_avg * router->tq_avg * |
c6c8fea2 SE |
131 | down * 100 * 100) / |
132 | (TQ_LOCAL_WINDOW_SIZE * | |
133 | TQ_LOCAL_WINDOW_SIZE * 64); | |
134 | ||
135 | if ((tmp_gw_factor > max_gw_factor) || | |
136 | ((tmp_gw_factor == max_gw_factor) && | |
2265c141 AQ |
137 | (router->tq_avg > max_tq))) { |
138 | if (curr_gw) | |
139 | gw_node_free_ref(curr_gw); | |
140 | curr_gw = gw_node; | |
141 | atomic_inc(&curr_gw->refcount); | |
142 | } | |
c6c8fea2 SE |
143 | break; |
144 | ||
145 | default: /** | |
146 | * 2: stable connection (use best statistic) | |
147 | * 3: fast-switch (use best statistic but change as | |
148 | * soon as a better gateway appears) | |
149 | * XX: late-switch (use best statistic but change as | |
150 | * soon as a better gateway appears which has | |
151 | * $routing_class more tq points) | |
152 | **/ | |
2265c141 AQ |
153 | if (router->tq_avg > max_tq) { |
154 | if (curr_gw) | |
155 | gw_node_free_ref(curr_gw); | |
156 | curr_gw = gw_node; | |
157 | atomic_inc(&curr_gw->refcount); | |
158 | } | |
c6c8fea2 SE |
159 | break; |
160 | } | |
161 | ||
e1a5382f LL |
162 | if (router->tq_avg > max_tq) |
163 | max_tq = router->tq_avg; | |
c6c8fea2 SE |
164 | |
165 | if (tmp_gw_factor > max_gw_factor) | |
166 | max_gw_factor = tmp_gw_factor; | |
e1a5382f | 167 | |
2265c141 AQ |
168 | gw_node_free_ref(gw_node); |
169 | ||
170 | next: | |
e1a5382f | 171 | neigh_node_free_ref(router); |
c6c8fea2 | 172 | } |
2265c141 | 173 | rcu_read_unlock(); |
c6c8fea2 | 174 | |
2265c141 AQ |
175 | return curr_gw; |
176 | } | |
c6c8fea2 | 177 | |
2265c141 AQ |
178 | void gw_election(struct bat_priv *bat_priv) |
179 | { | |
180 | struct gw_node *curr_gw = NULL, *next_gw = NULL; | |
181 | struct neigh_node *router = NULL; | |
19595e05 | 182 | char gw_addr[18] = { '\0' }; |
2265c141 AQ |
183 | |
184 | /** | |
185 | * The batman daemon checks here if we already passed a full originator | |
186 | * cycle in order to make sure we don't choose the first gateway we | |
187 | * hear about. This check is based on the daemon's uptime which we | |
188 | * don't have. | |
189 | **/ | |
190 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | |
191 | goto out; | |
192 | ||
193 | if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) | |
194 | goto out; | |
195 | ||
196 | curr_gw = gw_get_selected_gw_node(bat_priv); | |
197 | ||
198 | next_gw = gw_get_best_gw_node(bat_priv); | |
199 | ||
200 | if (curr_gw == next_gw) | |
201 | goto out; | |
202 | ||
203 | if (next_gw) { | |
19595e05 AQ |
204 | sprintf(gw_addr, "%pM", next_gw->orig_node->orig); |
205 | ||
2265c141 AQ |
206 | router = orig_node_get_router(next_gw->orig_node); |
207 | if (!router) { | |
208 | gw_deselect(bat_priv); | |
209 | goto out; | |
210 | } | |
c6c8fea2 SE |
211 | } |
212 | ||
2265c141 AQ |
213 | if ((curr_gw) && (!next_gw)) { |
214 | bat_dbg(DBG_BATMAN, bat_priv, | |
215 | "Removing selected gateway - no gateway in range\n"); | |
19595e05 | 216 | throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); |
2265c141 AQ |
217 | } else if ((!curr_gw) && (next_gw)) { |
218 | bat_dbg(DBG_BATMAN, bat_priv, | |
219 | "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", | |
220 | next_gw->orig_node->orig, | |
221 | next_gw->orig_node->gw_flags, | |
222 | router->tq_avg); | |
19595e05 | 223 | throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); |
2265c141 AQ |
224 | } else { |
225 | bat_dbg(DBG_BATMAN, bat_priv, | |
226 | "Changing route to gateway %pM " | |
227 | "(gw_flags: %i, tq: %i)\n", | |
228 | next_gw->orig_node->orig, | |
229 | next_gw->orig_node->gw_flags, | |
230 | router->tq_avg); | |
19595e05 | 231 | throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); |
2265c141 AQ |
232 | } |
233 | ||
234 | gw_select(bat_priv, next_gw); | |
235 | ||
c4aac1ab ML |
236 | out: |
237 | if (curr_gw) | |
238 | gw_node_free_ref(curr_gw); | |
2265c141 AQ |
239 | if (next_gw) |
240 | gw_node_free_ref(next_gw); | |
241 | if (router) | |
242 | neigh_node_free_ref(router); | |
c6c8fea2 SE |
243 | } |
244 | ||
245 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | |
246 | { | |
57f0c07c | 247 | struct orig_node *curr_gw_orig; |
e1a5382f | 248 | struct neigh_node *router_gw = NULL, *router_orig = NULL; |
c6c8fea2 SE |
249 | uint8_t gw_tq_avg, orig_tq_avg; |
250 | ||
c4aac1ab | 251 | curr_gw_orig = gw_get_selected_orig(bat_priv); |
57f0c07c LL |
252 | if (!curr_gw_orig) |
253 | goto deselect; | |
c6c8fea2 | 254 | |
e1a5382f LL |
255 | router_gw = orig_node_get_router(curr_gw_orig); |
256 | if (!router_gw) | |
257 | goto deselect; | |
c6c8fea2 SE |
258 | |
259 | /* this node already is the gateway */ | |
57f0c07c | 260 | if (curr_gw_orig == orig_node) |
e1a5382f | 261 | goto out; |
c6c8fea2 | 262 | |
e1a5382f LL |
263 | router_orig = orig_node_get_router(orig_node); |
264 | if (!router_orig) | |
265 | goto out; | |
5d02b3cd | 266 | |
e1a5382f LL |
267 | gw_tq_avg = router_gw->tq_avg; |
268 | orig_tq_avg = router_orig->tq_avg; | |
c6c8fea2 SE |
269 | |
270 | /* the TQ value has to be better */ | |
271 | if (orig_tq_avg < gw_tq_avg) | |
5d02b3cd | 272 | goto out; |
c6c8fea2 SE |
273 | |
274 | /** | |
275 | * if the routing class is greater than 3 the value tells us how much | |
276 | * greater the TQ value of the new gateway must be | |
277 | **/ | |
278 | if ((atomic_read(&bat_priv->gw_sel_class) > 3) && | |
279 | (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) | |
5d02b3cd | 280 | goto out; |
c6c8fea2 SE |
281 | |
282 | bat_dbg(DBG_BATMAN, bat_priv, | |
283 | "Restarting gateway selection: better gateway found (tq curr: " | |
284 | "%i, tq new: %i)\n", | |
285 | gw_tq_avg, orig_tq_avg); | |
286 | ||
287 | deselect: | |
288 | gw_deselect(bat_priv); | |
5d02b3cd | 289 | out: |
57f0c07c LL |
290 | if (curr_gw_orig) |
291 | orig_node_free_ref(curr_gw_orig); | |
e1a5382f LL |
292 | if (router_gw) |
293 | neigh_node_free_ref(router_gw); | |
294 | if (router_orig) | |
295 | neigh_node_free_ref(router_orig); | |
57f0c07c | 296 | |
5d02b3cd | 297 | return; |
c6c8fea2 SE |
298 | } |
299 | ||
300 | static void gw_node_add(struct bat_priv *bat_priv, | |
301 | struct orig_node *orig_node, uint8_t new_gwflags) | |
302 | { | |
303 | struct gw_node *gw_node; | |
304 | int down, up; | |
305 | ||
704509b8 | 306 | gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); |
c6c8fea2 SE |
307 | if (!gw_node) |
308 | return; | |
309 | ||
c6c8fea2 SE |
310 | INIT_HLIST_NODE(&gw_node->list); |
311 | gw_node->orig_node = orig_node; | |
25b6d3c1 | 312 | atomic_set(&gw_node->refcount, 1); |
c6c8fea2 SE |
313 | |
314 | spin_lock_bh(&bat_priv->gw_list_lock); | |
315 | hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); | |
316 | spin_unlock_bh(&bat_priv->gw_list_lock); | |
317 | ||
318 | gw_bandwidth_to_kbit(new_gwflags, &down, &up); | |
319 | bat_dbg(DBG_BATMAN, bat_priv, | |
320 | "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n", | |
321 | orig_node->orig, new_gwflags, | |
322 | (down > 2048 ? down / 1024 : down), | |
323 | (down > 2048 ? "MBit" : "KBit"), | |
324 | (up > 2048 ? up / 1024 : up), | |
325 | (up > 2048 ? "MBit" : "KBit")); | |
326 | } | |
327 | ||
328 | void gw_node_update(struct bat_priv *bat_priv, | |
329 | struct orig_node *orig_node, uint8_t new_gwflags) | |
330 | { | |
331 | struct hlist_node *node; | |
c4aac1ab ML |
332 | struct gw_node *gw_node, *curr_gw; |
333 | ||
71e4aa9c AQ |
334 | /** |
335 | * Note: We don't need a NULL check here, since curr_gw never gets | |
336 | * dereferenced. If curr_gw is NULL we also should not exit as we may | |
337 | * have this gateway in our list (duplication check!) even though we | |
338 | * have no currently selected gateway. | |
339 | */ | |
c4aac1ab | 340 | curr_gw = gw_get_selected_gw_node(bat_priv); |
c6c8fea2 SE |
341 | |
342 | rcu_read_lock(); | |
343 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | |
344 | if (gw_node->orig_node != orig_node) | |
345 | continue; | |
346 | ||
347 | bat_dbg(DBG_BATMAN, bat_priv, | |
348 | "Gateway class of originator %pM changed from " | |
349 | "%i to %i\n", | |
350 | orig_node->orig, gw_node->orig_node->gw_flags, | |
351 | new_gwflags); | |
352 | ||
353 | gw_node->deleted = 0; | |
354 | ||
ecbd5321 | 355 | if (new_gwflags == NO_FLAGS) { |
c6c8fea2 SE |
356 | gw_node->deleted = jiffies; |
357 | bat_dbg(DBG_BATMAN, bat_priv, | |
358 | "Gateway %pM removed from gateway list\n", | |
359 | orig_node->orig); | |
360 | ||
c4aac1ab ML |
361 | if (gw_node == curr_gw) |
362 | goto deselect; | |
c6c8fea2 SE |
363 | } |
364 | ||
c4aac1ab | 365 | goto unlock; |
c6c8fea2 | 366 | } |
c6c8fea2 | 367 | |
ecbd5321 | 368 | if (new_gwflags == NO_FLAGS) |
c4aac1ab | 369 | goto unlock; |
c6c8fea2 SE |
370 | |
371 | gw_node_add(bat_priv, orig_node, new_gwflags); | |
c4aac1ab ML |
372 | goto unlock; |
373 | ||
374 | deselect: | |
375 | gw_deselect(bat_priv); | |
376 | unlock: | |
377 | rcu_read_unlock(); | |
71e4aa9c | 378 | |
c4aac1ab ML |
379 | if (curr_gw) |
380 | gw_node_free_ref(curr_gw); | |
c6c8fea2 SE |
381 | } |
382 | ||
383 | void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) | |
384 | { | |
6b9aadfa | 385 | gw_node_update(bat_priv, orig_node, 0); |
c6c8fea2 SE |
386 | } |
387 | ||
388 | void gw_node_purge(struct bat_priv *bat_priv) | |
389 | { | |
c4aac1ab | 390 | struct gw_node *gw_node, *curr_gw; |
c6c8fea2 SE |
391 | struct hlist_node *node, *node_tmp; |
392 | unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; | |
b4e17054 | 393 | int do_deselect = 0; |
c4aac1ab ML |
394 | |
395 | curr_gw = gw_get_selected_gw_node(bat_priv); | |
c6c8fea2 SE |
396 | |
397 | spin_lock_bh(&bat_priv->gw_list_lock); | |
398 | ||
399 | hlist_for_each_entry_safe(gw_node, node, node_tmp, | |
400 | &bat_priv->gw_list, list) { | |
401 | if (((!gw_node->deleted) || | |
402 | (time_before(jiffies, gw_node->deleted + timeout))) && | |
403 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) | |
404 | continue; | |
405 | ||
c4aac1ab ML |
406 | if (curr_gw == gw_node) |
407 | do_deselect = 1; | |
c6c8fea2 SE |
408 | |
409 | hlist_del_rcu(&gw_node->list); | |
25b6d3c1 | 410 | gw_node_free_ref(gw_node); |
c6c8fea2 SE |
411 | } |
412 | ||
c6c8fea2 | 413 | spin_unlock_bh(&bat_priv->gw_list_lock); |
c4aac1ab ML |
414 | |
415 | /* gw_deselect() needs to acquire the gw_list_lock */ | |
416 | if (do_deselect) | |
417 | gw_deselect(bat_priv); | |
418 | ||
419 | if (curr_gw) | |
420 | gw_node_free_ref(curr_gw); | |
c6c8fea2 SE |
421 | } |
422 | ||
e1a5382f LL |
423 | /** |
424 | * fails if orig_node has no router | |
425 | */ | |
747e4221 SE |
426 | static int _write_buffer_text(struct bat_priv *bat_priv, struct seq_file *seq, |
427 | const struct gw_node *gw_node) | |
c6c8fea2 | 428 | { |
5d02b3cd | 429 | struct gw_node *curr_gw; |
e1a5382f LL |
430 | struct neigh_node *router; |
431 | int down, up, ret = -1; | |
c6c8fea2 SE |
432 | |
433 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); | |
434 | ||
e1a5382f LL |
435 | router = orig_node_get_router(gw_node->orig_node); |
436 | if (!router) | |
437 | goto out; | |
5d02b3cd | 438 | |
c4aac1ab | 439 | curr_gw = gw_get_selected_gw_node(bat_priv); |
5d02b3cd | 440 | |
5d02b3cd | 441 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", |
c4aac1ab ML |
442 | (curr_gw == gw_node ? "=>" : " "), |
443 | gw_node->orig_node->orig, | |
444 | router->tq_avg, router->addr, | |
445 | router->if_incoming->net_dev->name, | |
446 | gw_node->orig_node->gw_flags, | |
447 | (down > 2048 ? down / 1024 : down), | |
448 | (down > 2048 ? "MBit" : "KBit"), | |
449 | (up > 2048 ? up / 1024 : up), | |
450 | (up > 2048 ? "MBit" : "KBit")); | |
5d02b3cd | 451 | |
e1a5382f | 452 | neigh_node_free_ref(router); |
c4aac1ab ML |
453 | if (curr_gw) |
454 | gw_node_free_ref(curr_gw); | |
e1a5382f | 455 | out: |
5d02b3cd | 456 | return ret; |
c6c8fea2 SE |
457 | } |
458 | ||
459 | int gw_client_seq_print_text(struct seq_file *seq, void *offset) | |
460 | { | |
461 | struct net_device *net_dev = (struct net_device *)seq->private; | |
462 | struct bat_priv *bat_priv = netdev_priv(net_dev); | |
32ae9b22 | 463 | struct hard_iface *primary_if; |
c6c8fea2 SE |
464 | struct gw_node *gw_node; |
465 | struct hlist_node *node; | |
32ae9b22 | 466 | int gw_count = 0, ret = 0; |
c6c8fea2 | 467 | |
32ae9b22 ML |
468 | primary_if = primary_if_get_selected(bat_priv); |
469 | if (!primary_if) { | |
470 | ret = seq_printf(seq, "BATMAN mesh %s disabled - please " | |
471 | "specify interfaces to enable it\n", | |
472 | net_dev->name); | |
473 | goto out; | |
c6c8fea2 SE |
474 | } |
475 | ||
32ae9b22 ML |
476 | if (primary_if->if_status != IF_ACTIVE) { |
477 | ret = seq_printf(seq, "BATMAN mesh %s disabled - " | |
478 | "primary interface not active\n", | |
479 | net_dev->name); | |
480 | goto out; | |
c6c8fea2 SE |
481 | } |
482 | ||
483 | seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... " | |
484 | "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", | |
485 | "Gateway", "#", TQ_MAX_VALUE, "Nexthop", | |
486 | "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR, | |
32ae9b22 ML |
487 | primary_if->net_dev->name, |
488 | primary_if->net_dev->dev_addr, net_dev->name); | |
c6c8fea2 SE |
489 | |
490 | rcu_read_lock(); | |
491 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | |
492 | if (gw_node->deleted) | |
493 | continue; | |
494 | ||
e1a5382f LL |
495 | /* fails if orig_node has no router */ |
496 | if (_write_buffer_text(bat_priv, seq, gw_node) < 0) | |
c6c8fea2 SE |
497 | continue; |
498 | ||
c6c8fea2 SE |
499 | gw_count++; |
500 | } | |
501 | rcu_read_unlock(); | |
502 | ||
503 | if (gw_count == 0) | |
504 | seq_printf(seq, "No gateways in range ...\n"); | |
505 | ||
32ae9b22 ML |
506 | out: |
507 | if (primary_if) | |
508 | hardif_free_ref(primary_if); | |
509 | return ret; | |
c6c8fea2 SE |
510 | } |
511 | ||
512 | int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |
513 | { | |
514 | struct ethhdr *ethhdr; | |
515 | struct iphdr *iphdr; | |
516 | struct ipv6hdr *ipv6hdr; | |
517 | struct udphdr *udphdr; | |
c4aac1ab | 518 | struct gw_node *curr_gw; |
c6c8fea2 SE |
519 | unsigned int header_len = 0; |
520 | ||
521 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) | |
522 | return 0; | |
523 | ||
524 | /* check for ethernet header */ | |
525 | if (!pskb_may_pull(skb, header_len + ETH_HLEN)) | |
526 | return 0; | |
527 | ethhdr = (struct ethhdr *)skb->data; | |
528 | header_len += ETH_HLEN; | |
529 | ||
530 | /* check for initial vlan header */ | |
531 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { | |
532 | if (!pskb_may_pull(skb, header_len + VLAN_HLEN)) | |
533 | return 0; | |
534 | ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); | |
535 | header_len += VLAN_HLEN; | |
536 | } | |
537 | ||
538 | /* check for ip header */ | |
539 | switch (ntohs(ethhdr->h_proto)) { | |
540 | case ETH_P_IP: | |
704509b8 | 541 | if (!pskb_may_pull(skb, header_len + sizeof(*iphdr))) |
c6c8fea2 SE |
542 | return 0; |
543 | iphdr = (struct iphdr *)(skb->data + header_len); | |
544 | header_len += iphdr->ihl * 4; | |
545 | ||
546 | /* check for udp header */ | |
547 | if (iphdr->protocol != IPPROTO_UDP) | |
548 | return 0; | |
549 | ||
550 | break; | |
551 | case ETH_P_IPV6: | |
704509b8 | 552 | if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr))) |
c6c8fea2 SE |
553 | return 0; |
554 | ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); | |
704509b8 | 555 | header_len += sizeof(*ipv6hdr); |
c6c8fea2 SE |
556 | |
557 | /* check for udp header */ | |
558 | if (ipv6hdr->nexthdr != IPPROTO_UDP) | |
559 | return 0; | |
560 | ||
561 | break; | |
562 | default: | |
563 | return 0; | |
564 | } | |
565 | ||
704509b8 | 566 | if (!pskb_may_pull(skb, header_len + sizeof(*udphdr))) |
c6c8fea2 SE |
567 | return 0; |
568 | udphdr = (struct udphdr *)(skb->data + header_len); | |
704509b8 | 569 | header_len += sizeof(*udphdr); |
c6c8fea2 SE |
570 | |
571 | /* check for bootp port */ | |
572 | if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && | |
573 | (ntohs(udphdr->dest) != 67)) | |
574 | return 0; | |
575 | ||
576 | if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && | |
577 | (ntohs(udphdr->dest) != 547)) | |
578 | return 0; | |
579 | ||
580 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) | |
581 | return -1; | |
582 | ||
c4aac1ab ML |
583 | curr_gw = gw_get_selected_gw_node(bat_priv); |
584 | if (!curr_gw) | |
c6c8fea2 SE |
585 | return 0; |
586 | ||
c4aac1ab ML |
587 | if (curr_gw) |
588 | gw_node_free_ref(curr_gw); | |
c6c8fea2 SE |
589 | return 1; |
590 | } |