2005-04-17 02:20:36 +04:00
/*
* Authors :
* Copyright 2001 , 2002 by Robert Olsson < robert . olsson @ its . uu . se >
* Uppsala University and
* Swedish University of Agricultural Sciences
*
* Alexey Kuznetsov < kuznet @ ms2 . inr . ac . ru >
* Ben Greear < greearb @ candelatech . com >
* Jens L <EFBFBD> <EFBFBD> s < jens . laas @ data . slu . se >
*
* 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 .
*
*
* A tool for loading the network with preconfigurated packets .
2007-02-09 17:24:36 +03:00
* The tool is implemented as a linux module . Parameters are output
2005-04-17 02:20:36 +04:00
* device , delay ( to hard_xmit ) , number of packets , and whether
* to use multiple SKBs or just the same one .
* pktgen uses the installed interface ' s output routine .
*
* Additional hacking by :
*
* Jens . Laas @ data . slu . se
* Improved by ANK . 010120.
* Improved by ANK even more . 010212.
* MAC address typo fixed . 010417 - - ro
* Integrated . 020301 - - DaveM
* Added multiskb option 020301 - - DaveM
* Scaling of results . 020417 - - sigurdur @ linpro . no
* Significant re - work of the module :
* * Convert to threaded model to more efficiently be able to transmit
* and receive on multiple interfaces at once .
* * Converted many counters to __u64 to allow longer runs .
* * Allow configuration of ranges , like min / max IP address , MACs ,
* and UDP - ports , for both source and destination , and can
* set to use a random distribution or sequentially walk the range .
* * Can now change most values after starting .
* * Place 12 - byte packet in UDP payload with magic number ,
* sequence number , and timestamp .
* * Add receiver code that detects dropped pkts , re - ordered pkts , and
* latencies ( with micro - second ) precision .
* * Add IOCTL interface to easily get counters & configuration .
* - - Ben Greear < greearb @ candelatech . com >
*
2007-02-09 17:24:36 +03:00
* Renamed multiskb to clone_skb and cleaned up sending core for two distinct
* skb modes . A clone_skb = 0 mode for Ben " ranges " work and a clone_skb ! = 0
2005-04-17 02:20:36 +04:00
* as a " fastpath " with a configurable number of clones after alloc ' s .
2007-02-09 17:24:36 +03:00
* clone_skb = 0 means all packets are allocated this also means ranges time
* stamps etc can be used . clone_skb = 100 means 1 malloc is followed by 100
2005-04-17 02:20:36 +04:00
* clones .
*
2007-02-09 17:24:36 +03:00
* Also moved to / proc / net / pktgen /
2005-04-17 02:20:36 +04:00
* - - ro
*
* Sept 10 : Fixed threading / locking . Lots of bone - headed and more clever
* mistakes . Also merged in DaveM ' s patch in the - pre6 patch .
* - - Ben Greear < greearb @ candelatech . com >
*
* Integrated to 2.5 . x 02102 9 - - Lucio Maciel ( luciomaciel @ zipmail . com . br )
*
2007-02-09 17:24:36 +03:00
*
2005-04-17 02:20:36 +04:00
* 021124 Finished major redesign and rewrite for new functionality .
* See Documentation / networking / pktgen . txt for how to use this .
*
* The new operation :
2007-02-09 17:24:36 +03:00
* For each CPU one thread / process is created at start . This process checks
* for running devices in the if_list and sends packets until count is 0 it
* also the thread checks the thread - > control which is used for inter - process
* communication . controlling process " posts " operations to the threads this
2005-04-17 02:20:36 +04:00
* way . The if_lock should be possible to remove when add / rem_device is merged
* into this too .
*
2007-02-09 17:24:36 +03:00
* By design there should only be * one * " controlling " process . In practice
* multiple write accesses gives unpredictable result . Understood by " write "
2005-04-17 02:20:36 +04:00
* to / proc gives result code thats should be read be the " writer " .
2005-10-15 02:32:22 +04:00
* For practical use this should be no problem .
2005-04-17 02:20:36 +04:00
*
2007-02-09 17:24:36 +03:00
* Note when adding devices to a specific CPU there good idea to also assign
* / proc / irq / XX / smp_affinity so TX - interrupts gets bound to the same CPU .
2005-04-17 02:20:36 +04:00
* - - ro
*
2007-02-09 17:24:36 +03:00
* Fix refcount off by one if first packet fails , potential null deref ,
2005-04-17 02:20:36 +04:00
* memleak 030710 - KJP
*
* First " ranges " functionality for ipv6 030726 - - ro
*
* Included flow support . 030 802 ANK .
*
* Fixed unaligned access on IA - 64 Grant Grundler < grundler @ parisc - linux . org >
2007-02-09 17:24:36 +03:00
*
2005-04-17 02:20:36 +04:00
* Remove if fix from added Harald Welte < laforge @ netfilter . org > 04041 9
* ia64 compilation fix from Aron Griffis < aron @ hp . com > 040604
*
2007-02-09 17:24:36 +03:00
* New xmit ( ) return , do_div and misc clean up by Stephen Hemminger
2005-04-17 02:20:36 +04:00
* < shemminger @ osdl . org > 040 923
*
2007-02-09 17:24:36 +03:00
* Randy Dunlap fixed u64 printk compiler waring
2005-04-17 02:20:36 +04:00
*
* Remove FCS from BW calculation . Lennert Buytenhek < buytenh @ wantstofly . org >
* New time handling . Lennert Buytenhek < buytenh @ wantstofly . org > 041213
*
2007-02-09 17:24:36 +03:00
* Corrections from Nikolai Malykh ( nmalykh @ bilim . com )
2005-04-17 02:20:36 +04:00
* Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
*
2007-02-09 17:24:36 +03:00
* interruptible_sleep_on_timeout ( ) replaced Nishanth Aravamudan < nacc @ us . ibm . com >
2005-04-17 02:20:36 +04:00
* 050103
2006-03-23 12:10:26 +03:00
*
* MPLS support by Steven Whitehouse < steve @ chygwyn . com >
*
2006-09-28 03:30:44 +04:00
* 802.1 Q / Q - in - Q support by Francesco Fondelli ( FF ) < francesco . fondelli @ gmail . com >
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/sys.h>
# include <linux/types.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/kernel.h>
# include <linux/smp_lock.h>
2006-03-21 09:24:27 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/unistd.h>
# include <linux/string.h>
# include <linux/ptrace.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2007-04-13 01:45:32 +04:00
# include <linux/freezer.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
# include <linux/timer.h>
2006-03-21 09:16:40 +03:00
# include <linux/list.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/inet.h>
# include <linux/inetdevice.h>
# include <linux/rtnetlink.h>
# include <linux/if_arp.h>
2006-09-28 03:30:44 +04:00
# include <linux/if_vlan.h>
2005-04-17 02:20:36 +04:00
# include <linux/in.h>
# include <linux/ip.h>
# include <linux/ipv6.h>
# include <linux/udp.h>
# include <linux/proc_fs.h>
2005-10-15 02:42:33 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/wait.h>
2006-01-18 00:04:57 +03:00
# include <linux/etherdevice.h>
2007-01-02 07:51:53 +03:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# include <net/checksum.h>
# include <net/ipv6.h>
# include <net/addrconf.h>
# include <asm/byteorder.h>
# include <linux/rcupdate.h>
# include <asm/bitops.h>
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/uaccess.h>
2006-03-21 09:16:13 +03:00
# include <asm/div64.h> /* do_div */
2005-04-17 02:20:36 +04:00
# include <asm/timex.h>
2006-09-28 03:32:03 +04:00
# define VERSION "pktgen v2.68: Packet Generator for packet performance testing.\n"
2005-04-17 02:20:36 +04:00
/* #define PG_DEBUG(a) a */
2006-03-21 09:16:13 +03:00
# define PG_DEBUG(a)
2005-04-17 02:20:36 +04:00
/* The buckets are exponential in 'width' */
# define LAT_BUCKETS_MAX 32
# define IP_NAME_SZ 32
2006-03-23 12:10:26 +03:00
# define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
# define MPLS_STACK_BOTTOM __constant_htonl(0x00000100)
2005-04-17 02:20:36 +04:00
/* Device flag bits */
2006-03-21 09:16:13 +03:00
# define F_IPSRC_RND (1<<0) /* IP-Src Random */
# define F_IPDST_RND (1<<1) /* IP-Dst Random */
# define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
# define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
# define F_MACSRC_RND (1<<4) /* MAC-Src Random */
# define F_MACDST_RND (1<<5) /* MAC-Dst Random */
# define F_TXSIZE_RND (1<<6) /* Transmit size is random */
# define F_IPV6 (1<<7) /* Interface in IPV6 Mode */
2006-03-23 12:10:26 +03:00
# define F_MPLS_RND (1<<8) /* Random MPLS labels */
2006-09-28 03:30:44 +04:00
# define F_VID_RND (1<<9) /* Random VLAN ID */
# define F_SVID_RND (1<<10) /* Random SVLAN ID */
2005-04-17 02:20:36 +04:00
/* Thread control flag bits */
2006-03-21 09:16:13 +03:00
# define T_TERMINATE (1<<0)
# define T_STOP (1<<1) /* Stop run */
# define T_RUN (1<<2) /* Start run */
# define T_REMDEVALL (1<<3) /* Remove all devs */
# define T_REMDEV (1<<4) /* Remove one dev */
2005-04-17 02:20:36 +04:00
/* If lock -- can be removed after some work */
# define if_lock(t) spin_lock(&(t->if_lock));
# define if_unlock(t) spin_unlock(&(t->if_lock));
/* Used to help with determining the pkts on receive */
# define PKTGEN_MAGIC 0xbe9be955
2005-10-15 02:42:33 +04:00
# define PG_PROC_DIR "pktgen"
# define PGCTRL "pgctrl"
static struct proc_dir_entry * pg_proc_dir = NULL ;
2005-04-17 02:20:36 +04:00
# define MAX_CFLOWS 65536
2006-09-28 03:30:44 +04:00
# define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
# define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
2006-03-21 09:16:13 +03:00
struct flow_state {
2006-11-15 07:48:11 +03:00
__be32 cur_daddr ;
2006-03-21 09:16:13 +03:00
int count ;
2005-04-17 02:20:36 +04:00
} ;
struct pktgen_dev {
/*
* Try to keep frequent / infrequent used vars . separated .
*/
2006-03-21 09:16:13 +03:00
char ifname [ IFNAMSIZ ] ;
char result [ 512 ] ;
struct pktgen_thread * pg_thread ; /* the owner */
2006-03-21 09:18:16 +03:00
struct list_head list ; /* Used for chaining in the thread's run-queue */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
int running ; /* if this changes to false, the test will stop */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
/* If min != max, then we will either do a linear iteration, or
* we will do a random selection from within the range .
*/
__u32 flags ;
2006-03-21 08:26:56 +03:00
int removal_mark ; /* non-zero => the device is marked for
* removal by worker thread */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
int min_pkt_size ; /* = ETH_ZLEN; */
int max_pkt_size ; /* = ETH_ZLEN; */
int nfrags ;
__u32 delay_us ; /* Default delay */
__u32 delay_ns ;
__u64 count ; /* Default No packets to send */
__u64 sofar ; /* How many pkts we've sent so far */
__u64 tx_bytes ; /* How many bytes we've transmitted */
__u64 errors ; /* Errors when trying to transmit, pkts will be re-sent */
/* runtime counters relating to clone_skb */
__u64 next_tx_us ; /* timestamp of when to tx next */
__u32 next_tx_ns ;
__u64 allocated_skbs ;
__u32 clone_count ;
int last_ok ; /* Was last skb sent?
* Or a failed transmit of some sort ? This will keep
* sequence numbers in order , for example .
*/
__u64 started_at ; /* micro-seconds */
__u64 stopped_at ; /* micro-seconds */
__u64 idle_acc ; /* micro-seconds */
__u32 seq_num ;
int clone_skb ; /* Use multiple SKBs during packet gen. If this number
* is greater than 1 , then that many copies of the same
* packet will be sent before a new packet is allocated .
* For instance , if you want to send 1024 identical packets
* before creating a new packet , set clone_skb to 1024.
*/
char dst_min [ IP_NAME_SZ ] ; /* IP, ie 1.2.3.4 */
char dst_max [ IP_NAME_SZ ] ; /* IP, ie 1.2.3.4 */
char src_min [ IP_NAME_SZ ] ; /* IP, ie 1.2.3.4 */
char src_max [ IP_NAME_SZ ] ; /* IP, ie 1.2.3.4 */
struct in6_addr in6_saddr ;
struct in6_addr in6_daddr ;
struct in6_addr cur_in6_daddr ;
struct in6_addr cur_in6_saddr ;
2005-04-17 02:20:36 +04:00
/* For ranges */
2006-03-21 09:16:13 +03:00
struct in6_addr min_in6_daddr ;
struct in6_addr max_in6_daddr ;
struct in6_addr min_in6_saddr ;
struct in6_addr max_in6_saddr ;
/* If we're doing ranges, random or incremental, then this
* defines the min / max for those ranges .
*/
2006-11-15 07:48:11 +03:00
__be32 saddr_min ; /* inclusive, source IP address */
__be32 saddr_max ; /* exclusive, source IP address */
__be32 daddr_min ; /* inclusive, dest IP address */
__be32 daddr_max ; /* exclusive, dest IP address */
2006-03-21 09:16:13 +03:00
__u16 udp_src_min ; /* inclusive, source UDP port */
__u16 udp_src_max ; /* exclusive, source UDP port */
__u16 udp_dst_min ; /* inclusive, dest UDP port */
__u16 udp_dst_max ; /* exclusive, dest UDP port */
2006-09-28 03:32:03 +04:00
/* DSCP + ECN */
__u8 tos ; /* six most significant bits of (former) IPv4 TOS are for dscp codepoint */
__u8 traffic_class ; /* ditto for the (former) Traffic Class in IPv6 (see RFC 3260, sec. 4) */
2006-03-23 12:10:26 +03:00
/* MPLS */
unsigned nr_labels ; /* Depth of stack, 0 = no MPLS */
__be32 labels [ MAX_MPLS_LABELS ] ;
2006-09-28 03:30:44 +04:00
/* VLAN/SVLAN (802.1Q/Q-in-Q) */
__u8 vlan_p ;
__u8 vlan_cfi ;
__u16 vlan_id ; /* 0xffff means no vlan tag */
__u8 svlan_p ;
__u8 svlan_cfi ;
__u16 svlan_id ; /* 0xffff means no svlan tag */
2006-03-21 09:16:13 +03:00
__u32 src_mac_count ; /* How many MACs to iterate through */
__u32 dst_mac_count ; /* How many MACs to iterate through */
unsigned char dst_mac [ ETH_ALEN ] ;
unsigned char src_mac [ ETH_ALEN ] ;
__u32 cur_dst_mac_offset ;
__u32 cur_src_mac_offset ;
2006-11-15 07:48:11 +03:00
__be32 cur_saddr ;
__be32 cur_daddr ;
2006-03-21 09:16:13 +03:00
__u16 cur_udp_dst ;
__u16 cur_udp_src ;
__u32 cur_pkt_size ;
__u8 hh [ 14 ] ;
/* = {
0x00 , 0x80 , 0xC8 , 0x79 , 0xB3 , 0xCB ,
We fill in SRC address later
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x08 , 0x00
} ;
*/
__u16 pad ; /* pad out the hh struct to an even 16 bytes */
struct sk_buff * skb ; /* skb we are to transmit next, mainly used for when we
* are transmitting the same one multiple times
*/
struct net_device * odev ; /* The out-going device. Note that the device should
* have it ' s pg_info pointer pointing back to this
* device . This will be set when the user specifies
* the out - going device name ( not when the inject is
* started as it used to do . )
*/
2005-04-17 02:20:36 +04:00
struct flow_state * flows ;
2006-03-21 09:16:13 +03:00
unsigned cflows ; /* Concurrent flows (config) */
unsigned lflow ; /* Flow length (config) */
unsigned nflows ; /* accumulated flows (stats) */
2005-04-17 02:20:36 +04:00
} ;
struct pktgen_hdr {
2006-11-15 07:48:11 +03:00
__be32 pgh_magic ;
__be32 seq_num ;
__be32 tv_sec ;
__be32 tv_usec ;
2005-04-17 02:20:36 +04:00
} ;
struct pktgen_thread {
2006-03-21 09:16:13 +03:00
spinlock_t if_lock ;
2006-03-21 09:18:16 +03:00
struct list_head if_list ; /* All device here */
2006-03-21 09:16:40 +03:00
struct list_head th_list ;
2007-01-02 07:51:53 +03:00
struct task_struct * tsk ;
2006-03-21 09:16:13 +03:00
char result [ 512 ] ;
u32 max_before_softirq ; /* We'll call do_softirq to prevent starvation. */
/* Field for thread to receive "posted" events terminate, stop ifs etc. */
u32 control ;
2005-04-17 02:20:36 +04:00
int pid ;
int cpu ;
2006-03-21 09:16:13 +03:00
wait_queue_head_t queue ;
2005-04-17 02:20:36 +04:00
} ;
# define REMOVE 1
# define FIND 0
/* This code works around the fact that do_div cannot handle two 64-bit
numbers , and regular 64 - bit division doesn ' t work on x86 kernels .
- - Ben
*/
# define PG_DIV 0
/* This was emailed to LMKL by: Chris Caputo <ccaputo@alt.net>
* Function copied / adapted / optimized from :
*
* nemesis . sourceforge . net / browse / lib / static / intmath / ix86 / intmath . c . html
*
* Copyright 1994 , University of Cambridge Computer Laboratory
* All Rights Reserved .
*
*/
2005-07-27 22:46:09 +04:00
static inline s64 divremdi3 ( s64 x , s64 y , int type )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
u64 a = ( x < 0 ) ? - x : x ;
u64 b = ( y < 0 ) ? - y : y ;
u64 res = 0 , d = 1 ;
if ( b > 0 ) {
while ( b < a ) {
b < < = 1 ;
d < < = 1 ;
}
}
do {
if ( a > = b ) {
a - = b ;
res + = d ;
}
b > > = 1 ;
d > > = 1 ;
}
while ( d ) ;
if ( PG_DIV = = type ) {
return ( ( ( x ^ y ) & ( 1ll < < 63 ) ) = = 0 ) ? res : - ( s64 ) res ;
} else {
return ( ( x & ( 1ll < < 63 ) ) = = 0 ) ? a : - ( s64 ) a ;
}
2005-04-17 02:20:36 +04:00
}
/* End of hacks to deal with 64-bit math on x86 */
2005-10-15 02:32:22 +04:00
/** Convert to milliseconds */
2006-03-21 09:16:13 +03:00
static inline __u64 tv_to_ms ( const struct timeval * tv )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
__u64 ms = tv - > tv_usec / 1000 ;
ms + = ( __u64 ) tv - > tv_sec * ( __u64 ) 1000 ;
return ms ;
2005-04-17 02:20:36 +04:00
}
/** Convert to micro-seconds */
2006-03-21 09:16:13 +03:00
static inline __u64 tv_to_us ( const struct timeval * tv )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
__u64 us = tv - > tv_usec ;
us + = ( __u64 ) tv - > tv_sec * ( __u64 ) 1000000 ;
return us ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static inline __u64 pg_div ( __u64 n , __u32 base )
{
__u64 tmp = n ;
do_div ( tmp , base ) ;
/* printk("pktgen: pg_div, n: %llu base: %d rv: %llu\n",
n , base , tmp ) ; */
return tmp ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static inline __u64 pg_div64 ( __u64 n , __u64 base )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
__u64 tmp = n ;
2005-04-17 02:20:36 +04:00
/*
2005-10-15 02:32:22 +04:00
* How do we know if the architecture we are running on
2005-04-17 02:20:36 +04:00
* supports division with 64 bit base ?
2007-02-09 17:24:36 +03:00
*
2005-04-17 02:20:36 +04:00
*/
2006-03-21 09:16:13 +03:00
# if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
do_div ( tmp , base ) ;
2005-04-17 02:20:36 +04:00
# else
2006-03-21 09:16:13 +03:00
tmp = divremdi3 ( n , base , PG_DIV ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-03-21 09:16:13 +03:00
return tmp ;
2005-04-17 02:20:36 +04:00
}
static inline u32 pktgen_random ( void )
{
#if 0
__u32 n ;
get_random_bytes ( & n , 4 ) ;
return n ;
# else
return net_random ( ) ;
# endif
}
2006-03-21 09:16:13 +03:00
static inline __u64 getCurMs ( void )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
struct timeval tv ;
do_gettimeofday ( & tv ) ;
return tv_to_ms ( & tv ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static inline __u64 getCurUs ( void )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
struct timeval tv ;
do_gettimeofday ( & tv ) ;
return tv_to_us ( & tv ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static inline __u64 tv_diff ( const struct timeval * a , const struct timeval * b )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
return tv_to_us ( a ) - tv_to_us ( b ) ;
2005-04-17 02:20:36 +04:00
}
/* old include end */
static char version [ ] __initdata = VERSION ;
2006-03-21 09:16:13 +03:00
static int pktgen_remove_device ( struct pktgen_thread * t , struct pktgen_dev * i ) ;
static int pktgen_add_device ( struct pktgen_thread * t , const char * ifname ) ;
static struct pktgen_dev * pktgen_find_dev ( struct pktgen_thread * t ,
const char * ifname ) ;
2005-04-17 02:20:36 +04:00
static int pktgen_device_event ( struct notifier_block * , unsigned long , void * ) ;
static void pktgen_run_all_threads ( void ) ;
static void pktgen_stop_all_threads_ifs ( void ) ;
static int pktgen_stop_device ( struct pktgen_dev * pkt_dev ) ;
2006-03-21 09:16:13 +03:00
static void pktgen_stop ( struct pktgen_thread * t ) ;
2005-04-17 02:20:36 +04:00
static void pktgen_clear_counters ( struct pktgen_dev * pkt_dev ) ;
2006-03-21 09:16:13 +03:00
static int pktgen_mark_device ( const char * ifname ) ;
static unsigned int scan_ip6 ( const char * s , char ip [ 16 ] ) ;
static unsigned int fmt_ip6 ( char * s , const char ip [ 16 ] ) ;
2005-04-17 02:20:36 +04:00
/* Module parameters, defaults. */
2006-03-21 09:16:13 +03:00
static int pg_count_d = 1000 ; /* 1000 pkts by default */
2005-12-22 23:51:46 +03:00
static int pg_delay_d ;
static int pg_clone_skb_d ;
static int debug ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:24:27 +03:00
static DEFINE_MUTEX ( pktgen_thread_lock ) ;
2006-03-21 09:16:40 +03:00
static LIST_HEAD ( pktgen_threads ) ;
2005-04-17 02:20:36 +04:00
static struct notifier_block pktgen_notifier_block = {
. notifier_call = pktgen_device_event ,
} ;
/*
2007-02-09 17:24:36 +03:00
* / proc handling functions
2005-04-17 02:20:36 +04:00
*
*/
2005-10-15 02:42:33 +04:00
static int pgctrl_show ( struct seq_file * seq , void * v )
2006-03-21 09:16:13 +03:00
{
2005-10-15 02:42:33 +04:00
seq_puts ( seq , VERSION ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static ssize_t pgctrl_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
int err = 0 ;
2005-10-15 02:42:33 +04:00
char data [ 128 ] ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( ! capable ( CAP_NET_ADMIN ) ) {
err = - EPERM ;
2005-04-17 02:20:36 +04:00
goto out ;
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
if ( count > sizeof ( data ) )
count = sizeof ( data ) ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( data , buf , count ) ) {
2005-10-15 02:32:22 +04:00
err = - EFAULT ;
2005-10-15 02:42:33 +04:00
goto out ;
2006-03-21 09:16:13 +03:00
}
data [ count - 1 ] = 0 ; /* Make string */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( data , " stop " ) )
2005-04-17 02:20:36 +04:00
pktgen_stop_all_threads_ifs ( ) ;
2006-03-21 09:16:13 +03:00
else if ( ! strcmp ( data , " start " ) )
2005-04-17 02:20:36 +04:00
pktgen_run_all_threads ( ) ;
2006-03-21 09:16:13 +03:00
else
2005-04-17 02:20:36 +04:00
printk ( " pktgen: Unknown command: %s \n " , data ) ;
err = count ;
2006-03-21 09:16:13 +03:00
out :
return err ;
2005-04-17 02:20:36 +04:00
}
2005-10-15 02:42:33 +04:00
static int pgctrl_open ( struct inode * inode , struct file * file )
{
return single_open ( file , pgctrl_show , PDE ( inode ) - > data ) ;
}
2007-02-12 11:55:35 +03:00
static const struct file_operations pktgen_fops = {
2006-03-21 09:16:13 +03:00
. owner = THIS_MODULE ,
. open = pgctrl_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. write = pgctrl_write ,
. release = single_release ,
2005-10-15 02:42:33 +04:00
} ;
static int pktgen_if_show ( struct seq_file * seq , void * v )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-03-21 09:16:13 +03:00
struct pktgen_dev * pkt_dev = seq - > private ;
__u64 sa ;
__u64 stopped ;
__u64 now = getCurUs ( ) ;
seq_printf ( seq ,
" Params: count %llu min_pkt_size: %u max_pkt_size: %u \n " ,
( unsigned long long ) pkt_dev - > count , pkt_dev - > min_pkt_size ,
pkt_dev - > max_pkt_size ) ;
seq_printf ( seq ,
" frags: %d delay: %u clone_skb: %d ifname: %s \n " ,
pkt_dev - > nfrags ,
1000 * pkt_dev - > delay_us + pkt_dev - > delay_ns ,
pkt_dev - > clone_skb , pkt_dev - > ifname ) ;
seq_printf ( seq , " flows: %u flowlen: %u \n " , pkt_dev - > cflows ,
pkt_dev - > lflow ) ;
if ( pkt_dev - > flags & F_IPV6 ) {
char b1 [ 128 ] , b2 [ 128 ] , b3 [ 128 ] ;
fmt_ip6 ( b1 , pkt_dev - > in6_saddr . s6_addr ) ;
fmt_ip6 ( b2 , pkt_dev - > min_in6_saddr . s6_addr ) ;
fmt_ip6 ( b3 , pkt_dev - > max_in6_saddr . s6_addr ) ;
seq_printf ( seq ,
" saddr: %s min_saddr: %s max_saddr: %s \n " , b1 ,
b2 , b3 ) ;
fmt_ip6 ( b1 , pkt_dev - > in6_daddr . s6_addr ) ;
fmt_ip6 ( b2 , pkt_dev - > min_in6_daddr . s6_addr ) ;
fmt_ip6 ( b3 , pkt_dev - > max_in6_daddr . s6_addr ) ;
seq_printf ( seq ,
" daddr: %s min_daddr: %s max_daddr: %s \n " , b1 ,
b2 , b3 ) ;
} else
seq_printf ( seq ,
" dst_min: %s dst_max: %s \n src_min: %s src_max: %s \n " ,
pkt_dev - > dst_min , pkt_dev - > dst_max , pkt_dev - > src_min ,
pkt_dev - > src_max ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_puts ( seq , " src_mac: " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( is_zero_ether_addr ( pkt_dev - > src_mac ) )
for ( i = 0 ; i < 6 ; i + + )
seq_printf ( seq , " %02X%s " , pkt_dev - > odev - > dev_addr [ i ] ,
i = = 5 ? " " : " : " ) ;
else
for ( i = 0 ; i < 6 ; i + + )
seq_printf ( seq , " %02X%s " , pkt_dev - > src_mac [ i ] ,
i = = 5 ? " " : " : " ) ;
seq_printf ( seq , " dst_mac: " ) ;
for ( i = 0 ; i < 6 ; i + + )
seq_printf ( seq , " %02X%s " , pkt_dev - > dst_mac [ i ] ,
i = = 5 ? " \n " : " : " ) ;
seq_printf ( seq ,
" udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d \n " ,
pkt_dev - > udp_src_min , pkt_dev - > udp_src_max ,
pkt_dev - > udp_dst_min , pkt_dev - > udp_dst_max ) ;
seq_printf ( seq ,
2006-03-23 12:10:26 +03:00
" src_mac_count: %d dst_mac_count: %d \n " ,
2006-03-21 09:16:13 +03:00
pkt_dev - > src_mac_count , pkt_dev - > dst_mac_count ) ;
2005-04-17 02:20:36 +04:00
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > nr_labels ) {
unsigned i ;
seq_printf ( seq , " mpls: " ) ;
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + )
seq_printf ( seq , " %08x%s " , ntohl ( pkt_dev - > labels [ i ] ) ,
i = = pkt_dev - > nr_labels - 1 ? " \n " : " , " ) ;
}
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff ) {
seq_printf ( seq , " vlan_id: %u vlan_p: %u vlan_cfi: %u \n " ,
pkt_dev - > vlan_id , pkt_dev - > vlan_p , pkt_dev - > vlan_cfi ) ;
}
if ( pkt_dev - > svlan_id ! = 0xffff ) {
seq_printf ( seq , " svlan_id: %u vlan_p: %u vlan_cfi: %u \n " ,
pkt_dev - > svlan_id , pkt_dev - > svlan_p , pkt_dev - > svlan_cfi ) ;
}
2006-09-28 03:32:03 +04:00
if ( pkt_dev - > tos ) {
seq_printf ( seq , " tos: 0x%02x \n " , pkt_dev - > tos ) ;
}
if ( pkt_dev - > traffic_class ) {
seq_printf ( seq , " traffic_class: 0x%02x \n " , pkt_dev - > traffic_class ) ;
}
2006-03-23 12:10:26 +03:00
seq_printf ( seq , " Flags: " ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPV6 )
seq_printf ( seq , " IPV6 " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPSRC_RND )
seq_printf ( seq , " IPSRC_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPDST_RND )
seq_printf ( seq , " IPDST_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_TXSIZE_RND )
seq_printf ( seq , " TXSIZE_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_UDPSRC_RND )
seq_printf ( seq , " UDPSRC_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_UDPDST_RND )
seq_printf ( seq , " UDPDST_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > flags & F_MPLS_RND )
seq_printf ( seq , " MPLS_RND " ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_MACSRC_RND )
seq_printf ( seq , " MACSRC_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_MACDST_RND )
seq_printf ( seq , " MACDST_RND " ) ;
2005-04-17 02:20:36 +04:00
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > flags & F_VID_RND )
seq_printf ( seq , " VID_RND " ) ;
if ( pkt_dev - > flags & F_SVID_RND )
seq_printf ( seq , " SVID_RND " ) ;
2006-03-21 09:16:13 +03:00
seq_puts ( seq , " \n " ) ;
sa = pkt_dev - > started_at ;
stopped = pkt_dev - > stopped_at ;
if ( pkt_dev - > running )
stopped = now ; /* not really stopped, more like last-running-at */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq ,
" Current: \n pkts-sofar: %llu errors: %llu \n started: %lluus stopped: %lluus idle: %lluus \n " ,
( unsigned long long ) pkt_dev - > sofar ,
( unsigned long long ) pkt_dev - > errors , ( unsigned long long ) sa ,
( unsigned long long ) stopped ,
( unsigned long long ) pkt_dev - > idle_acc ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq ,
" seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d \n " ,
2005-10-15 02:42:33 +04:00
pkt_dev - > seq_num , pkt_dev - > cur_dst_mac_offset ,
pkt_dev - > cur_src_mac_offset ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPV6 ) {
2005-04-17 02:20:36 +04:00
char b1 [ 128 ] , b2 [ 128 ] ;
2006-03-21 09:16:13 +03:00
fmt_ip6 ( b1 , pkt_dev - > cur_in6_daddr . s6_addr ) ;
fmt_ip6 ( b2 , pkt_dev - > cur_in6_saddr . s6_addr ) ;
seq_printf ( seq , " cur_saddr: %s cur_daddr: %s \n " , b2 , b1 ) ;
} else
seq_printf ( seq , " cur_saddr: 0x%x cur_daddr: 0x%x \n " ,
2005-10-15 02:42:33 +04:00
pkt_dev - > cur_saddr , pkt_dev - > cur_daddr ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " cur_udp_dst: %d cur_udp_src: %d \n " ,
2005-10-15 02:42:33 +04:00
pkt_dev - > cur_udp_dst , pkt_dev - > cur_udp_src ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " flows: %u \n " , pkt_dev - > nflows ) ;
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > result [ 0 ] )
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " Result: %s \n " , pkt_dev - > result ) ;
2005-04-17 02:20:36 +04:00
else
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " Result: Idle \n " ) ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-23 12:10:26 +03:00
2006-09-28 03:32:03 +04:00
static int hex32_arg ( const char __user * user_buffer , unsigned long maxlen , __u32 * num )
2006-03-23 12:10:26 +03:00
{
int i = 0 ;
* num = 0 ;
2006-09-28 03:32:03 +04:00
for ( ; i < maxlen ; i + + ) {
2006-03-23 12:10:26 +03:00
char c ;
* num < < = 4 ;
if ( get_user ( c , & user_buffer [ i ] ) )
return - EFAULT ;
if ( ( c > = ' 0 ' ) & & ( c < = ' 9 ' ) )
* num | = c - ' 0 ' ;
else if ( ( c > = ' a ' ) & & ( c < = ' f ' ) )
* num | = c - ' a ' + 10 ;
else if ( ( c > = ' A ' ) & & ( c < = ' F ' ) )
* num | = c - ' A ' + 10 ;
else
break ;
}
return i ;
}
2006-03-21 09:16:13 +03:00
static int count_trail_chars ( const char __user * user_buffer ,
unsigned int maxlen )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < maxlen ; i + + ) {
2006-03-21 09:16:13 +03:00
char c ;
if ( get_user ( c , & user_buffer [ i ] ) )
return - EFAULT ;
switch ( c ) {
2005-04-17 02:20:36 +04:00
case ' \" ' :
case ' \n ' :
case ' \r ' :
case ' \t ' :
case ' ' :
case ' = ' :
break ;
default :
goto done ;
} ;
}
done :
return i ;
}
2006-03-21 09:16:13 +03:00
static unsigned long num_arg ( const char __user * user_buffer ,
unsigned long maxlen , unsigned long * num )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
* num = 0 ;
2006-03-21 09:16:13 +03:00
for ( ; i < maxlen ; i + + ) {
char c ;
if ( get_user ( c , & user_buffer [ i ] ) )
return - EFAULT ;
if ( ( c > = ' 0 ' ) & & ( c < = ' 9 ' ) ) {
2005-04-17 02:20:36 +04:00
* num * = 10 ;
2006-03-21 09:16:13 +03:00
* num + = c - ' 0 ' ;
2005-04-17 02:20:36 +04:00
} else
break ;
}
return i ;
}
2006-03-21 09:16:13 +03:00
static int strn_len ( const char __user * user_buffer , unsigned int maxlen )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
2006-03-21 09:16:13 +03:00
for ( ; i < maxlen ; i + + ) {
char c ;
if ( get_user ( c , & user_buffer [ i ] ) )
return - EFAULT ;
switch ( c ) {
2005-04-17 02:20:36 +04:00
case ' \" ' :
case ' \n ' :
case ' \r ' :
case ' \t ' :
case ' ' :
goto done_str ;
break ;
default :
break ;
} ;
}
done_str :
return i ;
}
2006-03-23 12:10:26 +03:00
static ssize_t get_labels ( const char __user * buffer , struct pktgen_dev * pkt_dev )
{
unsigned n = 0 ;
char c ;
ssize_t i = 0 ;
int len ;
pkt_dev - > nr_labels = 0 ;
do {
__u32 tmp ;
2006-09-28 03:32:03 +04:00
len = hex32_arg ( & buffer [ i ] , 8 , & tmp ) ;
2006-03-23 12:10:26 +03:00
if ( len < = 0 )
return len ;
pkt_dev - > labels [ n ] = htonl ( tmp ) ;
if ( pkt_dev - > labels [ n ] & MPLS_STACK_BOTTOM )
pkt_dev - > flags | = F_MPLS_RND ;
i + = len ;
if ( get_user ( c , & buffer [ i ] ) )
return - EFAULT ;
i + + ;
n + + ;
if ( n > = MAX_MPLS_LABELS )
return - E2BIG ;
} while ( c = = ' , ' ) ;
pkt_dev - > nr_labels = n ;
return i ;
}
2006-03-21 09:16:13 +03:00
static ssize_t pktgen_if_write ( struct file * file ,
const char __user * user_buffer , size_t count ,
loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
struct seq_file * seq = ( struct seq_file * ) file - > private_data ;
struct pktgen_dev * pkt_dev = seq - > private ;
2005-04-17 02:20:36 +04:00
int i = 0 , max , len ;
char name [ 16 ] , valstr [ 32 ] ;
unsigned long value = 0 ;
2006-03-21 09:16:13 +03:00
char * pg_result = NULL ;
int tmp = 0 ;
2005-04-17 02:20:36 +04:00
char buf [ 128 ] ;
2006-03-21 09:16:13 +03:00
pg_result = & ( pkt_dev - > result [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
if ( count < 1 ) {
printk ( " pktgen: wrong command format \n " ) ;
return - EINVAL ;
}
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
max = count - i ;
tmp = count_trail_chars ( & user_buffer [ i ] , max ) ;
2006-03-21 09:16:13 +03:00
if ( tmp < 0 ) {
2005-04-17 02:20:36 +04:00
printk ( " pktgen: illegal format \n " ) ;
2006-03-21 09:16:13 +03:00
return tmp ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
i + = tmp ;
2005-04-17 02:20:36 +04:00
/* Read variable name */
len = strn_len ( & user_buffer [ i ] , sizeof ( name ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
memset ( name , 0 , sizeof ( name ) ) ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( name , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
i + = len ;
2006-03-21 09:16:13 +03:00
max = count - i ;
2005-04-17 02:20:36 +04:00
len = count_trail_chars ( & user_buffer [ i ] , max ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
return len ;
2005-04-17 02:20:36 +04:00
i + = len ;
if ( debug ) {
2006-03-21 09:16:13 +03:00
char tb [ count + 1 ] ;
if ( copy_from_user ( tb , user_buffer , count ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
tb [ count ] = 0 ;
2005-10-15 02:42:33 +04:00
printk ( " pktgen: %s,%lu buffer -:%s:- \n " , name ,
2006-03-21 09:16:13 +03:00
( unsigned long ) count , tb ) ;
}
2005-04-17 02:20:36 +04:00
if ( ! strcmp ( name , " min_pkt_size " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value < 14 + 20 + 8 )
value = 14 + 20 + 8 ;
if ( value ! = pkt_dev - > min_pkt_size ) {
pkt_dev - > min_pkt_size = value ;
pkt_dev - > cur_pkt_size = value ;
}
sprintf ( pg_result , " OK: min_pkt_size=%u " ,
pkt_dev - > min_pkt_size ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " max_pkt_size " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value < 14 + 20 + 8 )
value = 14 + 20 + 8 ;
if ( value ! = pkt_dev - > max_pkt_size ) {
pkt_dev - > max_pkt_size = value ;
pkt_dev - > cur_pkt_size = value ;
}
sprintf ( pg_result , " OK: max_pkt_size=%u " ,
pkt_dev - > max_pkt_size ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2006-03-21 09:16:13 +03:00
/* Shortcut for min = max */
2005-04-17 02:20:36 +04:00
if ( ! strcmp ( name , " pkt_size " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value < 14 + 20 + 8 )
value = 14 + 20 + 8 ;
if ( value ! = pkt_dev - > min_pkt_size ) {
pkt_dev - > min_pkt_size = value ;
pkt_dev - > max_pkt_size = value ;
pkt_dev - > cur_pkt_size = value ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: pkt_size=%u " , pkt_dev - > min_pkt_size ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " debug " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
debug = value ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: debug=%u " , debug ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " frags " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
pkt_dev - > nfrags = value ;
sprintf ( pg_result , " OK: frags=%u " , pkt_dev - > nfrags ) ;
return count ;
}
if ( ! strcmp ( name , " delay " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
if ( value = = 0x7FFFFFFF ) {
pkt_dev - > delay_us = 0x7FFFFFFF ;
pkt_dev - > delay_ns = 0 ;
} else {
pkt_dev - > delay_us = value / 1000 ;
pkt_dev - > delay_ns = value % 1000 ;
}
2006-03-21 09:16:13 +03:00
sprintf ( pg_result , " OK: delay=%u " ,
1000 * pkt_dev - > delay_us + pkt_dev - > delay_ns ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " udp_src_min " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value ! = pkt_dev - > udp_src_min ) {
pkt_dev - > udp_src_min = value ;
pkt_dev - > cur_udp_src = value ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: udp_src_min=%u " , pkt_dev - > udp_src_min ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " udp_dst_min " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value ! = pkt_dev - > udp_dst_min ) {
pkt_dev - > udp_dst_min = value ;
pkt_dev - > cur_udp_dst = value ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: udp_dst_min=%u " , pkt_dev - > udp_dst_min ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " udp_src_max " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value ! = pkt_dev - > udp_src_max ) {
pkt_dev - > udp_src_max = value ;
pkt_dev - > cur_udp_src = value ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: udp_src_max=%u " , pkt_dev - > udp_src_max ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " udp_dst_max " ) ) {
2005-04-17 02:20:36 +04:00
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
if ( value ! = pkt_dev - > udp_dst_max ) {
pkt_dev - > udp_dst_max = value ;
pkt_dev - > cur_udp_dst = value ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: udp_dst_max=%u " , pkt_dev - > udp_dst_max ) ;
return count ;
}
if ( ! strcmp ( name , " clone_skb " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
2006-03-21 09:16:13 +03:00
pkt_dev - > clone_skb = value ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: clone_skb=%d " , pkt_dev - > clone_skb ) ;
return count ;
}
if ( ! strcmp ( name , " count " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
pkt_dev - > count = value ;
sprintf ( pg_result , " OK: count=%llu " ,
2006-03-21 09:16:13 +03:00
( unsigned long long ) pkt_dev - > count ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
if ( ! strcmp ( name , " src_mac_count " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
if ( pkt_dev - > src_mac_count ! = value ) {
2006-03-21 09:16:13 +03:00
pkt_dev - > src_mac_count = value ;
pkt_dev - > cur_src_mac_offset = 0 ;
}
sprintf ( pg_result , " OK: src_mac_count=%d " ,
pkt_dev - > src_mac_count ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
if ( ! strcmp ( name , " dst_mac_count " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
if ( pkt_dev - > dst_mac_count ! = value ) {
2006-03-21 09:16:13 +03:00
pkt_dev - > dst_mac_count = value ;
pkt_dev - > cur_dst_mac_offset = 0 ;
}
sprintf ( pg_result , " OK: dst_mac_count=%d " ,
pkt_dev - > dst_mac_count ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
if ( ! strcmp ( name , " flag " ) ) {
2006-03-21 09:16:13 +03:00
char f [ 32 ] ;
memset ( f , 0 , 32 ) ;
2005-04-17 02:20:36 +04:00
len = strn_len ( & user_buffer [ i ] , sizeof ( f ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( f , & user_buffer [ i ] , len ) )
return - EFAULT ;
i + = len ;
2006-03-21 09:16:13 +03:00
if ( strcmp ( f , " IPSRC_RND " ) = = 0 )
pkt_dev - > flags | = F_IPSRC_RND ;
else if ( strcmp ( f , " !IPSRC_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_IPSRC_RND ;
else if ( strcmp ( f , " TXSIZE_RND " ) = = 0 )
pkt_dev - > flags | = F_TXSIZE_RND ;
else if ( strcmp ( f , " !TXSIZE_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_TXSIZE_RND ;
else if ( strcmp ( f , " IPDST_RND " ) = = 0 )
pkt_dev - > flags | = F_IPDST_RND ;
else if ( strcmp ( f , " !IPDST_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_IPDST_RND ;
else if ( strcmp ( f , " UDPSRC_RND " ) = = 0 )
pkt_dev - > flags | = F_UDPSRC_RND ;
else if ( strcmp ( f , " !UDPSRC_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_UDPSRC_RND ;
else if ( strcmp ( f , " UDPDST_RND " ) = = 0 )
pkt_dev - > flags | = F_UDPDST_RND ;
else if ( strcmp ( f , " !UDPDST_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_UDPDST_RND ;
else if ( strcmp ( f , " MACSRC_RND " ) = = 0 )
pkt_dev - > flags | = F_MACSRC_RND ;
else if ( strcmp ( f , " !MACSRC_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_MACSRC_RND ;
else if ( strcmp ( f , " MACDST_RND " ) = = 0 )
pkt_dev - > flags | = F_MACDST_RND ;
else if ( strcmp ( f , " !MACDST_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_MACDST_RND ;
2006-03-23 12:10:26 +03:00
else if ( strcmp ( f , " MPLS_RND " ) = = 0 )
pkt_dev - > flags | = F_MPLS_RND ;
else if ( strcmp ( f , " !MPLS_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_MPLS_RND ;
2006-09-28 03:30:44 +04:00
else if ( strcmp ( f , " VID_RND " ) = = 0 )
pkt_dev - > flags | = F_VID_RND ;
else if ( strcmp ( f , " !VID_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_VID_RND ;
else if ( strcmp ( f , " SVID_RND " ) = = 0 )
pkt_dev - > flags | = F_SVID_RND ;
else if ( strcmp ( f , " !SVID_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_SVID_RND ;
2006-09-28 03:32:03 +04:00
else if ( strcmp ( f , " !IPV6 " ) = = 0 )
pkt_dev - > flags & = ~ F_IPV6 ;
2006-03-21 09:16:13 +03:00
else {
sprintf ( pg_result ,
" Flag -:%s:- unknown \n Available flags, (prepend ! to un-set flag): \n %s " ,
f ,
2006-09-28 03:32:03 +04:00
" IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
" MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND \n " ) ;
2006-03-21 09:16:13 +03:00
return count ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: flags=0x%x " , pkt_dev - > flags ) ;
return count ;
}
if ( ! strcmp ( name , " dst_min " ) | | ! strcmp ( name , " dst " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( pkt_dev - > dst_min ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
if ( strcmp ( buf , pkt_dev - > dst_min ) ! = 0 ) {
memset ( pkt_dev - > dst_min , 0 , sizeof ( pkt_dev - > dst_min ) ) ;
strncpy ( pkt_dev - > dst_min , buf , len ) ;
pkt_dev - > daddr_min = in_aton ( pkt_dev - > dst_min ) ;
pkt_dev - > cur_daddr = pkt_dev - > daddr_min ;
}
if ( debug )
printk ( " pktgen: dst_min set to: %s \n " ,
pkt_dev - > dst_min ) ;
i + = len ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: dst_min=%s " , pkt_dev - > dst_min ) ;
return count ;
}
if ( ! strcmp ( name , " dst_max " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( pkt_dev - > dst_max ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
if ( strcmp ( buf , pkt_dev - > dst_max ) ! = 0 ) {
memset ( pkt_dev - > dst_max , 0 , sizeof ( pkt_dev - > dst_max ) ) ;
strncpy ( pkt_dev - > dst_max , buf , len ) ;
pkt_dev - > daddr_max = in_aton ( pkt_dev - > dst_max ) ;
pkt_dev - > cur_daddr = pkt_dev - > daddr_max ;
}
if ( debug )
printk ( " pktgen: dst_max set to: %s \n " ,
pkt_dev - > dst_max ) ;
2005-04-17 02:20:36 +04:00
i + = len ;
sprintf ( pg_result , " OK: dst_max=%s " , pkt_dev - > dst_max ) ;
return count ;
}
if ( ! strcmp ( name , " dst6 " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( buf ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
return len ;
2005-04-17 02:20:36 +04:00
pkt_dev - > flags | = F_IPV6 ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
2005-04-17 02:20:36 +04:00
scan_ip6 ( buf , pkt_dev - > in6_daddr . s6_addr ) ;
2006-03-21 09:16:13 +03:00
fmt_ip6 ( buf , pkt_dev - > in6_daddr . s6_addr ) ;
2005-04-17 02:20:36 +04:00
ipv6_addr_copy ( & pkt_dev - > cur_in6_daddr , & pkt_dev - > in6_daddr ) ;
2006-03-21 09:16:13 +03:00
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " pktgen: dst6 set to: %s \n " , buf ) ;
2006-03-21 09:16:13 +03:00
i + = len ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: dst6=%s " , buf ) ;
return count ;
}
if ( ! strcmp ( name , " dst6_min " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( buf ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
return len ;
2005-04-17 02:20:36 +04:00
pkt_dev - > flags | = F_IPV6 ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
2005-04-17 02:20:36 +04:00
scan_ip6 ( buf , pkt_dev - > min_in6_daddr . s6_addr ) ;
2006-03-21 09:16:13 +03:00
fmt_ip6 ( buf , pkt_dev - > min_in6_daddr . s6_addr ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
ipv6_addr_copy ( & pkt_dev - > cur_in6_daddr ,
& pkt_dev - > min_in6_daddr ) ;
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " pktgen: dst6_min set to: %s \n " , buf ) ;
2006-03-21 09:16:13 +03:00
i + = len ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: dst6_min=%s " , buf ) ;
return count ;
}
if ( ! strcmp ( name , " dst6_max " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( buf ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
return len ;
2005-04-17 02:20:36 +04:00
pkt_dev - > flags | = F_IPV6 ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
2005-04-17 02:20:36 +04:00
scan_ip6 ( buf , pkt_dev - > max_in6_daddr . s6_addr ) ;
2006-03-21 09:16:13 +03:00
fmt_ip6 ( buf , pkt_dev - > max_in6_daddr . s6_addr ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " pktgen: dst6_max set to: %s \n " , buf ) ;
2006-03-21 09:16:13 +03:00
i + = len ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: dst6_max=%s " , buf ) ;
return count ;
}
if ( ! strcmp ( name , " src6 " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( buf ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
return len ;
2005-04-17 02:20:36 +04:00
pkt_dev - > flags | = F_IPV6 ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
2005-04-17 02:20:36 +04:00
scan_ip6 ( buf , pkt_dev - > in6_saddr . s6_addr ) ;
2006-03-21 09:16:13 +03:00
fmt_ip6 ( buf , pkt_dev - > in6_saddr . s6_addr ) ;
2005-04-17 02:20:36 +04:00
ipv6_addr_copy ( & pkt_dev - > cur_in6_saddr , & pkt_dev - > in6_saddr ) ;
2006-03-21 09:16:13 +03:00
if ( debug )
2005-04-17 02:20:36 +04:00
printk ( " pktgen: src6 set to: %s \n " , buf ) ;
2006-03-21 09:16:13 +03:00
i + = len ;
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: src6=%s " , buf ) ;
return count ;
}
if ( ! strcmp ( name , " src_min " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( pkt_dev - > src_min ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
if ( strcmp ( buf , pkt_dev - > src_min ) ! = 0 ) {
memset ( pkt_dev - > src_min , 0 , sizeof ( pkt_dev - > src_min ) ) ;
strncpy ( pkt_dev - > src_min , buf , len ) ;
pkt_dev - > saddr_min = in_aton ( pkt_dev - > src_min ) ;
pkt_dev - > cur_saddr = pkt_dev - > saddr_min ;
}
if ( debug )
printk ( " pktgen: src_min set to: %s \n " ,
pkt_dev - > src_min ) ;
2005-04-17 02:20:36 +04:00
i + = len ;
sprintf ( pg_result , " OK: src_min=%s " , pkt_dev - > src_min ) ;
return count ;
}
if ( ! strcmp ( name , " src_max " ) ) {
len = strn_len ( & user_buffer [ i ] , sizeof ( pkt_dev - > src_max ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
if ( copy_from_user ( buf , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-21 09:16:13 +03:00
buf [ len ] = 0 ;
if ( strcmp ( buf , pkt_dev - > src_max ) ! = 0 ) {
memset ( pkt_dev - > src_max , 0 , sizeof ( pkt_dev - > src_max ) ) ;
strncpy ( pkt_dev - > src_max , buf , len ) ;
pkt_dev - > saddr_max = in_aton ( pkt_dev - > src_max ) ;
pkt_dev - > cur_saddr = pkt_dev - > saddr_max ;
}
if ( debug )
printk ( " pktgen: src_max set to: %s \n " ,
pkt_dev - > src_max ) ;
2005-04-17 02:20:36 +04:00
i + = len ;
sprintf ( pg_result , " OK: src_max=%s " , pkt_dev - > src_max ) ;
return count ;
}
if ( ! strcmp ( name , " dst_mac " ) ) {
char * v = valstr ;
2006-01-18 00:04:57 +03:00
unsigned char old_dmac [ ETH_ALEN ] ;
2005-04-17 02:20:36 +04:00
unsigned char * m = pkt_dev - > dst_mac ;
2006-01-18 00:04:57 +03:00
memcpy ( old_dmac , pkt_dev - > dst_mac , ETH_ALEN ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
len = strn_len ( & user_buffer [ i ] , sizeof ( valstr ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
memset ( valstr , 0 , sizeof ( valstr ) ) ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( valstr , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
i + = len ;
2006-03-21 09:16:13 +03:00
for ( * m = 0 ; * v & & m < pkt_dev - > dst_mac + 6 ; v + + ) {
2005-04-17 02:20:36 +04:00
if ( * v > = ' 0 ' & & * v < = ' 9 ' ) {
* m * = 16 ;
* m + = * v - ' 0 ' ;
}
if ( * v > = ' A ' & & * v < = ' F ' ) {
* m * = 16 ;
* m + = * v - ' A ' + 10 ;
}
if ( * v > = ' a ' & & * v < = ' f ' ) {
* m * = 16 ;
* m + = * v - ' a ' + 10 ;
}
if ( * v = = ' : ' ) {
m + + ;
* m = 0 ;
}
}
/* Set up Dest MAC */
2006-01-18 00:04:57 +03:00
if ( compare_ether_addr ( old_dmac , pkt_dev - > dst_mac ) )
memcpy ( & ( pkt_dev - > hh [ 0 ] ) , pkt_dev - > dst_mac , ETH_ALEN ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
sprintf ( pg_result , " OK: dstmac " ) ;
return count ;
}
if ( ! strcmp ( name , " src_mac " ) ) {
char * v = valstr ;
unsigned char * m = pkt_dev - > src_mac ;
len = strn_len ( & user_buffer [ i ] , sizeof ( valstr ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
memset ( valstr , 0 , sizeof ( valstr ) ) ;
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( valstr , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
i + = len ;
2006-03-21 09:16:13 +03:00
for ( * m = 0 ; * v & & m < pkt_dev - > src_mac + 6 ; v + + ) {
2005-04-17 02:20:36 +04:00
if ( * v > = ' 0 ' & & * v < = ' 9 ' ) {
* m * = 16 ;
* m + = * v - ' 0 ' ;
}
if ( * v > = ' A ' & & * v < = ' F ' ) {
* m * = 16 ;
* m + = * v - ' A ' + 10 ;
}
if ( * v > = ' a ' & & * v < = ' f ' ) {
* m * = 16 ;
* m + = * v - ' a ' + 10 ;
}
if ( * v = = ' : ' ) {
m + + ;
* m = 0 ;
}
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
sprintf ( pg_result , " OK: srcmac " ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " clear_counters " ) ) {
pktgen_clear_counters ( pkt_dev ) ;
sprintf ( pg_result , " OK: Clearing counters. \n " ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
if ( ! strcmp ( name , " flows " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
if ( value > MAX_CFLOWS )
value = MAX_CFLOWS ;
pkt_dev - > cflows = value ;
sprintf ( pg_result , " OK: flows=%u " , pkt_dev - > cflows ) ;
return count ;
}
if ( ! strcmp ( name , " flowlen " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
return len ;
}
2005-04-17 02:20:36 +04:00
i + = len ;
pkt_dev - > lflow = value ;
sprintf ( pg_result , " OK: flowlen=%u " , pkt_dev - > lflow ) ;
return count ;
}
2006-03-21 09:16:13 +03:00
2006-03-23 12:10:26 +03:00
if ( ! strcmp ( name , " mpls " ) ) {
unsigned n , offset ;
len = get_labels ( & user_buffer [ i ] , pkt_dev ) ;
if ( len < 0 ) { return len ; }
i + = len ;
offset = sprintf ( pg_result , " OK: mpls= " ) ;
for ( n = 0 ; n < pkt_dev - > nr_labels ; n + + )
offset + = sprintf ( pg_result + offset ,
" %08x%s " , ntohl ( pkt_dev - > labels [ n ] ) ,
n = = pkt_dev - > nr_labels - 1 ? " " : " , " ) ;
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > nr_labels & & pkt_dev - > vlan_id ! = 0xffff ) {
pkt_dev - > vlan_id = 0xffff ; /* turn off VLAN/SVLAN */
pkt_dev - > svlan_id = 0xffff ;
if ( debug )
printk ( " pktgen: VLAN/SVLAN auto turned off \n " ) ;
}
return count ;
}
if ( ! strcmp ( name , " vlan_id " ) ) {
len = num_arg ( & user_buffer [ i ] , 4 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( value < = 4095 ) {
pkt_dev - > vlan_id = value ; /* turn on VLAN */
if ( debug )
printk ( " pktgen: VLAN turned on \n " ) ;
if ( debug & & pkt_dev - > nr_labels )
printk ( " pktgen: MPLS auto turned off \n " ) ;
pkt_dev - > nr_labels = 0 ; /* turn off MPLS */
sprintf ( pg_result , " OK: vlan_id=%u " , pkt_dev - > vlan_id ) ;
} else {
pkt_dev - > vlan_id = 0xffff ; /* turn off VLAN/SVLAN */
pkt_dev - > svlan_id = 0xffff ;
if ( debug )
printk ( " pktgen: VLAN/SVLAN turned off \n " ) ;
}
return count ;
}
if ( ! strcmp ( name , " vlan_p " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( ( value < = 7 ) & & ( pkt_dev - > vlan_id ! = 0xffff ) ) {
pkt_dev - > vlan_p = value ;
sprintf ( pg_result , " OK: vlan_p=%u " , pkt_dev - > vlan_p ) ;
} else {
sprintf ( pg_result , " ERROR: vlan_p must be 0-7 " ) ;
}
return count ;
}
if ( ! strcmp ( name , " vlan_cfi " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( ( value < = 1 ) & & ( pkt_dev - > vlan_id ! = 0xffff ) ) {
pkt_dev - > vlan_cfi = value ;
sprintf ( pg_result , " OK: vlan_cfi=%u " , pkt_dev - > vlan_cfi ) ;
} else {
sprintf ( pg_result , " ERROR: vlan_cfi must be 0-1 " ) ;
}
return count ;
}
if ( ! strcmp ( name , " svlan_id " ) ) {
len = num_arg ( & user_buffer [ i ] , 4 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( ( value < = 4095 ) & & ( ( pkt_dev - > vlan_id ! = 0xffff ) ) ) {
pkt_dev - > svlan_id = value ; /* turn on SVLAN */
if ( debug )
printk ( " pktgen: SVLAN turned on \n " ) ;
if ( debug & & pkt_dev - > nr_labels )
printk ( " pktgen: MPLS auto turned off \n " ) ;
pkt_dev - > nr_labels = 0 ; /* turn off MPLS */
sprintf ( pg_result , " OK: svlan_id=%u " , pkt_dev - > svlan_id ) ;
} else {
pkt_dev - > vlan_id = 0xffff ; /* turn off VLAN/SVLAN */
pkt_dev - > svlan_id = 0xffff ;
if ( debug )
printk ( " pktgen: VLAN/SVLAN turned off \n " ) ;
}
return count ;
}
if ( ! strcmp ( name , " svlan_p " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( ( value < = 7 ) & & ( pkt_dev - > svlan_id ! = 0xffff ) ) {
pkt_dev - > svlan_p = value ;
sprintf ( pg_result , " OK: svlan_p=%u " , pkt_dev - > svlan_p ) ;
} else {
sprintf ( pg_result , " ERROR: svlan_p must be 0-7 " ) ;
}
return count ;
}
if ( ! strcmp ( name , " svlan_cfi " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( ( value < = 1 ) & & ( pkt_dev - > svlan_id ! = 0xffff ) ) {
pkt_dev - > svlan_cfi = value ;
sprintf ( pg_result , " OK: svlan_cfi=%u " , pkt_dev - > svlan_cfi ) ;
} else {
sprintf ( pg_result , " ERROR: svlan_cfi must be 0-1 " ) ;
}
2006-03-23 12:10:26 +03:00
return count ;
}
2006-09-28 03:32:03 +04:00
if ( ! strcmp ( name , " tos " ) ) {
__u32 tmp_value = 0 ;
len = hex32_arg ( & user_buffer [ i ] , 2 , & tmp_value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( len = = 2 ) {
pkt_dev - > tos = tmp_value ;
sprintf ( pg_result , " OK: tos=0x%02x " , pkt_dev - > tos ) ;
} else {
sprintf ( pg_result , " ERROR: tos must be 00-ff " ) ;
}
return count ;
}
if ( ! strcmp ( name , " traffic_class " ) ) {
__u32 tmp_value = 0 ;
len = hex32_arg ( & user_buffer [ i ] , 2 , & tmp_value ) ;
if ( len < 0 ) {
return len ;
}
i + = len ;
if ( len = = 2 ) {
pkt_dev - > traffic_class = tmp_value ;
sprintf ( pg_result , " OK: traffic_class=0x%02x " , pkt_dev - > traffic_class ) ;
} else {
sprintf ( pg_result , " ERROR: traffic_class must be 00-ff " ) ;
}
return count ;
}
2005-04-17 02:20:36 +04:00
sprintf ( pkt_dev - > result , " No such parameter \" %s \" " , name ) ;
return - EINVAL ;
}
2005-10-15 02:42:33 +04:00
static int pktgen_if_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-10-15 02:42:33 +04:00
return single_open ( file , pktgen_if_show , PDE ( inode ) - > data ) ;
}
2005-04-17 02:20:36 +04:00
2007-02-12 11:55:35 +03:00
static const struct file_operations pktgen_if_fops = {
2006-03-21 09:16:13 +03:00
. owner = THIS_MODULE ,
. open = pktgen_if_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. write = pktgen_if_write ,
. release = single_release ,
2005-10-15 02:42:33 +04:00
} ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
static int pktgen_thread_show ( struct seq_file * seq , void * v )
{
2006-03-21 09:16:13 +03:00
struct pktgen_thread * t = seq - > private ;
2006-03-21 09:18:16 +03:00
struct pktgen_dev * pkt_dev ;
2005-10-15 02:42:33 +04:00
BUG_ON ( ! t ) ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
seq_printf ( seq , " Name: %s max_before_softirq: %d \n " ,
2007-01-02 07:51:53 +03:00
t - > tsk - > comm , t - > max_before_softirq ) ;
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " Running: " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list )
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > running )
2005-10-15 02:42:33 +04:00
seq_printf ( seq , " %s " , pkt_dev - > ifname ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " \n Stopped: " ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list )
2006-03-21 09:16:13 +03:00
if ( ! pkt_dev - > running )
2005-10-15 02:42:33 +04:00
seq_printf ( seq , " %s " , pkt_dev - > ifname ) ;
2005-04-17 02:20:36 +04:00
if ( t - > result [ 0 ] )
2005-10-15 02:42:33 +04:00
seq_printf ( seq , " \n Result: %s \n " , t - > result ) ;
2005-04-17 02:20:36 +04:00
else
2005-10-15 02:42:33 +04:00
seq_printf ( seq , " \n Result: NA \n " ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if_unlock ( t ) ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-10-15 02:42:33 +04:00
static ssize_t pktgen_thread_write ( struct file * file ,
2006-03-21 09:16:13 +03:00
const char __user * user_buffer ,
size_t count , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
struct seq_file * seq = ( struct seq_file * ) file - > private_data ;
struct pktgen_thread * t = seq - > private ;
2005-04-17 02:20:36 +04:00
int i = 0 , max , len , ret ;
char name [ 40 ] ;
2006-03-21 09:16:13 +03:00
char * pg_result ;
unsigned long value = 0 ;
2005-10-15 02:42:33 +04:00
2005-04-17 02:20:36 +04:00
if ( count < 1 ) {
2006-03-21 09:16:13 +03:00
// sprintf(pg_result, "Wrong command format");
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-10-15 02:42:33 +04:00
2005-04-17 02:20:36 +04:00
max = count - i ;
2006-03-21 09:16:13 +03:00
len = count_trail_chars ( & user_buffer [ i ] , max ) ;
if ( len < 0 )
2005-10-15 02:42:33 +04:00
return len ;
2005-04-17 02:20:36 +04:00
i + = len ;
2005-10-15 02:42:33 +04:00
2005-04-17 02:20:36 +04:00
/* Read variable name */
len = strn_len ( & user_buffer [ i ] , sizeof ( name ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
2005-10-15 02:42:33 +04:00
return len ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
memset ( name , 0 , sizeof ( name ) ) ;
if ( copy_from_user ( name , & user_buffer [ i ] , len ) )
return - EFAULT ;
i + = len ;
2005-10-15 02:42:33 +04:00
2006-03-21 09:16:13 +03:00
max = count - i ;
2005-04-17 02:20:36 +04:00
len = count_trail_chars ( & user_buffer [ i ] , max ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
2005-10-15 02:42:33 +04:00
return len ;
2005-04-17 02:20:36 +04:00
i + = len ;
2005-10-15 02:42:33 +04:00
if ( debug )
2006-03-21 09:16:13 +03:00
printk ( " pktgen: t=%s, count=%lu \n " , name , ( unsigned long ) count ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( ! t ) {
2005-04-17 02:20:36 +04:00
printk ( " pktgen: ERROR: No thread \n " ) ;
ret = - EINVAL ;
goto out ;
}
pg_result = & ( t - > result [ 0 ] ) ;
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " add_device " ) ) {
char f [ 32 ] ;
memset ( f , 0 , 32 ) ;
2005-04-17 02:20:36 +04:00
len = strn_len ( & user_buffer [ i ] , sizeof ( f ) - 1 ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 ) {
ret = len ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-03-21 09:16:13 +03:00
if ( copy_from_user ( f , & user_buffer [ i ] , len ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
i + = len ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
pktgen_add_device ( t , f ) ;
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
ret = count ;
sprintf ( pg_result , " OK: add_device=%s " , f ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " rem_device_all " ) ) {
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 08:26:56 +03:00
t - > control | = T_REMDEVALL ;
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
schedule_timeout_interruptible ( msecs_to_jiffies ( 125 ) ) ; /* Propagate thread->control */
2005-04-17 02:20:36 +04:00
ret = count ;
2006-03-21 09:16:13 +03:00
sprintf ( pg_result , " OK: rem_device_all " ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-03-21 09:16:13 +03:00
if ( ! strcmp ( name , " max_before_softirq " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
t - > max_before_softirq = value ;
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
ret = count ;
sprintf ( pg_result , " OK: max_before_softirq=%lu " , value ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
ret = - EINVAL ;
2006-03-21 09:16:13 +03:00
out :
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-10-15 02:42:33 +04:00
static int pktgen_thread_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-10-15 02:42:33 +04:00
return single_open ( file , pktgen_thread_show , PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:35 +03:00
static const struct file_operations pktgen_thread_fops = {
2006-03-21 09:16:13 +03:00
. owner = THIS_MODULE ,
. open = pktgen_thread_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. write = pktgen_thread_write ,
. release = single_release ,
2005-10-15 02:42:33 +04:00
} ;
2005-04-17 02:20:36 +04:00
/* Think find or remove for NN */
2006-03-21 09:16:13 +03:00
static struct pktgen_dev * __pktgen_NN_threads ( const char * ifname , int remove )
2005-04-17 02:20:36 +04:00
{
struct pktgen_thread * t ;
struct pktgen_dev * pkt_dev = NULL ;
2006-03-21 09:16:40 +03:00
list_for_each_entry ( t , & pktgen_threads , th_list ) {
2005-04-17 02:20:36 +04:00
pkt_dev = pktgen_find_dev ( t , ifname ) ;
if ( pkt_dev ) {
2006-03-21 09:16:13 +03:00
if ( remove ) {
if_lock ( t ) ;
pkt_dev - > removal_mark = 1 ;
t - > control | = T_REMDEV ;
if_unlock ( t ) ;
}
2005-04-17 02:20:36 +04:00
break ;
}
}
2006-03-21 09:16:13 +03:00
return pkt_dev ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 08:26:56 +03:00
/*
* mark a device for removal
*/
2006-03-21 09:16:13 +03:00
static int pktgen_mark_device ( const char * ifname )
2005-04-17 02:20:36 +04:00
{
struct pktgen_dev * pkt_dev = NULL ;
2006-03-21 08:26:56 +03:00
const int max_tries = 10 , msec_per_try = 125 ;
int i = 0 ;
int ret = 0 ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:13 +03:00
PG_DEBUG ( printk ( " pktgen: pktgen_mark_device marking %s for removal \n " ,
2006-03-21 08:26:56 +03:00
ifname ) ) ;
2006-03-21 09:16:13 +03:00
while ( 1 ) {
2006-03-21 08:26:56 +03:00
pkt_dev = __pktgen_NN_threads ( ifname , REMOVE ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev = = NULL )
break ; /* success */
2006-03-21 08:26:56 +03:00
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2006-03-21 08:26:56 +03:00
PG_DEBUG ( printk ( " pktgen: pktgen_mark_device waiting for %s "
2006-03-21 09:16:13 +03:00
" to disappear.... \n " , ifname ) ) ;
2006-03-21 08:26:56 +03:00
schedule_timeout_interruptible ( msecs_to_jiffies ( msec_per_try ) ) ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 08:26:56 +03:00
if ( + + i > = max_tries ) {
printk ( " pktgen_mark_device: timed out after waiting "
2006-03-21 09:16:13 +03:00
" %d msec for device %s to be removed \n " ,
msec_per_try * i , ifname ) ;
2006-03-21 08:26:56 +03:00
ret = 1 ;
break ;
}
}
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2006-03-21 08:26:56 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static int pktgen_device_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = ( struct net_device * ) ( ptr ) ;
/* It is OK that we do not hold the group lock right now,
* as we run under the RTNL lock .
*/
switch ( event ) {
case NETDEV_CHANGEADDR :
case NETDEV_GOING_DOWN :
case NETDEV_DOWN :
case NETDEV_UP :
/* Ignore for now */
break ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
case NETDEV_UNREGISTER :
2006-03-21 09:16:13 +03:00
pktgen_mark_device ( dev - > name ) ;
2005-04-17 02:20:36 +04:00
break ;
} ;
return NOTIFY_DONE ;
}
/* Associate pktgen_dev with a device. */
2006-03-21 09:16:13 +03:00
static struct net_device * pktgen_setup_dev ( struct pktgen_dev * pkt_dev )
{
2005-04-17 02:20:36 +04:00
struct net_device * odev ;
/* Clean old setups */
if ( pkt_dev - > odev ) {
dev_put ( pkt_dev - > odev ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > odev = NULL ;
}
2005-04-17 02:20:36 +04:00
odev = dev_get_by_name ( pkt_dev - > ifname ) ;
if ( ! odev ) {
printk ( " pktgen: no such netdevice: \" %s \" \n " , pkt_dev - > ifname ) ;
goto out ;
}
if ( odev - > type ! = ARPHRD_ETHER ) {
2006-03-21 09:16:13 +03:00
printk ( " pktgen: not an ethernet device: \" %s \" \n " ,
pkt_dev - > ifname ) ;
2005-04-17 02:20:36 +04:00
goto out_put ;
}
if ( ! netif_running ( odev ) ) {
printk ( " pktgen: device is down: \" %s \" \n " , pkt_dev - > ifname ) ;
goto out_put ;
}
pkt_dev - > odev = odev ;
2006-03-21 09:16:13 +03:00
return pkt_dev - > odev ;
2005-04-17 02:20:36 +04:00
out_put :
dev_put ( odev ) ;
out :
2006-03-21 09:16:13 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
/* Read pkt_dev from the interface and set up internal pktgen_dev
* structure to have the right information to create / send packets
*/
static void pktgen_setup_inject ( struct pktgen_dev * pkt_dev )
{
/* Try once more, just in case it works now. */
2006-03-21 09:16:13 +03:00
if ( ! pkt_dev - > odev )
pktgen_setup_dev ( pkt_dev ) ;
if ( ! pkt_dev - > odev ) {
printk ( " pktgen: ERROR: pkt_dev->odev == NULL in setup_inject. \n " ) ;
sprintf ( pkt_dev - > result ,
" ERROR: pkt_dev->odev == NULL in setup_inject. \n " ) ;
return ;
}
/* Default to the interface's mac if not explicitly set. */
2005-04-17 02:20:36 +04:00
2006-01-18 00:04:57 +03:00
if ( is_zero_ether_addr ( pkt_dev - > src_mac ) )
2006-03-21 09:16:13 +03:00
memcpy ( & ( pkt_dev - > hh [ 6 ] ) , pkt_dev - > odev - > dev_addr , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
/* Set up Dest MAC */
2006-01-18 00:04:57 +03:00
memcpy ( & ( pkt_dev - > hh [ 0 ] ) , pkt_dev - > dst_mac , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
/* Set up pkt size */
pkt_dev - > cur_pkt_size = pkt_dev - > min_pkt_size ;
if ( pkt_dev - > flags & F_IPV6 ) {
2005-04-17 02:20:36 +04:00
/*
2007-02-09 17:24:36 +03:00
* Skip this automatic address setting until locks or functions
2005-04-17 02:20:36 +04:00
* gets exported
*/
# ifdef NOTNOW
2006-03-21 09:16:13 +03:00
int i , set = 0 , err = 1 ;
2005-04-17 02:20:36 +04:00
struct inet6_dev * idev ;
2006-03-21 09:16:13 +03:00
for ( i = 0 ; i < IN6_ADDR_HSIZE ; i + + )
if ( pkt_dev - > cur_in6_saddr . s6_addr [ i ] ) {
2005-04-17 02:20:36 +04:00
set = 1 ;
break ;
}
2006-03-21 09:16:13 +03:00
if ( ! set ) {
2005-04-17 02:20:36 +04:00
/*
* Use linklevel address if unconfigured .
*
* use ipv6_get_lladdr if / when it ' s get exported
*/
2006-09-23 01:44:24 +04:00
rcu_read_lock ( ) ;
2005-04-17 02:20:36 +04:00
if ( ( idev = __in6_dev_get ( pkt_dev - > odev ) ) ! = NULL ) {
struct inet6_ifaddr * ifp ;
read_lock_bh ( & idev - > lock ) ;
2006-03-21 09:16:13 +03:00
for ( ifp = idev - > addr_list ; ifp ;
ifp = ifp - > if_next ) {
if ( ifp - > scope = = IFA_LINK
& & ! ( ifp - >
flags & IFA_F_TENTATIVE ) ) {
ipv6_addr_copy ( & pkt_dev - >
cur_in6_saddr ,
& ifp - > addr ) ;
2005-04-17 02:20:36 +04:00
err = 0 ;
break ;
}
}
read_unlock_bh ( & idev - > lock ) ;
}
2006-09-23 01:44:24 +04:00
rcu_read_unlock ( ) ;
2006-03-21 09:16:13 +03:00
if ( err )
printk ( " pktgen: ERROR: IPv6 link address not availble. \n " ) ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-03-21 09:16:13 +03:00
} else {
2005-04-17 02:20:36 +04:00
pkt_dev - > saddr_min = 0 ;
pkt_dev - > saddr_max = 0 ;
if ( strlen ( pkt_dev - > src_min ) = = 0 ) {
2006-03-21 09:16:13 +03:00
struct in_device * in_dev ;
2005-04-17 02:20:36 +04:00
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( pkt_dev - > odev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev ) {
if ( in_dev - > ifa_list ) {
2006-03-21 09:16:13 +03:00
pkt_dev - > saddr_min =
in_dev - > ifa_list - > ifa_address ;
2005-04-17 02:20:36 +04:00
pkt_dev - > saddr_max = pkt_dev - > saddr_min ;
}
}
rcu_read_unlock ( ) ;
2006-03-21 09:16:13 +03:00
} else {
2005-04-17 02:20:36 +04:00
pkt_dev - > saddr_min = in_aton ( pkt_dev - > src_min ) ;
pkt_dev - > saddr_max = in_aton ( pkt_dev - > src_max ) ;
}
pkt_dev - > daddr_min = in_aton ( pkt_dev - > dst_min ) ;
pkt_dev - > daddr_max = in_aton ( pkt_dev - > dst_max ) ;
}
2006-03-21 09:16:13 +03:00
/* Initialize current values. */
pkt_dev - > cur_dst_mac_offset = 0 ;
pkt_dev - > cur_src_mac_offset = 0 ;
pkt_dev - > cur_saddr = pkt_dev - > saddr_min ;
pkt_dev - > cur_daddr = pkt_dev - > daddr_min ;
pkt_dev - > cur_udp_dst = pkt_dev - > udp_dst_min ;
pkt_dev - > cur_udp_src = pkt_dev - > udp_src_min ;
2005-04-17 02:20:36 +04:00
pkt_dev - > nflows = 0 ;
}
static void spin ( struct pktgen_dev * pkt_dev , __u64 spin_until_us )
{
__u64 start ;
__u64 now ;
start = now = getCurUs ( ) ;
printk ( KERN_INFO " sleeping for %d \n " , ( int ) ( spin_until_us - now ) ) ;
while ( now < spin_until_us ) {
2005-10-15 02:32:22 +04:00
/* TODO: optimize sleeping behavior */
2006-03-21 09:16:13 +03:00
if ( spin_until_us - now > jiffies_to_usecs ( 1 ) + 1 )
2005-09-13 01:15:34 +04:00
schedule_timeout_interruptible ( 1 ) ;
else if ( spin_until_us - now > 100 ) {
2005-04-17 02:20:36 +04:00
do_softirq ( ) ;
if ( ! pkt_dev - > running )
return ;
if ( need_resched ( ) )
schedule ( ) ;
}
now = getCurUs ( ) ;
}
pkt_dev - > idle_acc + = now - start ;
}
/* Increment/randomize headers according to flags and current values
* for IP src / dest , UDP src / dst port , MAC - Addr src / dst
*/
2006-03-21 09:16:13 +03:00
static void mod_cur_headers ( struct pktgen_dev * pkt_dev )
{
__u32 imn ;
__u32 imx ;
int flow = 0 ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > cflows ) {
2005-04-17 02:20:36 +04:00
flow = pktgen_random ( ) % pkt_dev - > cflows ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > flows [ flow ] . count > pkt_dev - > lflow )
pkt_dev - > flows [ flow ] . count = 0 ;
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
/* Deal with source MAC */
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > src_mac_count > 1 ) {
__u32 mc ;
__u32 tmp ;
if ( pkt_dev - > flags & F_MACSRC_RND )
mc = pktgen_random ( ) % ( pkt_dev - > src_mac_count ) ;
else {
mc = pkt_dev - > cur_src_mac_offset + + ;
if ( pkt_dev - > cur_src_mac_offset >
pkt_dev - > src_mac_count )
pkt_dev - > cur_src_mac_offset = 0 ;
}
tmp = pkt_dev - > src_mac [ 5 ] + ( mc & 0xFF ) ;
pkt_dev - > hh [ 11 ] = tmp ;
tmp = ( pkt_dev - > src_mac [ 4 ] + ( ( mc > > 8 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 10 ] = tmp ;
tmp = ( pkt_dev - > src_mac [ 3 ] + ( ( mc > > 16 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 9 ] = tmp ;
tmp = ( pkt_dev - > src_mac [ 2 ] + ( ( mc > > 24 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 8 ] = tmp ;
tmp = ( pkt_dev - > src_mac [ 1 ] + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 7 ] = tmp ;
}
/* Deal with Destination MAC */
if ( pkt_dev - > dst_mac_count > 1 ) {
__u32 mc ;
__u32 tmp ;
if ( pkt_dev - > flags & F_MACDST_RND )
mc = pktgen_random ( ) % ( pkt_dev - > dst_mac_count ) ;
else {
mc = pkt_dev - > cur_dst_mac_offset + + ;
if ( pkt_dev - > cur_dst_mac_offset >
pkt_dev - > dst_mac_count ) {
pkt_dev - > cur_dst_mac_offset = 0 ;
}
}
tmp = pkt_dev - > dst_mac [ 5 ] + ( mc & 0xFF ) ;
pkt_dev - > hh [ 5 ] = tmp ;
tmp = ( pkt_dev - > dst_mac [ 4 ] + ( ( mc > > 8 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 4 ] = tmp ;
tmp = ( pkt_dev - > dst_mac [ 3 ] + ( ( mc > > 16 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 3 ] = tmp ;
tmp = ( pkt_dev - > dst_mac [ 2 ] + ( ( mc > > 24 ) & 0xFF ) + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 2 ] = tmp ;
tmp = ( pkt_dev - > dst_mac [ 1 ] + ( tmp > > 8 ) ) ;
pkt_dev - > hh [ 1 ] = tmp ;
}
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > flags & F_MPLS_RND ) {
unsigned i ;
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + )
if ( pkt_dev - > labels [ i ] & MPLS_STACK_BOTTOM )
pkt_dev - > labels [ i ] = MPLS_STACK_BOTTOM |
2006-11-15 07:48:11 +03:00
( ( __force __be32 ) pktgen_random ( ) &
2006-03-23 12:10:26 +03:00
htonl ( 0x000fffff ) ) ;
}
2006-09-28 03:30:44 +04:00
if ( ( pkt_dev - > flags & F_VID_RND ) & & ( pkt_dev - > vlan_id ! = 0xffff ) ) {
pkt_dev - > vlan_id = pktgen_random ( ) % 4096 ;
}
if ( ( pkt_dev - > flags & F_SVID_RND ) & & ( pkt_dev - > svlan_id ! = 0xffff ) ) {
pkt_dev - > svlan_id = pktgen_random ( ) % 4096 ;
}
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > udp_src_min < pkt_dev - > udp_src_max ) {
if ( pkt_dev - > flags & F_UDPSRC_RND )
pkt_dev - > cur_udp_src =
( ( pktgen_random ( ) %
( pkt_dev - > udp_src_max - pkt_dev - > udp_src_min ) ) +
pkt_dev - > udp_src_min ) ;
else {
2005-04-17 02:20:36 +04:00
pkt_dev - > cur_udp_src + + ;
if ( pkt_dev - > cur_udp_src > = pkt_dev - > udp_src_max )
pkt_dev - > cur_udp_src = pkt_dev - > udp_src_min ;
2006-03-21 09:16:13 +03:00
}
}
if ( pkt_dev - > udp_dst_min < pkt_dev - > udp_dst_max ) {
if ( pkt_dev - > flags & F_UDPDST_RND ) {
pkt_dev - > cur_udp_dst =
( ( pktgen_random ( ) %
( pkt_dev - > udp_dst_max - pkt_dev - > udp_dst_min ) ) +
pkt_dev - > udp_dst_min ) ;
} else {
2005-04-17 02:20:36 +04:00
pkt_dev - > cur_udp_dst + + ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > cur_udp_dst > = pkt_dev - > udp_dst_max )
2005-04-17 02:20:36 +04:00
pkt_dev - > cur_udp_dst = pkt_dev - > udp_dst_min ;
2006-03-21 09:16:13 +03:00
}
}
2005-04-17 02:20:36 +04:00
if ( ! ( pkt_dev - > flags & F_IPV6 ) ) {
2006-03-21 09:16:13 +03:00
if ( ( imn = ntohl ( pkt_dev - > saddr_min ) ) < ( imx =
ntohl ( pkt_dev - >
saddr_max ) ) ) {
2005-04-17 02:20:36 +04:00
__u32 t ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPSRC_RND )
2005-04-17 02:20:36 +04:00
t = ( ( pktgen_random ( ) % ( imx - imn ) ) + imn ) ;
else {
t = ntohl ( pkt_dev - > cur_saddr ) ;
t + + ;
if ( t > imx ) {
t = imn ;
}
}
pkt_dev - > cur_saddr = htonl ( t ) ;
}
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > cflows & & pkt_dev - > flows [ flow ] . count ! = 0 ) {
pkt_dev - > cur_daddr = pkt_dev - > flows [ flow ] . cur_daddr ;
} else {
2006-11-15 07:48:11 +03:00
imn = ntohl ( pkt_dev - > daddr_min ) ;
imx = ntohl ( pkt_dev - > daddr_max ) ;
if ( imn < imx ) {
2005-04-17 02:20:36 +04:00
__u32 t ;
2006-11-15 07:48:11 +03:00
__be32 s ;
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > flags & F_IPDST_RND ) {
2006-11-15 07:48:11 +03:00
t = pktgen_random ( ) % ( imx - imn ) + imn ;
s = htonl ( t ) ;
2005-04-17 02:20:36 +04:00
2006-11-15 07:48:11 +03:00
while ( LOOPBACK ( s ) | | MULTICAST ( s )
| | BADCLASS ( s ) | | ZERONET ( s )
| | LOCAL_MCAST ( s ) ) {
t = ( pktgen_random ( ) %
( imx - imn ) ) + imn ;
s = htonl ( t ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-15 07:48:11 +03:00
pkt_dev - > cur_daddr = s ;
} else {
2005-04-17 02:20:36 +04:00
t = ntohl ( pkt_dev - > cur_daddr ) ;
t + + ;
if ( t > imx ) {
t = imn ;
}
pkt_dev - > cur_daddr = htonl ( t ) ;
}
}
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > cflows ) {
pkt_dev - > flows [ flow ] . cur_daddr =
pkt_dev - > cur_daddr ;
2005-04-17 02:20:36 +04:00
pkt_dev - > nflows + + ;
}
}
2006-03-21 09:16:13 +03:00
} else { /* IPV6 * */
if ( pkt_dev - > min_in6_daddr . s6_addr32 [ 0 ] = = 0 & &
pkt_dev - > min_in6_daddr . s6_addr32 [ 1 ] = = 0 & &
pkt_dev - > min_in6_daddr . s6_addr32 [ 2 ] = = 0 & &
pkt_dev - > min_in6_daddr . s6_addr32 [ 3 ] = = 0 ) ;
2005-04-17 02:20:36 +04:00
else {
int i ;
/* Only random destinations yet */
2006-03-21 09:16:13 +03:00
for ( i = 0 ; i < 4 ; i + + ) {
2005-04-17 02:20:36 +04:00
pkt_dev - > cur_in6_daddr . s6_addr32 [ i ] =
2006-11-15 07:48:11 +03:00
( ( ( __force __be32 ) pktgen_random ( ) |
2006-03-21 09:16:13 +03:00
pkt_dev - > min_in6_daddr . s6_addr32 [ i ] ) &
pkt_dev - > max_in6_daddr . s6_addr32 [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > min_pkt_size < pkt_dev - > max_pkt_size ) {
__u32 t ;
if ( pkt_dev - > flags & F_TXSIZE_RND ) {
t = ( ( pktgen_random ( ) %
( pkt_dev - > max_pkt_size - pkt_dev - > min_pkt_size ) )
+ pkt_dev - > min_pkt_size ) ;
} else {
2005-04-17 02:20:36 +04:00
t = pkt_dev - > cur_pkt_size + 1 ;
2006-03-21 09:16:13 +03:00
if ( t > pkt_dev - > max_pkt_size )
2005-04-17 02:20:36 +04:00
t = pkt_dev - > min_pkt_size ;
2006-03-21 09:16:13 +03:00
}
pkt_dev - > cur_pkt_size = t ;
}
2005-04-17 02:20:36 +04:00
pkt_dev - > flows [ flow ] . count + + ;
}
2006-03-23 12:10:26 +03:00
static void mpls_push ( __be32 * mpls , struct pktgen_dev * pkt_dev )
{
unsigned i ;
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + ) {
* mpls + + = pkt_dev - > labels [ i ] & ~ MPLS_STACK_BOTTOM ;
}
mpls - - ;
* mpls | = MPLS_STACK_BOTTOM ;
}
2006-11-03 14:49:56 +03:00
static inline __be16 build_tci ( unsigned int id , unsigned int cfi ,
unsigned int prio )
{
return htons ( id | ( cfi < < 12 ) | ( prio < < 13 ) ) ;
}
2006-03-21 09:16:13 +03:00
static struct sk_buff * fill_packet_ipv4 ( struct net_device * odev ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb = NULL ;
__u8 * eth ;
struct udphdr * udph ;
int datalen , iplen ;
struct iphdr * iph ;
2006-03-21 09:16:13 +03:00
struct pktgen_hdr * pgh = NULL ;
2006-03-23 12:10:26 +03:00
__be16 protocol = __constant_htons ( ETH_P_IP ) ;
__be32 * mpls ;
2006-09-28 03:30:44 +04:00
__be16 * vlan_tci = NULL ; /* Encapsulates priority and VLAN ID */
__be16 * vlan_encapsulated_proto = NULL ; /* packet type ID field (or len) for VLAN tag */
__be16 * svlan_tci = NULL ; /* Encapsulates priority and SVLAN ID */
__be16 * svlan_encapsulated_proto = NULL ; /* packet type ID field (or len) for SVLAN tag */
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > nr_labels )
protocol = __constant_htons ( ETH_P_MPLS_UC ) ;
2006-03-21 09:16:13 +03:00
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff )
protocol = __constant_htons ( ETH_P_8021Q ) ;
2005-06-27 02:27:10 +04:00
/* Update any of the values, used when we're incrementing various
* fields .
*/
mod_cur_headers ( pkt_dev ) ;
2006-01-19 01:19:10 +03:00
datalen = ( odev - > hard_header_len + 16 ) & ~ 0xf ;
2006-03-23 12:10:26 +03:00
skb = alloc_skb ( pkt_dev - > cur_pkt_size + 64 + datalen +
2006-09-28 03:30:44 +04:00
pkt_dev - > nr_labels * sizeof ( u32 ) +
VLAN_TAG_SIZE ( pkt_dev ) + SVLAN_TAG_SIZE ( pkt_dev ) ,
GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! skb ) {
sprintf ( pkt_dev - > result , " No memory " ) ;
return NULL ;
}
2006-01-19 01:19:10 +03:00
skb_reserve ( skb , datalen ) ;
2005-04-17 02:20:36 +04:00
/* Reserve for ethernet and IP header */
eth = ( __u8 * ) skb_push ( skb , 14 ) ;
2006-03-23 12:10:26 +03:00
mpls = ( __be32 * ) skb_put ( skb , pkt_dev - > nr_labels * sizeof ( __u32 ) ) ;
if ( pkt_dev - > nr_labels )
mpls_push ( mpls , pkt_dev ) ;
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff ) {
if ( pkt_dev - > svlan_id ! = 0xffff ) {
svlan_tci = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
2006-11-03 14:49:56 +03:00
* svlan_tci = build_tci ( pkt_dev - > svlan_id ,
pkt_dev - > svlan_cfi ,
pkt_dev - > svlan_p ) ;
2006-09-28 03:30:44 +04:00
svlan_encapsulated_proto = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
* svlan_encapsulated_proto = __constant_htons ( ETH_P_8021Q ) ;
}
vlan_tci = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
2006-11-03 14:49:56 +03:00
* vlan_tci = build_tci ( pkt_dev - > vlan_id ,
pkt_dev - > vlan_cfi ,
pkt_dev - > vlan_p ) ;
2006-09-28 03:30:44 +04:00
vlan_encapsulated_proto = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
* vlan_encapsulated_proto = __constant_htons ( ETH_P_IP ) ;
}
2005-04-17 02:20:36 +04:00
iph = ( struct iphdr * ) skb_put ( skb , sizeof ( struct iphdr ) ) ;
udph = ( struct udphdr * ) skb_put ( skb , sizeof ( struct udphdr ) ) ;
memcpy ( eth , pkt_dev - > hh , 12 ) ;
2006-11-15 07:48:11 +03:00
* ( __be16 * ) & eth [ 12 ] = protocol ;
2005-04-17 02:20:36 +04:00
2006-03-23 12:10:26 +03:00
/* Eth + IPh + UDPh + mpls */
datalen = pkt_dev - > cur_pkt_size - 14 - 20 - 8 -
2006-09-28 03:30:44 +04:00
pkt_dev - > nr_labels * sizeof ( u32 ) - VLAN_TAG_SIZE ( pkt_dev ) - SVLAN_TAG_SIZE ( pkt_dev ) ;
2006-03-21 09:16:13 +03:00
if ( datalen < sizeof ( struct pktgen_hdr ) )
2005-04-17 02:20:36 +04:00
datalen = sizeof ( struct pktgen_hdr ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
udph - > source = htons ( pkt_dev - > cur_udp_src ) ;
udph - > dest = htons ( pkt_dev - > cur_udp_dst ) ;
2006-03-21 09:16:13 +03:00
udph - > len = htons ( datalen + 8 ) ; /* DATA + udphdr */
udph - > check = 0 ; /* No checksum */
2005-04-17 02:20:36 +04:00
iph - > ihl = 5 ;
iph - > version = 4 ;
iph - > ttl = 32 ;
2006-09-28 03:32:03 +04:00
iph - > tos = pkt_dev - > tos ;
2006-03-21 09:16:13 +03:00
iph - > protocol = IPPROTO_UDP ; /* UDP */
2005-04-17 02:20:36 +04:00
iph - > saddr = pkt_dev - > cur_saddr ;
iph - > daddr = pkt_dev - > cur_daddr ;
iph - > frag_off = 0 ;
iplen = 20 + 8 + datalen ;
iph - > tot_len = htons ( iplen ) ;
iph - > check = 0 ;
2006-03-21 09:16:13 +03:00
iph - > check = ip_fast_csum ( ( void * ) iph , iph - > ihl ) ;
2006-03-23 12:10:26 +03:00
skb - > protocol = protocol ;
2006-09-28 03:30:44 +04:00
skb - > mac . raw = ( ( u8 * ) iph ) - 14 - pkt_dev - > nr_labels * sizeof ( u32 ) -
VLAN_TAG_SIZE ( pkt_dev ) - SVLAN_TAG_SIZE ( pkt_dev ) ;
2005-04-17 02:20:36 +04:00
skb - > dev = odev ;
skb - > pkt_type = PACKET_HOST ;
2006-08-08 07:49:07 +04:00
skb - > nh . iph = iph ;
skb - > h . uh = udph ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > nfrags < = 0 )
pgh = ( struct pktgen_hdr * ) skb_put ( skb , datalen ) ;
2005-04-17 02:20:36 +04:00
else {
int frags = pkt_dev - > nfrags ;
int i ;
2006-03-21 09:16:13 +03:00
pgh = ( struct pktgen_hdr * ) ( ( ( char * ) ( udph ) ) + 8 ) ;
2005-04-17 02:20:36 +04:00
if ( frags > MAX_SKB_FRAGS )
frags = MAX_SKB_FRAGS ;
2006-03-21 09:16:13 +03:00
if ( datalen > frags * PAGE_SIZE ) {
skb_put ( skb , datalen - frags * PAGE_SIZE ) ;
datalen = frags * PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
}
i = 0 ;
while ( datalen > 0 ) {
struct page * page = alloc_pages ( GFP_KERNEL , 0 ) ;
skb_shinfo ( skb ) - > frags [ i ] . page = page ;
skb_shinfo ( skb ) - > frags [ i ] . page_offset = 0 ;
skb_shinfo ( skb ) - > frags [ i ] . size =
2006-03-21 09:16:13 +03:00
( datalen < PAGE_SIZE ? datalen : PAGE_SIZE ) ;
2005-04-17 02:20:36 +04:00
datalen - = skb_shinfo ( skb ) - > frags [ i ] . size ;
skb - > len + = skb_shinfo ( skb ) - > frags [ i ] . size ;
skb - > data_len + = skb_shinfo ( skb ) - > frags [ i ] . size ;
i + + ;
skb_shinfo ( skb ) - > nr_frags = i ;
}
while ( i < frags ) {
int rem ;
if ( i = = 0 )
break ;
rem = skb_shinfo ( skb ) - > frags [ i - 1 ] . size / 2 ;
if ( rem = = 0 )
break ;
skb_shinfo ( skb ) - > frags [ i - 1 ] . size - = rem ;
2006-03-21 09:16:13 +03:00
skb_shinfo ( skb ) - > frags [ i ] =
skb_shinfo ( skb ) - > frags [ i - 1 ] ;
2005-04-17 02:20:36 +04:00
get_page ( skb_shinfo ( skb ) - > frags [ i ] . page ) ;
2006-03-21 09:16:13 +03:00
skb_shinfo ( skb ) - > frags [ i ] . page =
skb_shinfo ( skb ) - > frags [ i - 1 ] . page ;
skb_shinfo ( skb ) - > frags [ i ] . page_offset + =
skb_shinfo ( skb ) - > frags [ i - 1 ] . size ;
2005-04-17 02:20:36 +04:00
skb_shinfo ( skb ) - > frags [ i ] . size = rem ;
i + + ;
skb_shinfo ( skb ) - > nr_frags = i ;
}
}
2006-03-21 09:16:13 +03:00
/* Stamp the time, and sequence number, convert them to network byte order */
if ( pgh ) {
struct timeval timestamp ;
pgh - > pgh_magic = htonl ( PKTGEN_MAGIC ) ;
pgh - > seq_num = htonl ( pkt_dev - > seq_num ) ;
do_gettimeofday ( & timestamp ) ;
pgh - > tv_sec = htonl ( timestamp . tv_sec ) ;
pgh - > tv_usec = htonl ( timestamp . tv_usec ) ;
}
2005-04-17 02:20:36 +04:00
return skb ;
}
/*
2007-02-09 17:24:36 +03:00
* scan_ip6 , fmt_ip taken from dietlibc - 0.21
2005-04-17 02:20:36 +04:00
* Author Felix von Leitner < felix - dietlibc @ fefe . de >
*
2007-02-09 17:24:36 +03:00
* Slightly modified for kernel .
2005-04-17 02:20:36 +04:00
* Should be candidate for net / ipv4 / utils . c
* - - ro
*/
2006-03-21 09:16:13 +03:00
static unsigned int scan_ip6 ( const char * s , char ip [ 16 ] )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
2006-03-21 09:16:13 +03:00
unsigned int len = 0 ;
2005-04-17 02:20:36 +04:00
unsigned long u ;
char suffix [ 16 ] ;
2006-03-21 09:16:13 +03:00
unsigned int prefixlen = 0 ;
unsigned int suffixlen = 0 ;
2006-11-15 07:48:11 +03:00
__be32 tmp ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
for ( i = 0 ; i < 16 ; i + + )
ip [ i ] = 0 ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
if ( * s = = ' : ' ) {
len + + ;
2006-03-21 09:16:13 +03:00
if ( s [ 1 ] = = ' : ' ) { /* Found "::", skip to part 2 */
s + = 2 ;
2005-04-17 02:20:36 +04:00
len + + ;
break ;
}
s + + ;
}
{
char * tmp ;
2006-03-21 09:16:13 +03:00
u = simple_strtoul ( s , & tmp , 16 ) ;
i = tmp - s ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
if ( ! i )
return 0 ;
if ( prefixlen = = 12 & & s [ i ] = = ' . ' ) {
2005-04-17 02:20:36 +04:00
/* the last 4 bytes may be written as IPv4 address */
tmp = in_aton ( s ) ;
2006-03-21 09:16:13 +03:00
memcpy ( ( struct in_addr * ) ( ip + 12 ) , & tmp , sizeof ( tmp ) ) ;
return i + len ;
2005-04-17 02:20:36 +04:00
}
ip [ prefixlen + + ] = ( u > > 8 ) ;
ip [ prefixlen + + ] = ( u & 255 ) ;
2006-03-21 09:16:13 +03:00
s + = i ;
len + = i ;
if ( prefixlen = = 16 )
2005-04-17 02:20:36 +04:00
return len ;
}
/* part 2, after "::" */
for ( ; ; ) {
if ( * s = = ' : ' ) {
2006-03-21 09:16:13 +03:00
if ( suffixlen = = 0 )
2005-04-17 02:20:36 +04:00
break ;
s + + ;
len + + ;
2006-03-21 09:16:13 +03:00
} else if ( suffixlen ! = 0 )
2005-04-17 02:20:36 +04:00
break ;
{
char * tmp ;
2006-03-21 09:16:13 +03:00
u = simple_strtol ( s , & tmp , 16 ) ;
i = tmp - s ;
2005-04-17 02:20:36 +04:00
}
if ( ! i ) {
2006-03-21 09:16:13 +03:00
if ( * s )
len - - ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-21 09:16:13 +03:00
if ( suffixlen + prefixlen < = 12 & & s [ i ] = = ' . ' ) {
2005-04-17 02:20:36 +04:00
tmp = in_aton ( s ) ;
2006-03-21 09:16:13 +03:00
memcpy ( ( struct in_addr * ) ( suffix + suffixlen ) , & tmp ,
sizeof ( tmp ) ) ;
suffixlen + = 4 ;
len + = strlen ( s ) ;
2005-04-17 02:20:36 +04:00
break ;
}
suffix [ suffixlen + + ] = ( u > > 8 ) ;
suffix [ suffixlen + + ] = ( u & 255 ) ;
2006-03-21 09:16:13 +03:00
s + = i ;
len + = i ;
if ( prefixlen + suffixlen = = 16 )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-21 09:16:13 +03:00
for ( i = 0 ; i < suffixlen ; i + + )
ip [ 16 - suffixlen + i ] = suffix [ i ] ;
2005-04-17 02:20:36 +04:00
return len ;
}
2006-03-21 09:16:13 +03:00
static char tohex ( char hexdigit )
{
return hexdigit > 9 ? hexdigit + ' a ' - 10 : hexdigit + ' 0 ' ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static int fmt_xlong ( char * s , unsigned int i )
{
char * bak = s ;
* s = tohex ( ( i > > 12 ) & 0xf ) ;
if ( s ! = bak | | * s ! = ' 0 ' )
+ + s ;
* s = tohex ( ( i > > 8 ) & 0xf ) ;
if ( s ! = bak | | * s ! = ' 0 ' )
+ + s ;
* s = tohex ( ( i > > 4 ) & 0xf ) ;
if ( s ! = bak | | * s ! = ' 0 ' )
+ + s ;
* s = tohex ( i & 0xf ) ;
return s - bak + 1 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static unsigned int fmt_ip6 ( char * s , const char ip [ 16 ] )
{
2005-04-17 02:20:36 +04:00
unsigned int len ;
unsigned int i ;
unsigned int temp ;
unsigned int compressing ;
int j ;
2006-03-21 09:16:13 +03:00
len = 0 ;
compressing = 0 ;
for ( j = 0 ; j < 16 ; j + = 2 ) {
2005-04-17 02:20:36 +04:00
# ifdef V4MAPPEDPREFIX
2006-03-21 09:16:13 +03:00
if ( j = = 12 & & ! memcmp ( ip , V4mappedprefix , 12 ) ) {
inet_ntoa_r ( * ( struct in_addr * ) ( ip + 12 ) , s ) ;
temp = strlen ( s ) ;
return len + temp ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-03-21 09:16:13 +03:00
temp = ( ( unsigned long ) ( unsigned char ) ip [ j ] < < 8 ) +
( unsigned long ) ( unsigned char ) ip [ j + 1 ] ;
2005-04-17 02:20:36 +04:00
if ( temp = = 0 ) {
if ( ! compressing ) {
2006-03-21 09:16:13 +03:00
compressing = 1 ;
if ( j = = 0 ) {
* s + + = ' : ' ;
+ + len ;
2005-04-17 02:20:36 +04:00
}
}
} else {
if ( compressing ) {
2006-03-21 09:16:13 +03:00
compressing = 0 ;
* s + + = ' : ' ;
+ + len ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
i = fmt_xlong ( s , temp ) ;
len + = i ;
s + = i ;
if ( j < 14 ) {
2005-04-17 02:20:36 +04:00
* s + + = ' : ' ;
+ + len ;
}
}
}
if ( compressing ) {
2006-03-21 09:16:13 +03:00
* s + + = ' : ' ;
+ + len ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
* s = 0 ;
2005-04-17 02:20:36 +04:00
return len ;
}
2006-03-21 09:16:13 +03:00
static struct sk_buff * fill_packet_ipv6 ( struct net_device * odev ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb = NULL ;
__u8 * eth ;
struct udphdr * udph ;
int datalen ;
struct ipv6hdr * iph ;
2006-03-21 09:16:13 +03:00
struct pktgen_hdr * pgh = NULL ;
2006-03-23 12:10:26 +03:00
__be16 protocol = __constant_htons ( ETH_P_IPV6 ) ;
__be32 * mpls ;
2006-09-28 03:30:44 +04:00
__be16 * vlan_tci = NULL ; /* Encapsulates priority and VLAN ID */
__be16 * vlan_encapsulated_proto = NULL ; /* packet type ID field (or len) for VLAN tag */
__be16 * svlan_tci = NULL ; /* Encapsulates priority and SVLAN ID */
__be16 * svlan_encapsulated_proto = NULL ; /* packet type ID field (or len) for SVLAN tag */
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > nr_labels )
protocol = __constant_htons ( ETH_P_MPLS_UC ) ;
2005-06-27 02:27:10 +04:00
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff )
protocol = __constant_htons ( ETH_P_8021Q ) ;
2005-06-27 02:27:10 +04:00
/* Update any of the values, used when we're incrementing various
* fields .
*/
mod_cur_headers ( pkt_dev ) ;
2006-03-23 12:10:26 +03:00
skb = alloc_skb ( pkt_dev - > cur_pkt_size + 64 + 16 +
2006-09-28 03:30:44 +04:00
pkt_dev - > nr_labels * sizeof ( u32 ) +
VLAN_TAG_SIZE ( pkt_dev ) + SVLAN_TAG_SIZE ( pkt_dev ) ,
GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! skb ) {
sprintf ( pkt_dev - > result , " No memory " ) ;
return NULL ;
}
skb_reserve ( skb , 16 ) ;
/* Reserve for ethernet and IP header */
eth = ( __u8 * ) skb_push ( skb , 14 ) ;
2006-03-23 12:10:26 +03:00
mpls = ( __be32 * ) skb_put ( skb , pkt_dev - > nr_labels * sizeof ( __u32 ) ) ;
if ( pkt_dev - > nr_labels )
mpls_push ( mpls , pkt_dev ) ;
2006-09-28 03:30:44 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff ) {
if ( pkt_dev - > svlan_id ! = 0xffff ) {
svlan_tci = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
2006-11-03 14:49:56 +03:00
* svlan_tci = build_tci ( pkt_dev - > svlan_id ,
pkt_dev - > svlan_cfi ,
pkt_dev - > svlan_p ) ;
2006-09-28 03:30:44 +04:00
svlan_encapsulated_proto = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
* svlan_encapsulated_proto = __constant_htons ( ETH_P_8021Q ) ;
}
vlan_tci = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
2006-11-03 14:49:56 +03:00
* vlan_tci = build_tci ( pkt_dev - > vlan_id ,
pkt_dev - > vlan_cfi ,
pkt_dev - > vlan_p ) ;
2006-09-28 03:30:44 +04:00
vlan_encapsulated_proto = ( __be16 * ) skb_put ( skb , sizeof ( __be16 ) ) ;
* vlan_encapsulated_proto = __constant_htons ( ETH_P_IPV6 ) ;
}
2005-04-17 02:20:36 +04:00
iph = ( struct ipv6hdr * ) skb_put ( skb , sizeof ( struct ipv6hdr ) ) ;
udph = ( struct udphdr * ) skb_put ( skb , sizeof ( struct udphdr ) ) ;
memcpy ( eth , pkt_dev - > hh , 12 ) ;
2006-11-15 07:48:11 +03:00
* ( __be16 * ) & eth [ 12 ] = protocol ;
2005-06-27 02:27:10 +04:00
2006-03-23 12:10:26 +03:00
/* Eth + IPh + UDPh + mpls */
datalen = pkt_dev - > cur_pkt_size - 14 -
sizeof ( struct ipv6hdr ) - sizeof ( struct udphdr ) -
2006-09-28 03:30:44 +04:00
pkt_dev - > nr_labels * sizeof ( u32 ) - VLAN_TAG_SIZE ( pkt_dev ) - SVLAN_TAG_SIZE ( pkt_dev ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( datalen < sizeof ( struct pktgen_hdr ) ) {
2005-04-17 02:20:36 +04:00
datalen = sizeof ( struct pktgen_hdr ) ;
if ( net_ratelimit ( ) )
2006-03-21 09:16:13 +03:00
printk ( KERN_INFO " pktgen: increased datalen to %d \n " ,
datalen ) ;
2005-04-17 02:20:36 +04:00
}
udph - > source = htons ( pkt_dev - > cur_udp_src ) ;
udph - > dest = htons ( pkt_dev - > cur_udp_dst ) ;
2006-03-21 09:16:13 +03:00
udph - > len = htons ( datalen + sizeof ( struct udphdr ) ) ;
udph - > check = 0 ; /* No checksum */
2005-04-17 02:20:36 +04:00
2006-11-15 07:48:11 +03:00
* ( __be32 * ) iph = __constant_htonl ( 0x60000000 ) ; /* Version + flow */
2005-04-17 02:20:36 +04:00
2006-09-28 03:32:03 +04:00
if ( pkt_dev - > traffic_class ) {
/* Version + traffic class + flow (0) */
2006-11-15 07:48:11 +03:00
* ( __be32 * ) iph | = htonl ( 0x60000000 | ( pkt_dev - > traffic_class < < 20 ) ) ;
2006-09-28 03:32:03 +04:00
}
2005-04-17 02:20:36 +04:00
iph - > hop_limit = 32 ;
iph - > payload_len = htons ( sizeof ( struct udphdr ) + datalen ) ;
iph - > nexthdr = IPPROTO_UDP ;
ipv6_addr_copy ( & iph - > daddr , & pkt_dev - > cur_in6_daddr ) ;
ipv6_addr_copy ( & iph - > saddr , & pkt_dev - > cur_in6_saddr ) ;
2006-09-28 03:30:44 +04:00
skb - > mac . raw = ( ( u8 * ) iph ) - 14 - pkt_dev - > nr_labels * sizeof ( u32 ) -
VLAN_TAG_SIZE ( pkt_dev ) - SVLAN_TAG_SIZE ( pkt_dev ) ;
2006-03-23 12:10:26 +03:00
skb - > protocol = protocol ;
2005-04-17 02:20:36 +04:00
skb - > dev = odev ;
skb - > pkt_type = PACKET_HOST ;
2006-08-08 07:52:10 +04:00
skb - > nh . ipv6h = iph ;
skb - > h . uh = udph ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > nfrags < = 0 )
pgh = ( struct pktgen_hdr * ) skb_put ( skb , datalen ) ;
2005-04-17 02:20:36 +04:00
else {
int frags = pkt_dev - > nfrags ;
int i ;
2006-03-21 09:16:13 +03:00
pgh = ( struct pktgen_hdr * ) ( ( ( char * ) ( udph ) ) + 8 ) ;
2005-04-17 02:20:36 +04:00
if ( frags > MAX_SKB_FRAGS )
frags = MAX_SKB_FRAGS ;
2006-03-21 09:16:13 +03:00
if ( datalen > frags * PAGE_SIZE ) {
skb_put ( skb , datalen - frags * PAGE_SIZE ) ;
datalen = frags * PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
}
i = 0 ;
while ( datalen > 0 ) {
struct page * page = alloc_pages ( GFP_KERNEL , 0 ) ;
skb_shinfo ( skb ) - > frags [ i ] . page = page ;
skb_shinfo ( skb ) - > frags [ i ] . page_offset = 0 ;
skb_shinfo ( skb ) - > frags [ i ] . size =
2006-03-21 09:16:13 +03:00
( datalen < PAGE_SIZE ? datalen : PAGE_SIZE ) ;
2005-04-17 02:20:36 +04:00
datalen - = skb_shinfo ( skb ) - > frags [ i ] . size ;
skb - > len + = skb_shinfo ( skb ) - > frags [ i ] . size ;
skb - > data_len + = skb_shinfo ( skb ) - > frags [ i ] . size ;
i + + ;
skb_shinfo ( skb ) - > nr_frags = i ;
}
while ( i < frags ) {
int rem ;
if ( i = = 0 )
break ;
rem = skb_shinfo ( skb ) - > frags [ i - 1 ] . size / 2 ;
if ( rem = = 0 )
break ;
skb_shinfo ( skb ) - > frags [ i - 1 ] . size - = rem ;
2006-03-21 09:16:13 +03:00
skb_shinfo ( skb ) - > frags [ i ] =
skb_shinfo ( skb ) - > frags [ i - 1 ] ;
2005-04-17 02:20:36 +04:00
get_page ( skb_shinfo ( skb ) - > frags [ i ] . page ) ;
2006-03-21 09:16:13 +03:00
skb_shinfo ( skb ) - > frags [ i ] . page =
skb_shinfo ( skb ) - > frags [ i - 1 ] . page ;
skb_shinfo ( skb ) - > frags [ i ] . page_offset + =
skb_shinfo ( skb ) - > frags [ i - 1 ] . size ;
2005-04-17 02:20:36 +04:00
skb_shinfo ( skb ) - > frags [ i ] . size = rem ;
i + + ;
skb_shinfo ( skb ) - > nr_frags = i ;
}
}
2006-03-21 09:16:13 +03:00
/* Stamp the time, and sequence number, convert them to network byte order */
2005-04-17 02:20:36 +04:00
/* should we update cloned packets too ? */
2006-03-21 09:16:13 +03:00
if ( pgh ) {
struct timeval timestamp ;
pgh - > pgh_magic = htonl ( PKTGEN_MAGIC ) ;
pgh - > seq_num = htonl ( pkt_dev - > seq_num ) ;
do_gettimeofday ( & timestamp ) ;
pgh - > tv_sec = htonl ( timestamp . tv_sec ) ;
pgh - > tv_usec = htonl ( timestamp . tv_usec ) ;
}
2006-09-28 03:30:44 +04:00
/* pkt_dev->seq_num++; FF: you really mean this? */
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
return skb ;
}
2006-03-21 09:16:13 +03:00
static inline struct sk_buff * fill_packet ( struct net_device * odev ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > flags & F_IPV6 )
2005-04-17 02:20:36 +04:00
return fill_packet_ipv6 ( odev , pkt_dev ) ;
else
return fill_packet_ipv4 ( odev , pkt_dev ) ;
}
2006-03-21 09:16:13 +03:00
static void pktgen_clear_counters ( struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
pkt_dev - > seq_num = 1 ;
pkt_dev - > idle_acc = 0 ;
2005-04-17 02:20:36 +04:00
pkt_dev - > sofar = 0 ;
2006-03-21 09:16:13 +03:00
pkt_dev - > tx_bytes = 0 ;
pkt_dev - > errors = 0 ;
2005-04-17 02:20:36 +04:00
}
/* Set up structure for sending pkts, clear counters */
static void pktgen_run ( struct pktgen_thread * t )
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * pkt_dev ;
2005-04-17 02:20:36 +04:00
int started = 0 ;
PG_DEBUG ( printk ( " pktgen: entering pktgen_run. %p \n " , t ) ) ;
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list ) {
2005-04-17 02:20:36 +04:00
/*
* setup odev and create initial packet .
*/
pktgen_setup_inject ( pkt_dev ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > odev ) {
2005-04-17 02:20:36 +04:00
pktgen_clear_counters ( pkt_dev ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > running = 1 ; /* Cranke yeself! */
2005-04-17 02:20:36 +04:00
pkt_dev - > skb = NULL ;
pkt_dev - > started_at = getCurUs ( ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > next_tx_us = getCurUs ( ) ; /* Transmit immediately */
2005-04-17 02:20:36 +04:00
pkt_dev - > next_tx_ns = 0 ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
strcpy ( pkt_dev - > result , " Starting " ) ;
started + + ;
2006-03-21 09:16:13 +03:00
} else
2005-04-17 02:20:36 +04:00
strcpy ( pkt_dev - > result , " Error starting " ) ;
}
if_unlock ( t ) ;
2006-03-21 09:16:13 +03:00
if ( started )
t - > control & = ~ ( T_STOP ) ;
2005-04-17 02:20:36 +04:00
}
static void pktgen_stop_all_threads_ifs ( void )
{
2006-03-21 09:16:40 +03:00
struct pktgen_thread * t ;
2005-04-17 02:20:36 +04:00
2006-03-21 08:26:56 +03:00
PG_DEBUG ( printk ( " pktgen: entering pktgen_stop_all_threads_ifs. \n " ) ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:40 +03:00
list_for_each_entry ( t , & pktgen_threads , th_list )
2006-03-21 08:26:56 +03:00
t - > control | = T_STOP ;
2006-03-21 09:16:40 +03:00
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static int thread_is_running ( struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * pkt_dev ;
2006-03-21 09:16:13 +03:00
int res = 0 ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list )
if ( pkt_dev - > running ) {
2005-04-17 02:20:36 +04:00
res = 1 ;
break ;
}
2006-03-21 09:16:13 +03:00
return res ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static int pktgen_wait_thread_run ( struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
while ( thread_is_running ( t ) ) {
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if_unlock ( t ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
msleep_interruptible ( 100 ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( signal_pending ( current ) )
goto signal ;
if_lock ( t ) ;
}
if_unlock ( t ) ;
return 1 ;
signal :
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int pktgen_wait_all_threads_run ( void )
{
2006-03-21 09:16:40 +03:00
struct pktgen_thread * t ;
2005-04-17 02:20:36 +04:00
int sig = 1 ;
2006-03-21 09:16:13 +03:00
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2006-03-21 09:16:40 +03:00
list_for_each_entry ( t , & pktgen_threads , th_list ) {
2005-04-17 02:20:36 +04:00
sig = pktgen_wait_thread_run ( t ) ;
2006-03-21 09:16:13 +03:00
if ( sig = = 0 )
break ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:40 +03:00
if ( sig = = 0 )
list_for_each_entry ( t , & pktgen_threads , th_list )
2005-04-17 02:20:36 +04:00
t - > control | = ( T_STOP ) ;
2006-03-21 09:16:40 +03:00
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
return sig ;
}
static void pktgen_run_all_threads ( void )
{
2006-03-21 09:16:40 +03:00
struct pktgen_thread * t ;
2005-04-17 02:20:36 +04:00
PG_DEBUG ( printk ( " pktgen: entering pktgen_run_all_threads. \n " ) ) ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:40 +03:00
list_for_each_entry ( t , & pktgen_threads , th_list )
2005-04-17 02:20:36 +04:00
t - > control | = ( T_RUN ) ;
2006-03-21 09:16:40 +03:00
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
schedule_timeout_interruptible ( msecs_to_jiffies ( 125 ) ) ; /* Propagate thread->control */
2005-04-17 02:20:36 +04:00
pktgen_wait_all_threads_run ( ) ;
}
static void show_results ( struct pktgen_dev * pkt_dev , int nr_frags )
{
2006-03-21 09:16:13 +03:00
__u64 total_us , bps , mbps , pps , idle ;
char * p = pkt_dev - > result ;
total_us = pkt_dev - > stopped_at - pkt_dev - > started_at ;
idle = pkt_dev - > idle_acc ;
p + = sprintf ( p , " OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) \n " ,
( unsigned long long ) total_us ,
( unsigned long long ) ( total_us - idle ) ,
( unsigned long long ) idle ,
( unsigned long long ) pkt_dev - > sofar ,
pkt_dev - > cur_pkt_size , nr_frags ) ;
pps = pkt_dev - > sofar * USEC_PER_SEC ;
while ( ( total_us > > 32 ) ! = 0 ) {
pps > > = 1 ;
total_us > > = 1 ;
}
do_div ( pps , total_us ) ;
bps = pps * 8 * pkt_dev - > cur_pkt_size ;
mbps = bps ;
do_div ( mbps , 1000000 ) ;
p + = sprintf ( p , " %llupps %lluMb/sec (%llubps) errors: %llu " ,
( unsigned long long ) pps ,
( unsigned long long ) mbps ,
( unsigned long long ) bps ,
( unsigned long long ) pkt_dev - > errors ) ;
2005-04-17 02:20:36 +04:00
}
/* Set stopped-at timer, remove from running list, do counters & statistics */
2006-03-21 09:16:13 +03:00
static int pktgen_stop_device ( struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
int nr_frags = pkt_dev - > skb ? skb_shinfo ( pkt_dev - > skb ) - > nr_frags : - 1 ;
2006-03-21 08:26:56 +03:00
2006-03-21 09:16:13 +03:00
if ( ! pkt_dev - > running ) {
printk ( " pktgen: interface: %s is already stopped \n " ,
pkt_dev - > ifname ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
pkt_dev - > stopped_at = getCurUs ( ) ;
pkt_dev - > running = 0 ;
2005-04-17 02:20:36 +04:00
2006-03-21 08:26:56 +03:00
show_results ( pkt_dev , nr_frags ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static struct pktgen_dev * next_to_run ( struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * pkt_dev , * best = NULL ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list ) {
if ( ! pkt_dev - > running )
2006-03-21 09:16:13 +03:00
continue ;
if ( best = = NULL )
2006-03-21 09:18:16 +03:00
best = pkt_dev ;
else if ( pkt_dev - > next_tx_us < best - > next_tx_us )
best = pkt_dev ;
2005-04-17 02:20:36 +04:00
}
if_unlock ( t ) ;
2006-03-21 09:16:13 +03:00
return best ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static void pktgen_stop ( struct pktgen_thread * t )
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * pkt_dev ;
2005-04-17 02:20:36 +04:00
2006-03-21 08:26:56 +03:00
PG_DEBUG ( printk ( " pktgen: entering pktgen_stop \n " ) ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:18:16 +03:00
list_for_each_entry ( pkt_dev , & t - > if_list , list ) {
pktgen_stop_device ( pkt_dev ) ;
if ( pkt_dev - > skb )
kfree_skb ( pkt_dev - > skb ) ;
2006-03-21 08:26:56 +03:00
2006-03-21 09:18:16 +03:00
pkt_dev - > skb = NULL ;
2006-03-21 08:26:56 +03:00
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if_unlock ( t ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 08:26:56 +03:00
/*
* one of our devices needs to be removed - find it
* and remove it
*/
static void pktgen_rem_one_if ( struct pktgen_thread * t )
{
2006-03-21 09:18:16 +03:00
struct list_head * q , * n ;
struct pktgen_dev * cur ;
2006-03-21 08:26:56 +03:00
PG_DEBUG ( printk ( " pktgen: entering pktgen_rem_one_if \n " ) ) ;
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_safe ( q , n , & t - > if_list ) {
cur = list_entry ( q , struct pktgen_dev , list ) ;
2006-03-21 08:26:56 +03:00
2006-03-21 09:16:13 +03:00
if ( ! cur - > removal_mark )
continue ;
2006-03-21 08:26:56 +03:00
if ( cur - > skb )
kfree_skb ( cur - > skb ) ;
cur - > skb = NULL ;
pktgen_remove_device ( t , cur ) ;
break ;
}
if_unlock ( t ) ;
}
2006-03-21 09:16:13 +03:00
static void pktgen_rem_all_ifs ( struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct list_head * q , * n ;
struct pktgen_dev * cur ;
2006-03-21 09:16:13 +03:00
/* Remove all devices, free mem */
2006-03-21 08:26:56 +03:00
PG_DEBUG ( printk ( " pktgen: entering pktgen_rem_all_ifs \n " ) ) ;
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:18:16 +03:00
list_for_each_safe ( q , n , & t - > if_list ) {
cur = list_entry ( q , struct pktgen_dev , list ) ;
2006-03-21 08:26:56 +03:00
if ( cur - > skb )
kfree_skb ( cur - > skb ) ;
cur - > skb = NULL ;
2005-04-17 02:20:36 +04:00
pktgen_remove_device ( t , cur ) ;
}
2006-03-21 09:16:13 +03:00
if_unlock ( t ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static void pktgen_rem_thread ( struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
/* Remove from the thread list */
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
remove_proc_entry ( t - > tsk - > comm , pg_proc_dir ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:40 +03:00
list_del ( & t - > th_list ) ;
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2005-04-17 02:20:36 +04:00
}
static __inline__ void pktgen_xmit ( struct pktgen_dev * pkt_dev )
{
struct net_device * odev = NULL ;
__u64 idle_start = 0 ;
int ret ;
odev = pkt_dev - > odev ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > delay_us | | pkt_dev - > delay_ns ) {
u64 now ;
now = getCurUs ( ) ;
if ( now < pkt_dev - > next_tx_us )
spin ( pkt_dev , pkt_dev - > next_tx_us ) ;
/* This is max DELAY, this has special meaning of
* " never transmit "
*/
if ( pkt_dev - > delay_us = = 0x7FFFFFFF ) {
pkt_dev - > next_tx_us = getCurUs ( ) + pkt_dev - > delay_us ;
pkt_dev - > next_tx_ns = pkt_dev - > delay_ns ;
goto out ;
}
}
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( netif_queue_stopped ( odev ) | | need_resched ( ) ) {
idle_start = getCurUs ( ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( ! netif_running ( odev ) ) {
pktgen_stop_device ( pkt_dev ) ;
2006-03-21 08:26:56 +03:00
if ( pkt_dev - > skb )
kfree_skb ( pkt_dev - > skb ) ;
pkt_dev - > skb = NULL ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-03-21 09:16:13 +03:00
if ( need_resched ( ) )
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
pkt_dev - > idle_acc + = getCurUs ( ) - idle_start ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( netif_queue_stopped ( odev ) ) {
2006-03-21 09:16:13 +03:00
pkt_dev - > next_tx_us = getCurUs ( ) ; /* TODO */
2005-04-17 02:20:36 +04:00
pkt_dev - > next_tx_ns = 0 ;
2006-03-21 09:16:13 +03:00
goto out ; /* Try the next interface */
2005-04-17 02:20:36 +04:00
}
}
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > last_ok | | ! pkt_dev - > skb ) {
2006-03-21 09:16:13 +03:00
if ( ( + + pkt_dev - > clone_count > = pkt_dev - > clone_skb )
| | ( ! pkt_dev - > skb ) ) {
2005-04-17 02:20:36 +04:00
/* build a new pkt */
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > skb )
2005-04-17 02:20:36 +04:00
kfree_skb ( pkt_dev - > skb ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
pkt_dev - > skb = fill_packet ( odev , pkt_dev ) ;
if ( pkt_dev - > skb = = NULL ) {
printk ( " pktgen: ERROR: couldn't allocate skb in fill_packet. \n " ) ;
schedule ( ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > clone_count - - ; /* back out increment, OOM */
2005-04-17 02:20:36 +04:00
goto out ;
}
pkt_dev - > allocated_skbs + + ;
2006-03-21 09:16:13 +03:00
pkt_dev - > clone_count = 0 ; /* reset counter */
2005-04-17 02:20:36 +04:00
}
}
2006-03-21 08:26:56 +03:00
2006-06-09 23:20:56 +04:00
netif_tx_lock_bh ( odev ) ;
2005-04-17 02:20:36 +04:00
if ( ! netif_queue_stopped ( odev ) ) {
atomic_inc ( & ( pkt_dev - > skb - > users ) ) ;
2006-03-21 09:16:13 +03:00
retry_now :
2005-04-17 02:20:36 +04:00
ret = odev - > hard_start_xmit ( pkt_dev - > skb , odev ) ;
if ( likely ( ret = = NETDEV_TX_OK ) ) {
2006-03-21 09:16:13 +03:00
pkt_dev - > last_ok = 1 ;
2005-04-17 02:20:36 +04:00
pkt_dev - > sofar + + ;
pkt_dev - > seq_num + + ;
pkt_dev - > tx_bytes + = pkt_dev - > cur_pkt_size ;
2006-03-21 09:16:13 +03:00
} else if ( ret = = NETDEV_TX_LOCKED
2005-04-17 02:20:36 +04:00
& & ( odev - > features & NETIF_F_LLTX ) ) {
cpu_relax ( ) ;
goto retry_now ;
2006-03-21 09:16:13 +03:00
} else { /* Retry it next time */
2005-04-17 02:20:36 +04:00
atomic_dec ( & ( pkt_dev - > skb - > users ) ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
if ( debug & & net_ratelimit ( ) )
printk ( KERN_INFO " pktgen: Hard xmit error \n " ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
pkt_dev - > errors + + ;
pkt_dev - > last_ok = 0 ;
}
pkt_dev - > next_tx_us = getCurUs ( ) ;
pkt_dev - > next_tx_ns = 0 ;
pkt_dev - > next_tx_us + = pkt_dev - > delay_us ;
pkt_dev - > next_tx_ns + = pkt_dev - > delay_ns ;
if ( pkt_dev - > next_tx_ns > 1000 ) {
pkt_dev - > next_tx_us + + ;
pkt_dev - > next_tx_ns - = 1000 ;
}
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
else { /* Retry it next time */
pkt_dev - > last_ok = 0 ;
pkt_dev - > next_tx_us = getCurUs ( ) ; /* TODO */
2005-04-17 02:20:36 +04:00
pkt_dev - > next_tx_ns = 0 ;
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2006-06-09 23:20:56 +04:00
netif_tx_unlock_bh ( odev ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
/* If pkt_dev->count is zero, then run forever */
if ( ( pkt_dev - > count ! = 0 ) & & ( pkt_dev - > sofar > = pkt_dev - > count ) ) {
if ( atomic_read ( & ( pkt_dev - > skb - > users ) ) ! = 1 ) {
idle_start = getCurUs ( ) ;
while ( atomic_read ( & ( pkt_dev - > skb - > users ) ) ! = 1 ) {
if ( signal_pending ( current ) ) {
break ;
}
schedule ( ) ;
}
pkt_dev - > idle_acc + = getCurUs ( ) - idle_start ;
}
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
/* Done with this */
pktgen_stop_device ( pkt_dev ) ;
2006-03-21 08:26:56 +03:00
if ( pkt_dev - > skb )
kfree_skb ( pkt_dev - > skb ) ;
pkt_dev - > skb = NULL ;
2006-03-21 09:16:13 +03:00
}
out : ;
}
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:36 +03:00
/*
2005-04-17 02:20:36 +04:00
* Main loop of the thread goes here
*/
2007-01-02 07:51:53 +03:00
static int pktgen_thread_worker ( void * arg )
2005-04-17 02:20:36 +04:00
{
DEFINE_WAIT ( wait ) ;
2007-01-02 07:51:53 +03:00
struct pktgen_thread * t = arg ;
2006-03-21 09:16:13 +03:00
struct pktgen_dev * pkt_dev = NULL ;
2005-04-17 02:20:36 +04:00
int cpu = t - > cpu ;
u32 max_before_softirq ;
2006-03-21 09:16:13 +03:00
u32 tx_since_softirq = 0 ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
BUG_ON ( smp_processor_id ( ) ! = cpu ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & t - > queue ) ;
2006-03-21 09:16:13 +03:00
t - > pid = current - > pid ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
PG_DEBUG ( printk ( " pktgen: starting pktgen/%d: pid=%d \n " , cpu , current - > pid ) ) ;
2005-04-17 02:20:36 +04:00
max_before_softirq = t - > max_before_softirq ;
2007-01-02 07:51:53 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-03-21 09:16:13 +03:00
2007-01-02 07:51:53 +03:00
while ( ! kthread_should_stop ( ) ) {
pkt_dev = next_to_run ( t ) ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
if ( ! pkt_dev & &
( t - > control & ( T_STOP | T_RUN | T_REMDEVALL | T_REMDEV ) )
= = 0 ) {
prepare_to_wait ( & ( t - > queue ) , & wait ,
TASK_INTERRUPTIBLE ) ;
schedule_timeout ( HZ / 10 ) ;
finish_wait ( & ( t - > queue ) , & wait ) ;
}
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
__set_current_state ( TASK_RUNNING ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev ) {
2005-04-17 02:20:36 +04:00
pktgen_xmit ( pkt_dev ) ;
/*
* We like to stay RUNNING but must also give
* others fair share .
*/
tx_since_softirq + = pkt_dev - > last_ok ;
if ( tx_since_softirq > max_before_softirq ) {
if ( local_softirq_pending ( ) )
do_softirq ( ) ;
tx_since_softirq = 0 ;
}
}
2006-03-21 09:16:13 +03:00
if ( t - > control & T_STOP ) {
2005-04-17 02:20:36 +04:00
pktgen_stop ( t ) ;
t - > control & = ~ ( T_STOP ) ;
}
2006-03-21 09:16:13 +03:00
if ( t - > control & T_RUN ) {
2005-04-17 02:20:36 +04:00
pktgen_run ( t ) ;
t - > control & = ~ ( T_RUN ) ;
}
2006-03-21 09:16:13 +03:00
if ( t - > control & T_REMDEVALL ) {
2005-04-17 02:20:36 +04:00
pktgen_rem_all_ifs ( t ) ;
2006-03-21 08:26:56 +03:00
t - > control & = ~ ( T_REMDEVALL ) ;
}
2006-03-21 09:16:13 +03:00
if ( t - > control & T_REMDEV ) {
2006-03-21 08:26:56 +03:00
pktgen_rem_one_if ( t ) ;
2005-04-17 02:20:36 +04:00
t - > control & = ~ ( T_REMDEV ) ;
}
2007-04-13 01:45:32 +04:00
try_to_freeze ( ) ;
2007-01-02 07:51:53 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
PG_DEBUG ( printk ( " pktgen: %s stopping all device \n " , t - > tsk - > comm ) ) ;
2006-03-21 09:16:13 +03:00
pktgen_stop ( t ) ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
PG_DEBUG ( printk ( " pktgen: %s removing all device \n " , t - > tsk - > comm ) ) ;
2006-03-21 09:16:13 +03:00
pktgen_rem_all_ifs ( t ) ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
PG_DEBUG ( printk ( " pktgen: %s removing thread. \n " , t - > tsk - > comm ) ) ;
2006-03-21 09:16:13 +03:00
pktgen_rem_thread ( t ) ;
2006-03-21 09:16:40 +03:00
2007-01-02 07:51:53 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static struct pktgen_dev * pktgen_find_dev ( struct pktgen_thread * t ,
const char * ifname )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * p , * pkt_dev = NULL ;
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( p , & t - > if_list , list )
if ( strncmp ( p - > ifname , ifname , IFNAMSIZ ) = = 0 ) {
pkt_dev = p ;
2006-03-21 09:16:13 +03:00
break ;
}
if_unlock ( t ) ;
PG_DEBUG ( printk ( " pktgen: find_dev(%s) returning %p \n " , ifname , pkt_dev ) ) ;
return pkt_dev ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:36 +03:00
/*
* Adds a dev at front of if_list .
2005-04-17 02:20:36 +04:00
*/
2006-03-21 09:16:13 +03:00
static int add_dev_to_thread ( struct pktgen_thread * t ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
int rv = 0 ;
2006-03-21 09:16:13 +03:00
if_lock ( t ) ;
if ( pkt_dev - > pg_thread ) {
printk ( " pktgen: ERROR: already assigned to a thread. \n " ) ;
rv = - EBUSY ;
goto out ;
}
2006-03-21 09:18:16 +03:00
list_add ( & pkt_dev - > list , & t - > if_list ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > pg_thread = t ;
2005-04-17 02:20:36 +04:00
pkt_dev - > running = 0 ;
2006-03-21 09:16:13 +03:00
out :
if_unlock ( t ) ;
return rv ;
2005-04-17 02:20:36 +04:00
}
/* Called under thread lock */
2006-03-21 09:16:13 +03:00
static int pktgen_add_device ( struct pktgen_thread * t , const char * ifname )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:13 +03:00
struct pktgen_dev * pkt_dev ;
2005-10-15 02:42:33 +04:00
struct proc_dir_entry * pe ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
/* We don't allow a device to be on several threads */
2005-10-15 02:42:33 +04:00
pkt_dev = __pktgen_NN_threads ( ifname , FIND ) ;
if ( pkt_dev ) {
2006-03-21 09:16:13 +03:00
printk ( " pktgen: ERROR: interface already used. \n " ) ;
return - EBUSY ;
}
2005-10-15 02:42:33 +04:00
pkt_dev = kzalloc ( sizeof ( struct pktgen_dev ) , GFP_KERNEL ) ;
if ( ! pkt_dev )
return - ENOMEM ;
2006-03-21 09:16:13 +03:00
pkt_dev - > flows = vmalloc ( MAX_CFLOWS * sizeof ( struct flow_state ) ) ;
2005-10-15 02:42:33 +04:00
if ( pkt_dev - > flows = = NULL ) {
kfree ( pkt_dev ) ;
return - ENOMEM ;
}
2006-03-21 09:16:13 +03:00
memset ( pkt_dev - > flows , 0 , MAX_CFLOWS * sizeof ( struct flow_state ) ) ;
2005-10-15 02:42:33 +04:00
2006-03-21 08:26:56 +03:00
pkt_dev - > removal_mark = 0 ;
2005-10-15 02:42:33 +04:00
pkt_dev - > min_pkt_size = ETH_ZLEN ;
pkt_dev - > max_pkt_size = ETH_ZLEN ;
pkt_dev - > nfrags = 0 ;
pkt_dev - > clone_skb = pg_clone_skb_d ;
pkt_dev - > delay_us = pg_delay_d / 1000 ;
pkt_dev - > delay_ns = pg_delay_d % 1000 ;
pkt_dev - > count = pg_count_d ;
pkt_dev - > sofar = 0 ;
2006-03-21 09:16:13 +03:00
pkt_dev - > udp_src_min = 9 ; /* sink port */
2005-10-15 02:42:33 +04:00
pkt_dev - > udp_src_max = 9 ;
pkt_dev - > udp_dst_min = 9 ;
pkt_dev - > udp_dst_max = 9 ;
2006-09-28 03:30:44 +04:00
pkt_dev - > vlan_p = 0 ;
pkt_dev - > vlan_cfi = 0 ;
pkt_dev - > vlan_id = 0xffff ;
pkt_dev - > svlan_p = 0 ;
pkt_dev - > svlan_cfi = 0 ;
pkt_dev - > svlan_id = 0xffff ;
2005-10-15 02:42:33 +04:00
strncpy ( pkt_dev - > ifname , ifname , IFNAMSIZ ) ;
2006-03-21 09:16:13 +03:00
if ( ! pktgen_setup_dev ( pkt_dev ) ) {
2005-10-15 02:42:33 +04:00
printk ( " pktgen: ERROR: pktgen_setup_dev failed. \n " ) ;
if ( pkt_dev - > flows )
vfree ( pkt_dev - > flows ) ;
kfree ( pkt_dev ) ;
return - ENODEV ;
}
pe = create_proc_entry ( ifname , 0600 , pg_proc_dir ) ;
if ( ! pe ) {
printk ( " pktgen: cannot create %s/%s procfs entry. \n " ,
PG_PROC_DIR , ifname ) ;
if ( pkt_dev - > flows )
vfree ( pkt_dev - > flows ) ;
kfree ( pkt_dev ) ;
return - EINVAL ;
}
pe - > proc_fops = & pktgen_if_fops ;
pe - > data = pkt_dev ;
return add_dev_to_thread ( t , pkt_dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-02 07:51:53 +03:00
static int __init pktgen_create_thread ( int cpu )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:16:40 +03:00
struct pktgen_thread * t ;
2005-10-15 02:42:33 +04:00
struct proc_dir_entry * pe ;
2007-01-02 07:51:53 +03:00
struct task_struct * p ;
2006-03-21 09:16:13 +03:00
t = kzalloc ( sizeof ( struct pktgen_thread ) , GFP_KERNEL ) ;
if ( ! t ) {
printk ( " pktgen: ERROR: out of memory, can't create new thread. \n " ) ;
return - ENOMEM ;
}
spin_lock_init ( & t - > if_lock ) ;
2005-04-17 02:20:36 +04:00
t - > cpu = cpu ;
2006-03-21 09:16:13 +03:00
2007-01-02 07:51:53 +03:00
INIT_LIST_HEAD ( & t - > if_list ) ;
list_add_tail ( & t - > th_list , & pktgen_threads ) ;
p = kthread_create ( pktgen_thread_worker , t , " kpktgend_%d " , cpu ) ;
if ( IS_ERR ( p ) ) {
printk ( " pktgen: kernel_thread() failed for cpu %d \n " , t - > cpu ) ;
list_del ( & t - > th_list ) ;
kfree ( t ) ;
return PTR_ERR ( p ) ;
}
kthread_bind ( p , cpu ) ;
t - > tsk = p ;
pe = create_proc_entry ( t - > tsk - > comm , 0600 , pg_proc_dir ) ;
2006-03-21 09:16:13 +03:00
if ( ! pe ) {
printk ( " pktgen: cannot create %s/%s procfs entry. \n " ,
2007-01-02 07:51:53 +03:00
PG_PROC_DIR , t - > tsk - > comm ) ;
kthread_stop ( p ) ;
list_del ( & t - > th_list ) ;
2006-03-21 09:16:13 +03:00
kfree ( t ) ;
return - EINVAL ;
}
2005-10-15 02:42:33 +04:00
pe - > proc_fops = & pktgen_thread_fops ;
pe - > data = t ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
wake_up_process ( p ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-09 17:24:36 +03:00
/*
* Removes a device from the thread if_list .
2005-04-17 02:20:36 +04:00
*/
2006-03-21 09:16:13 +03:00
static void _rem_dev_from_if_list ( struct pktgen_thread * t ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct list_head * q , * n ;
struct pktgen_dev * p ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:18:16 +03:00
list_for_each_safe ( q , n , & t - > if_list ) {
p = list_entry ( q , struct pktgen_dev , list ) ;
if ( p = = pkt_dev )
list_del ( & p - > list ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-03-21 09:16:13 +03:00
static int pktgen_remove_device ( struct pktgen_thread * t ,
struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
PG_DEBUG ( printk ( " pktgen: remove_device pkt_dev=%p \n " , pkt_dev ) ) ;
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > running ) {
printk ( " pktgen:WARNING: trying to remove a running interface, stopping it now. \n " ) ;
pktgen_stop_device ( pkt_dev ) ;
}
/* Dis-associate from the interface */
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > odev ) {
dev_put ( pkt_dev - > odev ) ;
2006-03-21 09:16:13 +03:00
pkt_dev - > odev = NULL ;
}
2005-04-17 02:20:36 +04:00
/* And update the thread if_list */
_rem_dev_from_if_list ( t , pkt_dev ) ;
2006-03-21 09:16:13 +03:00
/* Clean up proc file system */
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
remove_proc_entry ( pkt_dev - > ifname , pg_proc_dir ) ;
2005-04-17 02:20:36 +04:00
if ( pkt_dev - > flows )
vfree ( pkt_dev - > flows ) ;
kfree ( pkt_dev ) ;
2006-03-21 09:16:13 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
static int __init pg_init ( void )
2005-04-17 02:20:36 +04:00
{
int cpu ;
2005-10-15 02:42:33 +04:00
struct proc_dir_entry * pe ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
printk ( version ) ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
pg_proc_dir = proc_mkdir ( PG_PROC_DIR , proc_net ) ;
if ( ! pg_proc_dir )
return - ENODEV ;
pg_proc_dir - > owner = THIS_MODULE ;
2005-04-17 02:20:36 +04:00
2005-10-15 02:42:33 +04:00
pe = create_proc_entry ( PGCTRL , 0600 , pg_proc_dir ) ;
2006-03-21 09:16:13 +03:00
if ( pe = = NULL ) {
printk ( " pktgen: ERROR: cannot create %s procfs entry. \n " ,
PGCTRL ) ;
2005-10-15 02:42:33 +04:00
proc_net_remove ( PG_PROC_DIR ) ;
2006-03-21 09:16:13 +03:00
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
pe - > proc_fops = & pktgen_fops ;
pe - > data = NULL ;
2005-04-17 02:20:36 +04:00
/* Register us to receive netdevice events */
register_netdevice_notifier ( & pktgen_notifier_block ) ;
2006-03-21 09:16:13 +03:00
2005-10-13 20:30:31 +04:00
for_each_online_cpu ( cpu ) {
2006-03-21 09:17:55 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
err = pktgen_create_thread ( cpu ) ;
2006-03-21 09:17:55 +03:00
if ( err )
printk ( " pktgen: WARNING: Cannot create thread for cpu %d (%d) \n " ,
cpu , err ) ;
2006-03-21 09:16:13 +03:00
}
2006-03-21 09:17:55 +03:00
if ( list_empty ( & pktgen_threads ) ) {
printk ( " pktgen: ERROR: Initialization failed for all threads \n " ) ;
unregister_netdevice_notifier ( & pktgen_notifier_block ) ;
remove_proc_entry ( PGCTRL , pg_proc_dir ) ;
proc_net_remove ( PG_PROC_DIR ) ;
return - ENODEV ;
}
2006-03-21 09:16:13 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void __exit pg_cleanup ( void )
{
2006-03-21 09:16:40 +03:00
struct pktgen_thread * t ;
struct list_head * q , * n ;
2005-04-17 02:20:36 +04:00
wait_queue_head_t queue ;
init_waitqueue_head ( & queue ) ;
2006-03-21 09:16:13 +03:00
/* Stop all interfaces & threads */
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:40 +03:00
list_for_each_safe ( q , n , & pktgen_threads ) {
t = list_entry ( q , struct pktgen_thread , th_list ) ;
2007-01-02 07:51:53 +03:00
kthread_stop ( t - > tsk ) ;
kfree ( t ) ;
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
/* Un-register us from receiving netdevice events */
2005-04-17 02:20:36 +04:00
unregister_netdevice_notifier ( & pktgen_notifier_block ) ;
2006-03-21 09:16:13 +03:00
/* Clean up proc file system */
2005-10-15 02:42:33 +04:00
remove_proc_entry ( PGCTRL , pg_proc_dir ) ;
proc_net_remove ( PG_PROC_DIR ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( pg_init ) ;
module_exit ( pg_cleanup ) ;
MODULE_AUTHOR ( " Robert Olsson <robert.olsson@its.uu.se " ) ;
MODULE_DESCRIPTION ( " Packet Generator tool " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( pg_count_d , int , 0 ) ;
module_param ( pg_delay_d , int , 0 ) ;
module_param ( pg_clone_skb_d , int , 0 ) ;
module_param ( debug , int , 0 ) ;