xref: /openbmc/linux/net/batman-adv/gateway_client.c (revision f7f8ed5695ef441495cfab5287951927d9d5f12e)
10b873931SAntonio Quartulli /* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors:
2c6c8fea2SSven Eckelmann  *
3c6c8fea2SSven Eckelmann  * Marek Lindner
4c6c8fea2SSven Eckelmann  *
5c6c8fea2SSven Eckelmann  * This program is free software; you can redistribute it and/or
6c6c8fea2SSven Eckelmann  * modify it under the terms of version 2 of the GNU General Public
7c6c8fea2SSven Eckelmann  * License as published by the Free Software Foundation.
8c6c8fea2SSven Eckelmann  *
9c6c8fea2SSven Eckelmann  * This program is distributed in the hope that it will be useful, but
10c6c8fea2SSven Eckelmann  * WITHOUT ANY WARRANTY; without even the implied warranty of
11c6c8fea2SSven Eckelmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12c6c8fea2SSven Eckelmann  * General Public License for more details.
13c6c8fea2SSven Eckelmann  *
14c6c8fea2SSven Eckelmann  * You should have received a copy of the GNU General Public License
15c6c8fea2SSven Eckelmann  * along with this program; if not, write to the Free Software
16c6c8fea2SSven Eckelmann  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17c6c8fea2SSven Eckelmann  * 02110-1301, USA
18c6c8fea2SSven Eckelmann  */
19c6c8fea2SSven Eckelmann 
20c6c8fea2SSven Eckelmann #include "main.h"
21b706b13bSSven Eckelmann #include "sysfs.h"
22c6c8fea2SSven Eckelmann #include "gateway_client.h"
23c6c8fea2SSven Eckelmann #include "gateway_common.h"
24c6c8fea2SSven Eckelmann #include "hard-interface.h"
2557f0c07cSLinus Lüssing #include "originator.h"
26be7af5cfSMarek Lindner #include "translation-table.h"
2743676ab5SAntonio Quartulli #include "routing.h"
28c6c8fea2SSven Eckelmann #include <linux/ip.h>
29c6c8fea2SSven Eckelmann #include <linux/ipv6.h>
30c6c8fea2SSven Eckelmann #include <linux/udp.h>
31c6c8fea2SSven Eckelmann #include <linux/if_vlan.h>
32c6c8fea2SSven Eckelmann 
3343676ab5SAntonio Quartulli /* This is the offset of the options field in a dhcp packet starting at
349cfc7bd6SSven Eckelmann  * the beginning of the dhcp header
359cfc7bd6SSven Eckelmann  */
36347c80f0SSven Eckelmann #define BATADV_DHCP_OPTIONS_OFFSET 240
37347c80f0SSven Eckelmann #define BATADV_DHCP_REQUEST 3
3843676ab5SAntonio Quartulli 
3956303d34SSven Eckelmann static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node)
4025b6d3c1SMarek Lindner {
4125b6d3c1SMarek Lindner 	if (atomic_dec_and_test(&gw_node->refcount))
42eb340b2fSPaul E. McKenney 		kfree_rcu(gw_node, rcu);
43c6c8fea2SSven Eckelmann }
44c6c8fea2SSven Eckelmann 
4556303d34SSven Eckelmann static struct batadv_gw_node *
4656303d34SSven Eckelmann batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
47c6c8fea2SSven Eckelmann {
4856303d34SSven Eckelmann 	struct batadv_gw_node *gw_node;
49c6c8fea2SSven Eckelmann 
505d02b3cdSLinus Lüssing 	rcu_read_lock();
51807736f6SSven Eckelmann 	gw_node = rcu_dereference(bat_priv->gw.curr_gw);
52c4aac1abSMarek Lindner 	if (!gw_node)
535d02b3cdSLinus Lüssing 		goto out;
54c6c8fea2SSven Eckelmann 
55c4aac1abSMarek Lindner 	if (!atomic_inc_not_zero(&gw_node->refcount))
56c4aac1abSMarek Lindner 		gw_node = NULL;
57c4aac1abSMarek Lindner 
58c4aac1abSMarek Lindner out:
59c4aac1abSMarek Lindner 	rcu_read_unlock();
60c4aac1abSMarek Lindner 	return gw_node;
61c4aac1abSMarek Lindner }
62c4aac1abSMarek Lindner 
6356303d34SSven Eckelmann struct batadv_orig_node *
6456303d34SSven Eckelmann batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
65c4aac1abSMarek Lindner {
6656303d34SSven Eckelmann 	struct batadv_gw_node *gw_node;
6756303d34SSven Eckelmann 	struct batadv_orig_node *orig_node = NULL;
68c4aac1abSMarek Lindner 
691409a834SSven Eckelmann 	gw_node = batadv_gw_get_selected_gw_node(bat_priv);
70c4aac1abSMarek Lindner 	if (!gw_node)
717b36e8eeSMarek Lindner 		goto out;
725d02b3cdSLinus Lüssing 
73c4aac1abSMarek Lindner 	rcu_read_lock();
74c4aac1abSMarek Lindner 	orig_node = gw_node->orig_node;
75c4aac1abSMarek Lindner 	if (!orig_node)
76c4aac1abSMarek Lindner 		goto unlock;
77c4aac1abSMarek Lindner 
787b36e8eeSMarek Lindner 	if (!atomic_inc_not_zero(&orig_node->refcount))
797b36e8eeSMarek Lindner 		orig_node = NULL;
8043c70ad5SLinus Lüssing 
81c4aac1abSMarek Lindner unlock:
825d02b3cdSLinus Lüssing 	rcu_read_unlock();
83c4aac1abSMarek Lindner out:
84c6c8fea2SSven Eckelmann 	if (gw_node)
851409a834SSven Eckelmann 		batadv_gw_node_free_ref(gw_node);
86c4aac1abSMarek Lindner 	return orig_node;
87c6c8fea2SSven Eckelmann }
88c6c8fea2SSven Eckelmann 
8956303d34SSven Eckelmann static void batadv_gw_select(struct batadv_priv *bat_priv,
9056303d34SSven Eckelmann 			     struct batadv_gw_node *new_gw_node)
91c6c8fea2SSven Eckelmann {
9256303d34SSven Eckelmann 	struct batadv_gw_node *curr_gw_node;
93c6c8fea2SSven Eckelmann 
94807736f6SSven Eckelmann 	spin_lock_bh(&bat_priv->gw.list_lock);
95c4aac1abSMarek Lindner 
9625b6d3c1SMarek Lindner 	if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
9725b6d3c1SMarek Lindner 		new_gw_node = NULL;
98c6c8fea2SSven Eckelmann 
99807736f6SSven Eckelmann 	curr_gw_node = rcu_dereference_protected(bat_priv->gw.curr_gw, 1);
100807736f6SSven Eckelmann 	rcu_assign_pointer(bat_priv->gw.curr_gw, new_gw_node);
10125b6d3c1SMarek Lindner 
10225b6d3c1SMarek Lindner 	if (curr_gw_node)
1031409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw_node);
104c4aac1abSMarek Lindner 
105807736f6SSven Eckelmann 	spin_unlock_bh(&bat_priv->gw.list_lock);
106c4aac1abSMarek Lindner }
107c4aac1abSMarek Lindner 
10856303d34SSven Eckelmann void batadv_gw_deselect(struct batadv_priv *bat_priv)
109c4aac1abSMarek Lindner {
110807736f6SSven Eckelmann 	atomic_set(&bat_priv->gw.reselect, 1);
111c6c8fea2SSven Eckelmann }
112c6c8fea2SSven Eckelmann 
11356303d34SSven Eckelmann static struct batadv_gw_node *
11456303d34SSven Eckelmann batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
115c6c8fea2SSven Eckelmann {
11656303d34SSven Eckelmann 	struct batadv_neigh_node *router;
11756303d34SSven Eckelmann 	struct batadv_gw_node *gw_node, *curr_gw = NULL;
118c6c8fea2SSven Eckelmann 	uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
119c67893d1SSven Eckelmann 	uint32_t gw_divisor;
1202265c141SAntonio Quartulli 	uint8_t max_tq = 0;
121c67893d1SSven Eckelmann 	uint8_t tq_avg;
12256303d34SSven Eckelmann 	struct batadv_orig_node *orig_node;
123c6c8fea2SSven Eckelmann 
124c67893d1SSven Eckelmann 	gw_divisor = BATADV_TQ_LOCAL_WINDOW_SIZE * BATADV_TQ_LOCAL_WINDOW_SIZE;
125c67893d1SSven Eckelmann 	gw_divisor *= 64;
126c67893d1SSven Eckelmann 
127c6c8fea2SSven Eckelmann 	rcu_read_lock();
128b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
129e1a5382fSLinus Lüssing 		if (gw_node->deleted)
130c6c8fea2SSven Eckelmann 			continue;
131c6c8fea2SSven Eckelmann 
13284d5e5e0SSven Eckelmann 		orig_node = gw_node->orig_node;
1337d211efcSSven Eckelmann 		router = batadv_orig_node_get_router(orig_node);
134e1a5382fSLinus Lüssing 		if (!router)
135c6c8fea2SSven Eckelmann 			continue;
136c6c8fea2SSven Eckelmann 
1372265c141SAntonio Quartulli 		if (!atomic_inc_not_zero(&gw_node->refcount))
1382265c141SAntonio Quartulli 			goto next;
1392265c141SAntonio Quartulli 
140c67893d1SSven Eckelmann 		tq_avg = router->tq_avg;
141c67893d1SSven Eckelmann 
142c6c8fea2SSven Eckelmann 		switch (atomic_read(&bat_priv->gw_sel_class)) {
143c6c8fea2SSven Eckelmann 		case 1: /* fast connection */
144414254e3SMarek Lindner 			tmp_gw_factor = tq_avg * tq_avg;
145414254e3SMarek Lindner 			tmp_gw_factor *= gw_node->bandwidth_down;
146414254e3SMarek Lindner 			tmp_gw_factor *= 100 * 100;
147c67893d1SSven Eckelmann 			tmp_gw_factor /= gw_divisor;
148c6c8fea2SSven Eckelmann 
149c6c8fea2SSven Eckelmann 			if ((tmp_gw_factor > max_gw_factor) ||
150c6c8fea2SSven Eckelmann 			    ((tmp_gw_factor == max_gw_factor) &&
151c67893d1SSven Eckelmann 			     (tq_avg > max_tq))) {
1522265c141SAntonio Quartulli 				if (curr_gw)
1531409a834SSven Eckelmann 					batadv_gw_node_free_ref(curr_gw);
1542265c141SAntonio Quartulli 				curr_gw = gw_node;
1552265c141SAntonio Quartulli 				atomic_inc(&curr_gw->refcount);
1562265c141SAntonio Quartulli 			}
157c6c8fea2SSven Eckelmann 			break;
158c6c8fea2SSven Eckelmann 
1599cfc7bd6SSven Eckelmann 		default: /* 2:  stable connection (use best statistic)
160c6c8fea2SSven Eckelmann 			  * 3:  fast-switch (use best statistic but change as
161c6c8fea2SSven Eckelmann 			  *     soon as a better gateway appears)
162c6c8fea2SSven Eckelmann 			  * XX: late-switch (use best statistic but change as
163c6c8fea2SSven Eckelmann 			  *     soon as a better gateway appears which has
164c6c8fea2SSven Eckelmann 			  *     $routing_class more tq points)
1659cfc7bd6SSven Eckelmann 			  */
166c67893d1SSven Eckelmann 			if (tq_avg > max_tq) {
1672265c141SAntonio Quartulli 				if (curr_gw)
1681409a834SSven Eckelmann 					batadv_gw_node_free_ref(curr_gw);
1692265c141SAntonio Quartulli 				curr_gw = gw_node;
1702265c141SAntonio Quartulli 				atomic_inc(&curr_gw->refcount);
1712265c141SAntonio Quartulli 			}
172c6c8fea2SSven Eckelmann 			break;
173c6c8fea2SSven Eckelmann 		}
174c6c8fea2SSven Eckelmann 
175c67893d1SSven Eckelmann 		if (tq_avg > max_tq)
176c67893d1SSven Eckelmann 			max_tq = tq_avg;
177c6c8fea2SSven Eckelmann 
178c6c8fea2SSven Eckelmann 		if (tmp_gw_factor > max_gw_factor)
179c6c8fea2SSven Eckelmann 			max_gw_factor = tmp_gw_factor;
180e1a5382fSLinus Lüssing 
1811409a834SSven Eckelmann 		batadv_gw_node_free_ref(gw_node);
1822265c141SAntonio Quartulli 
1832265c141SAntonio Quartulli next:
1847d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(router);
185c6c8fea2SSven Eckelmann 	}
1862265c141SAntonio Quartulli 	rcu_read_unlock();
187c6c8fea2SSven Eckelmann 
1882265c141SAntonio Quartulli 	return curr_gw;
1892265c141SAntonio Quartulli }
190e1a5382fSLinus Lüssing 
191c6eaa3f0SAntonio Quartulli /**
192c6eaa3f0SAntonio Quartulli  * batadv_gw_check_client_stop - check if client mode has been switched off
193c6eaa3f0SAntonio Quartulli  * @bat_priv: the bat priv with all the soft interface information
194c6eaa3f0SAntonio Quartulli  *
195c6eaa3f0SAntonio Quartulli  * This function assumes the caller has checked that the gw state *is actually
196c6eaa3f0SAntonio Quartulli  * changing*. This function is not supposed to be called when there is no state
197c6eaa3f0SAntonio Quartulli  * change.
198c6eaa3f0SAntonio Quartulli  */
199c6eaa3f0SAntonio Quartulli void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
200c6eaa3f0SAntonio Quartulli {
201c6eaa3f0SAntonio Quartulli 	struct batadv_gw_node *curr_gw;
202c6eaa3f0SAntonio Quartulli 
203c6eaa3f0SAntonio Quartulli 	if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
204c6eaa3f0SAntonio Quartulli 		return;
205c6eaa3f0SAntonio Quartulli 
206c6eaa3f0SAntonio Quartulli 	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
207c6eaa3f0SAntonio Quartulli 	if (!curr_gw)
208c6eaa3f0SAntonio Quartulli 		return;
209c6eaa3f0SAntonio Quartulli 
210c6eaa3f0SAntonio Quartulli 	/* if batman-adv is switching the gw client mode off and a gateway was
211c6eaa3f0SAntonio Quartulli 	 * already selected, send a DEL uevent
212c6eaa3f0SAntonio Quartulli 	 */
213c6eaa3f0SAntonio Quartulli 	batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
214c6eaa3f0SAntonio Quartulli 
215c6eaa3f0SAntonio Quartulli 	batadv_gw_node_free_ref(curr_gw);
216c6eaa3f0SAntonio Quartulli }
217c6eaa3f0SAntonio Quartulli 
21856303d34SSven Eckelmann void batadv_gw_election(struct batadv_priv *bat_priv)
2192265c141SAntonio Quartulli {
22056303d34SSven Eckelmann 	struct batadv_gw_node *curr_gw = NULL, *next_gw = NULL;
22156303d34SSven Eckelmann 	struct batadv_neigh_node *router = NULL;
22219595e05SAntonio Quartulli 	char gw_addr[18] = { '\0' };
2232265c141SAntonio Quartulli 
2249cfc7bd6SSven Eckelmann 	/* The batman daemon checks here if we already passed a full originator
2252265c141SAntonio Quartulli 	 * cycle in order to make sure we don't choose the first gateway we
2262265c141SAntonio Quartulli 	 * hear about. This check is based on the daemon's uptime which we
2272265c141SAntonio Quartulli 	 * don't have.
2289cfc7bd6SSven Eckelmann 	 */
229cd646ab1SSven Eckelmann 	if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
2302265c141SAntonio Quartulli 		goto out;
2312265c141SAntonio Quartulli 
2321409a834SSven Eckelmann 	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
2332265c141SAntonio Quartulli 
234807736f6SSven Eckelmann 	if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw)
235caa0bf64SMarek Lindner 		goto out;
236caa0bf64SMarek Lindner 
2371409a834SSven Eckelmann 	next_gw = batadv_gw_get_best_gw_node(bat_priv);
2382265c141SAntonio Quartulli 
2392265c141SAntonio Quartulli 	if (curr_gw == next_gw)
2402265c141SAntonio Quartulli 		goto out;
2412265c141SAntonio Quartulli 
2422265c141SAntonio Quartulli 	if (next_gw) {
24319595e05SAntonio Quartulli 		sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
24419595e05SAntonio Quartulli 
2457d211efcSSven Eckelmann 		router = batadv_orig_node_get_router(next_gw->orig_node);
2462265c141SAntonio Quartulli 		if (!router) {
2477cf06bc6SSven Eckelmann 			batadv_gw_deselect(bat_priv);
2482265c141SAntonio Quartulli 			goto out;
2492265c141SAntonio Quartulli 		}
2502265c141SAntonio Quartulli 	}
2512265c141SAntonio Quartulli 
2522265c141SAntonio Quartulli 	if ((curr_gw) && (!next_gw)) {
25339c75a51SSven Eckelmann 		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
2542265c141SAntonio Quartulli 			   "Removing selected gateway - no gateway in range\n");
25539c75a51SSven Eckelmann 		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL,
25639c75a51SSven Eckelmann 				    NULL);
2572265c141SAntonio Quartulli 	} else if ((!curr_gw) && (next_gw)) {
25839c75a51SSven Eckelmann 		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
259414254e3SMarek Lindner 			   "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
2601eda58bfSSven Eckelmann 			   next_gw->orig_node->orig,
261414254e3SMarek Lindner 			   next_gw->bandwidth_down / 10,
262414254e3SMarek Lindner 			   next_gw->bandwidth_down % 10,
263414254e3SMarek Lindner 			   next_gw->bandwidth_up / 10,
264414254e3SMarek Lindner 			   next_gw->bandwidth_up % 10, router->tq_avg);
26539c75a51SSven Eckelmann 		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
26639c75a51SSven Eckelmann 				    gw_addr);
2672265c141SAntonio Quartulli 	} else {
26839c75a51SSven Eckelmann 		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
269414254e3SMarek Lindner 			   "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
2701eda58bfSSven Eckelmann 			   next_gw->orig_node->orig,
271414254e3SMarek Lindner 			   next_gw->bandwidth_down / 10,
272414254e3SMarek Lindner 			   next_gw->bandwidth_down % 10,
273414254e3SMarek Lindner 			   next_gw->bandwidth_up / 10,
274414254e3SMarek Lindner 			   next_gw->bandwidth_up % 10, router->tq_avg);
27539c75a51SSven Eckelmann 		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
27639c75a51SSven Eckelmann 				    gw_addr);
277c6c8fea2SSven Eckelmann 	}
278c6c8fea2SSven Eckelmann 
2791409a834SSven Eckelmann 	batadv_gw_select(bat_priv, next_gw);
2802265c141SAntonio Quartulli 
281c4aac1abSMarek Lindner out:
282c4aac1abSMarek Lindner 	if (curr_gw)
2831409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw);
2842265c141SAntonio Quartulli 	if (next_gw)
2851409a834SSven Eckelmann 		batadv_gw_node_free_ref(next_gw);
2862265c141SAntonio Quartulli 	if (router)
2877d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(router);
288c6c8fea2SSven Eckelmann }
289c6c8fea2SSven Eckelmann 
29056303d34SSven Eckelmann void batadv_gw_check_election(struct batadv_priv *bat_priv,
29156303d34SSven Eckelmann 			      struct batadv_orig_node *orig_node)
292c6c8fea2SSven Eckelmann {
29356303d34SSven Eckelmann 	struct batadv_orig_node *curr_gw_orig;
29456303d34SSven Eckelmann 	struct batadv_neigh_node *router_gw = NULL, *router_orig = NULL;
295c6c8fea2SSven Eckelmann 	uint8_t gw_tq_avg, orig_tq_avg;
296c6c8fea2SSven Eckelmann 
2977cf06bc6SSven Eckelmann 	curr_gw_orig = batadv_gw_get_selected_orig(bat_priv);
29857f0c07cSLinus Lüssing 	if (!curr_gw_orig)
29957f0c07cSLinus Lüssing 		goto deselect;
300c6c8fea2SSven Eckelmann 
3017d211efcSSven Eckelmann 	router_gw = batadv_orig_node_get_router(curr_gw_orig);
302e1a5382fSLinus Lüssing 	if (!router_gw)
303e1a5382fSLinus Lüssing 		goto deselect;
304c6c8fea2SSven Eckelmann 
305c6c8fea2SSven Eckelmann 	/* this node already is the gateway */
30657f0c07cSLinus Lüssing 	if (curr_gw_orig == orig_node)
307e1a5382fSLinus Lüssing 		goto out;
308c6c8fea2SSven Eckelmann 
3097d211efcSSven Eckelmann 	router_orig = batadv_orig_node_get_router(orig_node);
310e1a5382fSLinus Lüssing 	if (!router_orig)
311e1a5382fSLinus Lüssing 		goto out;
312c6c8fea2SSven Eckelmann 
313e1a5382fSLinus Lüssing 	gw_tq_avg = router_gw->tq_avg;
314e1a5382fSLinus Lüssing 	orig_tq_avg = router_orig->tq_avg;
315c6c8fea2SSven Eckelmann 
316c6c8fea2SSven Eckelmann 	/* the TQ value has to be better */
317c6c8fea2SSven Eckelmann 	if (orig_tq_avg < gw_tq_avg)
3185d02b3cdSLinus Lüssing 		goto out;
319c6c8fea2SSven Eckelmann 
3209cfc7bd6SSven Eckelmann 	/* if the routing class is greater than 3 the value tells us how much
321c6c8fea2SSven Eckelmann 	 * greater the TQ value of the new gateway must be
3229cfc7bd6SSven Eckelmann 	 */
323c6c8fea2SSven Eckelmann 	if ((atomic_read(&bat_priv->gw_sel_class) > 3) &&
324c6c8fea2SSven Eckelmann 	    (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class)))
3255d02b3cdSLinus Lüssing 		goto out;
326c6c8fea2SSven Eckelmann 
32739c75a51SSven Eckelmann 	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
32886ceb360SSven Eckelmann 		   "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n",
329c6c8fea2SSven Eckelmann 		   gw_tq_avg, orig_tq_avg);
330c6c8fea2SSven Eckelmann 
331c6c8fea2SSven Eckelmann deselect:
3327cf06bc6SSven Eckelmann 	batadv_gw_deselect(bat_priv);
3335d02b3cdSLinus Lüssing out:
33457f0c07cSLinus Lüssing 	if (curr_gw_orig)
3357d211efcSSven Eckelmann 		batadv_orig_node_free_ref(curr_gw_orig);
336e1a5382fSLinus Lüssing 	if (router_gw)
3377d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(router_gw);
338e1a5382fSLinus Lüssing 	if (router_orig)
3397d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(router_orig);
34057f0c07cSLinus Lüssing 
3415d02b3cdSLinus Lüssing 	return;
342c6c8fea2SSven Eckelmann }
343c6c8fea2SSven Eckelmann 
344414254e3SMarek Lindner /**
345414254e3SMarek Lindner  * batadv_gw_node_add - add gateway node to list of available gateways
346414254e3SMarek Lindner  * @bat_priv: the bat priv with all the soft interface information
347414254e3SMarek Lindner  * @orig_node: originator announcing gateway capabilities
348414254e3SMarek Lindner  * @gateway: announced bandwidth information
349414254e3SMarek Lindner  */
35056303d34SSven Eckelmann static void batadv_gw_node_add(struct batadv_priv *bat_priv,
35156303d34SSven Eckelmann 			       struct batadv_orig_node *orig_node,
352414254e3SMarek Lindner 			       struct batadv_tvlv_gateway_data *gateway)
353c6c8fea2SSven Eckelmann {
35456303d34SSven Eckelmann 	struct batadv_gw_node *gw_node;
355414254e3SMarek Lindner 
356414254e3SMarek Lindner 	if (gateway->bandwidth_down == 0)
357414254e3SMarek Lindner 		return;
358c6c8fea2SSven Eckelmann 
359704509b8SSven Eckelmann 	gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
360c6c8fea2SSven Eckelmann 	if (!gw_node)
361c6c8fea2SSven Eckelmann 		return;
362c6c8fea2SSven Eckelmann 
363c6c8fea2SSven Eckelmann 	INIT_HLIST_NODE(&gw_node->list);
364c6c8fea2SSven Eckelmann 	gw_node->orig_node = orig_node;
36525b6d3c1SMarek Lindner 	atomic_set(&gw_node->refcount, 1);
366c6c8fea2SSven Eckelmann 
367807736f6SSven Eckelmann 	spin_lock_bh(&bat_priv->gw.list_lock);
368807736f6SSven Eckelmann 	hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
369807736f6SSven Eckelmann 	spin_unlock_bh(&bat_priv->gw.list_lock);
370c6c8fea2SSven Eckelmann 
37139c75a51SSven Eckelmann 	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
372414254e3SMarek Lindner 		   "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
373414254e3SMarek Lindner 		   orig_node->orig,
374414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_down) / 10,
375414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_down) % 10,
376414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_up) / 10,
377414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_up) % 10);
378c6c8fea2SSven Eckelmann }
379c6c8fea2SSven Eckelmann 
380414254e3SMarek Lindner /**
381414254e3SMarek Lindner  * batadv_gw_node_get - retrieve gateway node from list of available gateways
382414254e3SMarek Lindner  * @bat_priv: the bat priv with all the soft interface information
383414254e3SMarek Lindner  * @orig_node: originator announcing gateway capabilities
384414254e3SMarek Lindner  *
385414254e3SMarek Lindner  * Returns gateway node if found or NULL otherwise.
38671e4aa9cSAntonio Quartulli  */
387414254e3SMarek Lindner static struct batadv_gw_node *
388414254e3SMarek Lindner batadv_gw_node_get(struct batadv_priv *bat_priv,
389414254e3SMarek Lindner 		   struct batadv_orig_node *orig_node)
390414254e3SMarek Lindner {
391414254e3SMarek Lindner 	struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
392c6c8fea2SSven Eckelmann 
393c6c8fea2SSven Eckelmann 	rcu_read_lock();
394414254e3SMarek Lindner 	hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) {
395414254e3SMarek Lindner 		if (gw_node_tmp->orig_node != orig_node)
396c6c8fea2SSven Eckelmann 			continue;
397c6c8fea2SSven Eckelmann 
398414254e3SMarek Lindner 		if (gw_node_tmp->deleted)
399414254e3SMarek Lindner 			continue;
400414254e3SMarek Lindner 
401414254e3SMarek Lindner 		if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
402414254e3SMarek Lindner 			continue;
403414254e3SMarek Lindner 
404414254e3SMarek Lindner 		gw_node = gw_node_tmp;
405414254e3SMarek Lindner 		break;
406414254e3SMarek Lindner 	}
407414254e3SMarek Lindner 	rcu_read_unlock();
408414254e3SMarek Lindner 
409414254e3SMarek Lindner 	return gw_node;
410414254e3SMarek Lindner }
411414254e3SMarek Lindner 
412414254e3SMarek Lindner /**
413414254e3SMarek Lindner  * batadv_gw_node_update - update list of available gateways with changed
414414254e3SMarek Lindner  *  bandwidth information
415414254e3SMarek Lindner  * @bat_priv: the bat priv with all the soft interface information
416414254e3SMarek Lindner  * @orig_node: originator announcing gateway capabilities
417414254e3SMarek Lindner  * @gateway: announced bandwidth information
418414254e3SMarek Lindner  */
419414254e3SMarek Lindner void batadv_gw_node_update(struct batadv_priv *bat_priv,
420414254e3SMarek Lindner 			   struct batadv_orig_node *orig_node,
421414254e3SMarek Lindner 			   struct batadv_tvlv_gateway_data *gateway)
422414254e3SMarek Lindner {
423414254e3SMarek Lindner 	struct batadv_gw_node *gw_node, *curr_gw = NULL;
424414254e3SMarek Lindner 
425414254e3SMarek Lindner 	gw_node = batadv_gw_node_get(bat_priv, orig_node);
426414254e3SMarek Lindner 	if (!gw_node) {
427414254e3SMarek Lindner 		batadv_gw_node_add(bat_priv, orig_node, gateway);
428414254e3SMarek Lindner 		goto out;
429414254e3SMarek Lindner 	}
430414254e3SMarek Lindner 
431414254e3SMarek Lindner 	if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) &&
432414254e3SMarek Lindner 	    (gw_node->bandwidth_up == ntohl(gateway->bandwidth_up)))
433414254e3SMarek Lindner 		goto out;
434414254e3SMarek Lindner 
43539c75a51SSven Eckelmann 	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
436414254e3SMarek Lindner 		   "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
437414254e3SMarek Lindner 		   orig_node->orig,
438414254e3SMarek Lindner 		   gw_node->bandwidth_down / 10,
439414254e3SMarek Lindner 		   gw_node->bandwidth_down % 10,
440414254e3SMarek Lindner 		   gw_node->bandwidth_up / 10,
441414254e3SMarek Lindner 		   gw_node->bandwidth_up % 10,
442414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_down) / 10,
443414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_down) % 10,
444414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_up) / 10,
445414254e3SMarek Lindner 		   ntohl(gateway->bandwidth_up) % 10);
446414254e3SMarek Lindner 
447414254e3SMarek Lindner 	gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
448414254e3SMarek Lindner 	gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
449c6c8fea2SSven Eckelmann 
450c6c8fea2SSven Eckelmann 	gw_node->deleted = 0;
451414254e3SMarek Lindner 	if (ntohl(gateway->bandwidth_down) == 0) {
452c6c8fea2SSven Eckelmann 		gw_node->deleted = jiffies;
45339c75a51SSven Eckelmann 		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
454c6c8fea2SSven Eckelmann 			   "Gateway %pM removed from gateway list\n",
455c6c8fea2SSven Eckelmann 			   orig_node->orig);
456c6c8fea2SSven Eckelmann 
457414254e3SMarek Lindner 		/* Note: We don't need a NULL check here, since curr_gw never
458414254e3SMarek Lindner 		 * gets dereferenced.
459414254e3SMarek Lindner 		 */
460414254e3SMarek Lindner 		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
461c4aac1abSMarek Lindner 		if (gw_node == curr_gw)
4627cf06bc6SSven Eckelmann 			batadv_gw_deselect(bat_priv);
463414254e3SMarek Lindner 	}
46471e4aa9cSAntonio Quartulli 
465414254e3SMarek Lindner out:
466c4aac1abSMarek Lindner 	if (curr_gw)
4671409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw);
468414254e3SMarek Lindner 	if (gw_node)
469414254e3SMarek Lindner 		batadv_gw_node_free_ref(gw_node);
470c6c8fea2SSven Eckelmann }
471c6c8fea2SSven Eckelmann 
47256303d34SSven Eckelmann void batadv_gw_node_delete(struct batadv_priv *bat_priv,
47356303d34SSven Eckelmann 			   struct batadv_orig_node *orig_node)
474c6c8fea2SSven Eckelmann {
475414254e3SMarek Lindner 	struct batadv_tvlv_gateway_data gateway;
476414254e3SMarek Lindner 
477414254e3SMarek Lindner 	gateway.bandwidth_down = 0;
478414254e3SMarek Lindner 	gateway.bandwidth_up = 0;
479414254e3SMarek Lindner 
480414254e3SMarek Lindner 	batadv_gw_node_update(bat_priv, orig_node, &gateway);
481c6c8fea2SSven Eckelmann }
482c6c8fea2SSven Eckelmann 
48356303d34SSven Eckelmann void batadv_gw_node_purge(struct batadv_priv *bat_priv)
484c6c8fea2SSven Eckelmann {
48556303d34SSven Eckelmann 	struct batadv_gw_node *gw_node, *curr_gw;
486b67bfe0dSSasha Levin 	struct hlist_node *node_tmp;
48742d0b044SSven Eckelmann 	unsigned long timeout = msecs_to_jiffies(2 * BATADV_PURGE_TIMEOUT);
488b4e17054SSven Eckelmann 	int do_deselect = 0;
489c4aac1abSMarek Lindner 
4901409a834SSven Eckelmann 	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
491c6c8fea2SSven Eckelmann 
492807736f6SSven Eckelmann 	spin_lock_bh(&bat_priv->gw.list_lock);
493c6c8fea2SSven Eckelmann 
494b67bfe0dSSasha Levin 	hlist_for_each_entry_safe(gw_node, node_tmp,
495807736f6SSven Eckelmann 				  &bat_priv->gw.list, list) {
496c6c8fea2SSven Eckelmann 		if (((!gw_node->deleted) ||
497c6c8fea2SSven Eckelmann 		     (time_before(jiffies, gw_node->deleted + timeout))) &&
49839c75a51SSven Eckelmann 		    atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE)
499c6c8fea2SSven Eckelmann 			continue;
500c6c8fea2SSven Eckelmann 
501c4aac1abSMarek Lindner 		if (curr_gw == gw_node)
502c4aac1abSMarek Lindner 			do_deselect = 1;
503c6c8fea2SSven Eckelmann 
504c6c8fea2SSven Eckelmann 		hlist_del_rcu(&gw_node->list);
5051409a834SSven Eckelmann 		batadv_gw_node_free_ref(gw_node);
506c6c8fea2SSven Eckelmann 	}
507c6c8fea2SSven Eckelmann 
508807736f6SSven Eckelmann 	spin_unlock_bh(&bat_priv->gw.list_lock);
509c4aac1abSMarek Lindner 
510c4aac1abSMarek Lindner 	/* gw_deselect() needs to acquire the gw_list_lock */
511c4aac1abSMarek Lindner 	if (do_deselect)
5127cf06bc6SSven Eckelmann 		batadv_gw_deselect(bat_priv);
513c4aac1abSMarek Lindner 
514c4aac1abSMarek Lindner 	if (curr_gw)
5151409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw);
516c6c8fea2SSven Eckelmann }
517c6c8fea2SSven Eckelmann 
5189cfc7bd6SSven Eckelmann /* fails if orig_node has no router */
51956303d34SSven Eckelmann static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
5201409a834SSven Eckelmann 				    struct seq_file *seq,
52156303d34SSven Eckelmann 				    const struct batadv_gw_node *gw_node)
522c6c8fea2SSven Eckelmann {
52356303d34SSven Eckelmann 	struct batadv_gw_node *curr_gw;
52456303d34SSven Eckelmann 	struct batadv_neigh_node *router;
525414254e3SMarek Lindner 	int ret = -1;
526c6c8fea2SSven Eckelmann 
5277d211efcSSven Eckelmann 	router = batadv_orig_node_get_router(gw_node->orig_node);
528e1a5382fSLinus Lüssing 	if (!router)
529e1a5382fSLinus Lüssing 		goto out;
530e1a5382fSLinus Lüssing 
5311409a834SSven Eckelmann 	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
5325d02b3cdSLinus Lüssing 
533414254e3SMarek Lindner 	ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
5345d02b3cdSLinus Lüssing 			 (curr_gw == gw_node ? "=>" : "  "),
535c6c8fea2SSven Eckelmann 			 gw_node->orig_node->orig,
536e1a5382fSLinus Lüssing 			 router->tq_avg, router->addr,
537e1a5382fSLinus Lüssing 			 router->if_incoming->net_dev->name,
538414254e3SMarek Lindner 			 gw_node->bandwidth_down / 10,
539414254e3SMarek Lindner 			 gw_node->bandwidth_down % 10,
540414254e3SMarek Lindner 			 gw_node->bandwidth_up / 10,
541414254e3SMarek Lindner 			 gw_node->bandwidth_up % 10);
5425d02b3cdSLinus Lüssing 
5437d211efcSSven Eckelmann 	batadv_neigh_node_free_ref(router);
544c4aac1abSMarek Lindner 	if (curr_gw)
5451409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw);
546e1a5382fSLinus Lüssing out:
5475d02b3cdSLinus Lüssing 	return ret;
548c6c8fea2SSven Eckelmann }
549c6c8fea2SSven Eckelmann 
5507cf06bc6SSven Eckelmann int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
551c6c8fea2SSven Eckelmann {
552c6c8fea2SSven Eckelmann 	struct net_device *net_dev = (struct net_device *)seq->private;
55356303d34SSven Eckelmann 	struct batadv_priv *bat_priv = netdev_priv(net_dev);
55456303d34SSven Eckelmann 	struct batadv_hard_iface *primary_if;
55556303d34SSven Eckelmann 	struct batadv_gw_node *gw_node;
55630da63a6SMarek Lindner 	int gw_count = 0;
557c6c8fea2SSven Eckelmann 
55830da63a6SMarek Lindner 	primary_if = batadv_seq_print_text_primary_if_get(seq);
55930da63a6SMarek Lindner 	if (!primary_if)
56032ae9b22SMarek Lindner 		goto out;
561c6c8fea2SSven Eckelmann 
56286ceb360SSven Eckelmann 	seq_printf(seq,
563414254e3SMarek Lindner 		   "      %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
56442d0b044SSven Eckelmann 		   "Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF",
56542d0b044SSven Eckelmann 		   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
56632ae9b22SMarek Lindner 		   primary_if->net_dev->dev_addr, net_dev->name);
567c6c8fea2SSven Eckelmann 
568c6c8fea2SSven Eckelmann 	rcu_read_lock();
569b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
570c6c8fea2SSven Eckelmann 		if (gw_node->deleted)
571c6c8fea2SSven Eckelmann 			continue;
572c6c8fea2SSven Eckelmann 
573e1a5382fSLinus Lüssing 		/* fails if orig_node has no router */
5741409a834SSven Eckelmann 		if (batadv_write_buffer_text(bat_priv, seq, gw_node) < 0)
575c6c8fea2SSven Eckelmann 			continue;
576c6c8fea2SSven Eckelmann 
577c6c8fea2SSven Eckelmann 		gw_count++;
578c6c8fea2SSven Eckelmann 	}
579c6c8fea2SSven Eckelmann 	rcu_read_unlock();
580c6c8fea2SSven Eckelmann 
581c6c8fea2SSven Eckelmann 	if (gw_count == 0)
5820c814653SAntonio Quartulli 		seq_puts(seq, "No gateways in range ...\n");
583c6c8fea2SSven Eckelmann 
58432ae9b22SMarek Lindner out:
58532ae9b22SMarek Lindner 	if (primary_if)
586e5d89254SSven Eckelmann 		batadv_hardif_free_ref(primary_if);
58730da63a6SMarek Lindner 	return 0;
588c6c8fea2SSven Eckelmann }
589c6c8fea2SSven Eckelmann 
5909d2c9488SLinus Lüssing /* this call might reallocate skb data */
5911409a834SSven Eckelmann static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len)
59243676ab5SAntonio Quartulli {
59343676ab5SAntonio Quartulli 	int ret = false;
59443676ab5SAntonio Quartulli 	unsigned char *p;
59543676ab5SAntonio Quartulli 	int pkt_len;
59643676ab5SAntonio Quartulli 
59743676ab5SAntonio Quartulli 	if (skb_linearize(skb) < 0)
59843676ab5SAntonio Quartulli 		goto out;
59943676ab5SAntonio Quartulli 
60043676ab5SAntonio Quartulli 	pkt_len = skb_headlen(skb);
60143676ab5SAntonio Quartulli 
602347c80f0SSven Eckelmann 	if (pkt_len < header_len + BATADV_DHCP_OPTIONS_OFFSET + 1)
60343676ab5SAntonio Quartulli 		goto out;
60443676ab5SAntonio Quartulli 
605347c80f0SSven Eckelmann 	p = skb->data + header_len + BATADV_DHCP_OPTIONS_OFFSET;
606347c80f0SSven Eckelmann 	pkt_len -= header_len + BATADV_DHCP_OPTIONS_OFFSET + 1;
60743676ab5SAntonio Quartulli 
60843676ab5SAntonio Quartulli 	/* Access the dhcp option lists. Each entry is made up by:
609015758d0SAntonio Quartulli 	 * - octet 1: option type
610015758d0SAntonio Quartulli 	 * - octet 2: option data len (only if type != 255 and 0)
6119cfc7bd6SSven Eckelmann 	 * - octet 3: option data
6129cfc7bd6SSven Eckelmann 	 */
61343676ab5SAntonio Quartulli 	while (*p != 255 && !ret) {
614015758d0SAntonio Quartulli 		/* p now points to the first octet: option type */
61543676ab5SAntonio Quartulli 		if (*p == 53) {
61643676ab5SAntonio Quartulli 			/* type 53 is the message type option.
6179cfc7bd6SSven Eckelmann 			 * Jump the len octet and go to the data octet
6189cfc7bd6SSven Eckelmann 			 */
61943676ab5SAntonio Quartulli 			if (pkt_len < 2)
62043676ab5SAntonio Quartulli 				goto out;
62143676ab5SAntonio Quartulli 			p += 2;
62243676ab5SAntonio Quartulli 
62343676ab5SAntonio Quartulli 			/* check if the message type is what we need */
624347c80f0SSven Eckelmann 			if (*p == BATADV_DHCP_REQUEST)
62543676ab5SAntonio Quartulli 				ret = true;
62643676ab5SAntonio Quartulli 			break;
62743676ab5SAntonio Quartulli 		} else if (*p == 0) {
62843676ab5SAntonio Quartulli 			/* option type 0 (padding), just go forward */
62943676ab5SAntonio Quartulli 			if (pkt_len < 1)
63043676ab5SAntonio Quartulli 				goto out;
63143676ab5SAntonio Quartulli 			pkt_len--;
63243676ab5SAntonio Quartulli 			p++;
63343676ab5SAntonio Quartulli 		} else {
63443676ab5SAntonio Quartulli 			/* This is any other option. So we get the length... */
63543676ab5SAntonio Quartulli 			if (pkt_len < 1)
63643676ab5SAntonio Quartulli 				goto out;
63743676ab5SAntonio Quartulli 			pkt_len--;
63843676ab5SAntonio Quartulli 			p++;
63943676ab5SAntonio Quartulli 
64043676ab5SAntonio Quartulli 			/* ...and then we jump over the data */
6419205cc52SAntonio Quartulli 			if (pkt_len < 1 + (*p))
64243676ab5SAntonio Quartulli 				goto out;
6439205cc52SAntonio Quartulli 			pkt_len -= 1 + (*p);
6449205cc52SAntonio Quartulli 			p += 1 + (*p);
64543676ab5SAntonio Quartulli 		}
64643676ab5SAntonio Quartulli 	}
64743676ab5SAntonio Quartulli out:
64843676ab5SAntonio Quartulli 	return ret;
64943676ab5SAntonio Quartulli }
65043676ab5SAntonio Quartulli 
6519d2c9488SLinus Lüssing /* this call might reallocate skb data */
6527cf06bc6SSven Eckelmann bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
653c6c8fea2SSven Eckelmann {
654c6c8fea2SSven Eckelmann 	struct ethhdr *ethhdr;
655c6c8fea2SSven Eckelmann 	struct iphdr *iphdr;
656c6c8fea2SSven Eckelmann 	struct ipv6hdr *ipv6hdr;
657c6c8fea2SSven Eckelmann 	struct udphdr *udphdr;
658*f7f8ed56SAntonio Quartulli 	struct vlan_ethhdr *vhdr;
659*f7f8ed56SAntonio Quartulli 	__be16 proto;
660c6c8fea2SSven Eckelmann 
661c6c8fea2SSven Eckelmann 	/* check for ethernet header */
662be7af5cfSMarek Lindner 	if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
663be7af5cfSMarek Lindner 		return false;
664c6c8fea2SSven Eckelmann 	ethhdr = (struct ethhdr *)skb->data;
665*f7f8ed56SAntonio Quartulli 	proto = ethhdr->h_proto;
666be7af5cfSMarek Lindner 	*header_len += ETH_HLEN;
667c6c8fea2SSven Eckelmann 
668c6c8fea2SSven Eckelmann 	/* check for initial vlan header */
669*f7f8ed56SAntonio Quartulli 	if (proto == htons(ETH_P_8021Q)) {
670be7af5cfSMarek Lindner 		if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
671be7af5cfSMarek Lindner 			return false;
672*f7f8ed56SAntonio Quartulli 
673*f7f8ed56SAntonio Quartulli 		vhdr = (struct vlan_ethhdr *)skb->data;
674*f7f8ed56SAntonio Quartulli 		proto = vhdr->h_vlan_encapsulated_proto;
675be7af5cfSMarek Lindner 		*header_len += VLAN_HLEN;
676c6c8fea2SSven Eckelmann 	}
677c6c8fea2SSven Eckelmann 
678c6c8fea2SSven Eckelmann 	/* check for ip header */
679*f7f8ed56SAntonio Quartulli 	switch (proto) {
680*f7f8ed56SAntonio Quartulli 	case htons(ETH_P_IP):
681be7af5cfSMarek Lindner 		if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
682be7af5cfSMarek Lindner 			return false;
683be7af5cfSMarek Lindner 		iphdr = (struct iphdr *)(skb->data + *header_len);
684be7af5cfSMarek Lindner 		*header_len += iphdr->ihl * 4;
685c6c8fea2SSven Eckelmann 
686c6c8fea2SSven Eckelmann 		/* check for udp header */
687c6c8fea2SSven Eckelmann 		if (iphdr->protocol != IPPROTO_UDP)
688be7af5cfSMarek Lindner 			return false;
689c6c8fea2SSven Eckelmann 
690c6c8fea2SSven Eckelmann 		break;
691*f7f8ed56SAntonio Quartulli 	case htons(ETH_P_IPV6):
692be7af5cfSMarek Lindner 		if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
693be7af5cfSMarek Lindner 			return false;
694be7af5cfSMarek Lindner 		ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
695be7af5cfSMarek Lindner 		*header_len += sizeof(*ipv6hdr);
696c6c8fea2SSven Eckelmann 
697c6c8fea2SSven Eckelmann 		/* check for udp header */
698c6c8fea2SSven Eckelmann 		if (ipv6hdr->nexthdr != IPPROTO_UDP)
699be7af5cfSMarek Lindner 			return false;
700c6c8fea2SSven Eckelmann 
701c6c8fea2SSven Eckelmann 		break;
702c6c8fea2SSven Eckelmann 	default:
703be7af5cfSMarek Lindner 		return false;
704c6c8fea2SSven Eckelmann 	}
705c6c8fea2SSven Eckelmann 
706be7af5cfSMarek Lindner 	if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
707be7af5cfSMarek Lindner 		return false;
7089d2c9488SLinus Lüssing 
7099d2c9488SLinus Lüssing 	/* skb->data might have been reallocated by pskb_may_pull() */
7109d2c9488SLinus Lüssing 	ethhdr = (struct ethhdr *)skb->data;
7119d2c9488SLinus Lüssing 	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
7129d2c9488SLinus Lüssing 		ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
7139d2c9488SLinus Lüssing 
714be7af5cfSMarek Lindner 	udphdr = (struct udphdr *)(skb->data + *header_len);
715be7af5cfSMarek Lindner 	*header_len += sizeof(*udphdr);
716c6c8fea2SSven Eckelmann 
717c6c8fea2SSven Eckelmann 	/* check for bootp port */
718*f7f8ed56SAntonio Quartulli 	if ((proto == htons(ETH_P_IP)) &&
719c6c8fea2SSven Eckelmann 	    (ntohs(udphdr->dest) != 67))
720be7af5cfSMarek Lindner 		return false;
721c6c8fea2SSven Eckelmann 
722*f7f8ed56SAntonio Quartulli 	if ((proto == htons(ETH_P_IPV6)) &&
723c6c8fea2SSven Eckelmann 	    (ntohs(udphdr->dest) != 547))
724be7af5cfSMarek Lindner 		return false;
725c6c8fea2SSven Eckelmann 
726be7af5cfSMarek Lindner 	return true;
727be7af5cfSMarek Lindner }
728c6c8fea2SSven Eckelmann 
7299d2c9488SLinus Lüssing /* this call might reallocate skb data */
73056303d34SSven Eckelmann bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
7319d2c9488SLinus Lüssing 			    struct sk_buff *skb)
732be7af5cfSMarek Lindner {
73356303d34SSven Eckelmann 	struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL;
73456303d34SSven Eckelmann 	struct batadv_orig_node *orig_dst_node = NULL;
735414254e3SMarek Lindner 	struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL;
7369d2c9488SLinus Lüssing 	struct ethhdr *ethhdr;
737be7af5cfSMarek Lindner 	bool ret, out_of_range = false;
738be7af5cfSMarek Lindner 	unsigned int header_len = 0;
739be7af5cfSMarek Lindner 	uint8_t curr_tq_avg;
740be7af5cfSMarek Lindner 
7417cf06bc6SSven Eckelmann 	ret = batadv_gw_is_dhcp_target(skb, &header_len);
742be7af5cfSMarek Lindner 	if (!ret)
743be7af5cfSMarek Lindner 		goto out;
744be7af5cfSMarek Lindner 
7459d2c9488SLinus Lüssing 	ethhdr = (struct ethhdr *)skb->data;
74608c36d3eSSven Eckelmann 	orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
747be7af5cfSMarek Lindner 						 ethhdr->h_dest);
748be7af5cfSMarek Lindner 	if (!orig_dst_node)
749be7af5cfSMarek Lindner 		goto out;
750be7af5cfSMarek Lindner 
751414254e3SMarek Lindner 	gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
752414254e3SMarek Lindner 	if (!gw_node->bandwidth_down == 0)
753be7af5cfSMarek Lindner 		goto out;
754be7af5cfSMarek Lindner 
7551409a834SSven Eckelmann 	ret = batadv_is_type_dhcprequest(skb, header_len);
756be7af5cfSMarek Lindner 	if (!ret)
757be7af5cfSMarek Lindner 		goto out;
758be7af5cfSMarek Lindner 
759be7af5cfSMarek Lindner 	switch (atomic_read(&bat_priv->gw_mode)) {
760cd646ab1SSven Eckelmann 	case BATADV_GW_MODE_SERVER:
761be7af5cfSMarek Lindner 		/* If we are a GW then we are our best GW. We can artificially
7629cfc7bd6SSven Eckelmann 		 * set the tq towards ourself as the maximum value
7639cfc7bd6SSven Eckelmann 		 */
76442d0b044SSven Eckelmann 		curr_tq_avg = BATADV_TQ_MAX_VALUE;
765be7af5cfSMarek Lindner 		break;
766cd646ab1SSven Eckelmann 	case BATADV_GW_MODE_CLIENT:
7671409a834SSven Eckelmann 		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
768c4aac1abSMarek Lindner 		if (!curr_gw)
769be7af5cfSMarek Lindner 			goto out;
770c6c8fea2SSven Eckelmann 
771be7af5cfSMarek Lindner 		/* packet is going to our gateway */
772be7af5cfSMarek Lindner 		if (curr_gw->orig_node == orig_dst_node)
773be7af5cfSMarek Lindner 			goto out;
774be7af5cfSMarek Lindner 
77543676ab5SAntonio Quartulli 		/* If the dhcp packet has been sent to a different gw,
77643676ab5SAntonio Quartulli 		 * we have to evaluate whether the old gw is still
7779cfc7bd6SSven Eckelmann 		 * reliable enough
7789cfc7bd6SSven Eckelmann 		 */
77930d3c511SSven Eckelmann 		neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node,
78030d3c511SSven Eckelmann 						NULL);
781be7af5cfSMarek Lindner 		if (!neigh_curr)
782be7af5cfSMarek Lindner 			goto out;
783be7af5cfSMarek Lindner 
784be7af5cfSMarek Lindner 		curr_tq_avg = neigh_curr->tq_avg;
785be7af5cfSMarek Lindner 		break;
786cd646ab1SSven Eckelmann 	case BATADV_GW_MODE_OFF:
787be7af5cfSMarek Lindner 	default:
788be7af5cfSMarek Lindner 		goto out;
78943676ab5SAntonio Quartulli 	}
790be7af5cfSMarek Lindner 
79130d3c511SSven Eckelmann 	neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL);
7922ef04f47SDan Carpenter 	if (!neigh_old)
793be7af5cfSMarek Lindner 		goto out;
794be7af5cfSMarek Lindner 
79542d0b044SSven Eckelmann 	if (curr_tq_avg - neigh_old->tq_avg > BATADV_GW_THRESHOLD)
796be7af5cfSMarek Lindner 		out_of_range = true;
797be7af5cfSMarek Lindner 
798be7af5cfSMarek Lindner out:
799be7af5cfSMarek Lindner 	if (orig_dst_node)
8007d211efcSSven Eckelmann 		batadv_orig_node_free_ref(orig_dst_node);
801be7af5cfSMarek Lindner 	if (curr_gw)
8021409a834SSven Eckelmann 		batadv_gw_node_free_ref(curr_gw);
803414254e3SMarek Lindner 	if (gw_node)
804414254e3SMarek Lindner 		batadv_gw_node_free_ref(gw_node);
80543676ab5SAntonio Quartulli 	if (neigh_old)
8067d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(neigh_old);
80743676ab5SAntonio Quartulli 	if (neigh_curr)
8087d211efcSSven Eckelmann 		batadv_neigh_node_free_ref(neigh_curr);
809be7af5cfSMarek Lindner 	return out_of_range;
810c6c8fea2SSven Eckelmann }
811