2013-01-04 06:05:31 +04:00
/* Copyright (C) 2007-2013 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
2013-11-03 23:40:48 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2010-12-13 14:19:28 +03:00
*/
# include "main.h"
# include <linux/debugfs.h>
# include <linux/slab.h>
# include "icmp_socket.h"
# include "send.h"
# include "hash.h"
# include "originator.h"
# include "hard-interface.h"
2012-06-06 00:31:31 +04:00
static struct batadv_socket_client * batadv_socket_client_hash [ 256 ] ;
2010-12-13 14:19:28 +03:00
2012-06-06 00:31:31 +04:00
static void batadv_socket_add_packet ( struct batadv_socket_client * socket_client ,
2013-10-23 00:50:09 +04:00
struct batadv_icmp_header * icmph ,
2012-05-12 20:33:59 +04:00
size_t icmp_len ) ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:33 +04:00
void batadv_socket_init ( void )
2010-12-13 14:19:28 +03:00
{
2012-05-12 20:33:59 +04:00
memset ( batadv_socket_client_hash , 0 , sizeof ( batadv_socket_client_hash ) ) ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 20:33:59 +04:00
static int batadv_socket_open ( struct inode * inode , struct file * file )
2010-12-13 14:19:28 +03:00
{
unsigned int i ;
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * socket_client ;
2010-12-13 14:19:28 +03:00
2012-08-21 01:37:26 +04:00
if ( ! try_module_get ( THIS_MODULE ) )
return - EBUSY ;
2010-12-13 14:19:28 +03:00
nonseekable_open ( inode , file ) ;
2011-05-15 01:14:54 +04:00
socket_client = kmalloc ( sizeof ( * socket_client ) , GFP_KERNEL ) ;
2012-08-21 01:37:26 +04:00
if ( ! socket_client ) {
module_put ( THIS_MODULE ) ;
2010-12-13 14:19:28 +03:00
return - ENOMEM ;
2012-08-21 01:37:26 +04:00
}
2010-12-13 14:19:28 +03:00
2012-05-12 20:33:59 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( batadv_socket_client_hash ) ; i + + ) {
if ( ! batadv_socket_client_hash [ i ] ) {
batadv_socket_client_hash [ i ] = socket_client ;
2010-12-13 14:19:28 +03:00
break ;
}
}
2012-05-12 20:33:59 +04:00
if ( i = = ARRAY_SIZE ( batadv_socket_client_hash ) ) {
2012-03-07 12:07:45 +04:00
pr_err ( " Error - can't add another packet client: maximum number of clients reached \n " ) ;
2010-12-13 14:19:28 +03:00
kfree ( socket_client ) ;
2012-08-21 01:37:26 +04:00
module_put ( THIS_MODULE ) ;
2010-12-13 14:19:28 +03:00
return - EXFULL ;
}
INIT_LIST_HEAD ( & socket_client - > queue_list ) ;
socket_client - > queue_len = 0 ;
socket_client - > index = i ;
socket_client - > bat_priv = inode - > i_private ;
spin_lock_init ( & socket_client - > lock ) ;
init_waitqueue_head ( & socket_client - > queue_wait ) ;
file - > private_data = socket_client ;
return 0 ;
}
2012-05-12 20:33:59 +04:00
static int batadv_socket_release ( struct inode * inode , struct file * file )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * socket_client = file - > private_data ;
struct batadv_socket_packet * socket_packet ;
2010-12-13 14:19:28 +03:00
struct list_head * list_pos , * list_pos_tmp ;
spin_lock_bh ( & socket_client - > lock ) ;
/* for all packets in the queue ... */
list_for_each_safe ( list_pos , list_pos_tmp , & socket_client - > queue_list ) {
socket_packet = list_entry ( list_pos ,
2012-06-06 00:31:31 +04:00
struct batadv_socket_packet , list ) ;
2010-12-13 14:19:28 +03:00
list_del ( list_pos ) ;
kfree ( socket_packet ) ;
}
2012-05-12 20:33:59 +04:00
batadv_socket_client_hash [ socket_client - > index ] = NULL ;
2010-12-13 14:19:28 +03:00
spin_unlock_bh ( & socket_client - > lock ) ;
kfree ( socket_client ) ;
2012-08-21 01:37:26 +04:00
module_put ( THIS_MODULE ) ;
2010-12-13 14:19:28 +03:00
return 0 ;
}
2012-05-12 20:33:59 +04:00
static ssize_t batadv_socket_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * socket_client = file - > private_data ;
struct batadv_socket_packet * socket_packet ;
2010-12-13 14:19:28 +03:00
size_t packet_len ;
int error ;
if ( ( file - > f_flags & O_NONBLOCK ) & & ( socket_client - > queue_len = = 0 ) )
return - EAGAIN ;
2012-06-06 00:31:30 +04:00
if ( ( ! buf ) | | ( count < sizeof ( struct batadv_icmp_packet ) ) )
2010-12-13 14:19:28 +03:00
return - EINVAL ;
if ( ! access_ok ( VERIFY_WRITE , buf , count ) )
return - EFAULT ;
error = wait_event_interruptible ( socket_client - > queue_wait ,
socket_client - > queue_len ) ;
if ( error )
return error ;
spin_lock_bh ( & socket_client - > lock ) ;
socket_packet = list_first_entry ( & socket_client - > queue_list ,
2012-06-06 00:31:31 +04:00
struct batadv_socket_packet , list ) ;
2010-12-13 14:19:28 +03:00
list_del ( & socket_packet - > list ) ;
socket_client - > queue_len - - ;
spin_unlock_bh ( & socket_client - > lock ) ;
2011-12-10 18:28:36 +04:00
packet_len = min ( count , socket_packet - > icmp_len ) ;
error = copy_to_user ( buf , & socket_packet - > icmp_packet , packet_len ) ;
2010-12-13 14:19:28 +03:00
kfree ( socket_packet ) ;
if ( error )
return - EFAULT ;
return packet_len ;
}
2012-05-12 20:33:59 +04:00
static ssize_t batadv_socket_write ( struct file * file , const char __user * buff ,
size_t len , loff_t * off )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * socket_client = file - > private_data ;
struct batadv_priv * bat_priv = socket_client - > bat_priv ;
struct batadv_hard_iface * primary_if = NULL ;
2010-12-13 14:19:28 +03:00
struct sk_buff * skb ;
2013-10-23 00:50:09 +04:00
struct batadv_icmp_packet_rr * icmp_packet_rr ;
struct batadv_icmp_header * icmp_header ;
2012-06-06 00:31:31 +04:00
struct batadv_orig_node * orig_node = NULL ;
struct batadv_neigh_node * neigh_node = NULL ;
2012-06-06 00:31:30 +04:00
size_t packet_len = sizeof ( struct batadv_icmp_packet ) ;
2010-12-13 14:19:28 +03:00
2013-10-23 00:50:09 +04:00
if ( len < sizeof ( struct batadv_icmp_header ) ) {
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Error - can't send packet from char device: invalid packet size \n " ) ;
2010-12-13 14:19:28 +03:00
return - EINVAL ;
}
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 ) {
len = - EFAULT ;
goto out ;
}
2010-12-13 14:19:28 +03:00
2013-10-23 00:50:09 +04:00
if ( len > = BATADV_ICMP_MAX_PACKET_SIZE )
packet_len = BATADV_ICMP_MAX_PACKET_SIZE ;
else
packet_len = len ;
2010-12-13 14:19:28 +03:00
2013-04-03 00:28:44 +04:00
skb = netdev_alloc_skb_ip_align ( NULL , packet_len + ETH_HLEN ) ;
2011-04-20 17:40:58 +04:00
if ( ! skb ) {
len = - ENOMEM ;
goto out ;
}
2010-12-13 14:19:28 +03:00
2013-07-29 19:56:44 +04:00
skb - > priority = TC_PRIO_CONTROL ;
2013-04-03 00:28:44 +04:00
skb_reserve ( skb , ETH_HLEN ) ;
2013-10-23 00:50:09 +04:00
icmp_header = ( struct batadv_icmp_header * ) skb_put ( skb , packet_len ) ;
2010-12-13 14:19:28 +03:00
2013-10-23 00:50:09 +04:00
if ( copy_from_user ( icmp_header , buff , packet_len ) ) {
2010-12-13 14:19:28 +03:00
len = - EFAULT ;
goto free_skb ;
}
2013-12-02 23:38:31 +04:00
if ( icmp_header - > packet_type ! = BATADV_ICMP ) {
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2012-05-12 15:48:58 +04:00
" Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP) \n " ) ;
2010-12-13 14:19:28 +03:00
len = - EINVAL ;
goto free_skb ;
}
2013-10-23 00:50:09 +04:00
switch ( icmp_header - > msg_type ) {
case BATADV_ECHO_REQUEST :
if ( len < sizeof ( struct batadv_icmp_packet ) ) {
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
" Error - can't send packet from char device: invalid packet size \n " ) ;
len = - EINVAL ;
goto free_skb ;
}
if ( atomic_read ( & bat_priv - > mesh_state ) ! = BATADV_MESH_ACTIVE )
goto dst_unreach ;
orig_node = batadv_orig_hash_find ( bat_priv , icmp_header - > dst ) ;
if ( ! orig_node )
goto dst_unreach ;
2013-11-13 22:14:47 +04:00
neigh_node = batadv_orig_router_get ( orig_node ,
BATADV_IF_DEFAULT ) ;
2013-10-23 00:50:09 +04:00
if ( ! neigh_node )
goto dst_unreach ;
if ( ! neigh_node - > if_incoming )
goto dst_unreach ;
if ( neigh_node - > if_incoming - > if_status ! = BATADV_IF_ACTIVE )
goto dst_unreach ;
icmp_packet_rr = ( struct batadv_icmp_packet_rr * ) icmp_header ;
if ( packet_len = = sizeof ( * icmp_packet_rr ) )
memcpy ( icmp_packet_rr - > rr ,
neigh_node - > if_incoming - > net_dev - > dev_addr ,
ETH_ALEN ) ;
break ;
default :
2012-06-04 00:19:22 +04:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
2013-10-23 00:50:09 +04:00
" Error - can't send packet from char device: got unknown message type \n " ) ;
2010-12-13 14:19:28 +03:00
len = - EINVAL ;
goto free_skb ;
}
2013-10-23 00:50:09 +04:00
icmp_header - > uid = socket_client - > index ;
2010-12-13 14:19:28 +03:00
2013-12-02 23:38:31 +04:00
if ( icmp_header - > version ! = BATADV_COMPAT_VERSION ) {
2013-10-23 00:50:09 +04:00
icmp_header - > msg_type = BATADV_PARAMETER_PROBLEM ;
2013-12-02 23:38:31 +04:00
icmp_header - > version = BATADV_COMPAT_VERSION ;
2013-10-23 00:50:09 +04:00
batadv_socket_add_packet ( socket_client , icmp_header ,
2012-05-12 20:33:59 +04:00
packet_len ) ;
2010-12-13 14:19:28 +03:00
goto free_skb ;
}
2013-10-23 00:50:09 +04:00
memcpy ( icmp_header - > orig , primary_if - > net_dev - > dev_addr , ETH_ALEN ) ;
2010-12-13 14:19:28 +03:00
2012-05-12 04:09:37 +04:00
batadv_send_skb_packet ( skb , neigh_node - > if_incoming , neigh_node - > addr ) ;
2010-12-13 14:19:28 +03:00
goto out ;
dst_unreach :
2013-10-23 00:50:09 +04:00
icmp_header - > msg_type = BATADV_DESTINATION_UNREACHABLE ;
batadv_socket_add_packet ( socket_client , icmp_header , packet_len ) ;
2010-12-13 14:19:28 +03:00
free_skb :
kfree_skb ( skb ) ;
out :
2011-04-20 17:40:58 +04:00
if ( primary_if )
2012-05-12 15:48:54 +04:00
batadv_hardif_free_ref ( primary_if ) ;
2011-02-10 17:33:53 +03:00
if ( neigh_node )
2012-05-12 04:09:34 +04:00
batadv_neigh_node_free_ref ( neigh_node ) ;
2011-02-10 17:33:53 +03:00
if ( orig_node )
2012-05-12 04:09:34 +04:00
batadv_orig_node_free_ref ( orig_node ) ;
2010-12-13 14:19:28 +03:00
return len ;
}
2012-05-12 20:33:59 +04:00
static unsigned int batadv_socket_poll ( struct file * file , poll_table * wait )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * socket_client = file - > private_data ;
2010-12-13 14:19:28 +03:00
poll_wait ( file , & socket_client - > queue_wait , wait ) ;
if ( socket_client - > queue_len > 0 )
return POLLIN | POLLRDNORM ;
return 0 ;
}
2012-05-12 20:33:59 +04:00
static const struct file_operations batadv_fops = {
2010-12-13 14:19:28 +03:00
. owner = THIS_MODULE ,
2012-05-12 20:33:59 +04:00
. open = batadv_socket_open ,
. release = batadv_socket_release ,
. read = batadv_socket_read ,
. write = batadv_socket_write ,
. poll = batadv_socket_poll ,
2010-12-13 14:19:28 +03:00
. llseek = no_llseek ,
} ;
2012-06-06 00:31:31 +04:00
int batadv_socket_setup ( struct batadv_priv * bat_priv )
2010-12-13 14:19:28 +03:00
{
struct dentry * d ;
if ( ! bat_priv - > debug_dir )
goto err ;
2012-06-04 00:19:12 +04:00
d = debugfs_create_file ( BATADV_ICMP_SOCKET , S_IFREG | S_IWUSR | S_IRUSR ,
2012-05-12 20:33:59 +04:00
bat_priv - > debug_dir , bat_priv , & batadv_fops ) ;
2012-05-05 15:27:28 +04:00
if ( ! d )
2010-12-13 14:19:28 +03:00
goto err ;
return 0 ;
err :
2012-05-05 15:27:28 +04:00
return - ENOMEM ;
2010-12-13 14:19:28 +03:00
}
2013-10-23 00:50:09 +04:00
/**
* batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace
* on an icmp socket .
* @ socket_client : the socket this packet belongs to
* @ icmph : pointer to the header of the icmp packet
* @ icmp_len : total length of the icmp packet
*/
2012-06-06 00:31:31 +04:00
static void batadv_socket_add_packet ( struct batadv_socket_client * socket_client ,
2013-10-23 00:50:09 +04:00
struct batadv_icmp_header * icmph ,
2012-05-12 20:33:59 +04:00
size_t icmp_len )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_packet * socket_packet ;
2013-10-23 00:50:09 +04:00
size_t len ;
2010-12-13 14:19:28 +03:00
2011-05-15 01:14:54 +04:00
socket_packet = kmalloc ( sizeof ( * socket_packet ) , GFP_ATOMIC ) ;
2010-12-13 14:19:28 +03:00
if ( ! socket_packet )
return ;
2013-10-23 00:50:09 +04:00
len = icmp_len ;
/* check the maximum length before filling the buffer */
if ( len > sizeof ( socket_packet - > icmp_packet ) )
len = sizeof ( socket_packet - > icmp_packet ) ;
2010-12-13 14:19:28 +03:00
INIT_LIST_HEAD ( & socket_packet - > list ) ;
2013-10-23 00:50:09 +04:00
memcpy ( & socket_packet - > icmp_packet , icmph , len ) ;
socket_packet - > icmp_len = len ;
2010-12-13 14:19:28 +03:00
spin_lock_bh ( & socket_client - > lock ) ;
/* while waiting for the lock the socket_client could have been
2012-05-12 04:09:43 +04:00
* deleted
*/
2013-10-23 00:50:09 +04:00
if ( ! batadv_socket_client_hash [ icmph - > uid ] ) {
2010-12-13 14:19:28 +03:00
spin_unlock_bh ( & socket_client - > lock ) ;
kfree ( socket_packet ) ;
return ;
}
list_add_tail ( & socket_packet - > list , & socket_client - > queue_list ) ;
socket_client - > queue_len + + ;
if ( socket_client - > queue_len > 100 ) {
socket_packet = list_first_entry ( & socket_client - > queue_list ,
2012-06-06 00:31:31 +04:00
struct batadv_socket_packet ,
list ) ;
2010-12-13 14:19:28 +03:00
list_del ( & socket_packet - > list ) ;
kfree ( socket_packet ) ;
socket_client - > queue_len - - ;
}
spin_unlock_bh ( & socket_client - > lock ) ;
wake_up ( & socket_client - > queue_wait ) ;
}
2013-10-23 00:50:09 +04:00
/**
* batadv_socket_receive_packet - schedule an icmp packet to be received
* locally and sent to userspace .
* @ icmph : pointer to the header of the icmp packet
* @ icmp_len : total length of the icmp packet
*/
void batadv_socket_receive_packet ( struct batadv_icmp_header * icmph ,
2012-05-12 04:09:33 +04:00
size_t icmp_len )
2010-12-13 14:19:28 +03:00
{
2012-06-06 00:31:31 +04:00
struct batadv_socket_client * hash ;
2010-12-13 14:19:28 +03:00
2013-10-23 00:50:09 +04:00
hash = batadv_socket_client_hash [ icmph - > uid ] ;
2010-12-13 14:19:28 +03:00
if ( hash )
2013-10-23 00:50:09 +04:00
batadv_socket_add_packet ( hash , icmph , icmp_len ) ;
2010-12-13 14:19:28 +03:00
}