blob: 150b6ce23df38d124d6d8cdef9f0f6d904f8f45f [file] [log] [blame]
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +00001/*
Sven Eckelmann64afe352011-01-27 10:38:15 +01002 * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +00003 *
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"
26#include <linux/ip.h>
27#include <linux/ipv6.h>
28#include <linux/udp.h>
29#include <linux/if_vlan.h>
30
Marek Lindner25b6d3c2011-02-10 14:33:49 +000031static void gw_node_free_ref(struct gw_node *gw_node)
32{
33 if (atomic_dec_and_test(&gw_node->refcount))
Paul E. McKenneyeb340b22011-05-01 23:25:02 -070034 kfree_rcu(gw_node, rcu);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000035}
36
37void *gw_get_selected(struct bat_priv *bat_priv)
38{
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000039 struct gw_node *curr_gateway_tmp;
40 struct orig_node *orig_node = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000041
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000042 rcu_read_lock();
43 curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000044 if (!curr_gateway_tmp)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000045 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000046
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000047 orig_node = curr_gateway_tmp->orig_node;
Marek Lindner7b36e8e2011-02-18 12:28:10 +000048 if (!orig_node)
49 goto out;
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000050
Marek Lindner7b36e8e2011-02-18 12:28:10 +000051 if (!atomic_inc_not_zero(&orig_node->refcount))
52 orig_node = NULL;
Linus Lüssing43c70ad2011-02-13 21:13:04 +000053
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000054out:
55 rcu_read_unlock();
56 return orig_node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000057}
58
59void gw_deselect(struct bat_priv *bat_priv)
60{
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000061 struct gw_node *gw_node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000062
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000063 spin_lock_bh(&bat_priv->gw_list_lock);
64 gw_node = rcu_dereference(bat_priv->curr_gw);
65 rcu_assign_pointer(bat_priv->curr_gw, NULL);
66 spin_unlock_bh(&bat_priv->gw_list_lock);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000067
68 if (gw_node)
Marek Lindner25b6d3c2011-02-10 14:33:49 +000069 gw_node_free_ref(gw_node);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000070}
71
Marek Lindner25b6d3c2011-02-10 14:33:49 +000072static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000073{
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000074 struct gw_node *curr_gw_node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000075
Marek Lindner25b6d3c2011-02-10 14:33:49 +000076 if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
77 new_gw_node = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000078
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000079 spin_lock_bh(&bat_priv->gw_list_lock);
80 curr_gw_node = rcu_dereference(bat_priv->curr_gw);
81 rcu_assign_pointer(bat_priv->curr_gw, new_gw_node);
82 spin_unlock_bh(&bat_priv->gw_list_lock);
Marek Lindner25b6d3c2011-02-10 14:33:49 +000083
84 if (curr_gw_node)
85 gw_node_free_ref(curr_gw_node);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000086}
87
88void gw_election(struct bat_priv *bat_priv)
89{
90 struct hlist_node *node;
Linus Lüssing5d02b3c2011-02-13 21:13:02 +000091 struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000092 uint8_t max_tq = 0;
93 uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
94 int down, up;
95
96 /**
97 * The batman daemon checks here if we already passed a full originator
98 * cycle in order to make sure we don't choose the first gateway we
99 * hear about. This check is based on the daemon's uptime which we
100 * don't have.
101 **/
102 if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT)
103 return;
104
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000105 rcu_read_lock();
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000106 curr_gw = rcu_dereference(bat_priv->curr_gw);
107 if (curr_gw) {
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000108 rcu_read_unlock();
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000109 return;
110 }
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000111
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000112 if (hlist_empty(&bat_priv->gw_list)) {
113
114 if (curr_gw) {
115 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000116 bat_dbg(DBG_BATMAN, bat_priv,
117 "Removing selected gateway - "
118 "no gateway in range\n");
119 gw_deselect(bat_priv);
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000120 } else
121 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000122
123 return;
124 }
125
126 hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
127 if (!gw_node->orig_node->router)
128 continue;
129
130 if (gw_node->deleted)
131 continue;
132
133 switch (atomic_read(&bat_priv->gw_sel_class)) {
134 case 1: /* fast connection */
135 gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags,
136 &down, &up);
137
138 tmp_gw_factor = (gw_node->orig_node->router->tq_avg *
139 gw_node->orig_node->router->tq_avg *
140 down * 100 * 100) /
141 (TQ_LOCAL_WINDOW_SIZE *
142 TQ_LOCAL_WINDOW_SIZE * 64);
143
144 if ((tmp_gw_factor > max_gw_factor) ||
145 ((tmp_gw_factor == max_gw_factor) &&
146 (gw_node->orig_node->router->tq_avg > max_tq)))
147 curr_gw_tmp = gw_node;
148 break;
149
150 default: /**
151 * 2: stable connection (use best statistic)
152 * 3: fast-switch (use best statistic but change as
153 * soon as a better gateway appears)
154 * XX: late-switch (use best statistic but change as
155 * soon as a better gateway appears which has
156 * $routing_class more tq points)
157 **/
158 if (gw_node->orig_node->router->tq_avg > max_tq)
159 curr_gw_tmp = gw_node;
160 break;
161 }
162
163 if (gw_node->orig_node->router->tq_avg > max_tq)
164 max_tq = gw_node->orig_node->router->tq_avg;
165
166 if (tmp_gw_factor > max_gw_factor)
167 max_gw_factor = tmp_gw_factor;
168 }
169
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000170 if (curr_gw != curr_gw_tmp) {
171 if ((curr_gw) && (!curr_gw_tmp))
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000172 bat_dbg(DBG_BATMAN, bat_priv,
173 "Removing selected gateway - "
174 "no gateway in range\n");
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000175 else if ((!curr_gw) && (curr_gw_tmp))
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000176 bat_dbg(DBG_BATMAN, bat_priv,
177 "Adding route to gateway %pM "
178 "(gw_flags: %i, tq: %i)\n",
179 curr_gw_tmp->orig_node->orig,
180 curr_gw_tmp->orig_node->gw_flags,
181 curr_gw_tmp->orig_node->router->tq_avg);
182 else
183 bat_dbg(DBG_BATMAN, bat_priv,
184 "Changing route to gateway %pM "
185 "(gw_flags: %i, tq: %i)\n",
186 curr_gw_tmp->orig_node->orig,
187 curr_gw_tmp->orig_node->gw_flags,
188 curr_gw_tmp->orig_node->router->tq_avg);
189
Marek Lindner25b6d3c2011-02-10 14:33:49 +0000190 gw_select(bat_priv, curr_gw_tmp);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000191 }
192
193 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000194}
195
196void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
197{
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000198 struct gw_node *curr_gateway_tmp;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000199 uint8_t gw_tq_avg, orig_tq_avg;
200
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000201 rcu_read_lock();
202 curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000203 if (!curr_gateway_tmp)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000204 goto out_rcu;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000205
206 if (!curr_gateway_tmp->orig_node)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000207 goto deselect_rcu;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000208
209 if (!curr_gateway_tmp->orig_node->router)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000210 goto deselect_rcu;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000211
212 /* this node already is the gateway */
213 if (curr_gateway_tmp->orig_node == orig_node)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000214 goto out_rcu;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000215
216 if (!orig_node->router)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000217 goto out_rcu;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000218
219 gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg;
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000220 rcu_read_unlock();
221
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000222 orig_tq_avg = orig_node->router->tq_avg;
223
224 /* the TQ value has to be better */
225 if (orig_tq_avg < gw_tq_avg)
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000226 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000227
228 /**
229 * if the routing class is greater than 3 the value tells us how much
230 * greater the TQ value of the new gateway must be
231 **/
232 if ((atomic_read(&bat_priv->gw_sel_class) > 3) &&
233 (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class)))
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000234 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000235
236 bat_dbg(DBG_BATMAN, bat_priv,
237 "Restarting gateway selection: better gateway found (tq curr: "
238 "%i, tq new: %i)\n",
239 gw_tq_avg, orig_tq_avg);
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000240 goto deselect;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000241
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000242out_rcu:
243 rcu_read_unlock();
244 goto out;
245deselect_rcu:
246 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000247deselect:
248 gw_deselect(bat_priv);
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000249out:
250 return;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000251}
252
253static void gw_node_add(struct bat_priv *bat_priv,
254 struct orig_node *orig_node, uint8_t new_gwflags)
255{
256 struct gw_node *gw_node;
257 int down, up;
258
259 gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC);
260 if (!gw_node)
261 return;
262
263 memset(gw_node, 0, sizeof(struct gw_node));
264 INIT_HLIST_NODE(&gw_node->list);
265 gw_node->orig_node = orig_node;
Marek Lindner25b6d3c2011-02-10 14:33:49 +0000266 atomic_set(&gw_node->refcount, 1);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000267
268 spin_lock_bh(&bat_priv->gw_list_lock);
269 hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list);
270 spin_unlock_bh(&bat_priv->gw_list_lock);
271
272 gw_bandwidth_to_kbit(new_gwflags, &down, &up);
273 bat_dbg(DBG_BATMAN, bat_priv,
274 "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
275 orig_node->orig, new_gwflags,
276 (down > 2048 ? down / 1024 : down),
277 (down > 2048 ? "MBit" : "KBit"),
278 (up > 2048 ? up / 1024 : up),
279 (up > 2048 ? "MBit" : "KBit"));
280}
281
282void gw_node_update(struct bat_priv *bat_priv,
283 struct orig_node *orig_node, uint8_t new_gwflags)
284{
285 struct hlist_node *node;
286 struct gw_node *gw_node;
287
288 rcu_read_lock();
289 hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
290 if (gw_node->orig_node != orig_node)
291 continue;
292
293 bat_dbg(DBG_BATMAN, bat_priv,
294 "Gateway class of originator %pM changed from "
295 "%i to %i\n",
296 orig_node->orig, gw_node->orig_node->gw_flags,
297 new_gwflags);
298
299 gw_node->deleted = 0;
300
301 if (new_gwflags == 0) {
302 gw_node->deleted = jiffies;
303 bat_dbg(DBG_BATMAN, bat_priv,
304 "Gateway %pM removed from gateway list\n",
305 orig_node->orig);
306
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000307 if (gw_node == rcu_dereference(bat_priv->curr_gw)) {
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000308 rcu_read_unlock();
309 gw_deselect(bat_priv);
310 return;
311 }
312 }
313
314 rcu_read_unlock();
315 return;
316 }
317 rcu_read_unlock();
318
319 if (new_gwflags == 0)
320 return;
321
322 gw_node_add(bat_priv, orig_node, new_gwflags);
323}
324
325void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node)
326{
327 return gw_node_update(bat_priv, orig_node, 0);
328}
329
330void gw_node_purge(struct bat_priv *bat_priv)
331{
332 struct gw_node *gw_node;
333 struct hlist_node *node, *node_tmp;
334 unsigned long timeout = 2 * PURGE_TIMEOUT * HZ;
335
336 spin_lock_bh(&bat_priv->gw_list_lock);
337
338 hlist_for_each_entry_safe(gw_node, node, node_tmp,
339 &bat_priv->gw_list, list) {
340 if (((!gw_node->deleted) ||
341 (time_before(jiffies, gw_node->deleted + timeout))) &&
342 atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)
343 continue;
344
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000345 if (rcu_dereference(bat_priv->curr_gw) == gw_node)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000346 gw_deselect(bat_priv);
347
348 hlist_del_rcu(&gw_node->list);
Marek Lindner25b6d3c2011-02-10 14:33:49 +0000349 gw_node_free_ref(gw_node);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000350 }
351
352
353 spin_unlock_bh(&bat_priv->gw_list_lock);
354}
355
356static int _write_buffer_text(struct bat_priv *bat_priv,
357 struct seq_file *seq, struct gw_node *gw_node)
358{
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000359 struct gw_node *curr_gw;
360 int down, up, ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000361
362 gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
363
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000364 rcu_read_lock();
365 curr_gw = rcu_dereference(bat_priv->curr_gw);
366
367 ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
368 (curr_gw == gw_node ? "=>" : " "),
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000369 gw_node->orig_node->orig,
370 gw_node->orig_node->router->tq_avg,
371 gw_node->orig_node->router->addr,
372 gw_node->orig_node->router->if_incoming->net_dev->name,
373 gw_node->orig_node->gw_flags,
374 (down > 2048 ? down / 1024 : down),
375 (down > 2048 ? "MBit" : "KBit"),
376 (up > 2048 ? up / 1024 : up),
377 (up > 2048 ? "MBit" : "KBit"));
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000378
379 rcu_read_unlock();
380 return ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000381}
382
383int gw_client_seq_print_text(struct seq_file *seq, void *offset)
384{
385 struct net_device *net_dev = (struct net_device *)seq->private;
386 struct bat_priv *bat_priv = netdev_priv(net_dev);
387 struct gw_node *gw_node;
388 struct hlist_node *node;
389 int gw_count = 0;
390
391 if (!bat_priv->primary_if) {
392
393 return seq_printf(seq, "BATMAN mesh %s disabled - please "
394 "specify interfaces to enable it\n",
395 net_dev->name);
396 }
397
398 if (bat_priv->primary_if->if_status != IF_ACTIVE) {
399
400 return seq_printf(seq, "BATMAN mesh %s disabled - "
401 "primary interface not active\n",
402 net_dev->name);
403 }
404
405 seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... "
406 "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
407 "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
408 "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
409 bat_priv->primary_if->net_dev->name,
410 bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
411
412 rcu_read_lock();
413 hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
414 if (gw_node->deleted)
415 continue;
416
417 if (!gw_node->orig_node->router)
418 continue;
419
420 _write_buffer_text(bat_priv, seq, gw_node);
421 gw_count++;
422 }
423 rcu_read_unlock();
424
425 if (gw_count == 0)
426 seq_printf(seq, "No gateways in range ...\n");
427
428 return 0;
429}
430
431int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
432{
433 struct ethhdr *ethhdr;
434 struct iphdr *iphdr;
435 struct ipv6hdr *ipv6hdr;
436 struct udphdr *udphdr;
437 unsigned int header_len = 0;
438
439 if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
440 return 0;
441
442 /* check for ethernet header */
443 if (!pskb_may_pull(skb, header_len + ETH_HLEN))
444 return 0;
445 ethhdr = (struct ethhdr *)skb->data;
446 header_len += ETH_HLEN;
447
448 /* check for initial vlan header */
449 if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
450 if (!pskb_may_pull(skb, header_len + VLAN_HLEN))
451 return 0;
452 ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
453 header_len += VLAN_HLEN;
454 }
455
456 /* check for ip header */
457 switch (ntohs(ethhdr->h_proto)) {
458 case ETH_P_IP:
459 if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr)))
460 return 0;
461 iphdr = (struct iphdr *)(skb->data + header_len);
462 header_len += iphdr->ihl * 4;
463
464 /* check for udp header */
465 if (iphdr->protocol != IPPROTO_UDP)
466 return 0;
467
468 break;
469 case ETH_P_IPV6:
470 if (!pskb_may_pull(skb, header_len + sizeof(struct ipv6hdr)))
471 return 0;
472 ipv6hdr = (struct ipv6hdr *)(skb->data + header_len);
473 header_len += sizeof(struct ipv6hdr);
474
475 /* check for udp header */
476 if (ipv6hdr->nexthdr != IPPROTO_UDP)
477 return 0;
478
479 break;
480 default:
481 return 0;
482 }
483
484 if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr)))
485 return 0;
486 udphdr = (struct udphdr *)(skb->data + header_len);
487 header_len += sizeof(struct udphdr);
488
489 /* check for bootp port */
490 if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
491 (ntohs(udphdr->dest) != 67))
492 return 0;
493
494 if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
495 (ntohs(udphdr->dest) != 547))
496 return 0;
497
498 if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)
499 return -1;
500
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000501 rcu_read_lock();
502 if (!rcu_dereference(bat_priv->curr_gw)) {
503 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000504 return 0;
Linus Lüssing5d02b3c2011-02-13 21:13:02 +0000505 }
506 rcu_read_unlock();
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000507
508 return 1;
509}