2010-12-13 11:19:28 +00:00
/*
2011-01-27 10:38:15 +01:00
* Copyright ( C ) 2010 - 2011 B . A . T . M . A . N . contributors :
2010-12-13 11:19:28 +00:00
*
* Andreas Langer
*
* 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 "unicast.h"
# include "send.h"
# include "soft-interface.h"
# include "gateway_client.h"
# include "originator.h"
# include "hash.h"
# include "translation-table.h"
# include "routing.h"
# include "hard-interface.h"
static struct sk_buff * frag_merge_packet ( struct list_head * head ,
struct frag_packet_list_entry * tfp ,
struct sk_buff * skb )
{
struct unicast_frag_packet * up =
( struct unicast_frag_packet * ) skb - > data ;
struct sk_buff * tmp_skb ;
struct unicast_packet * unicast_packet ;
2011-05-14 23:14:54 +02:00
int hdr_len = sizeof ( * unicast_packet ) ;
int uni_diff = sizeof ( * up ) - hdr_len ;
2010-12-13 11:19:28 +00:00
/* set skb to the first part and tmp_skb to the second part */
if ( up - > flags & UNI_FRAG_HEAD ) {
tmp_skb = tfp - > skb ;
} else {
tmp_skb = skb ;
skb = tfp - > skb ;
}
2011-02-06 23:26:43 +00:00
if ( skb_linearize ( skb ) < 0 | | skb_linearize ( tmp_skb ) < 0 )
goto err ;
2011-05-14 23:14:54 +02:00
skb_pull ( tmp_skb , sizeof ( * up ) ) ;
2011-02-06 23:26:43 +00:00
if ( pskb_expand_head ( skb , 0 , tmp_skb - > len , GFP_ATOMIC ) < 0 )
goto err ;
2010-12-13 11:19:28 +00:00
/* move free entry to end */
tfp - > skb = NULL ;
tfp - > seqno = 0 ;
list_move_tail ( & tfp - > list , head ) ;
memcpy ( skb_put ( skb , tmp_skb - > len ) , tmp_skb - > data , tmp_skb - > len ) ;
kfree_skb ( tmp_skb ) ;
memmove ( skb - > data + uni_diff , skb - > data , hdr_len ) ;
unicast_packet = ( struct unicast_packet * ) skb_pull ( skb , uni_diff ) ;
unicast_packet - > packet_type = BAT_UNICAST ;
return skb ;
2011-02-06 23:26:43 +00:00
err :
/* free buffered skb, skb will be freed later */
kfree_skb ( tfp - > skb ) ;
return NULL ;
2010-12-13 11:19:28 +00:00
}
static void frag_create_entry ( struct list_head * head , struct sk_buff * skb )
{
struct frag_packet_list_entry * tfp ;
struct unicast_frag_packet * up =
( struct unicast_frag_packet * ) skb - > data ;
/* free and oldest packets stand at the end */
tfp = list_entry ( ( head ) - > prev , typeof ( * tfp ) , list ) ;
kfree_skb ( tfp - > skb ) ;
tfp - > seqno = ntohs ( up - > seqno ) ;
tfp - > skb = skb ;
list_move ( & tfp - > list , head ) ;
return ;
}
static int frag_create_buffer ( struct list_head * head )
{
int i ;
struct frag_packet_list_entry * tfp ;
for ( i = 0 ; i < FRAG_BUFFER_SIZE ; i + + ) {
2011-05-14 23:14:54 +02:00
tfp = kmalloc ( sizeof ( * tfp ) , GFP_ATOMIC ) ;
2010-12-13 11:19:28 +00:00
if ( ! tfp ) {
frag_list_free ( head ) ;
return - ENOMEM ;
}
tfp - > skb = NULL ;
tfp - > seqno = 0 ;
INIT_LIST_HEAD ( & tfp - > list ) ;
list_add ( & tfp - > list , head ) ;
}
return 0 ;
}
static struct frag_packet_list_entry * frag_search_packet ( struct list_head * head ,
2011-05-14 23:14:50 +02:00
const struct unicast_frag_packet * up )
2010-12-13 11:19:28 +00:00
{
struct frag_packet_list_entry * tfp ;
struct unicast_frag_packet * tmp_up = NULL ;
uint16_t search_seqno ;
if ( up - > flags & UNI_FRAG_HEAD )
search_seqno = ntohs ( up - > seqno ) + 1 ;
else
search_seqno = ntohs ( up - > seqno ) - 1 ;
list_for_each_entry ( tfp , head , list ) {
if ( ! tfp - > skb )
continue ;
if ( tfp - > seqno = = ntohs ( up - > seqno ) )
goto mov_tail ;
tmp_up = ( struct unicast_frag_packet * ) tfp - > skb - > data ;
if ( tfp - > seqno = = search_seqno ) {
if ( ( tmp_up - > flags & UNI_FRAG_HEAD ) ! =
( up - > flags & UNI_FRAG_HEAD ) )
return tfp ;
else
goto mov_tail ;
}
}
return NULL ;
mov_tail :
list_move_tail ( & tfp - > list , head ) ;
return NULL ;
}
void frag_list_free ( struct list_head * head )
{
struct frag_packet_list_entry * pf , * tmp_pf ;
if ( ! list_empty ( head ) ) {
list_for_each_entry_safe ( pf , tmp_pf , head , list ) {
kfree_skb ( pf - > skb ) ;
list_del ( & pf - > list ) ;
kfree ( pf ) ;
}
}
return ;
}
/* frag_reassemble_skb():
* returns NET_RX_DROP if the operation failed - skb is left intact
* returns NET_RX_SUCCESS if the fragment was buffered ( skb_new will be NULL )
* or the skb could be reassembled ( skb_new will point to the new packet and
* skb was freed )
*/
int frag_reassemble_skb ( struct sk_buff * skb , struct bat_priv * bat_priv ,
struct sk_buff * * new_skb )
{
struct orig_node * orig_node ;
struct frag_packet_list_entry * tmp_frag_entry ;
int ret = NET_RX_DROP ;
struct unicast_frag_packet * unicast_packet =
( struct unicast_frag_packet * ) skb - > data ;
* new_skb = NULL ;
2011-02-18 12:28:09 +00:00
orig_node = orig_hash_find ( bat_priv , unicast_packet - > orig ) ;
if ( ! orig_node )
2010-12-13 11:19:28 +00:00
goto out ;
orig_node - > last_frag_packet = jiffies ;
if ( list_empty ( & orig_node - > frag_list ) & &
frag_create_buffer ( & orig_node - > frag_list ) ) {
pr_debug ( " couldn't create frag buffer \n " ) ;
goto out ;
}
tmp_frag_entry = frag_search_packet ( & orig_node - > frag_list ,
unicast_packet ) ;
if ( ! tmp_frag_entry ) {
frag_create_entry ( & orig_node - > frag_list , skb ) ;
ret = NET_RX_SUCCESS ;
goto out ;
}
* new_skb = frag_merge_packet ( & orig_node - > frag_list , tmp_frag_entry ,
skb ) ;
/* if not, merge failed */
if ( * new_skb )
ret = NET_RX_SUCCESS ;
2011-02-18 12:28:09 +00:00
out :
if ( orig_node )
2011-02-18 12:28:10 +00:00
orig_node_free_ref ( orig_node ) ;
2010-12-13 11:19:28 +00:00
return ret ;
}
int frag_send_skb ( struct sk_buff * skb , struct bat_priv * bat_priv ,
2011-05-14 23:14:50 +02:00
struct hard_iface * hard_iface , const uint8_t dstaddr [ ] )
2010-12-13 11:19:28 +00:00
{
struct unicast_packet tmp_uc , * unicast_packet ;
2011-04-20 15:40:58 +02:00
struct hard_iface * primary_if ;
2010-12-13 11:19:28 +00:00
struct sk_buff * frag_skb ;
struct unicast_frag_packet * frag1 , * frag2 ;
2011-05-14 23:14:54 +02:00
int uc_hdr_len = sizeof ( * unicast_packet ) ;
int ucf_hdr_len = sizeof ( * frag1 ) ;
2011-01-25 21:59:26 +00:00
int data_len = skb - > len - uc_hdr_len ;
2011-04-20 15:40:58 +02:00
int large_tail = 0 , ret = NET_RX_DROP ;
2011-02-10 14:33:56 +00:00
uint16_t seqno ;
2010-12-13 11:19:28 +00:00
2011-04-20 15:40:58 +02:00
primary_if = primary_if_get_selected ( bat_priv ) ;
if ( ! primary_if )
2010-12-13 11:19:28 +00:00
goto dropped ;
2011-01-13 21:53:38 +01:00
frag_skb = dev_alloc_skb ( data_len - ( data_len / 2 ) + ucf_hdr_len ) ;
if ( ! frag_skb )
goto dropped ;
2011-01-25 21:59:26 +00:00
skb_reserve ( frag_skb , ucf_hdr_len ) ;
2010-12-13 11:19:28 +00:00
2011-01-13 21:53:38 +01:00
unicast_packet = ( struct unicast_packet * ) skb - > data ;
2010-12-13 11:19:28 +00:00
memcpy ( & tmp_uc , unicast_packet , uc_hdr_len ) ;
2011-01-25 21:59:26 +00:00
skb_split ( skb , frag_skb , data_len / 2 + uc_hdr_len ) ;
2010-12-13 11:19:28 +00:00
if ( my_skb_head_push ( skb , ucf_hdr_len - uc_hdr_len ) < 0 | |
my_skb_head_push ( frag_skb , ucf_hdr_len ) < 0 )
goto drop_frag ;
frag1 = ( struct unicast_frag_packet * ) skb - > data ;
frag2 = ( struct unicast_frag_packet * ) frag_skb - > data ;
2011-05-14 23:14:54 +02:00
memcpy ( frag1 , & tmp_uc , sizeof ( tmp_uc ) ) ;
2010-12-13 11:19:28 +00:00
frag1 - > ttl - - ;
frag1 - > version = COMPAT_VERSION ;
frag1 - > packet_type = BAT_UNICAST_FRAG ;
2011-04-20 15:40:58 +02:00
memcpy ( frag1 - > orig , primary_if - > net_dev - > dev_addr , ETH_ALEN ) ;
2011-05-14 23:14:54 +02:00
memcpy ( frag2 , frag1 , sizeof ( * frag2 ) ) ;
2010-12-13 11:19:28 +00:00
2011-01-25 22:02:31 +00:00
if ( data_len & 1 )
large_tail = UNI_FRAG_LARGETAIL ;
frag1 - > flags = UNI_FRAG_HEAD | large_tail ;
frag2 - > flags = large_tail ;
2010-12-13 11:19:28 +00:00
2011-02-18 12:33:20 +00:00
seqno = atomic_add_return ( 2 , & hard_iface - > frag_seqno ) ;
2011-02-10 14:33:56 +00:00
frag1 - > seqno = htons ( seqno - 1 ) ;
frag2 - > seqno = htons ( seqno ) ;
2010-12-13 11:19:28 +00:00
2011-02-18 12:33:20 +00:00
send_skb_packet ( skb , hard_iface , dstaddr ) ;
send_skb_packet ( frag_skb , hard_iface , dstaddr ) ;
2011-04-20 15:40:58 +02:00
ret = NET_RX_SUCCESS ;
goto out ;
2010-12-13 11:19:28 +00:00
drop_frag :
kfree_skb ( frag_skb ) ;
dropped :
kfree_skb ( skb ) ;
2011-04-20 15:40:58 +02:00
out :
if ( primary_if )
hardif_free_ref ( primary_if ) ;
return ret ;
2010-12-13 11:19:28 +00:00
}
int unicast_send_skb ( struct sk_buff * skb , struct bat_priv * bat_priv )
{
struct ethhdr * ethhdr = ( struct ethhdr * ) skb - > data ;
struct unicast_packet * unicast_packet ;
2011-02-18 12:28:10 +00:00
struct orig_node * orig_node ;
2011-02-10 14:33:53 +00:00
struct neigh_node * neigh_node ;
2010-12-13 11:19:28 +00:00
int data_len = skb - > len ;
2011-02-10 14:33:53 +00:00
int ret = 1 ;
2010-12-13 11:19:28 +00:00
/* get routing information */
2011-02-13 21:13:04 +00:00
if ( is_multicast_ether_addr ( ethhdr - > h_dest ) ) {
2011-05-14 23:14:53 +02:00
orig_node = gw_get_selected_orig ( bat_priv ) ;
2011-02-13 21:13:04 +00:00
if ( orig_node )
2011-02-10 14:33:53 +00:00
goto find_router ;
}
2010-12-13 11:19:28 +00:00
2011-07-07 15:35:37 +02:00
/* check for tt host - increases orig_node refcount.
* returns NULL in case of AP isolation */
orig_node = transtable_search ( bat_priv , ethhdr - > h_source ,
ethhdr - > h_dest ) ;
2010-12-13 11:19:28 +00:00
2011-02-10 14:33:53 +00:00
find_router :
2011-01-19 20:01:44 +00:00
/**
* find_router ( ) :
* - if orig_node is NULL it returns NULL
* - increases neigh_nodes refcount if found .
*/
2011-02-10 14:33:53 +00:00
neigh_node = find_router ( bat_priv , orig_node , NULL ) ;
2010-12-13 11:19:28 +00:00
2011-02-10 14:33:53 +00:00
if ( ! neigh_node )
2011-01-19 20:01:44 +00:00
goto out ;
2010-12-13 11:19:28 +00:00
2011-05-14 23:14:54 +02:00
if ( my_skb_head_push ( skb , sizeof ( * unicast_packet ) ) < 0 )
2011-01-19 20:01:44 +00:00
goto out ;
2010-12-13 11:19:28 +00:00
unicast_packet = ( struct unicast_packet * ) skb - > data ;
unicast_packet - > version = COMPAT_VERSION ;
/* batman packet type: unicast */
unicast_packet - > packet_type = BAT_UNICAST ;
/* set unicast ttl */
unicast_packet - > ttl = TTL ;
/* copy the destination for faster routing */
memcpy ( unicast_packet - > dest , orig_node - > orig , ETH_ALEN ) ;
2011-04-27 14:27:44 +02:00
/* set the destination tt version number */
unicast_packet - > ttvn =
( uint8_t ) atomic_read ( & orig_node - > last_ttvn ) ;
2010-12-13 11:19:28 +00:00
if ( atomic_read ( & bat_priv - > fragmentation ) & &
2011-05-14 23:14:54 +02:00
data_len + sizeof ( * unicast_packet ) >
2011-01-19 20:01:44 +00:00
neigh_node - > if_incoming - > net_dev - > mtu ) {
2010-12-13 11:19:28 +00:00
/* send frag skb decreases ttl */
unicast_packet - > ttl + + ;
2011-01-19 20:01:44 +00:00
ret = frag_send_skb ( skb , bat_priv ,
neigh_node - > if_incoming , neigh_node - > addr ) ;
2011-02-10 14:33:53 +00:00
goto out ;
2010-12-13 11:19:28 +00:00
}
2011-01-19 20:01:44 +00:00
send_skb_packet ( skb , neigh_node - > if_incoming , neigh_node - > addr ) ;
2011-02-10 14:33:53 +00:00
ret = 0 ;
goto out ;
2010-12-13 11:19:28 +00:00
2011-02-10 14:33:53 +00:00
out :
if ( neigh_node )
neigh_node_free_ref ( neigh_node ) ;
if ( orig_node )
2011-02-18 12:28:10 +00:00
orig_node_free_ref ( orig_node ) ;
2011-02-10 14:33:53 +00:00
if ( ret = = 1 )
kfree_skb ( skb ) ;
return ret ;
2010-12-13 11:19:28 +00:00
}