2010-12-13 14:19:28 +03:00
/*
2011-01-27 12:38:15 +03:00
* Copyright ( C ) 2007 - 2011 B . A . T . M . A . N . contributors :
2010-12-13 14:19:28 +03:00
*
* Marek Lindner , Simon Wunderlich
*
* 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"
# include "routing.h"
# include "send.h"
# include "soft-interface.h"
# include "hard-interface.h"
# include "icmp_socket.h"
# include "translation-table.h"
# include "originator.h"
# include "vis.h"
# include "unicast.h"
2011-07-30 14:04:12 +04:00
# include "bat_ogm.h"
2010-12-13 14:19:28 +03:00
2011-02-18 15:33:20 +03:00
void slide_own_bcast_window ( struct hard_iface * hard_iface )
2010-12-13 14:19:28 +03:00
{
2011-02-18 15:33:20 +03:00
struct bat_priv * bat_priv = netdev_priv ( hard_iface - > soft_iface ) ;
2010-12-13 14:19:28 +03:00
struct hashtable_t * hash = bat_priv - > orig_hash ;
2011-02-18 15:28:09 +03:00
struct hlist_node * node ;
2010-12-13 14:19:28 +03:00
struct hlist_head * head ;
struct orig_node * orig_node ;
unsigned long * word ;
2011-10-05 19:05:25 +04:00
uint32_t i ;
2010-12-13 14:19:28 +03:00
size_t word_index ;
for ( i = 0 ; i < hash - > size ; i + + ) {
head = & hash - > table [ i ] ;
2011-01-19 23:01:40 +03:00
rcu_read_lock ( ) ;
2011-02-18 15:28:09 +03:00
hlist_for_each_entry_rcu ( orig_node , node , head , hash_entry ) {
2011-01-19 23:01:42 +03:00
spin_lock_bh ( & orig_node - > ogm_cnt_lock ) ;
2011-02-18 15:33:20 +03:00
word_index = hard_iface - > if_num * NUM_WORDS ;
2010-12-13 14:19:28 +03:00
word = & ( orig_node - > bcast_own [ word_index ] ) ;
bit_get_packet ( bat_priv , word , 1 , 0 ) ;
2011-02-18 15:33:20 +03:00
orig_node - > bcast_own_sum [ hard_iface - > if_num ] =
2010-12-13 14:19:28 +03:00
bit_packet_count ( word ) ;
2011-01-19 23:01:42 +03:00
spin_unlock_bh ( & orig_node - > ogm_cnt_lock ) ;
2010-12-13 14:19:28 +03:00
}
2011-01-19 23:01:40 +03:00
rcu_read_unlock ( ) ;
2010-12-13 14:19:28 +03:00
}
}
2011-07-30 14:04:12 +04:00
static void _update_route ( struct bat_priv * bat_priv ,
struct orig_node * orig_node ,
struct neigh_node * neigh_node )
2010-12-13 14:19:28 +03:00
{
2011-03-15 01:43:37 +03:00
struct neigh_node * curr_router ;
curr_router = orig_node_get_router ( orig_node ) ;
2010-12-13 00:57:10 +03:00
2010-12-13 14:19:28 +03:00
/* route deleted */
2011-03-15 01:43:37 +03:00
if ( ( curr_router ) & & ( ! neigh_node ) ) {
2010-12-13 14:19:28 +03:00
bat_dbg ( DBG_ROUTES , bat_priv , " Deleting route towards: %pM \n " ,
orig_node - > orig ) ;
2011-05-05 10:42:45 +04:00
tt_global_del_orig ( bat_priv , orig_node ,
2011-04-27 16:27:44 +04:00
" Deleted route towards originator " ) ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
/* route added */
} else if ( ( ! curr_router ) & & ( neigh_node ) ) {
2010-12-13 14:19:28 +03:00
bat_dbg ( DBG_ROUTES , bat_priv ,
" Adding route towards: %pM (via %pM) \n " ,
orig_node - > orig , neigh_node - > addr ) ;
2011-03-15 01:43:37 +03:00
/* route changed */
2011-05-10 13:22:37 +04:00
} else if ( neigh_node & & curr_router ) {
2010-12-13 14:19:28 +03:00
bat_dbg ( DBG_ROUTES , bat_priv ,
" Changing route towards: %pM "
" (now via %pM - was via %pM) \n " ,
orig_node - > orig , neigh_node - > addr ,
2011-03-15 01:43:37 +03:00
curr_router - > addr ) ;
2010-12-13 14:19:28 +03:00
}
2011-03-15 01:43:37 +03:00
if ( curr_router )
neigh_node_free_ref ( curr_router ) ;
/* increase refcount of new best neighbor */
2011-02-10 17:33:53 +03:00
if ( neigh_node & & ! atomic_inc_not_zero ( & neigh_node - > refcount ) )
neigh_node = NULL ;
2011-03-15 01:43:37 +03:00
spin_lock_bh ( & orig_node - > neigh_list_lock ) ;
rcu_assign_pointer ( orig_node - > router , neigh_node ) ;
spin_unlock_bh ( & orig_node - > neigh_list_lock ) ;
/* decrease refcount of previous best neighbor */
if ( curr_router )
neigh_node_free_ref ( curr_router ) ;
2010-12-13 14:19:28 +03:00
}
2011-07-30 14:04:12 +04:00
void update_route ( struct bat_priv * bat_priv , struct orig_node * orig_node ,
struct neigh_node * neigh_node )
2010-12-13 14:19:28 +03:00
{
2011-03-15 01:43:37 +03:00
struct neigh_node * router = NULL ;
2010-12-13 14:19:28 +03:00
if ( ! orig_node )
2011-03-15 01:43:37 +03:00
goto out ;
router = orig_node_get_router ( orig_node ) ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
if ( router ! = neigh_node )
2011-07-30 14:04:12 +04:00
_update_route ( bat_priv , orig_node , neigh_node ) ;
2011-03-15 01:43:37 +03:00
out :
if ( router )
neigh_node_free_ref ( router ) ;
2010-12-13 14:19:28 +03:00
}
2011-01-19 23:01:43 +03:00
/* caller must hold the neigh_list_lock */
void bonding_candidate_del ( struct orig_node * orig_node ,
struct neigh_node * neigh_node )
{
/* this neighbor is not part of our candidate list */
if ( list_empty ( & neigh_node - > bonding_list ) )
goto out ;
list_del_rcu ( & neigh_node - > bonding_list ) ;
INIT_LIST_HEAD ( & neigh_node - > bonding_list ) ;
2011-02-10 17:33:53 +03:00
neigh_node_free_ref ( neigh_node ) ;
2011-01-19 23:01:43 +03:00
atomic_dec ( & orig_node - > bond_candidates ) ;
out :
return ;
}
2011-07-30 14:04:12 +04:00
void bonding_candidate_add ( struct orig_node * orig_node ,
struct neigh_node * neigh_node )
2011-01-19 23:01:43 +03:00
{
struct hlist_node * node ;
2011-03-15 01:43:37 +03:00
struct neigh_node * tmp_neigh_node , * router = NULL ;
uint8_t interference_candidate = 0 ;
2011-01-19 23:01:43 +03:00
spin_lock_bh ( & orig_node - > neigh_list_lock ) ;
/* only consider if it has the same primary address ... */
2011-02-18 15:28:08 +03:00
if ( ! compare_eth ( orig_node - > orig ,
neigh_node - > orig_node - > primary_addr ) )
2011-01-19 23:01:43 +03:00
goto candidate_del ;
2011-03-15 01:43:37 +03:00
router = orig_node_get_router ( orig_node ) ;
if ( ! router )
2011-01-19 23:01:43 +03:00
goto candidate_del ;
/* ... and is good enough to be considered */
2011-03-15 01:43:37 +03:00
if ( neigh_node - > tq_avg < router - > tq_avg - BONDING_TQ_THRESHOLD )
2011-01-19 23:01:43 +03:00
goto candidate_del ;
/**
* check if we have another candidate with the same mac address or
* interface . If we do , we won ' t select this candidate because of
* possible interference .
*/
hlist_for_each_entry_rcu ( tmp_neigh_node , node ,
& orig_node - > neigh_list , list ) {
if ( tmp_neigh_node = = neigh_node )
continue ;
/* we only care if the other candidate is even
* considered as candidate . */
if ( list_empty ( & tmp_neigh_node - > bonding_list ) )
continue ;
if ( ( neigh_node - > if_incoming = = tmp_neigh_node - > if_incoming ) | |
2011-02-18 15:28:08 +03:00
( compare_eth ( neigh_node - > addr , tmp_neigh_node - > addr ) ) ) {
2011-01-19 23:01:43 +03:00
interference_candidate = 1 ;
break ;
}
}
/* don't care further if it is an interference candidate */
if ( interference_candidate )
goto candidate_del ;
/* this neighbor already is part of our candidate list */
if ( ! list_empty ( & neigh_node - > bonding_list ) )
goto out ;
2011-02-10 17:33:53 +03:00
if ( ! atomic_inc_not_zero ( & neigh_node - > refcount ) )
goto out ;
2011-01-19 23:01:43 +03:00
list_add_rcu ( & neigh_node - > bonding_list , & orig_node - > bond_list ) ;
atomic_inc ( & orig_node - > bond_candidates ) ;
goto out ;
candidate_del :
bonding_candidate_del ( orig_node , neigh_node ) ;
out :
spin_unlock_bh ( & orig_node - > neigh_list_lock ) ;
2011-03-15 01:43:37 +03:00
if ( router )
neigh_node_free_ref ( router ) ;
2011-01-19 23:01:43 +03:00
}
/* copy primary address for bonding */
2011-07-30 14:04:12 +04:00
void bonding_save_primary ( const struct orig_node * orig_node ,
struct orig_node * orig_neigh_node ,
const struct batman_ogm_packet * batman_ogm_packet )
2011-01-19 23:01:43 +03:00
{
2011-07-29 19:31:50 +04:00
if ( ! ( batman_ogm_packet - > flags & PRIMARIES_FIRST_HOP ) )
2011-01-19 23:01:43 +03:00
return ;
memcpy ( orig_neigh_node - > primary_addr , orig_node - > orig , ETH_ALEN ) ;
}
2010-12-13 14:19:28 +03:00
/* checks whether the host restarted and is in the protection time.
* returns :
* 0 if the packet is to be accepted
* 1 if the packet is to be ignored .
*/
2011-07-30 14:04:12 +04:00
int window_protected ( struct bat_priv * bat_priv , int32_t seq_num_diff ,
unsigned long * last_reset )
2010-12-13 14:19:28 +03:00
{
if ( ( seq_num_diff < = - TQ_LOCAL_WINDOW_SIZE )
| | ( seq_num_diff > = EXPECTED_SEQNO_RANGE ) ) {
if ( time_after ( jiffies , * last_reset +
msecs_to_jiffies ( RESET_PROTECTION_MS ) ) ) {
* last_reset = jiffies ;
bat_dbg ( DBG_BATMAN , bat_priv ,
" old packet received, start protection \n " ) ;
return 0 ;
} else
return 1 ;
}
return 0 ;
}
2011-07-30 14:04:12 +04:00
int recv_bat_ogm_packet ( struct sk_buff * skb , struct hard_iface * hard_iface )
2010-12-13 14:19:28 +03:00
{
struct ethhdr * ethhdr ;
/* drop packet if it has not necessary minimum size */
2011-07-29 19:31:50 +04:00
if ( unlikely ( ! pskb_may_pull ( skb , BATMAN_OGM_LEN ) ) )
2010-12-13 14:19:28 +03:00
return NET_RX_DROP ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with broadcast indication but unicast recipient */
if ( ! is_broadcast_ether_addr ( ethhdr - > h_dest ) )
return NET_RX_DROP ;
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
return NET_RX_DROP ;
/* create a copy of the skb, if needed, to modify it. */
if ( skb_cow ( skb , 0 ) < 0 )
return NET_RX_DROP ;
/* keep skb linear */
if ( skb_linearize ( skb ) < 0 )
return NET_RX_DROP ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
2011-07-30 14:04:12 +04:00
bat_ogm_receive ( ethhdr , skb - > data , skb_headlen ( skb ) , hard_iface ) ;
2010-12-13 14:19:28 +03:00
kfree_skb ( skb ) ;
return NET_RX_SUCCESS ;
}
static int recv_my_icmp_packet ( struct bat_priv * bat_priv ,
struct sk_buff * skb , size_t icmp_len )
{
2011-04-20 17:40:58 +04:00
struct hard_iface * primary_if = NULL ;
2011-02-10 17:33:53 +03:00
struct orig_node * orig_node = NULL ;
2011-03-15 01:43:37 +03:00
struct neigh_node * router = NULL ;
2010-12-13 14:19:28 +03:00
struct icmp_packet_rr * icmp_packet ;
2011-02-10 17:33:53 +03:00
int ret = NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
icmp_packet = ( struct icmp_packet_rr * ) skb - > data ;
/* add data to device queue */
if ( icmp_packet - > msg_type ! = ECHO_REQUEST ) {
bat_socket_receive_packet ( icmp_packet , icmp_len ) ;
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
}
2011-04-20 17:40:58 +04:00
primary_if = primary_if_get_selected ( bat_priv ) ;
if ( ! primary_if )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* answer echo request (ping) */
/* get routing information */
2011-02-18 15:28:09 +03:00
orig_node = orig_hash_find ( bat_priv , icmp_packet - > orig ) ;
2011-02-10 17:33:53 +03:00
if ( ! orig_node )
2011-03-15 01:43:37 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
router = orig_node_get_router ( orig_node ) ;
if ( ! router )
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
/* create a copy of the skb, if needed, to modify it. */
if ( skb_cow ( skb , sizeof ( struct ethhdr ) ) < 0 )
goto out ;
icmp_packet = ( struct icmp_packet_rr * ) skb - > data ;
memcpy ( icmp_packet - > dst , icmp_packet - > orig , ETH_ALEN ) ;
2011-04-20 17:40:58 +04:00
memcpy ( icmp_packet - > orig , primary_if - > net_dev - > dev_addr , ETH_ALEN ) ;
2011-02-10 17:33:53 +03:00
icmp_packet - > msg_type = ECHO_REPLY ;
icmp_packet - > ttl = TTL ;
2011-03-15 01:43:37 +03:00
send_skb_packet ( skb , router - > if_incoming , router - > addr ) ;
2011-02-10 17:33:53 +03:00
ret = NET_RX_SUCCESS ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
out :
2011-04-20 17:40:58 +04:00
if ( primary_if )
hardif_free_ref ( primary_if ) ;
2011-03-15 01:43:37 +03:00
if ( router )
neigh_node_free_ref ( router ) ;
2011-02-10 17:33:53 +03:00
if ( orig_node )
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( orig_node ) ;
2010-12-13 14:19:28 +03:00
return ret ;
}
static int recv_icmp_ttl_exceeded ( struct bat_priv * bat_priv ,
2010-12-29 19:15:19 +03:00
struct sk_buff * skb )
2010-12-13 14:19:28 +03:00
{
2011-04-20 17:40:58 +04:00
struct hard_iface * primary_if = NULL ;
2011-02-10 17:33:53 +03:00
struct orig_node * orig_node = NULL ;
2011-03-15 01:43:37 +03:00
struct neigh_node * router = NULL ;
2010-12-13 14:19:28 +03:00
struct icmp_packet * icmp_packet ;
2011-02-10 17:33:53 +03:00
int ret = NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
icmp_packet = ( struct icmp_packet * ) skb - > data ;
/* send TTL exceeded if packet is an echo request (traceroute) */
if ( icmp_packet - > msg_type ! = ECHO_REQUEST ) {
pr_debug ( " Warning - can't forward icmp packet from %pM to "
" %pM: ttl exceeded \n " , icmp_packet - > orig ,
icmp_packet - > dst ) ;
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
}
2011-04-20 17:40:58 +04:00
primary_if = primary_if_get_selected ( bat_priv ) ;
if ( ! primary_if )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* get routing information */
2011-02-18 15:28:09 +03:00
orig_node = orig_hash_find ( bat_priv , icmp_packet - > orig ) ;
2011-02-10 17:33:53 +03:00
if ( ! orig_node )
2011-03-15 01:43:37 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
router = orig_node_get_router ( orig_node ) ;
if ( ! router )
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
/* create a copy of the skb, if needed, to modify it. */
if ( skb_cow ( skb , sizeof ( struct ethhdr ) ) < 0 )
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
icmp_packet = ( struct icmp_packet * ) skb - > data ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
memcpy ( icmp_packet - > dst , icmp_packet - > orig , ETH_ALEN ) ;
2011-04-20 17:40:58 +04:00
memcpy ( icmp_packet - > orig , primary_if - > net_dev - > dev_addr , ETH_ALEN ) ;
2011-02-10 17:33:53 +03:00
icmp_packet - > msg_type = TTL_EXCEEDED ;
icmp_packet - > ttl = TTL ;
2011-03-15 01:43:37 +03:00
send_skb_packet ( skb , router - > if_incoming , router - > addr ) ;
2011-02-10 17:33:53 +03:00
ret = NET_RX_SUCCESS ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
out :
2011-04-20 17:40:58 +04:00
if ( primary_if )
hardif_free_ref ( primary_if ) ;
2011-03-15 01:43:37 +03:00
if ( router )
neigh_node_free_ref ( router ) ;
2011-02-10 17:33:53 +03:00
if ( orig_node )
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( orig_node ) ;
2010-12-13 14:19:28 +03:00
return ret ;
}
2011-02-18 15:33:20 +03:00
int recv_icmp_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
struct icmp_packet_rr * icmp_packet ;
struct ethhdr * ethhdr ;
2011-02-10 17:33:53 +03:00
struct orig_node * orig_node = NULL ;
2011-03-15 01:43:37 +03:00
struct neigh_node * router = NULL ;
2010-12-13 14:19:28 +03:00
int hdr_size = sizeof ( struct icmp_packet ) ;
2011-02-10 17:33:53 +03:00
int ret = NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
/**
* we truncate all incoming icmp packets if they don ' t match our size
*/
if ( skb - > len > = sizeof ( struct icmp_packet_rr ) )
hdr_size = sizeof ( struct icmp_packet_rr ) ;
/* drop packet if it has not necessary minimum size */
if ( unlikely ( ! pskb_may_pull ( skb , hdr_size ) ) )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with unicast indication but broadcast recipient */
if ( is_broadcast_ether_addr ( ethhdr - > h_dest ) )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* not for me */
if ( ! is_my_mac ( ethhdr - > h_dest ) )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
icmp_packet = ( struct icmp_packet_rr * ) skb - > data ;
/* add record route information if not full */
if ( ( hdr_size = = sizeof ( struct icmp_packet_rr ) ) & &
( icmp_packet - > rr_cur < BAT_RR_LEN ) ) {
memcpy ( & ( icmp_packet - > rr [ icmp_packet - > rr_cur ] ) ,
ethhdr - > h_dest , ETH_ALEN ) ;
icmp_packet - > rr_cur + + ;
}
/* packet for me */
if ( is_my_mac ( icmp_packet - > dst ) )
return recv_my_icmp_packet ( bat_priv , skb , hdr_size ) ;
/* TTL exceeded */
if ( icmp_packet - > ttl < 2 )
2010-12-29 19:15:19 +03:00
return recv_icmp_ttl_exceeded ( bat_priv , skb ) ;
2010-12-13 14:19:28 +03:00
/* get routing information */
2011-02-18 15:28:09 +03:00
orig_node = orig_hash_find ( bat_priv , icmp_packet - > dst ) ;
2011-02-10 17:33:53 +03:00
if ( ! orig_node )
2011-03-15 01:43:37 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:37 +03:00
router = orig_node_get_router ( orig_node ) ;
if ( ! router )
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
/* create a copy of the skb, if needed, to modify it. */
if ( skb_cow ( skb , sizeof ( struct ethhdr ) ) < 0 )
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
icmp_packet = ( struct icmp_packet_rr * ) skb - > data ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
/* decrement ttl */
icmp_packet - > ttl - - ;
/* route it */
2011-03-15 01:43:37 +03:00
send_skb_packet ( skb , router - > if_incoming , router - > addr ) ;
2011-02-10 17:33:53 +03:00
ret = NET_RX_SUCCESS ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
out :
2011-03-15 01:43:37 +03:00
if ( router )
neigh_node_free_ref ( router ) ;
2011-02-10 17:33:53 +03:00
if ( orig_node )
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( orig_node ) ;
2010-12-13 14:19:28 +03:00
return ret ;
}
2011-03-15 01:43:27 +03:00
/* In the bonding case, send the packets in a round
* robin fashion over the remaining interfaces .
*
* This method rotates the bonding list and increases the
* returned router ' s refcount . */
static struct neigh_node * find_bond_router ( struct orig_node * primary_orig ,
2011-05-15 01:14:50 +04:00
const struct hard_iface * recv_if )
2011-03-15 01:43:27 +03:00
{
struct neigh_node * tmp_neigh_node ;
struct neigh_node * router = NULL , * first_candidate = NULL ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( tmp_neigh_node , & primary_orig - > bond_list ,
bonding_list ) {
if ( ! first_candidate )
first_candidate = tmp_neigh_node ;
/* recv_if == NULL on the first node. */
if ( tmp_neigh_node - > if_incoming = = recv_if )
continue ;
if ( ! atomic_inc_not_zero ( & tmp_neigh_node - > refcount ) )
continue ;
router = tmp_neigh_node ;
break ;
}
/* use the first candidate if nothing was found. */
if ( ! router & & first_candidate & &
atomic_inc_not_zero ( & first_candidate - > refcount ) )
router = first_candidate ;
if ( ! router )
goto out ;
/* selected should point to the next element
* after the current router */
spin_lock_bh ( & primary_orig - > neigh_list_lock ) ;
/* this is a list_move(), which unfortunately
* does not exist as rcu version */
list_del_rcu ( & primary_orig - > bond_list ) ;
list_add_rcu ( & primary_orig - > bond_list ,
& router - > bonding_list ) ;
spin_unlock_bh ( & primary_orig - > neigh_list_lock ) ;
out :
rcu_read_unlock ( ) ;
return router ;
}
/* Interface Alternating: Use the best of the
* remaining candidates which are not using
* this interface .
*
* Increases the returned router ' s refcount */
static struct neigh_node * find_ifalter_router ( struct orig_node * primary_orig ,
2011-05-15 01:14:50 +04:00
const struct hard_iface * recv_if )
2011-03-15 01:43:27 +03:00
{
struct neigh_node * tmp_neigh_node ;
struct neigh_node * router = NULL , * first_candidate = NULL ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( tmp_neigh_node , & primary_orig - > bond_list ,
bonding_list ) {
if ( ! first_candidate )
first_candidate = tmp_neigh_node ;
/* recv_if == NULL on the first node. */
if ( tmp_neigh_node - > if_incoming = = recv_if )
continue ;
if ( ! atomic_inc_not_zero ( & tmp_neigh_node - > refcount ) )
continue ;
/* if we don't have a router yet
* or this one is better , choose it . */
if ( ( ! router ) | |
( tmp_neigh_node - > tq_avg > router - > tq_avg ) ) {
/* decrement refcount of
* previously selected router */
if ( router )
neigh_node_free_ref ( router ) ;
router = tmp_neigh_node ;
atomic_inc_not_zero ( & router - > refcount ) ;
}
neigh_node_free_ref ( tmp_neigh_node ) ;
}
/* use the first candidate if nothing was found. */
if ( ! router & & first_candidate & &
atomic_inc_not_zero ( & first_candidate - > refcount ) )
router = first_candidate ;
rcu_read_unlock ( ) ;
return router ;
}
2011-04-27 16:27:44 +04:00
int recv_tt_query ( struct sk_buff * skb , struct hard_iface * recv_if )
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
struct tt_query_packet * tt_query ;
struct ethhdr * ethhdr ;
/* drop packet if it has not necessary minimum size */
if ( unlikely ( ! pskb_may_pull ( skb , sizeof ( struct tt_query_packet ) ) ) )
goto out ;
/* I could need to modify it */
if ( skb_cow ( skb , sizeof ( struct tt_query_packet ) ) < 0 )
goto out ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with unicast indication but broadcast recipient */
if ( is_broadcast_ether_addr ( ethhdr - > h_dest ) )
goto out ;
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
goto out ;
tt_query = ( struct tt_query_packet * ) skb - > data ;
tt_query - > tt_data = ntohs ( tt_query - > tt_data ) ;
switch ( tt_query - > flags & TT_QUERY_TYPE_MASK ) {
case TT_REQUEST :
/* If we cannot provide an answer the tt_request is
* forwarded */
if ( ! send_tt_response ( bat_priv , tt_query ) ) {
bat_dbg ( DBG_TT , bat_priv ,
" Routing TT_REQUEST to %pM [%c] \n " ,
tt_query - > dst ,
( tt_query - > flags & TT_FULL_TABLE ? ' F ' : ' . ' ) ) ;
tt_query - > tt_data = htons ( tt_query - > tt_data ) ;
return route_unicast_packet ( skb , recv_if ) ;
}
break ;
case TT_RESPONSE :
2011-07-09 19:52:13 +04:00
/* packet needs to be linearized to access the TT changes */
2011-04-27 16:27:44 +04:00
if ( skb_linearize ( skb ) < 0 )
goto out ;
if ( is_my_mac ( tt_query - > dst ) )
handle_tt_response ( bat_priv , tt_query ) ;
else {
bat_dbg ( DBG_TT , bat_priv ,
" Routing TT_RESPONSE to %pM [%c] \n " ,
tt_query - > dst ,
( tt_query - > flags & TT_FULL_TABLE ? ' F ' : ' . ' ) ) ;
tt_query - > tt_data = htons ( tt_query - > tt_data ) ;
return route_unicast_packet ( skb , recv_if ) ;
}
break ;
}
out :
/* returning NET_RX_DROP will make the caller function kfree the skb */
return NET_RX_DROP ;
}
2011-04-27 16:27:57 +04:00
int recv_roam_adv ( struct sk_buff * skb , struct hard_iface * recv_if )
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
struct roam_adv_packet * roam_adv_packet ;
struct orig_node * orig_node ;
struct ethhdr * ethhdr ;
/* drop packet if it has not necessary minimum size */
if ( unlikely ( ! pskb_may_pull ( skb , sizeof ( struct roam_adv_packet ) ) ) )
goto out ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with unicast indication but broadcast recipient */
if ( is_broadcast_ether_addr ( ethhdr - > h_dest ) )
goto out ;
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
goto out ;
roam_adv_packet = ( struct roam_adv_packet * ) skb - > data ;
if ( ! is_my_mac ( roam_adv_packet - > dst ) )
return route_unicast_packet ( skb , recv_if ) ;
orig_node = orig_hash_find ( bat_priv , roam_adv_packet - > src ) ;
if ( ! orig_node )
goto out ;
bat_dbg ( DBG_TT , bat_priv , " Received ROAMING_ADV from %pM "
" (client %pM) \n " , roam_adv_packet - > src ,
roam_adv_packet - > client ) ;
tt_global_add ( bat_priv , orig_node , roam_adv_packet - > client ,
2011-07-07 17:35:35 +04:00
atomic_read ( & orig_node - > last_ttvn ) + 1 , true , false ) ;
2011-04-27 16:27:57 +04:00
/* Roaming phase starts: I have new information but the ttvn has not
* been incremented yet . This flag will make me check all the incoming
* packets for the correct destination . */
bat_priv - > tt_poss_change = true ;
orig_node_free_ref ( orig_node ) ;
out :
/* returning NET_RX_DROP will make the caller function kfree the skb */
return NET_RX_DROP ;
}
2010-12-13 14:19:28 +03:00
/* find a suitable router for this originator, and use
2011-01-19 23:01:43 +03:00
* bonding if possible . increases the found neighbors
* refcount . */
2010-12-13 14:19:28 +03:00
struct neigh_node * find_router ( struct bat_priv * bat_priv ,
struct orig_node * orig_node ,
2011-05-15 01:14:50 +04:00
const struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct orig_node * primary_orig_node ;
struct orig_node * router_orig ;
2011-03-15 01:43:27 +03:00
struct neigh_node * router ;
2010-12-13 14:19:28 +03:00
static uint8_t zero_mac [ ETH_ALEN ] = { 0 , 0 , 0 , 0 , 0 , 0 } ;
int bonding_enabled ;
if ( ! orig_node )
return NULL ;
2011-03-15 01:43:37 +03:00
router = orig_node_get_router ( orig_node ) ;
if ( ! router )
2011-05-05 16:14:46 +04:00
goto err ;
2010-12-13 14:19:28 +03:00
/* without bonding, the first node should
* always choose the default router . */
bonding_enabled = atomic_read ( & bat_priv - > bonding ) ;
2011-01-19 23:01:43 +03:00
rcu_read_lock ( ) ;
/* select default router to output */
2011-03-15 01:43:37 +03:00
router_orig = router - > orig_node ;
2011-05-05 16:14:46 +04:00
if ( ! router_orig )
goto err_unlock ;
2011-01-19 23:01:43 +03:00
if ( ( ! recv_if ) & & ( ! bonding_enabled ) )
goto return_router ;
2010-12-13 14:19:28 +03:00
/* if we have something in the primary_addr, we can search
* for a potential bonding candidate . */
2011-02-18 15:28:08 +03:00
if ( compare_eth ( router_orig - > primary_addr , zero_mac ) )
2011-01-19 23:01:43 +03:00
goto return_router ;
2010-12-13 14:19:28 +03:00
/* find the orig_node which has the primary interface. might
* even be the same as our router_orig in many cases */
2011-02-18 15:28:08 +03:00
if ( compare_eth ( router_orig - > primary_addr , router_orig - > orig ) ) {
2010-12-13 14:19:28 +03:00
primary_orig_node = router_orig ;
} else {
2011-02-18 15:28:09 +03:00
primary_orig_node = orig_hash_find ( bat_priv ,
router_orig - > primary_addr ) ;
2010-12-13 14:19:28 +03:00
if ( ! primary_orig_node )
2011-01-19 23:01:43 +03:00
goto return_router ;
2011-02-18 15:28:09 +03:00
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( primary_orig_node ) ;
2010-12-13 14:19:28 +03:00
}
/* with less than 2 candidates, we can't do any
* bonding and prefer the original router . */
2011-01-19 23:01:43 +03:00
if ( atomic_read ( & primary_orig_node - > bond_candidates ) < 2 )
goto return_router ;
2010-12-13 14:19:28 +03:00
/* all nodes between should choose a candidate which
* is is not on the interface where the packet came
* in . */
2011-01-19 23:01:43 +03:00
2011-02-10 17:33:53 +03:00
neigh_node_free_ref ( router ) ;
2010-12-13 14:19:28 +03:00
2011-03-15 01:43:27 +03:00
if ( bonding_enabled )
router = find_bond_router ( primary_orig_node , recv_if ) ;
else
router = find_ifalter_router ( primary_orig_node , recv_if ) ;
2010-12-13 14:19:28 +03:00
2011-01-19 23:01:43 +03:00
return_router :
2011-05-08 22:52:57 +04:00
if ( router & & router - > if_incoming - > if_status ! = IF_ACTIVE )
goto err_unlock ;
2011-01-19 23:01:43 +03:00
rcu_read_unlock ( ) ;
2010-12-13 14:19:28 +03:00
return router ;
2011-05-05 16:14:46 +04:00
err_unlock :
rcu_read_unlock ( ) ;
err :
if ( router )
neigh_node_free_ref ( router ) ;
return NULL ;
2010-12-13 14:19:28 +03:00
}
static int check_unicast_packet ( struct sk_buff * skb , int hdr_size )
{
struct ethhdr * ethhdr ;
/* drop packet if it has not necessary minimum size */
if ( unlikely ( ! pskb_may_pull ( skb , hdr_size ) ) )
return - 1 ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with unicast indication but broadcast recipient */
if ( is_broadcast_ether_addr ( ethhdr - > h_dest ) )
return - 1 ;
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
return - 1 ;
/* not for me */
if ( ! is_my_mac ( ethhdr - > h_dest ) )
return - 1 ;
return 0 ;
}
2011-03-02 20:39:31 +03:00
int route_unicast_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
2011-02-10 17:33:53 +03:00
struct orig_node * orig_node = NULL ;
struct neigh_node * neigh_node = NULL ;
2010-12-13 14:19:28 +03:00
struct unicast_packet * unicast_packet ;
struct ethhdr * ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
2011-02-10 17:33:53 +03:00
int ret = NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
struct sk_buff * new_skb ;
unicast_packet = ( struct unicast_packet * ) skb - > data ;
/* TTL exceeded */
if ( unicast_packet - > ttl < 2 ) {
pr_debug ( " Warning - can't forward unicast packet from %pM to "
" %pM: ttl exceeded \n " , ethhdr - > h_source ,
unicast_packet - > dest ) ;
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
}
/* get routing information */
2011-02-18 15:28:09 +03:00
orig_node = orig_hash_find ( bat_priv , unicast_packet - > dest ) ;
2011-02-10 17:33:53 +03:00
if ( ! orig_node )
2011-04-16 13:30:57 +04:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-01-19 23:01:43 +03:00
/* find_router() increases neigh_nodes refcount if found. */
2011-02-10 17:33:53 +03:00
neigh_node = find_router ( bat_priv , orig_node , recv_if ) ;
2010-12-13 14:19:28 +03:00
2011-01-19 23:01:44 +03:00
if ( ! neigh_node )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* create a copy of the skb, if needed, to modify it. */
if ( skb_cow ( skb , sizeof ( struct ethhdr ) ) < 0 )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
unicast_packet = ( struct unicast_packet * ) skb - > data ;
if ( unicast_packet - > packet_type = = BAT_UNICAST & &
atomic_read ( & bat_priv - > fragmentation ) & &
2011-01-19 23:01:44 +03:00
skb - > len > neigh_node - > if_incoming - > net_dev - > mtu ) {
ret = frag_send_skb ( skb , bat_priv ,
neigh_node - > if_incoming , neigh_node - > addr ) ;
goto out ;
}
2010-12-13 14:19:28 +03:00
if ( unicast_packet - > packet_type = = BAT_UNICAST_FRAG & &
2011-01-19 23:01:44 +03:00
frag_can_reassemble ( skb , neigh_node - > if_incoming - > net_dev - > mtu ) ) {
2010-12-13 14:19:28 +03:00
ret = frag_reassemble_skb ( skb , bat_priv , & new_skb ) ;
if ( ret = = NET_RX_DROP )
2011-02-10 17:33:53 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* packet was buffered for late merge */
2011-02-10 17:33:53 +03:00
if ( ! new_skb ) {
ret = NET_RX_SUCCESS ;
goto out ;
}
2010-12-13 14:19:28 +03:00
skb = new_skb ;
unicast_packet = ( struct unicast_packet * ) skb - > data ;
}
/* decrement ttl */
unicast_packet - > ttl - - ;
/* route it */
2011-01-19 23:01:44 +03:00
send_skb_packet ( skb , neigh_node - > if_incoming , neigh_node - > addr ) ;
2011-02-10 17:33:53 +03:00
ret = NET_RX_SUCCESS ;
2010-12-13 14:19:28 +03:00
2011-02-10 17:33:53 +03:00
out :
if ( neigh_node )
neigh_node_free_ref ( neigh_node ) ;
if ( orig_node )
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( orig_node ) ;
2011-02-10 17:33:53 +03:00
return ret ;
2010-12-13 14:19:28 +03:00
}
2011-04-27 16:27:44 +04:00
static int check_unicast_ttvn ( struct bat_priv * bat_priv ,
struct sk_buff * skb ) {
uint8_t curr_ttvn ;
struct orig_node * orig_node ;
struct ethhdr * ethhdr ;
struct hard_iface * primary_if ;
struct unicast_packet * unicast_packet ;
2011-04-27 16:27:57 +04:00
bool tt_poss_change ;
2011-04-27 16:27:44 +04:00
/* I could need to modify it */
if ( skb_cow ( skb , sizeof ( struct unicast_packet ) ) < 0 )
return 0 ;
unicast_packet = ( struct unicast_packet * ) skb - > data ;
2011-04-27 16:27:57 +04:00
if ( is_my_mac ( unicast_packet - > dest ) ) {
tt_poss_change = bat_priv - > tt_poss_change ;
2011-04-27 16:27:44 +04:00
curr_ttvn = ( uint8_t ) atomic_read ( & bat_priv - > ttvn ) ;
2011-04-27 16:27:57 +04:00
} else {
2011-04-27 16:27:44 +04:00
orig_node = orig_hash_find ( bat_priv , unicast_packet - > dest ) ;
if ( ! orig_node )
return 0 ;
curr_ttvn = ( uint8_t ) atomic_read ( & orig_node - > last_ttvn ) ;
2011-04-27 16:27:57 +04:00
tt_poss_change = orig_node - > tt_poss_change ;
2011-04-27 16:27:44 +04:00
orig_node_free_ref ( orig_node ) ;
}
/* Check whether I have to reroute the packet */
2011-04-27 16:27:57 +04:00
if ( seq_before ( unicast_packet - > ttvn , curr_ttvn ) | | tt_poss_change ) {
2011-04-27 16:27:44 +04:00
/* Linearize the skb before accessing it */
if ( skb_linearize ( skb ) < 0 )
return 0 ;
ethhdr = ( struct ethhdr * ) ( skb - > data +
sizeof ( struct unicast_packet ) ) ;
2011-07-07 17:35:37 +04:00
orig_node = transtable_search ( bat_priv , NULL , ethhdr - > h_dest ) ;
2011-04-27 16:27:44 +04:00
if ( ! orig_node ) {
if ( ! is_my_client ( bat_priv , ethhdr - > h_dest ) )
return 0 ;
primary_if = primary_if_get_selected ( bat_priv ) ;
if ( ! primary_if )
return 0 ;
memcpy ( unicast_packet - > dest ,
primary_if - > net_dev - > dev_addr , ETH_ALEN ) ;
hardif_free_ref ( primary_if ) ;
} else {
memcpy ( unicast_packet - > dest , orig_node - > orig ,
ETH_ALEN ) ;
curr_ttvn = ( uint8_t )
atomic_read ( & orig_node - > last_ttvn ) ;
orig_node_free_ref ( orig_node ) ;
}
bat_dbg ( DBG_ROUTES , bat_priv , " TTVN mismatch (old_ttvn %u "
" new_ttvn %u)! Rerouting unicast packet (for %pM) to "
" %pM \n " , unicast_packet - > ttvn , curr_ttvn ,
ethhdr - > h_dest , unicast_packet - > dest ) ;
unicast_packet - > ttvn = curr_ttvn ;
}
return 1 ;
}
2011-02-18 15:33:20 +03:00
int recv_unicast_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
2011-04-27 16:27:44 +04:00
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
2010-12-13 14:19:28 +03:00
struct unicast_packet * unicast_packet ;
2011-05-15 01:14:54 +04:00
int hdr_size = sizeof ( * unicast_packet ) ;
2010-12-13 14:19:28 +03:00
if ( check_unicast_packet ( skb , hdr_size ) < 0 )
return NET_RX_DROP ;
2011-04-27 16:27:44 +04:00
if ( ! check_unicast_ttvn ( bat_priv , skb ) )
return NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
unicast_packet = ( struct unicast_packet * ) skb - > data ;
/* packet for me */
if ( is_my_mac ( unicast_packet - > dest ) ) {
interface_rx ( recv_if - > soft_iface , skb , recv_if , hdr_size ) ;
return NET_RX_SUCCESS ;
}
2011-03-02 20:39:31 +03:00
return route_unicast_packet ( skb , recv_if ) ;
2010-12-13 14:19:28 +03:00
}
2011-02-18 15:33:20 +03:00
int recv_ucast_frag_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
struct unicast_frag_packet * unicast_packet ;
2011-05-15 01:14:54 +04:00
int hdr_size = sizeof ( * unicast_packet ) ;
2010-12-13 14:19:28 +03:00
struct sk_buff * new_skb = NULL ;
int ret ;
if ( check_unicast_packet ( skb , hdr_size ) < 0 )
return NET_RX_DROP ;
2011-04-27 16:27:44 +04:00
if ( ! check_unicast_ttvn ( bat_priv , skb ) )
return NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
unicast_packet = ( struct unicast_frag_packet * ) skb - > data ;
/* packet for me */
if ( is_my_mac ( unicast_packet - > dest ) ) {
ret = frag_reassemble_skb ( skb , bat_priv , & new_skb ) ;
if ( ret = = NET_RX_DROP )
return NET_RX_DROP ;
/* packet was buffered for late merge */
if ( ! new_skb )
return NET_RX_SUCCESS ;
interface_rx ( recv_if - > soft_iface , new_skb , recv_if ,
sizeof ( struct unicast_packet ) ) ;
return NET_RX_SUCCESS ;
}
2011-03-02 20:39:31 +03:00
return route_unicast_packet ( skb , recv_if ) ;
2010-12-13 14:19:28 +03:00
}
2011-02-18 15:33:20 +03:00
int recv_bcast_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
2011-01-26 00:52:11 +03:00
struct orig_node * orig_node = NULL ;
2010-12-13 14:19:28 +03:00
struct bcast_packet * bcast_packet ;
struct ethhdr * ethhdr ;
2011-05-15 01:14:54 +04:00
int hdr_size = sizeof ( * bcast_packet ) ;
2011-01-26 00:52:11 +03:00
int ret = NET_RX_DROP ;
2010-12-13 14:19:28 +03:00
int32_t seq_diff ;
/* drop packet if it has not necessary minimum size */
if ( unlikely ( ! pskb_may_pull ( skb , hdr_size ) ) )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* packet with broadcast indication but unicast recipient */
if ( ! is_broadcast_ether_addr ( ethhdr - > h_dest ) )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* packet with broadcast sender address */
if ( is_broadcast_ether_addr ( ethhdr - > h_source ) )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
/* ignore broadcasts sent by myself */
if ( is_my_mac ( ethhdr - > h_source ) )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
bcast_packet = ( struct bcast_packet * ) skb - > data ;
/* ignore broadcasts originated by myself */
if ( is_my_mac ( bcast_packet - > orig ) )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
if ( bcast_packet - > ttl < 2 )
2011-01-26 00:52:11 +03:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-02-18 15:28:09 +03:00
orig_node = orig_hash_find ( bat_priv , bcast_packet - > orig ) ;
2011-01-26 00:52:11 +03:00
if ( ! orig_node )
2011-04-16 13:30:57 +04:00
goto out ;
2010-12-13 14:19:28 +03:00
2011-01-26 00:52:11 +03:00
spin_lock_bh ( & orig_node - > bcast_seqno_lock ) ;
2010-12-13 14:19:28 +03:00
/* check whether the packet is a duplicate */
2011-01-26 00:52:11 +03:00
if ( get_bit_status ( orig_node - > bcast_bits , orig_node - > last_bcast_seqno ,
ntohl ( bcast_packet - > seqno ) ) )
goto spin_unlock ;
2010-12-13 14:19:28 +03:00
seq_diff = ntohl ( bcast_packet - > seqno ) - orig_node - > last_bcast_seqno ;
/* check whether the packet is old and the host just restarted. */
if ( window_protected ( bat_priv , seq_diff ,
2011-01-26 00:52:11 +03:00
& orig_node - > bcast_seqno_reset ) )
goto spin_unlock ;
2010-12-13 14:19:28 +03:00
/* mark broadcast in flood history, update window position
* if required . */
if ( bit_get_packet ( bat_priv , orig_node - > bcast_bits , seq_diff , 1 ) )
orig_node - > last_bcast_seqno = ntohl ( bcast_packet - > seqno ) ;
2011-01-26 00:52:11 +03:00
spin_unlock_bh ( & orig_node - > bcast_seqno_lock ) ;
2010-12-13 14:19:28 +03:00
/* rebroadcast packet */
2011-06-25 21:09:12 +04:00
add_bcast_packet_to_list ( bat_priv , skb , 1 ) ;
2010-12-13 14:19:28 +03:00
/* broadcast for me */
interface_rx ( recv_if - > soft_iface , skb , recv_if , hdr_size ) ;
2011-01-26 00:52:11 +03:00
ret = NET_RX_SUCCESS ;
goto out ;
2010-12-13 14:19:28 +03:00
2011-01-26 00:52:11 +03:00
spin_unlock :
spin_unlock_bh ( & orig_node - > bcast_seqno_lock ) ;
out :
if ( orig_node )
2011-02-18 15:28:10 +03:00
orig_node_free_ref ( orig_node ) ;
2011-01-26 00:52:11 +03:00
return ret ;
2010-12-13 14:19:28 +03:00
}
2011-02-18 15:33:20 +03:00
int recv_vis_packet ( struct sk_buff * skb , struct hard_iface * recv_if )
2010-12-13 14:19:28 +03:00
{
struct vis_packet * vis_packet ;
struct ethhdr * ethhdr ;
struct bat_priv * bat_priv = netdev_priv ( recv_if - > soft_iface ) ;
2011-05-15 01:14:54 +04:00
int hdr_size = sizeof ( * vis_packet ) ;
2010-12-13 14:19:28 +03:00
/* keep skb linear */
if ( skb_linearize ( skb ) < 0 )
return NET_RX_DROP ;
if ( unlikely ( ! pskb_may_pull ( skb , hdr_size ) ) )
return NET_RX_DROP ;
vis_packet = ( struct vis_packet * ) skb - > data ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
/* not for me */
if ( ! is_my_mac ( ethhdr - > h_dest ) )
return NET_RX_DROP ;
/* ignore own packets */
if ( is_my_mac ( vis_packet - > vis_orig ) )
return NET_RX_DROP ;
if ( is_my_mac ( vis_packet - > sender_orig ) )
return NET_RX_DROP ;
switch ( vis_packet - > vis_type ) {
case VIS_TYPE_SERVER_SYNC :
receive_server_sync_packet ( bat_priv , vis_packet ,
skb_headlen ( skb ) ) ;
break ;
case VIS_TYPE_CLIENT_UPDATE :
receive_client_update_packet ( bat_priv , vis_packet ,
skb_headlen ( skb ) ) ;
break ;
default : /* ignore unknown packet */
break ;
}
/* We take a copy of the data in the packet, so we should
always free the skbuf . */
return NET_RX_DROP ;
}