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