2012-05-12 04:09:43 +04:00
/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
2010-12-13 14:19:28 +03:00
*
* Marek Lindner
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA
*/
# include "main.h"
2011-04-27 02:22:00 +04:00
# include "bat_sysfs.h"
2010-12-13 14:19:28 +03:00
# include "gateway_client.h"
# include "gateway_common.h"
# include "hard-interface.h"
2011-03-15 01:43:33 +03:00
# include "originator.h"
2011-09-08 15:12:53 +04:00
# include "translation-table.h"
2011-04-26 23:31:45 +04:00
# include "routing.h"
2010-12-13 14:19:28 +03:00
# include <linux/ip.h>
# include <linux/ipv6.h>
# include <linux/udp.h>
# include <linux/if_vlan.h>
2011-04-26 23:31:45 +04:00
/* This is the offset of the options field in a dhcp packet starting at
2012-05-12 04:09:43 +04:00
* the beginning of the dhcp header
*/
2012-06-04 00:19:07 +04:00
# define BATADV_DHCP_OPTIONS_OFFSET 240
# define BATADV_DHCP_REQUEST 3
2011-04-26 23:31:45 +04:00
2012-06-06 00:31:31 +04:00
static void batadv_gw_node_free_ref ( struct batadv_gw_node * gw_node )
2010-12-13 14:19:28 +03:00
{
2011-02-10 17:33:49 +03:00
if ( atomic_dec_and_test ( & gw_node - > refcount ) )
2011-05-02 10:25:02 +04:00
kfree_rcu ( gw_node , rcu ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
static struct batadv_gw_node *
batadv_gw_get_selected_gw_node ( struct batadv_priv * bat_priv )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node ;
2010-12-13 14:19:28 +03:00
2011-02-14 00:13:02 +03:00
rcu_read_lock ( ) ;
2011-03-23 13:24:34 +03:00
gw_node = rcu_dereference ( bat_priv - > curr_gw ) ;
if ( ! gw_node )
2011-02-18 15:28:10 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-03-23 13:24:34 +03:00
if ( ! atomic_inc_not_zero ( & gw_node - > refcount ) )
gw_node = NULL ;
2011-02-14 00:13:04 +03:00
2011-02-14 00:13:02 +03:00
out :
rcu_read_unlock ( ) ;
2011-03-23 13:24:34 +03:00
return gw_node ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
struct batadv_orig_node *
batadv_gw_get_selected_orig ( struct batadv_priv * bat_priv )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node ;
struct batadv_orig_node * orig_node = NULL ;
2010-12-13 14:19:28 +03:00
2012-05-12 20:33:55 +04:00
gw_node = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2011-03-23 13:24:34 +03:00
if ( ! gw_node )
goto out ;
rcu_read_lock ( ) ;
orig_node = gw_node - > orig_node ;
if ( ! orig_node )
goto unlock ;
if ( ! atomic_inc_not_zero ( & orig_node - > refcount ) )
orig_node = NULL ;
2010-12-13 14:19:28 +03:00
2011-03-23 13:24:34 +03:00
unlock :
rcu_read_unlock ( ) ;
out :
2010-12-13 14:19:28 +03:00
if ( gw_node )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( gw_node ) ;
2011-03-23 13:24:34 +03:00
return orig_node ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
static void batadv_gw_select ( struct batadv_priv * bat_priv ,
struct batadv_gw_node * new_gw_node )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * curr_gw_node ;
2010-12-13 14:19:28 +03:00
2011-03-23 13:24:34 +03:00
spin_lock_bh ( & bat_priv - > gw_list_lock ) ;
2011-02-10 17:33:49 +03:00
if ( new_gw_node & & ! atomic_inc_not_zero ( & new_gw_node - > refcount ) )
new_gw_node = NULL ;
2010-12-13 14:19:28 +03:00
2011-05-15 02:50:21 +04:00
curr_gw_node = rcu_dereference_protected ( bat_priv - > curr_gw , 1 ) ;
2011-02-14 00:13:02 +03:00
rcu_assign_pointer ( bat_priv - > curr_gw , new_gw_node ) ;
2011-02-10 17:33:49 +03:00
if ( curr_gw_node )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw_node ) ;
2011-03-23 13:24:34 +03:00
spin_unlock_bh ( & bat_priv - > gw_list_lock ) ;
}
2012-06-06 00:31:31 +04:00
void batadv_gw_deselect ( struct batadv_priv * bat_priv )
2011-03-23 13:24:34 +03:00
{
2011-04-27 02:22:00 +04:00
atomic_set ( & bat_priv - > gw_reselect , 1 ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
static struct batadv_gw_node *
batadv_gw_get_best_gw_node ( struct batadv_priv * bat_priv )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_neigh_node * router ;
2011-04-27 02:22:00 +04:00
struct hlist_node * node ;
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node , * curr_gw = NULL ;
2010-12-13 14:19:28 +03:00
uint32_t max_gw_factor = 0 , tmp_gw_factor = 0 ;
2011-04-27 02:22:00 +04:00
uint8_t max_tq = 0 ;
2010-12-13 14:19:28 +03:00
int down , up ;
2012-06-06 00:31:31 +04:00
struct batadv_orig_node * orig_node ;
2010-12-13 14:19:28 +03:00
2011-03-23 13:24:34 +03:00
rcu_read_lock ( ) ;
2010-12-13 14:19:28 +03:00
hlist_for_each_entry_rcu ( gw_node , node , & bat_priv - > gw_list , list ) {
2011-03-15 01:43:37 +03:00
if ( gw_node - > deleted )
2010-12-13 14:19:28 +03:00
continue ;
2012-05-12 04:09:30 +04:00
orig_node = gw_node - > orig_node ;
2012-05-12 04:09:34 +04:00
router = batadv_orig_node_get_router ( orig_node ) ;
2011-03-15 01:43:37 +03:00
if ( ! router )
2010-12-13 14:19:28 +03:00
continue ;
2011-04-27 02:22:00 +04:00
if ( ! atomic_inc_not_zero ( & gw_node - > refcount ) )
goto next ;
2010-12-13 14:19:28 +03:00
switch ( atomic_read ( & bat_priv - > gw_sel_class ) ) {
case 1 : /* fast connection */
2012-05-12 04:09:30 +04:00
batadv_gw_bandwidth_to_kbit ( orig_node - > gw_flags ,
& down , & up ) ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
tmp_gw_factor = ( router - > tq_avg * router - > tq_avg *
2010-12-13 14:19:28 +03:00
down * 100 * 100 ) /
2012-06-04 00:19:17 +04:00
( BATADV_TQ_LOCAL_WINDOW_SIZE *
BATADV_TQ_LOCAL_WINDOW_SIZE * 64 ) ;
2010-12-13 14:19:28 +03:00
if ( ( tmp_gw_factor > max_gw_factor ) | |
( ( tmp_gw_factor = = max_gw_factor ) & &
2011-04-27 02:22:00 +04:00
( router - > tq_avg > max_tq ) ) ) {
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2011-04-27 02:22:00 +04:00
curr_gw = gw_node ;
atomic_inc ( & curr_gw - > refcount ) ;
}
2010-12-13 14:19:28 +03:00
break ;
2012-05-12 04:09:43 +04:00
default : /* 2: stable connection (use best statistic)
2010-12-13 14:19:28 +03:00
* 3 : fast - switch ( use best statistic but change as
* soon as a better gateway appears )
* XX : late - switch ( use best statistic but change as
* soon as a better gateway appears which has
* $ routing_class more tq points )
2012-05-12 04:09:43 +04:00
*/
2011-04-27 02:22:00 +04:00
if ( router - > tq_avg > max_tq ) {
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2011-04-27 02:22:00 +04:00
curr_gw = gw_node ;
atomic_inc ( & curr_gw - > refcount ) ;
}
2010-12-13 14:19:28 +03:00
break ;
}
2011-03-15 01:43:37 +03:00
if ( router - > tq_avg > max_tq )
max_tq = router - > tq_avg ;
2010-12-13 14:19:28 +03:00
if ( tmp_gw_factor > max_gw_factor )
max_gw_factor = tmp_gw_factor ;
2011-03-15 01:43:37 +03:00
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( gw_node ) ;
2011-04-27 02:22:00 +04:00
next :
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( router ) ;
2010-12-13 14:19:28 +03:00
}
2011-04-27 02:22:00 +04:00
rcu_read_unlock ( ) ;
2010-12-13 14:19:28 +03:00
2011-04-27 02:22:00 +04:00
return curr_gw ;
}
2010-12-13 14:19:28 +03:00
2012-06-06 00:31:31 +04:00
void batadv_gw_election ( struct batadv_priv * bat_priv )
2011-04-27 02:22:00 +04:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * curr_gw = NULL , * next_gw = NULL ;
struct batadv_neigh_node * router = NULL ;
2011-06-12 13:58:58 +04:00
char gw_addr [ 18 ] = { ' \0 ' } ;
2011-04-27 02:22:00 +04:00
2012-05-12 04:09:43 +04:00
/* The batman daemon checks here if we already passed a full originator
2011-04-27 02:22:00 +04:00
* cycle in order to make sure we don ' t choose the first gateway we
* hear about . This check is based on the daemon ' s uptime which we
* don ' t have .
2012-05-12 04:09:43 +04:00
*/
2012-06-04 00:19:18 +04:00
if ( atomic_read ( & bat_priv - > gw_mode ) ! = BATADV_GW_MODE_CLIENT )
2011-04-27 02:22:00 +04:00
goto out ;
2012-05-16 22:23:22 +04:00
if ( ! batadv_atomic_dec_not_zero ( & bat_priv - > gw_reselect ) )
2011-04-27 02:22:00 +04:00
goto out ;
2012-05-12 20:33:55 +04:00
curr_gw = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2011-04-27 02:22:00 +04:00
2012-05-12 20:33:55 +04:00
next_gw = batadv_gw_get_best_gw_node ( bat_priv ) ;
2011-04-27 02:22:00 +04:00
if ( curr_gw = = next_gw )
goto out ;
if ( next_gw ) {
2011-06-12 13:58:58 +04:00
sprintf ( gw_addr , " %pM " , next_gw - > orig_node - > orig ) ;
2012-05-12 04:09:34 +04:00
router = batadv_orig_node_get_router ( next_gw - > orig_node ) ;
2011-04-27 02:22:00 +04:00
if ( ! router ) {
2012-05-12 04:09:29 +04:00
batadv_gw_deselect ( bat_priv ) ;
2011-04-27 02:22:00 +04:00
goto out ;
}
2010-12-13 14:19:28 +03:00
}
2011-04-27 02:22:00 +04:00
if ( ( curr_gw ) & & ( ! next_gw ) ) {
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Removing selected gateway - no gateway in range \n " ) ;
2012-06-04 00:19:22 +04:00
batadv_throw_uevent ( bat_priv , BATADV_UEV_GW , BATADV_UEV_DEL ,
NULL ) ;
2011-04-27 02:22:00 +04:00
} else if ( ( ! curr_gw ) & & ( next_gw ) ) {
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Adding route to gateway %pM (gw_flags: %i, tq: %i) \n " ,
next_gw - > orig_node - > orig ,
next_gw - > orig_node - > gw_flags , router - > tq_avg ) ;
2012-06-04 00:19:22 +04:00
batadv_throw_uevent ( bat_priv , BATADV_UEV_GW , BATADV_UEV_ADD ,
gw_addr ) ;
2011-04-27 02:22:00 +04:00
} else {
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Changing route to gateway %pM (gw_flags: %i, tq: %i) \n " ,
next_gw - > orig_node - > orig ,
next_gw - > orig_node - > gw_flags , router - > tq_avg ) ;
2012-06-04 00:19:22 +04:00
batadv_throw_uevent ( bat_priv , BATADV_UEV_GW , BATADV_UEV_CHANGE ,
gw_addr ) ;
2011-04-27 02:22:00 +04:00
}
2012-05-12 20:33:55 +04:00
batadv_gw_select ( bat_priv , next_gw ) ;
2011-04-27 02:22:00 +04:00
2011-03-23 13:24:34 +03:00
out :
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2011-04-27 02:22:00 +04:00
if ( next_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( next_gw ) ;
2011-04-27 02:22:00 +04:00
if ( router )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( router ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
void batadv_gw_check_election ( struct batadv_priv * bat_priv ,
struct batadv_orig_node * orig_node )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_orig_node * curr_gw_orig ;
struct batadv_neigh_node * router_gw = NULL , * router_orig = NULL ;
2010-12-13 14:19:28 +03:00
uint8_t gw_tq_avg , orig_tq_avg ;
2012-05-12 04:09:29 +04:00
curr_gw_orig = batadv_gw_get_selected_orig ( bat_priv ) ;
2011-03-15 01:43:33 +03:00
if ( ! curr_gw_orig )
goto deselect ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:34 +04:00
router_gw = batadv_orig_node_get_router ( curr_gw_orig ) ;
2011-03-15 01:43:37 +03:00
if ( ! router_gw )
goto deselect ;
2010-12-13 14:19:28 +03:00
/* this node already is the gateway */
2011-03-15 01:43:33 +03:00
if ( curr_gw_orig = = orig_node )
2011-03-15 01:43:37 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:34 +04:00
router_orig = batadv_orig_node_get_router ( orig_node ) ;
2011-03-15 01:43:37 +03:00
if ( ! router_orig )
goto out ;
2011-02-14 00:13:02 +03:00
2011-03-15 01:43:37 +03:00
gw_tq_avg = router_gw - > tq_avg ;
orig_tq_avg = router_orig - > tq_avg ;
2010-12-13 14:19:28 +03:00
/* the TQ value has to be better */
if ( orig_tq_avg < gw_tq_avg )
2011-02-14 00:13:02 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:43 +04:00
/* if the routing class is greater than 3 the value tells us how much
2010-12-13 14:19:28 +03:00
* greater the TQ value of the new gateway must be
2012-05-12 04:09:43 +04:00
*/
2010-12-13 14:19:28 +03:00
if ( ( atomic_read ( & bat_priv - > gw_sel_class ) > 3 ) & &
( orig_tq_avg - gw_tq_avg < atomic_read ( & bat_priv - > gw_sel_class ) ) )
2011-02-14 00:13:02 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i) \n " ,
gw_tq_avg , orig_tq_avg ) ;
2010-12-13 14:19:28 +03:00
deselect :
2012-05-12 04:09:29 +04:00
batadv_gw_deselect ( bat_priv ) ;
2011-02-14 00:13:02 +03:00
out :
2011-03-15 01:43:33 +03:00
if ( curr_gw_orig )
2012-05-12 04:09:34 +04:00
batadv_orig_node_free_ref ( curr_gw_orig ) ;
2011-03-15 01:43:37 +03:00
if ( router_gw )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( router_gw ) ;
2011-03-15 01:43:37 +03:00
if ( router_orig )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( router_orig ) ;
2011-03-15 01:43:33 +03:00
2011-02-14 00:13:02 +03:00
return ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
static void batadv_gw_node_add ( struct batadv_priv * bat_priv ,
struct batadv_orig_node * orig_node ,
2012-05-12 20:33:55 +04:00
uint8_t new_gwflags )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node ;
2010-12-13 14:19:28 +03:00
int down , up ;
2011-05-15 01:14:54 +04:00
gw_node = kzalloc ( sizeof ( * gw_node ) , GFP_ATOMIC ) ;
2010-12-13 14:19:28 +03:00
if ( ! gw_node )
return ;
INIT_HLIST_NODE ( & gw_node - > list ) ;
gw_node - > orig_node = orig_node ;
2011-02-10 17:33:49 +03:00
atomic_set ( & gw_node - > refcount , 1 ) ;
2010-12-13 14:19:28 +03:00
spin_lock_bh ( & bat_priv - > gw_list_lock ) ;
hlist_add_head_rcu ( & gw_node - > list , & bat_priv - > gw_list ) ;
spin_unlock_bh ( & bat_priv - > gw_list_lock ) ;
2012-05-12 04:09:30 +04:00
batadv_gw_bandwidth_to_kbit ( new_gwflags , & down , & up ) ;
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Found new gateway %pM -> gw_class: %i - %i%s/%i%s \n " ,
orig_node - > orig , new_gwflags ,
( down > 2048 ? down / 1024 : down ) ,
( down > 2048 ? " MBit " : " KBit " ) ,
( up > 2048 ? up / 1024 : up ) ,
( up > 2048 ? " MBit " : " KBit " ) ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
void batadv_gw_node_update ( struct batadv_priv * bat_priv ,
struct batadv_orig_node * orig_node ,
uint8_t new_gwflags )
2010-12-13 14:19:28 +03:00
{
struct hlist_node * node ;
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node , * curr_gw ;
2011-03-23 13:24:34 +03:00
2012-05-12 04:09:43 +04:00
/* Note: We don't need a NULL check here, since curr_gw never gets
2011-04-26 00:44:32 +04:00
* dereferenced . If curr_gw is NULL we also should not exit as we may
* have this gateway in our list ( duplication check ! ) even though we
* have no currently selected gateway .
*/
2012-05-12 20:33:55 +04:00
curr_gw = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2010-12-13 14:19:28 +03:00
rcu_read_lock ( ) ;
hlist_for_each_entry_rcu ( gw_node , node , & bat_priv - > gw_list , list ) {
if ( gw_node - > orig_node ! = orig_node )
continue ;
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Gateway class of originator %pM changed from %i to %i \n " ,
orig_node - > orig , gw_node - > orig_node - > gw_flags ,
new_gwflags ) ;
2010-12-13 14:19:28 +03:00
gw_node - > deleted = 0 ;
2012-06-04 00:19:17 +04:00
if ( new_gwflags = = BATADV_NO_FLAGS ) {
2010-12-13 14:19:28 +03:00
gw_node - > deleted = jiffies ;
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Gateway %pM removed from gateway list \n " ,
orig_node - > orig ) ;
2010-12-13 14:19:28 +03:00
2011-03-23 13:24:34 +03:00
if ( gw_node = = curr_gw )
goto deselect ;
2010-12-13 14:19:28 +03:00
}
2011-03-23 13:24:34 +03:00
goto unlock ;
2010-12-13 14:19:28 +03:00
}
2012-06-04 00:19:17 +04:00
if ( new_gwflags = = BATADV_NO_FLAGS )
2011-03-23 13:24:34 +03:00
goto unlock ;
2010-12-13 14:19:28 +03:00
2012-05-12 20:33:55 +04:00
batadv_gw_node_add ( bat_priv , orig_node , new_gwflags ) ;
2011-03-23 13:24:34 +03:00
goto unlock ;
deselect :
2012-05-12 04:09:29 +04:00
batadv_gw_deselect ( bat_priv ) ;
2011-03-23 13:24:34 +03:00
unlock :
rcu_read_unlock ( ) ;
2011-04-26 00:44:32 +04:00
2011-03-23 13:24:34 +03:00
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
void batadv_gw_node_delete ( struct batadv_priv * bat_priv ,
struct batadv_orig_node * orig_node )
2010-12-13 14:19:28 +03:00
{
2012-05-12 04:09:29 +04:00
batadv_gw_node_update ( bat_priv , orig_node , 0 ) ;
2010-12-13 14:19:28 +03:00
}
2012-06-06 00:31:31 +04:00
void batadv_gw_node_purge ( struct batadv_priv * bat_priv )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * gw_node , * curr_gw ;
2010-12-13 14:19:28 +03:00
struct hlist_node * node , * node_tmp ;
2012-06-04 00:19:17 +04:00
unsigned long timeout = msecs_to_jiffies ( 2 * BATADV_PURGE_TIMEOUT ) ;
2011-06-15 11:41:37 +04:00
int do_deselect = 0 ;
2011-03-23 13:24:34 +03:00
2012-05-12 20:33:55 +04:00
curr_gw = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2010-12-13 14:19:28 +03:00
spin_lock_bh ( & bat_priv - > gw_list_lock ) ;
hlist_for_each_entry_safe ( gw_node , node , node_tmp ,
& bat_priv - > gw_list , list ) {
if ( ( ( ! gw_node - > deleted ) | |
( time_before ( jiffies , gw_node - > deleted + timeout ) ) ) & &
2012-06-04 00:19:22 +04:00
atomic_read ( & bat_priv - > mesh_state ) = = BATADV_MESH_ACTIVE )
2010-12-13 14:19:28 +03:00
continue ;
2011-03-23 13:24:34 +03:00
if ( curr_gw = = gw_node )
do_deselect = 1 ;
2010-12-13 14:19:28 +03:00
hlist_del_rcu ( & gw_node - > list ) ;
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( gw_node ) ;
2010-12-13 14:19:28 +03:00
}
spin_unlock_bh ( & bat_priv - > gw_list_lock ) ;
2011-03-23 13:24:34 +03:00
/* gw_deselect() needs to acquire the gw_list_lock */
if ( do_deselect )
2012-05-12 04:09:29 +04:00
batadv_gw_deselect ( bat_priv ) ;
2011-03-23 13:24:34 +03:00
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 04:09:43 +04:00
/* fails if orig_node has no router */
2012-06-06 00:31:31 +04:00
static int batadv_write_buffer_text ( struct batadv_priv * bat_priv ,
struct seq_file * seq ,
const struct batadv_gw_node * gw_node )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_gw_node * curr_gw ;
struct batadv_neigh_node * router ;
2011-03-15 01:43:37 +03:00
int down , up , ret = - 1 ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:30 +04:00
batadv_gw_bandwidth_to_kbit ( gw_node - > orig_node - > gw_flags , & down , & up ) ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:34 +04:00
router = batadv_orig_node_get_router ( gw_node - > orig_node ) ;
2011-03-15 01:43:37 +03:00
if ( ! router )
goto out ;
2011-02-14 00:13:02 +03:00
2012-05-12 20:33:55 +04:00
curr_gw = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2011-02-14 00:13:02 +03:00
ret = seq_printf ( seq , " %s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s \n " ,
2011-03-23 13:24:34 +03:00
( curr_gw = = gw_node ? " => " : " " ) ,
gw_node - > orig_node - > orig ,
router - > tq_avg , router - > addr ,
router - > if_incoming - > net_dev - > name ,
gw_node - > orig_node - > gw_flags ,
( down > 2048 ? down / 1024 : down ) ,
( down > 2048 ? " MBit " : " KBit " ) ,
( up > 2048 ? up / 1024 : up ) ,
( up > 2048 ? " MBit " : " KBit " ) ) ;
2011-02-14 00:13:02 +03:00
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( router ) ;
2011-03-23 13:24:34 +03:00
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2011-03-15 01:43:37 +03:00
out :
2011-02-14 00:13:02 +03:00
return ret ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 04:09:29 +04:00
int batadv_gw_client_seq_print_text ( struct seq_file * seq , void * offset )
2010-12-13 14:19:28 +03:00
{
struct net_device * net_dev = ( struct net_device * ) seq - > private ;
2012-06-06 00:31:31 +04:00
struct batadv_priv * bat_priv = netdev_priv ( net_dev ) ;
struct batadv_hard_iface * primary_if ;
struct batadv_gw_node * gw_node ;
2010-12-13 14:19:28 +03:00
struct hlist_node * node ;
2011-04-20 17:40:58 +04:00
int gw_count = 0 , ret = 0 ;
2010-12-13 14:19:28 +03:00
2012-05-12 15:48:54 +04:00
primary_if = batadv_primary_if_get_selected ( bat_priv ) ;
2011-04-20 17:40:58 +04:00
if ( ! primary_if ) {
2012-03-07 12:07:45 +04:00
ret = seq_printf ( seq ,
" BATMAN mesh %s disabled - please specify interfaces to enable it \n " ,
2011-04-20 17:40:58 +04:00
net_dev - > name ) ;
goto out ;
2010-12-13 14:19:28 +03:00
}
2012-06-04 00:19:19 +04:00
if ( primary_if - > if_status ! = BATADV_IF_ACTIVE ) {
2012-03-07 12:07:45 +04:00
ret = seq_printf ( seq ,
" BATMAN mesh %s disabled - primary interface not active \n " ,
2011-04-20 17:40:58 +04:00
net_dev - > name ) ;
goto out ;
2010-12-13 14:19:28 +03:00
}
2012-03-07 12:07:45 +04:00
seq_printf ( seq ,
" %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)] \n " ,
2012-06-04 00:19:17 +04:00
" Gateway " , " # " , BATADV_TQ_MAX_VALUE , " Nexthop " , " outgoingIF " ,
BATADV_SOURCE_VERSION , primary_if - > net_dev - > name ,
2011-04-20 17:40:58 +04:00
primary_if - > net_dev - > dev_addr , net_dev - > name ) ;
2010-12-13 14:19:28 +03:00
rcu_read_lock ( ) ;
hlist_for_each_entry_rcu ( gw_node , node , & bat_priv - > gw_list , list ) {
if ( gw_node - > deleted )
continue ;
2011-03-15 01:43:37 +03:00
/* fails if orig_node has no router */
2012-05-12 20:33:55 +04:00
if ( batadv_write_buffer_text ( bat_priv , seq , gw_node ) < 0 )
2010-12-13 14:19:28 +03:00
continue ;
gw_count + + ;
}
rcu_read_unlock ( ) ;
if ( gw_count = = 0 )
seq_printf ( seq , " No gateways in range ... \n " ) ;
2011-04-20 17:40:58 +04:00
out :
if ( primary_if )
2012-05-12 15:48:54 +04:00
batadv_hardif_free_ref ( primary_if ) ;
2011-04-20 17:40:58 +04:00
return ret ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 20:33:55 +04:00
static bool batadv_is_type_dhcprequest ( struct sk_buff * skb , int header_len )
2011-04-26 23:31:45 +04:00
{
int ret = false ;
unsigned char * p ;
int pkt_len ;
if ( skb_linearize ( skb ) < 0 )
goto out ;
pkt_len = skb_headlen ( skb ) ;
2012-06-04 00:19:07 +04:00
if ( pkt_len < header_len + BATADV_DHCP_OPTIONS_OFFSET + 1 )
2011-04-26 23:31:45 +04:00
goto out ;
2012-06-04 00:19:07 +04:00
p = skb - > data + header_len + BATADV_DHCP_OPTIONS_OFFSET ;
pkt_len - = header_len + BATADV_DHCP_OPTIONS_OFFSET + 1 ;
2011-04-26 23:31:45 +04:00
/* Access the dhcp option lists. Each entry is made up by:
2011-07-09 19:52:13 +04:00
* - octet 1 : option type
* - octet 2 : option data len ( only if type ! = 255 and 0 )
2012-05-12 04:09:43 +04:00
* - octet 3 : option data
*/
2011-04-26 23:31:45 +04:00
while ( * p ! = 255 & & ! ret ) {
2011-07-09 19:52:13 +04:00
/* p now points to the first octet: option type */
2011-04-26 23:31:45 +04:00
if ( * p = = 53 ) {
/* type 53 is the message type option.
2012-05-12 04:09:43 +04:00
* Jump the len octet and go to the data octet
*/
2011-04-26 23:31:45 +04:00
if ( pkt_len < 2 )
goto out ;
p + = 2 ;
/* check if the message type is what we need */
2012-06-04 00:19:07 +04:00
if ( * p = = BATADV_DHCP_REQUEST )
2011-04-26 23:31:45 +04:00
ret = true ;
break ;
} else if ( * p = = 0 ) {
/* option type 0 (padding), just go forward */
if ( pkt_len < 1 )
goto out ;
pkt_len - - ;
p + + ;
} else {
/* This is any other option. So we get the length... */
if ( pkt_len < 1 )
goto out ;
pkt_len - - ;
p + + ;
/* ...and then we jump over the data */
2012-02-27 14:29:53 +04:00
if ( pkt_len < 1 + ( * p ) )
2011-04-26 23:31:45 +04:00
goto out ;
2012-02-27 14:29:53 +04:00
pkt_len - = 1 + ( * p ) ;
p + = 1 + ( * p ) ;
2011-04-26 23:31:45 +04:00
}
}
out :
return ret ;
}
2012-05-12 04:09:29 +04:00
bool batadv_gw_is_dhcp_target ( struct sk_buff * skb , unsigned int * header_len )
2010-12-13 14:19:28 +03:00
{
struct ethhdr * ethhdr ;
struct iphdr * iphdr ;
struct ipv6hdr * ipv6hdr ;
struct udphdr * udphdr ;
/* check for ethernet header */
2011-09-08 15:12:53 +04:00
if ( ! pskb_may_pull ( skb , * header_len + ETH_HLEN ) )
return false ;
2010-12-13 14:19:28 +03:00
ethhdr = ( struct ethhdr * ) skb - > data ;
2011-09-08 15:12:53 +04:00
* header_len + = ETH_HLEN ;
2010-12-13 14:19:28 +03:00
/* check for initial vlan header */
if ( ntohs ( ethhdr - > h_proto ) = = ETH_P_8021Q ) {
2011-09-08 15:12:53 +04:00
if ( ! pskb_may_pull ( skb , * header_len + VLAN_HLEN ) )
return false ;
2010-12-13 14:19:28 +03:00
ethhdr = ( struct ethhdr * ) ( skb - > data + VLAN_HLEN ) ;
2011-09-08 15:12:53 +04:00
* header_len + = VLAN_HLEN ;
2010-12-13 14:19:28 +03:00
}
/* check for ip header */
switch ( ntohs ( ethhdr - > h_proto ) ) {
case ETH_P_IP :
2011-09-08 15:12:53 +04:00
if ( ! pskb_may_pull ( skb , * header_len + sizeof ( * iphdr ) ) )
return false ;
iphdr = ( struct iphdr * ) ( skb - > data + * header_len ) ;
* header_len + = iphdr - > ihl * 4 ;
2010-12-13 14:19:28 +03:00
/* check for udp header */
if ( iphdr - > protocol ! = IPPROTO_UDP )
2011-09-08 15:12:53 +04:00
return false ;
2010-12-13 14:19:28 +03:00
break ;
case ETH_P_IPV6 :
2011-09-08 15:12:53 +04:00
if ( ! pskb_may_pull ( skb , * header_len + sizeof ( * ipv6hdr ) ) )
return false ;
ipv6hdr = ( struct ipv6hdr * ) ( skb - > data + * header_len ) ;
* header_len + = sizeof ( * ipv6hdr ) ;
2010-12-13 14:19:28 +03:00
/* check for udp header */
if ( ipv6hdr - > nexthdr ! = IPPROTO_UDP )
2011-09-08 15:12:53 +04:00
return false ;
2010-12-13 14:19:28 +03:00
break ;
default :
2011-09-08 15:12:53 +04:00
return false ;
2010-12-13 14:19:28 +03:00
}
2011-09-08 15:12:53 +04:00
if ( ! pskb_may_pull ( skb , * header_len + sizeof ( * udphdr ) ) )
return false ;
udphdr = ( struct udphdr * ) ( skb - > data + * header_len ) ;
* header_len + = sizeof ( * udphdr ) ;
2010-12-13 14:19:28 +03:00
/* check for bootp port */
if ( ( ntohs ( ethhdr - > h_proto ) = = ETH_P_IP ) & &
2012-02-28 13:55:36 +04:00
( ntohs ( udphdr - > dest ) ! = 67 ) )
2011-09-08 15:12:53 +04:00
return false ;
2010-12-13 14:19:28 +03:00
if ( ( ntohs ( ethhdr - > h_proto ) = = ETH_P_IPV6 ) & &
( ntohs ( udphdr - > dest ) ! = 547 ) )
2011-09-08 15:12:53 +04:00
return false ;
2010-12-13 14:19:28 +03:00
2011-09-08 15:12:53 +04:00
return true ;
}
2010-12-13 14:19:28 +03:00
2012-06-06 00:31:31 +04:00
bool batadv_gw_out_of_range ( struct batadv_priv * bat_priv ,
2012-05-12 04:09:29 +04:00
struct sk_buff * skb , struct ethhdr * ethhdr )
2011-09-08 15:12:53 +04:00
{
2012-06-06 00:31:31 +04:00
struct batadv_neigh_node * neigh_curr = NULL , * neigh_old = NULL ;
struct batadv_orig_node * orig_dst_node = NULL ;
struct batadv_gw_node * curr_gw = NULL ;
2011-09-08 15:12:53 +04:00
bool ret , out_of_range = false ;
unsigned int header_len = 0 ;
uint8_t curr_tq_avg ;
2012-05-12 04:09:29 +04:00
ret = batadv_gw_is_dhcp_target ( skb , & header_len ) ;
2011-09-08 15:12:53 +04:00
if ( ! ret )
goto out ;
2012-05-12 04:09:39 +04:00
orig_dst_node = batadv_transtable_search ( bat_priv , ethhdr - > h_source ,
ethhdr - > h_dest ) ;
2011-09-08 15:12:53 +04:00
if ( ! orig_dst_node )
goto out ;
if ( ! orig_dst_node - > gw_flags )
goto out ;
2012-05-12 20:33:55 +04:00
ret = batadv_is_type_dhcprequest ( skb , header_len ) ;
2011-09-08 15:12:53 +04:00
if ( ! ret )
goto out ;
switch ( atomic_read ( & bat_priv - > gw_mode ) ) {
2012-06-04 00:19:18 +04:00
case BATADV_GW_MODE_SERVER :
2011-09-08 15:12:53 +04:00
/* If we are a GW then we are our best GW. We can artificially
2012-05-12 04:09:43 +04:00
* set the tq towards ourself as the maximum value
*/
2012-06-04 00:19:17 +04:00
curr_tq_avg = BATADV_TQ_MAX_VALUE ;
2011-09-08 15:12:53 +04:00
break ;
2012-06-04 00:19:18 +04:00
case BATADV_GW_MODE_CLIENT :
2012-05-12 20:33:55 +04:00
curr_gw = batadv_gw_get_selected_gw_node ( bat_priv ) ;
2011-09-08 15:12:53 +04:00
if ( ! curr_gw )
goto out ;
/* packet is going to our gateway */
if ( curr_gw - > orig_node = = orig_dst_node )
goto out ;
/* If the dhcp packet has been sent to a different gw,
* we have to evaluate whether the old gw is still
2012-05-12 04:09:43 +04:00
* reliable enough
*/
2012-05-12 04:09:36 +04:00
neigh_curr = batadv_find_router ( bat_priv , curr_gw - > orig_node ,
NULL ) ;
2011-09-08 15:12:53 +04:00
if ( ! neigh_curr )
goto out ;
curr_tq_avg = neigh_curr - > tq_avg ;
break ;
2012-06-04 00:19:18 +04:00
case BATADV_GW_MODE_OFF :
2011-09-08 15:12:53 +04:00
default :
goto out ;
2011-04-26 23:31:45 +04:00
}
2011-09-08 15:12:53 +04:00
2012-05-12 04:09:36 +04:00
neigh_old = batadv_find_router ( bat_priv , orig_dst_node , NULL ) ;
2011-11-29 10:09:09 +04:00
if ( ! neigh_old )
2011-09-08 15:12:53 +04:00
goto out ;
2012-06-04 00:19:17 +04:00
if ( curr_tq_avg - neigh_old - > tq_avg > BATADV_GW_THRESHOLD )
2011-09-08 15:12:53 +04:00
out_of_range = true ;
out :
if ( orig_dst_node )
2012-05-12 04:09:34 +04:00
batadv_orig_node_free_ref ( orig_dst_node ) ;
2011-09-08 15:12:53 +04:00
if ( curr_gw )
2012-05-12 20:33:55 +04:00
batadv_gw_node_free_ref ( curr_gw ) ;
2011-04-26 23:31:45 +04:00
if ( neigh_old )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( neigh_old ) ;
2011-04-26 23:31:45 +04:00
if ( neigh_curr )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( neigh_curr ) ;
2011-09-08 15:12:53 +04:00
return out_of_range ;
2010-12-13 14:19:28 +03:00
}