2006-06-06 04:30:32 +04:00
/*
* tcpprobe - Observe the TCP flow with kprobes .
*
* The idea for this came from Werner Almesberger ' s umlsim
* Copyright ( C ) 2004 , Stephen Hemminger < shemminger @ osdl . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/kprobes.h>
# include <linux/socket.h>
# include <linux/tcp.h>
# include <linux/proc_fs.h>
# include <linux/module.h>
# include <linux/kfifo.h>
# include <linux/vmalloc.h>
# include <net/tcp.h>
2007-01-23 22:38:57 +03:00
MODULE_AUTHOR ( " Stephen Hemminger <shemminger@linux-foundation.org> " ) ;
2006-06-06 04:30:32 +04:00
MODULE_DESCRIPTION ( " TCP cwnd snooper " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int port = 0 ;
MODULE_PARM_DESC ( port , " Port to match (0=all) " ) ;
module_param ( port , int , 0 ) ;
static int bufsize = 64 * 1024 ;
MODULE_PARM_DESC ( bufsize , " Log buffer size (default 64k) " ) ;
module_param ( bufsize , int , 0 ) ;
static const char procname [ ] = " tcpprobe " ;
struct {
struct kfifo * fifo ;
spinlock_t lock ;
wait_queue_head_t wait ;
struct timeval tstart ;
} tcpw ;
static void printl ( const char * fmt , . . . )
{
va_list args ;
int len ;
struct timeval now ;
char tbuf [ 256 ] ;
va_start ( args , fmt ) ;
do_gettimeofday ( & now ) ;
now . tv_sec - = tcpw . tstart . tv_sec ;
now . tv_usec - = tcpw . tstart . tv_usec ;
if ( now . tv_usec < 0 ) {
- - now . tv_sec ;
now . tv_usec + = 1000000 ;
}
2006-06-06 04:59:20 +04:00
len = sprintf ( tbuf , " %lu.%06lu " ,
( unsigned long ) now . tv_sec ,
( unsigned long ) now . tv_usec ) ;
2006-06-06 04:30:32 +04:00
len + = vscnprintf ( tbuf + len , sizeof ( tbuf ) - len , fmt , args ) ;
va_end ( args ) ;
kfifo_put ( tcpw . fifo , tbuf , len ) ;
wake_up ( & tcpw . wait ) ;
}
static int jtcp_sendmsg ( struct kiocb * iocb , struct sock * sk ,
struct msghdr * msg , size_t size )
{
const struct tcp_sock * tp = tcp_sk ( sk ) ;
const struct inet_sock * inet = inet_sk ( sk ) ;
if ( port = = 0 | | ntohs ( inet - > dport ) = = port | |
ntohs ( inet - > sport ) = = port ) {
printl ( " %d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u \n " ,
NIPQUAD ( inet - > saddr ) , ntohs ( inet - > sport ) ,
NIPQUAD ( inet - > daddr ) , ntohs ( inet - > dport ) ,
size , tp - > snd_nxt , tp - > snd_una ,
tp - > snd_cwnd , tcp_current_ssthresh ( sk ) ,
tp - > snd_wnd ) ;
}
jprobe_return ( ) ;
return 0 ;
}
static struct jprobe tcp_send_probe = {
2006-10-02 13:17:30 +04:00
. kp = {
. symbol_name = " tcp_sendmsg " ,
} ,
. entry = JPROBE_ENTRY ( jtcp_sendmsg ) ,
2006-06-06 04:30:32 +04:00
} ;
static int tcpprobe_open ( struct inode * inode , struct file * file )
{
kfifo_reset ( tcpw . fifo ) ;
do_gettimeofday ( & tcpw . tstart ) ;
return 0 ;
}
static ssize_t tcpprobe_read ( struct file * file , char __user * buf ,
size_t len , loff_t * ppos )
{
2006-07-31 07:21:45 +04:00
int error = 0 , cnt = 0 ;
2006-06-06 04:30:32 +04:00
unsigned char * tbuf ;
if ( ! buf | | len < 0 )
return - EINVAL ;
if ( len = = 0 )
return 0 ;
tbuf = vmalloc ( len ) ;
if ( ! tbuf )
return - ENOMEM ;
error = wait_event_interruptible ( tcpw . wait ,
__kfifo_len ( tcpw . fifo ) ! = 0 ) ;
if ( error )
2006-08-14 05:05:09 +04:00
goto out_free ;
2006-06-06 04:30:32 +04:00
cnt = kfifo_get ( tcpw . fifo , tbuf , len ) ;
error = copy_to_user ( buf , tbuf , cnt ) ;
2006-08-14 05:05:09 +04:00
out_free :
2006-06-06 04:30:32 +04:00
vfree ( tbuf ) ;
return error ? error : cnt ;
}
2007-02-12 11:55:35 +03:00
static const struct file_operations tcpprobe_fops = {
2006-06-06 04:30:32 +04:00
. owner = THIS_MODULE ,
. open = tcpprobe_open ,
. read = tcpprobe_read ,
} ;
static __init int tcpprobe_init ( void )
{
int ret = - ENOMEM ;
init_waitqueue_head ( & tcpw . wait ) ;
spin_lock_init ( & tcpw . lock ) ;
tcpw . fifo = kfifo_alloc ( bufsize , GFP_KERNEL , & tcpw . lock ) ;
2006-11-23 07:26:11 +03:00
if ( IS_ERR ( tcpw . fifo ) )
return PTR_ERR ( tcpw . fifo ) ;
2006-06-06 04:30:32 +04:00
if ( ! proc_net_fops_create ( procname , S_IRUSR , & tcpprobe_fops ) )
goto err0 ;
ret = register_jprobe ( & tcp_send_probe ) ;
if ( ret )
goto err1 ;
pr_info ( " TCP watch registered (port=%d) \n " , port ) ;
return 0 ;
err1 :
proc_net_remove ( procname ) ;
err0 :
kfifo_free ( tcpw . fifo ) ;
return ret ;
}
module_init ( tcpprobe_init ) ;
static __exit void tcpprobe_exit ( void )
{
kfifo_free ( tcpw . fifo ) ;
proc_net_remove ( procname ) ;
unregister_jprobe ( & tcp_send_probe ) ;
}
module_exit ( tcpprobe_exit ) ;