xref: /openbmc/linux/net/batman-adv/gateway_client.c (revision c6c8fea29769d998d94fcec9b9f14d4b52b349d3)
1*c6c8fea2SSven Eckelmann /*
2*c6c8fea2SSven Eckelmann  * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors:
3*c6c8fea2SSven Eckelmann  *
4*c6c8fea2SSven Eckelmann  * Marek Lindner
5*c6c8fea2SSven Eckelmann  *
6*c6c8fea2SSven Eckelmann  * This program is free software; you can redistribute it and/or
7*c6c8fea2SSven Eckelmann  * modify it under the terms of version 2 of the GNU General Public
8*c6c8fea2SSven Eckelmann  * License as published by the Free Software Foundation.
9*c6c8fea2SSven Eckelmann  *
10*c6c8fea2SSven Eckelmann  * This program is distributed in the hope that it will be useful, but
11*c6c8fea2SSven Eckelmann  * WITHOUT ANY WARRANTY; without even the implied warranty of
12*c6c8fea2SSven Eckelmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13*c6c8fea2SSven Eckelmann  * General Public License for more details.
14*c6c8fea2SSven Eckelmann  *
15*c6c8fea2SSven Eckelmann  * You should have received a copy of the GNU General Public License
16*c6c8fea2SSven Eckelmann  * along with this program; if not, write to the Free Software
17*c6c8fea2SSven Eckelmann  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18*c6c8fea2SSven Eckelmann  * 02110-1301, USA
19*c6c8fea2SSven Eckelmann  *
20*c6c8fea2SSven Eckelmann  */
21*c6c8fea2SSven Eckelmann 
22*c6c8fea2SSven Eckelmann #include "main.h"
23*c6c8fea2SSven Eckelmann #include "gateway_client.h"
24*c6c8fea2SSven Eckelmann #include "gateway_common.h"
25*c6c8fea2SSven Eckelmann #include "hard-interface.h"
26*c6c8fea2SSven Eckelmann #include <linux/ip.h>
27*c6c8fea2SSven Eckelmann #include <linux/ipv6.h>
28*c6c8fea2SSven Eckelmann #include <linux/udp.h>
29*c6c8fea2SSven Eckelmann #include <linux/if_vlan.h>
30*c6c8fea2SSven Eckelmann 
31*c6c8fea2SSven Eckelmann static void gw_node_free_ref(struct kref *refcount)
32*c6c8fea2SSven Eckelmann {
33*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
34*c6c8fea2SSven Eckelmann 
35*c6c8fea2SSven Eckelmann 	gw_node = container_of(refcount, struct gw_node, refcount);
36*c6c8fea2SSven Eckelmann 	kfree(gw_node);
37*c6c8fea2SSven Eckelmann }
38*c6c8fea2SSven Eckelmann 
39*c6c8fea2SSven Eckelmann static void gw_node_free_rcu(struct rcu_head *rcu)
40*c6c8fea2SSven Eckelmann {
41*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
42*c6c8fea2SSven Eckelmann 
43*c6c8fea2SSven Eckelmann 	gw_node = container_of(rcu, struct gw_node, rcu);
44*c6c8fea2SSven Eckelmann 	kref_put(&gw_node->refcount, gw_node_free_ref);
45*c6c8fea2SSven Eckelmann }
46*c6c8fea2SSven Eckelmann 
47*c6c8fea2SSven Eckelmann void *gw_get_selected(struct bat_priv *bat_priv)
48*c6c8fea2SSven Eckelmann {
49*c6c8fea2SSven Eckelmann 	struct gw_node *curr_gateway_tmp = bat_priv->curr_gw;
50*c6c8fea2SSven Eckelmann 
51*c6c8fea2SSven Eckelmann 	if (!curr_gateway_tmp)
52*c6c8fea2SSven Eckelmann 		return NULL;
53*c6c8fea2SSven Eckelmann 
54*c6c8fea2SSven Eckelmann 	return curr_gateway_tmp->orig_node;
55*c6c8fea2SSven Eckelmann }
56*c6c8fea2SSven Eckelmann 
57*c6c8fea2SSven Eckelmann void gw_deselect(struct bat_priv *bat_priv)
58*c6c8fea2SSven Eckelmann {
59*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node = bat_priv->curr_gw;
60*c6c8fea2SSven Eckelmann 
61*c6c8fea2SSven Eckelmann 	bat_priv->curr_gw = NULL;
62*c6c8fea2SSven Eckelmann 
63*c6c8fea2SSven Eckelmann 	if (gw_node)
64*c6c8fea2SSven Eckelmann 		kref_put(&gw_node->refcount, gw_node_free_ref);
65*c6c8fea2SSven Eckelmann }
66*c6c8fea2SSven Eckelmann 
67*c6c8fea2SSven Eckelmann static struct gw_node *gw_select(struct bat_priv *bat_priv,
68*c6c8fea2SSven Eckelmann 			  struct gw_node *new_gw_node)
69*c6c8fea2SSven Eckelmann {
70*c6c8fea2SSven Eckelmann 	struct gw_node *curr_gw_node = bat_priv->curr_gw;
71*c6c8fea2SSven Eckelmann 
72*c6c8fea2SSven Eckelmann 	if (new_gw_node)
73*c6c8fea2SSven Eckelmann 		kref_get(&new_gw_node->refcount);
74*c6c8fea2SSven Eckelmann 
75*c6c8fea2SSven Eckelmann 	bat_priv->curr_gw = new_gw_node;
76*c6c8fea2SSven Eckelmann 	return curr_gw_node;
77*c6c8fea2SSven Eckelmann }
78*c6c8fea2SSven Eckelmann 
79*c6c8fea2SSven Eckelmann void gw_election(struct bat_priv *bat_priv)
80*c6c8fea2SSven Eckelmann {
81*c6c8fea2SSven Eckelmann 	struct hlist_node *node;
82*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL;
83*c6c8fea2SSven Eckelmann 	uint8_t max_tq = 0;
84*c6c8fea2SSven Eckelmann 	uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
85*c6c8fea2SSven Eckelmann 	int down, up;
86*c6c8fea2SSven Eckelmann 
87*c6c8fea2SSven Eckelmann 	/**
88*c6c8fea2SSven Eckelmann 	 * The batman daemon checks here if we already passed a full originator
89*c6c8fea2SSven Eckelmann 	 * cycle in order to make sure we don't choose the first gateway we
90*c6c8fea2SSven Eckelmann 	 * hear about. This check is based on the daemon's uptime which we
91*c6c8fea2SSven Eckelmann 	 * don't have.
92*c6c8fea2SSven Eckelmann 	 **/
93*c6c8fea2SSven Eckelmann 	if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT)
94*c6c8fea2SSven Eckelmann 		return;
95*c6c8fea2SSven Eckelmann 
96*c6c8fea2SSven Eckelmann 	if (bat_priv->curr_gw)
97*c6c8fea2SSven Eckelmann 		return;
98*c6c8fea2SSven Eckelmann 
99*c6c8fea2SSven Eckelmann 	rcu_read_lock();
100*c6c8fea2SSven Eckelmann 	if (hlist_empty(&bat_priv->gw_list)) {
101*c6c8fea2SSven Eckelmann 		rcu_read_unlock();
102*c6c8fea2SSven Eckelmann 
103*c6c8fea2SSven Eckelmann 		if (bat_priv->curr_gw) {
104*c6c8fea2SSven Eckelmann 			bat_dbg(DBG_BATMAN, bat_priv,
105*c6c8fea2SSven Eckelmann 				"Removing selected gateway - "
106*c6c8fea2SSven Eckelmann 				"no gateway in range\n");
107*c6c8fea2SSven Eckelmann 			gw_deselect(bat_priv);
108*c6c8fea2SSven Eckelmann 		}
109*c6c8fea2SSven Eckelmann 
110*c6c8fea2SSven Eckelmann 		return;
111*c6c8fea2SSven Eckelmann 	}
112*c6c8fea2SSven Eckelmann 
113*c6c8fea2SSven Eckelmann 	hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
114*c6c8fea2SSven Eckelmann 		if (!gw_node->orig_node->router)
115*c6c8fea2SSven Eckelmann 			continue;
116*c6c8fea2SSven Eckelmann 
117*c6c8fea2SSven Eckelmann 		if (gw_node->deleted)
118*c6c8fea2SSven Eckelmann 			continue;
119*c6c8fea2SSven Eckelmann 
120*c6c8fea2SSven Eckelmann 		switch (atomic_read(&bat_priv->gw_sel_class)) {
121*c6c8fea2SSven Eckelmann 		case 1: /* fast connection */
122*c6c8fea2SSven Eckelmann 			gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags,
123*c6c8fea2SSven Eckelmann 					     &down, &up);
124*c6c8fea2SSven Eckelmann 
125*c6c8fea2SSven Eckelmann 			tmp_gw_factor = (gw_node->orig_node->router->tq_avg *
126*c6c8fea2SSven Eckelmann 					 gw_node->orig_node->router->tq_avg *
127*c6c8fea2SSven Eckelmann 					 down * 100 * 100) /
128*c6c8fea2SSven Eckelmann 					 (TQ_LOCAL_WINDOW_SIZE *
129*c6c8fea2SSven Eckelmann 					 TQ_LOCAL_WINDOW_SIZE * 64);
130*c6c8fea2SSven Eckelmann 
131*c6c8fea2SSven Eckelmann 			if ((tmp_gw_factor > max_gw_factor) ||
132*c6c8fea2SSven Eckelmann 			    ((tmp_gw_factor == max_gw_factor) &&
133*c6c8fea2SSven Eckelmann 			     (gw_node->orig_node->router->tq_avg > max_tq)))
134*c6c8fea2SSven Eckelmann 				curr_gw_tmp = gw_node;
135*c6c8fea2SSven Eckelmann 			break;
136*c6c8fea2SSven Eckelmann 
137*c6c8fea2SSven Eckelmann 		default: /**
138*c6c8fea2SSven Eckelmann 			  * 2:  stable connection (use best statistic)
139*c6c8fea2SSven Eckelmann 			  * 3:  fast-switch (use best statistic but change as
140*c6c8fea2SSven Eckelmann 			  *     soon as a better gateway appears)
141*c6c8fea2SSven Eckelmann 			  * XX: late-switch (use best statistic but change as
142*c6c8fea2SSven Eckelmann 			  *     soon as a better gateway appears which has
143*c6c8fea2SSven Eckelmann 			  *     $routing_class more tq points)
144*c6c8fea2SSven Eckelmann 			  **/
145*c6c8fea2SSven Eckelmann 			if (gw_node->orig_node->router->tq_avg > max_tq)
146*c6c8fea2SSven Eckelmann 				curr_gw_tmp = gw_node;
147*c6c8fea2SSven Eckelmann 			break;
148*c6c8fea2SSven Eckelmann 		}
149*c6c8fea2SSven Eckelmann 
150*c6c8fea2SSven Eckelmann 		if (gw_node->orig_node->router->tq_avg > max_tq)
151*c6c8fea2SSven Eckelmann 			max_tq = gw_node->orig_node->router->tq_avg;
152*c6c8fea2SSven Eckelmann 
153*c6c8fea2SSven Eckelmann 		if (tmp_gw_factor > max_gw_factor)
154*c6c8fea2SSven Eckelmann 			max_gw_factor = tmp_gw_factor;
155*c6c8fea2SSven Eckelmann 	}
156*c6c8fea2SSven Eckelmann 
157*c6c8fea2SSven Eckelmann 	if (bat_priv->curr_gw != curr_gw_tmp) {
158*c6c8fea2SSven Eckelmann 		if ((bat_priv->curr_gw) && (!curr_gw_tmp))
159*c6c8fea2SSven Eckelmann 			bat_dbg(DBG_BATMAN, bat_priv,
160*c6c8fea2SSven Eckelmann 				"Removing selected gateway - "
161*c6c8fea2SSven Eckelmann 				"no gateway in range\n");
162*c6c8fea2SSven Eckelmann 		else if ((!bat_priv->curr_gw) && (curr_gw_tmp))
163*c6c8fea2SSven Eckelmann 			bat_dbg(DBG_BATMAN, bat_priv,
164*c6c8fea2SSven Eckelmann 				"Adding route to gateway %pM "
165*c6c8fea2SSven Eckelmann 				"(gw_flags: %i, tq: %i)\n",
166*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->orig,
167*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->gw_flags,
168*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->router->tq_avg);
169*c6c8fea2SSven Eckelmann 		else
170*c6c8fea2SSven Eckelmann 			bat_dbg(DBG_BATMAN, bat_priv,
171*c6c8fea2SSven Eckelmann 				"Changing route to gateway %pM "
172*c6c8fea2SSven Eckelmann 				"(gw_flags: %i, tq: %i)\n",
173*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->orig,
174*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->gw_flags,
175*c6c8fea2SSven Eckelmann 				curr_gw_tmp->orig_node->router->tq_avg);
176*c6c8fea2SSven Eckelmann 
177*c6c8fea2SSven Eckelmann 		old_gw_node = gw_select(bat_priv, curr_gw_tmp);
178*c6c8fea2SSven Eckelmann 	}
179*c6c8fea2SSven Eckelmann 
180*c6c8fea2SSven Eckelmann 	rcu_read_unlock();
181*c6c8fea2SSven Eckelmann 
182*c6c8fea2SSven Eckelmann 	/* the kfree() has to be outside of the rcu lock */
183*c6c8fea2SSven Eckelmann 	if (old_gw_node)
184*c6c8fea2SSven Eckelmann 		kref_put(&old_gw_node->refcount, gw_node_free_ref);
185*c6c8fea2SSven Eckelmann }
186*c6c8fea2SSven Eckelmann 
187*c6c8fea2SSven Eckelmann void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
188*c6c8fea2SSven Eckelmann {
189*c6c8fea2SSven Eckelmann 	struct gw_node *curr_gateway_tmp = bat_priv->curr_gw;
190*c6c8fea2SSven Eckelmann 	uint8_t gw_tq_avg, orig_tq_avg;
191*c6c8fea2SSven Eckelmann 
192*c6c8fea2SSven Eckelmann 	if (!curr_gateway_tmp)
193*c6c8fea2SSven Eckelmann 		return;
194*c6c8fea2SSven Eckelmann 
195*c6c8fea2SSven Eckelmann 	if (!curr_gateway_tmp->orig_node)
196*c6c8fea2SSven Eckelmann 		goto deselect;
197*c6c8fea2SSven Eckelmann 
198*c6c8fea2SSven Eckelmann 	if (!curr_gateway_tmp->orig_node->router)
199*c6c8fea2SSven Eckelmann 		goto deselect;
200*c6c8fea2SSven Eckelmann 
201*c6c8fea2SSven Eckelmann 	/* this node already is the gateway */
202*c6c8fea2SSven Eckelmann 	if (curr_gateway_tmp->orig_node == orig_node)
203*c6c8fea2SSven Eckelmann 		return;
204*c6c8fea2SSven Eckelmann 
205*c6c8fea2SSven Eckelmann 	if (!orig_node->router)
206*c6c8fea2SSven Eckelmann 		return;
207*c6c8fea2SSven Eckelmann 
208*c6c8fea2SSven Eckelmann 	gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg;
209*c6c8fea2SSven Eckelmann 	orig_tq_avg = orig_node->router->tq_avg;
210*c6c8fea2SSven Eckelmann 
211*c6c8fea2SSven Eckelmann 	/* the TQ value has to be better */
212*c6c8fea2SSven Eckelmann 	if (orig_tq_avg < gw_tq_avg)
213*c6c8fea2SSven Eckelmann 		return;
214*c6c8fea2SSven Eckelmann 
215*c6c8fea2SSven Eckelmann 	/**
216*c6c8fea2SSven Eckelmann 	 * if the routing class is greater than 3 the value tells us how much
217*c6c8fea2SSven Eckelmann 	 * greater the TQ value of the new gateway must be
218*c6c8fea2SSven Eckelmann 	 **/
219*c6c8fea2SSven Eckelmann 	if ((atomic_read(&bat_priv->gw_sel_class) > 3) &&
220*c6c8fea2SSven Eckelmann 	    (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class)))
221*c6c8fea2SSven Eckelmann 		return;
222*c6c8fea2SSven Eckelmann 
223*c6c8fea2SSven Eckelmann 	bat_dbg(DBG_BATMAN, bat_priv,
224*c6c8fea2SSven Eckelmann 		"Restarting gateway selection: better gateway found (tq curr: "
225*c6c8fea2SSven Eckelmann 		"%i, tq new: %i)\n",
226*c6c8fea2SSven Eckelmann 		gw_tq_avg, orig_tq_avg);
227*c6c8fea2SSven Eckelmann 
228*c6c8fea2SSven Eckelmann deselect:
229*c6c8fea2SSven Eckelmann 	gw_deselect(bat_priv);
230*c6c8fea2SSven Eckelmann }
231*c6c8fea2SSven Eckelmann 
232*c6c8fea2SSven Eckelmann static void gw_node_add(struct bat_priv *bat_priv,
233*c6c8fea2SSven Eckelmann 			struct orig_node *orig_node, uint8_t new_gwflags)
234*c6c8fea2SSven Eckelmann {
235*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
236*c6c8fea2SSven Eckelmann 	int down, up;
237*c6c8fea2SSven Eckelmann 
238*c6c8fea2SSven Eckelmann 	gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC);
239*c6c8fea2SSven Eckelmann 	if (!gw_node)
240*c6c8fea2SSven Eckelmann 		return;
241*c6c8fea2SSven Eckelmann 
242*c6c8fea2SSven Eckelmann 	memset(gw_node, 0, sizeof(struct gw_node));
243*c6c8fea2SSven Eckelmann 	INIT_HLIST_NODE(&gw_node->list);
244*c6c8fea2SSven Eckelmann 	gw_node->orig_node = orig_node;
245*c6c8fea2SSven Eckelmann 	kref_init(&gw_node->refcount);
246*c6c8fea2SSven Eckelmann 
247*c6c8fea2SSven Eckelmann 	spin_lock_bh(&bat_priv->gw_list_lock);
248*c6c8fea2SSven Eckelmann 	hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list);
249*c6c8fea2SSven Eckelmann 	spin_unlock_bh(&bat_priv->gw_list_lock);
250*c6c8fea2SSven Eckelmann 
251*c6c8fea2SSven Eckelmann 	gw_bandwidth_to_kbit(new_gwflags, &down, &up);
252*c6c8fea2SSven Eckelmann 	bat_dbg(DBG_BATMAN, bat_priv,
253*c6c8fea2SSven Eckelmann 		"Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
254*c6c8fea2SSven Eckelmann 		orig_node->orig, new_gwflags,
255*c6c8fea2SSven Eckelmann 		(down > 2048 ? down / 1024 : down),
256*c6c8fea2SSven Eckelmann 		(down > 2048 ? "MBit" : "KBit"),
257*c6c8fea2SSven Eckelmann 		(up > 2048 ? up / 1024 : up),
258*c6c8fea2SSven Eckelmann 		(up > 2048 ? "MBit" : "KBit"));
259*c6c8fea2SSven Eckelmann }
260*c6c8fea2SSven Eckelmann 
261*c6c8fea2SSven Eckelmann void gw_node_update(struct bat_priv *bat_priv,
262*c6c8fea2SSven Eckelmann 		    struct orig_node *orig_node, uint8_t new_gwflags)
263*c6c8fea2SSven Eckelmann {
264*c6c8fea2SSven Eckelmann 	struct hlist_node *node;
265*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
266*c6c8fea2SSven Eckelmann 
267*c6c8fea2SSven Eckelmann 	rcu_read_lock();
268*c6c8fea2SSven Eckelmann 	hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
269*c6c8fea2SSven Eckelmann 		if (gw_node->orig_node != orig_node)
270*c6c8fea2SSven Eckelmann 			continue;
271*c6c8fea2SSven Eckelmann 
272*c6c8fea2SSven Eckelmann 		bat_dbg(DBG_BATMAN, bat_priv,
273*c6c8fea2SSven Eckelmann 			"Gateway class of originator %pM changed from "
274*c6c8fea2SSven Eckelmann 			"%i to %i\n",
275*c6c8fea2SSven Eckelmann 			orig_node->orig, gw_node->orig_node->gw_flags,
276*c6c8fea2SSven Eckelmann 			new_gwflags);
277*c6c8fea2SSven Eckelmann 
278*c6c8fea2SSven Eckelmann 		gw_node->deleted = 0;
279*c6c8fea2SSven Eckelmann 
280*c6c8fea2SSven Eckelmann 		if (new_gwflags == 0) {
281*c6c8fea2SSven Eckelmann 			gw_node->deleted = jiffies;
282*c6c8fea2SSven Eckelmann 			bat_dbg(DBG_BATMAN, bat_priv,
283*c6c8fea2SSven Eckelmann 				"Gateway %pM removed from gateway list\n",
284*c6c8fea2SSven Eckelmann 				orig_node->orig);
285*c6c8fea2SSven Eckelmann 
286*c6c8fea2SSven Eckelmann 			if (gw_node == bat_priv->curr_gw) {
287*c6c8fea2SSven Eckelmann 				rcu_read_unlock();
288*c6c8fea2SSven Eckelmann 				gw_deselect(bat_priv);
289*c6c8fea2SSven Eckelmann 				return;
290*c6c8fea2SSven Eckelmann 			}
291*c6c8fea2SSven Eckelmann 		}
292*c6c8fea2SSven Eckelmann 
293*c6c8fea2SSven Eckelmann 		rcu_read_unlock();
294*c6c8fea2SSven Eckelmann 		return;
295*c6c8fea2SSven Eckelmann 	}
296*c6c8fea2SSven Eckelmann 	rcu_read_unlock();
297*c6c8fea2SSven Eckelmann 
298*c6c8fea2SSven Eckelmann 	if (new_gwflags == 0)
299*c6c8fea2SSven Eckelmann 		return;
300*c6c8fea2SSven Eckelmann 
301*c6c8fea2SSven Eckelmann 	gw_node_add(bat_priv, orig_node, new_gwflags);
302*c6c8fea2SSven Eckelmann }
303*c6c8fea2SSven Eckelmann 
304*c6c8fea2SSven Eckelmann void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node)
305*c6c8fea2SSven Eckelmann {
306*c6c8fea2SSven Eckelmann 	return gw_node_update(bat_priv, orig_node, 0);
307*c6c8fea2SSven Eckelmann }
308*c6c8fea2SSven Eckelmann 
309*c6c8fea2SSven Eckelmann void gw_node_purge(struct bat_priv *bat_priv)
310*c6c8fea2SSven Eckelmann {
311*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
312*c6c8fea2SSven Eckelmann 	struct hlist_node *node, *node_tmp;
313*c6c8fea2SSven Eckelmann 	unsigned long timeout = 2 * PURGE_TIMEOUT * HZ;
314*c6c8fea2SSven Eckelmann 
315*c6c8fea2SSven Eckelmann 	spin_lock_bh(&bat_priv->gw_list_lock);
316*c6c8fea2SSven Eckelmann 
317*c6c8fea2SSven Eckelmann 	hlist_for_each_entry_safe(gw_node, node, node_tmp,
318*c6c8fea2SSven Eckelmann 				  &bat_priv->gw_list, list) {
319*c6c8fea2SSven Eckelmann 		if (((!gw_node->deleted) ||
320*c6c8fea2SSven Eckelmann 		     (time_before(jiffies, gw_node->deleted + timeout))) &&
321*c6c8fea2SSven Eckelmann 		    atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)
322*c6c8fea2SSven Eckelmann 			continue;
323*c6c8fea2SSven Eckelmann 
324*c6c8fea2SSven Eckelmann 		if (bat_priv->curr_gw == gw_node)
325*c6c8fea2SSven Eckelmann 			gw_deselect(bat_priv);
326*c6c8fea2SSven Eckelmann 
327*c6c8fea2SSven Eckelmann 		hlist_del_rcu(&gw_node->list);
328*c6c8fea2SSven Eckelmann 		call_rcu(&gw_node->rcu, gw_node_free_rcu);
329*c6c8fea2SSven Eckelmann 	}
330*c6c8fea2SSven Eckelmann 
331*c6c8fea2SSven Eckelmann 
332*c6c8fea2SSven Eckelmann 	spin_unlock_bh(&bat_priv->gw_list_lock);
333*c6c8fea2SSven Eckelmann }
334*c6c8fea2SSven Eckelmann 
335*c6c8fea2SSven Eckelmann static int _write_buffer_text(struct bat_priv *bat_priv,
336*c6c8fea2SSven Eckelmann 			      struct seq_file *seq, struct gw_node *gw_node)
337*c6c8fea2SSven Eckelmann {
338*c6c8fea2SSven Eckelmann 	int down, up;
339*c6c8fea2SSven Eckelmann 
340*c6c8fea2SSven Eckelmann 	gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
341*c6c8fea2SSven Eckelmann 
342*c6c8fea2SSven Eckelmann 	return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
343*c6c8fea2SSven Eckelmann 		       (bat_priv->curr_gw == gw_node ? "=>" : "  "),
344*c6c8fea2SSven Eckelmann 		       gw_node->orig_node->orig,
345*c6c8fea2SSven Eckelmann 		       gw_node->orig_node->router->tq_avg,
346*c6c8fea2SSven Eckelmann 		       gw_node->orig_node->router->addr,
347*c6c8fea2SSven Eckelmann 		       gw_node->orig_node->router->if_incoming->net_dev->name,
348*c6c8fea2SSven Eckelmann 		       gw_node->orig_node->gw_flags,
349*c6c8fea2SSven Eckelmann 		       (down > 2048 ? down / 1024 : down),
350*c6c8fea2SSven Eckelmann 		       (down > 2048 ? "MBit" : "KBit"),
351*c6c8fea2SSven Eckelmann 		       (up > 2048 ? up / 1024 : up),
352*c6c8fea2SSven Eckelmann 		       (up > 2048 ? "MBit" : "KBit"));
353*c6c8fea2SSven Eckelmann }
354*c6c8fea2SSven Eckelmann 
355*c6c8fea2SSven Eckelmann int gw_client_seq_print_text(struct seq_file *seq, void *offset)
356*c6c8fea2SSven Eckelmann {
357*c6c8fea2SSven Eckelmann 	struct net_device *net_dev = (struct net_device *)seq->private;
358*c6c8fea2SSven Eckelmann 	struct bat_priv *bat_priv = netdev_priv(net_dev);
359*c6c8fea2SSven Eckelmann 	struct gw_node *gw_node;
360*c6c8fea2SSven Eckelmann 	struct hlist_node *node;
361*c6c8fea2SSven Eckelmann 	int gw_count = 0;
362*c6c8fea2SSven Eckelmann 
363*c6c8fea2SSven Eckelmann 	if (!bat_priv->primary_if) {
364*c6c8fea2SSven Eckelmann 
365*c6c8fea2SSven Eckelmann 		return seq_printf(seq, "BATMAN mesh %s disabled - please "
366*c6c8fea2SSven Eckelmann 				  "specify interfaces to enable it\n",
367*c6c8fea2SSven Eckelmann 				  net_dev->name);
368*c6c8fea2SSven Eckelmann 	}
369*c6c8fea2SSven Eckelmann 
370*c6c8fea2SSven Eckelmann 	if (bat_priv->primary_if->if_status != IF_ACTIVE) {
371*c6c8fea2SSven Eckelmann 
372*c6c8fea2SSven Eckelmann 		return seq_printf(seq, "BATMAN mesh %s disabled - "
373*c6c8fea2SSven Eckelmann 				       "primary interface not active\n",
374*c6c8fea2SSven Eckelmann 				       net_dev->name);
375*c6c8fea2SSven Eckelmann 	}
376*c6c8fea2SSven Eckelmann 
377*c6c8fea2SSven Eckelmann 	seq_printf(seq, "      %-12s (%s/%i) %17s [%10s]: gw_class ... "
378*c6c8fea2SSven Eckelmann 		   "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
379*c6c8fea2SSven Eckelmann 		   "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
380*c6c8fea2SSven Eckelmann 		   "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
381*c6c8fea2SSven Eckelmann 		   bat_priv->primary_if->net_dev->name,
382*c6c8fea2SSven Eckelmann 		   bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
383*c6c8fea2SSven Eckelmann 
384*c6c8fea2SSven Eckelmann 	rcu_read_lock();
385*c6c8fea2SSven Eckelmann 	hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
386*c6c8fea2SSven Eckelmann 		if (gw_node->deleted)
387*c6c8fea2SSven Eckelmann 			continue;
388*c6c8fea2SSven Eckelmann 
389*c6c8fea2SSven Eckelmann 		if (!gw_node->orig_node->router)
390*c6c8fea2SSven Eckelmann 			continue;
391*c6c8fea2SSven Eckelmann 
392*c6c8fea2SSven Eckelmann 		_write_buffer_text(bat_priv, seq, gw_node);
393*c6c8fea2SSven Eckelmann 		gw_count++;
394*c6c8fea2SSven Eckelmann 	}
395*c6c8fea2SSven Eckelmann 	rcu_read_unlock();
396*c6c8fea2SSven Eckelmann 
397*c6c8fea2SSven Eckelmann 	if (gw_count == 0)
398*c6c8fea2SSven Eckelmann 		seq_printf(seq, "No gateways in range ...\n");
399*c6c8fea2SSven Eckelmann 
400*c6c8fea2SSven Eckelmann 	return 0;
401*c6c8fea2SSven Eckelmann }
402*c6c8fea2SSven Eckelmann 
403*c6c8fea2SSven Eckelmann int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
404*c6c8fea2SSven Eckelmann {
405*c6c8fea2SSven Eckelmann 	struct ethhdr *ethhdr;
406*c6c8fea2SSven Eckelmann 	struct iphdr *iphdr;
407*c6c8fea2SSven Eckelmann 	struct ipv6hdr *ipv6hdr;
408*c6c8fea2SSven Eckelmann 	struct udphdr *udphdr;
409*c6c8fea2SSven Eckelmann 	unsigned int header_len = 0;
410*c6c8fea2SSven Eckelmann 
411*c6c8fea2SSven Eckelmann 	if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
412*c6c8fea2SSven Eckelmann 		return 0;
413*c6c8fea2SSven Eckelmann 
414*c6c8fea2SSven Eckelmann 	/* check for ethernet header */
415*c6c8fea2SSven Eckelmann 	if (!pskb_may_pull(skb, header_len + ETH_HLEN))
416*c6c8fea2SSven Eckelmann 		return 0;
417*c6c8fea2SSven Eckelmann 	ethhdr = (struct ethhdr *)skb->data;
418*c6c8fea2SSven Eckelmann 	header_len += ETH_HLEN;
419*c6c8fea2SSven Eckelmann 
420*c6c8fea2SSven Eckelmann 	/* check for initial vlan header */
421*c6c8fea2SSven Eckelmann 	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
422*c6c8fea2SSven Eckelmann 		if (!pskb_may_pull(skb, header_len + VLAN_HLEN))
423*c6c8fea2SSven Eckelmann 			return 0;
424*c6c8fea2SSven Eckelmann 		ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
425*c6c8fea2SSven Eckelmann 		header_len += VLAN_HLEN;
426*c6c8fea2SSven Eckelmann 	}
427*c6c8fea2SSven Eckelmann 
428*c6c8fea2SSven Eckelmann 	/* check for ip header */
429*c6c8fea2SSven Eckelmann 	switch (ntohs(ethhdr->h_proto)) {
430*c6c8fea2SSven Eckelmann 	case ETH_P_IP:
431*c6c8fea2SSven Eckelmann 		if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr)))
432*c6c8fea2SSven Eckelmann 			return 0;
433*c6c8fea2SSven Eckelmann 		iphdr = (struct iphdr *)(skb->data + header_len);
434*c6c8fea2SSven Eckelmann 		header_len += iphdr->ihl * 4;
435*c6c8fea2SSven Eckelmann 
436*c6c8fea2SSven Eckelmann 		/* check for udp header */
437*c6c8fea2SSven Eckelmann 		if (iphdr->protocol != IPPROTO_UDP)
438*c6c8fea2SSven Eckelmann 			return 0;
439*c6c8fea2SSven Eckelmann 
440*c6c8fea2SSven Eckelmann 		break;
441*c6c8fea2SSven Eckelmann 	case ETH_P_IPV6:
442*c6c8fea2SSven Eckelmann 		if (!pskb_may_pull(skb, header_len + sizeof(struct ipv6hdr)))
443*c6c8fea2SSven Eckelmann 			return 0;
444*c6c8fea2SSven Eckelmann 		ipv6hdr = (struct ipv6hdr *)(skb->data + header_len);
445*c6c8fea2SSven Eckelmann 		header_len += sizeof(struct ipv6hdr);
446*c6c8fea2SSven Eckelmann 
447*c6c8fea2SSven Eckelmann 		/* check for udp header */
448*c6c8fea2SSven Eckelmann 		if (ipv6hdr->nexthdr != IPPROTO_UDP)
449*c6c8fea2SSven Eckelmann 			return 0;
450*c6c8fea2SSven Eckelmann 
451*c6c8fea2SSven Eckelmann 		break;
452*c6c8fea2SSven Eckelmann 	default:
453*c6c8fea2SSven Eckelmann 		return 0;
454*c6c8fea2SSven Eckelmann 	}
455*c6c8fea2SSven Eckelmann 
456*c6c8fea2SSven Eckelmann 	if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr)))
457*c6c8fea2SSven Eckelmann 		return 0;
458*c6c8fea2SSven Eckelmann 	udphdr = (struct udphdr *)(skb->data + header_len);
459*c6c8fea2SSven Eckelmann 	header_len += sizeof(struct udphdr);
460*c6c8fea2SSven Eckelmann 
461*c6c8fea2SSven Eckelmann 	/* check for bootp port */
462*c6c8fea2SSven Eckelmann 	if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
463*c6c8fea2SSven Eckelmann 	     (ntohs(udphdr->dest) != 67))
464*c6c8fea2SSven Eckelmann 		return 0;
465*c6c8fea2SSven Eckelmann 
466*c6c8fea2SSven Eckelmann 	if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
467*c6c8fea2SSven Eckelmann 	    (ntohs(udphdr->dest) != 547))
468*c6c8fea2SSven Eckelmann 		return 0;
469*c6c8fea2SSven Eckelmann 
470*c6c8fea2SSven Eckelmann 	if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)
471*c6c8fea2SSven Eckelmann 		return -1;
472*c6c8fea2SSven Eckelmann 
473*c6c8fea2SSven Eckelmann 	if (!bat_priv->curr_gw)
474*c6c8fea2SSven Eckelmann 		return 0;
475*c6c8fea2SSven Eckelmann 
476*c6c8fea2SSven Eckelmann 	return 1;
477*c6c8fea2SSven Eckelmann }
478