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 >
2007-10-20 01:21:04 +04:00
* Jens Låås < jens . laas @ data . slu . se >
2005-04-17 02:20:36 +04:00
*
* 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 >
*
2007-09-17 01:52:15 +04:00
* Fixed src_mac command to set source mac of packet to value specified in
* command by Adit Ranadive < adit .262 @ gmail . com >
*
2005-04-17 02:20:36 +04:00
*/
2010-06-21 16:29:14 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
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>
2009-08-29 10:41:29 +04:00
# include <linux/hrtimer.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>
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2005-04-17 02:20:36 +04:00
# include <net/checksum.h>
# include <net/ipv6.h>
# include <net/addrconf.h>
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
# include <net/xfrm.h>
# endif
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
# include <linux/rcupdate.h>
2007-10-19 10:40:25 +04:00
# include <linux/bitops.h>
2009-08-27 17:55:19 +04:00
# include <linux/io.h>
# include <linux/timex.h>
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/dma.h>
2006-03-21 09:16:13 +03:00
# include <asm/div64.h> /* do_div */
2005-04-17 02:20:36 +04:00
2010-06-10 02:49:57 +04:00
# define VERSION "2.74"
2005-04-17 02:20:36 +04:00
# 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 */
2007-03-05 03:08:08 +03:00
# define MPLS_STACK_BOTTOM htonl(0x00000100)
2005-04-17 02:20:36 +04:00
2010-06-21 16:29:14 +04:00
# define func_enter() pr_debug("entering %s\n", __func__);
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 */
2007-07-03 09:40:36 +04:00
# define F_FLOW_SEQ (1<<11) /* Sequential flows */
2007-07-03 09:41:59 +04:00
# define F_IPSEC_ON (1<<12) /* ipsec on for flows */
2007-08-29 02:45:55 +04:00
# define F_QUEUE_MAP_RND (1<<13) /* queue map Random */
2008-08-07 13:23:01 +04:00
# define F_QUEUE_MAP_CPU (1<<14) /* queue map mirrors smp_processor_id() */
2010-03-19 01:44:30 +03:00
# define F_NODE (1<<15) /* Node memory alloc*/
2005-04-17 02:20:36 +04:00
/* Thread control flag bits */
2009-09-22 23:41:42 +04:00
# define T_STOP (1<<0) /* Stop run */
# define T_RUN (1<<1) /* Start run */
# define T_REMDEVALL (1<<2) /* Remove all devs */
# define T_REMDEV (1<<3) /* 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"
2009-08-27 17:55:19 +04:00
static struct proc_dir_entry * pg_proc_dir ;
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 ;
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
struct xfrm_state * x ;
# endif
2007-07-03 09:40:36 +04:00
__u32 flags ;
2005-04-17 02:20:36 +04:00
} ;
2007-07-03 09:40:36 +04:00
/* flow flag bits */
# define F_INIT (1<<0) /* flow has been initialized */
2005-04-17 02:20:36 +04:00
struct pktgen_dev {
/*
* Try to keep frequent / infrequent used vars . separated .
*/
2007-03-05 03:11:51 +03:00
struct proc_dir_entry * entry ; /* proc file */
struct pktgen_thread * pg_thread ; /* the owner */
2009-08-27 17:55:19 +04:00
struct list_head list ; /* chaining in the thread's run-queue */
2005-04-17 02:20:36 +04:00
2009-08-27 17:55:19 +04:00
int running ; /* if 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; */
2007-07-03 09:39:50 +04:00
int pkt_overhead ; /* overhead for MPLS, VLANs, IPSEC etc */
2006-03-21 09:16:13 +03:00
int nfrags ;
2009-08-27 17:55:16 +04:00
u64 delay ; /* nano-seconds */
2006-03-21 09:16:13 +03:00
__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 */
2009-12-24 09:02:57 +03:00
__u64 errors ; /* Errors when trying to transmit, */
2006-03-21 09:16:13 +03:00
/* runtime counters relating to clone_skb */
__u64 allocated_skbs ;
__u32 clone_count ;
int last_ok ; /* Was last skb sent?
2009-08-27 17:55:19 +04:00
* Or a failed transmit of some sort ?
* This will keep sequence numbers in order
2006-03-21 09:16:13 +03:00
*/
2009-08-27 17:55:16 +04:00
ktime_t next_tx ;
ktime_t started_at ;
ktime_t stopped_at ;
u64 idle_acc ; /* nano-seconds */
2006-03-21 09:16:13 +03:00
__u32 seq_num ;
2009-08-27 17:55:19 +04:00
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 .
* If you want to send 1024 identical packets
* before creating a new packet ,
* set clone_skb to 1024.
2006-03-21 09:16:13 +03:00
*/
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 */
2009-08-27 17:55:19 +04:00
__u8 tos ; /* six MSB 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-09-28 03:32:03 +04:00
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 ;
2009-10-24 17:55:20 +04:00
__u16 ip_id ;
2006-03-21 09:16:13 +03:00
__u16 cur_udp_dst ;
__u16 cur_udp_src ;
2007-08-29 02:45:55 +04:00
__u16 cur_queue_map ;
2006-03-21 09:16:13 +03:00
__u32 cur_pkt_size ;
2009-11-06 08:04:32 +03:00
__u32 last_pkt_size ;
2006-03-21 09:16:13 +03:00
__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 */
2009-08-27 17:55:19 +04:00
struct sk_buff * skb ; /* skb we are to transmit next, used for when we
2006-03-21 09:16:13 +03:00
* are transmitting the same one multiple times
*/
2009-08-27 17:55:19 +04:00
struct net_device * odev ; /* The out-going device.
* Note that the device should have it ' s
* pg_info pointer pointing back to this
* device .
* Set when the user specifies the out - going
* device name ( not when the inject is
* started as it used to do . )
*/
2009-11-23 04:44:37 +03:00
char odevname [ 32 ] ;
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) */
2007-07-03 09:40:36 +04:00
unsigned curfl ; /* current sequenced flow (state)*/
2007-08-29 02:45:55 +04:00
u16 queue_map_min ;
u16 queue_map_max ;
2010-11-16 22:12:28 +03:00
__u32 skb_priority ; /* skb priority field */
2010-03-19 01:44:30 +03:00
int node ; /* Memory node */
2007-08-29 02:45:55 +04:00
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
__u8 ipsmode ; /* IPSEC mode (config) */
__u8 ipsproto ; /* IPSEC type (config) */
# endif
2007-03-05 03:11:51 +03:00
char result [ 512 ] ;
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
} ;
2010-11-21 21:26:44 +03:00
static bool pktgen_exiting __read_mostly ;
2005-04-17 02:20:36 +04:00
struct pktgen_thread {
2009-08-27 17:55:19 +04:00
spinlock_t if_lock ; /* for list of devices */
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 ] ;
2009-08-27 17:55:19 +04:00
/* Field for thread to receive "posted" events terminate,
stop ifs etc . */
2006-03-21 09:16:13 +03:00
u32 control ;
2005-04-17 02:20:36 +04:00
int cpu ;
2006-03-21 09:16:13 +03:00
wait_queue_head_t queue ;
2008-05-21 02:12:44 +04:00
struct completion start_done ;
2005-04-17 02:20:36 +04:00
} ;
# define REMOVE 1
# define FIND 0
2009-08-27 17:55:16 +04:00
static inline ktime_t ktime_now ( void )
2005-04-17 02:20:36 +04:00
{
2009-08-27 17:55:16 +04:00
struct timespec ts ;
ktime_get_ts ( & ts ) ;
return timespec_to_ktime ( ts ) ;
2005-04-17 02:20:36 +04:00
}
2009-08-27 17:55:16 +04:00
/* This works even if 32 bit because of careful byte order choice */
static inline int ktime_lt ( const ktime_t cmp1 , const ktime_t cmp2 )
2005-04-17 02:20:36 +04:00
{
2009-08-27 17:55:16 +04:00
return cmp1 . tv64 < cmp2 . tv64 ;
2005-04-17 02:20:36 +04:00
}
2009-08-27 17:55:20 +04:00
static const char version [ ] =
2010-06-21 16:29:14 +04:00
" Packet Generator for packet performance testing. "
" Version: " VERSION " \n " ;
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 * i ) ;
static int pktgen_add_device ( struct pktgen_thread * t , const char * ifname ) ;
static struct pktgen_dev * pktgen_find_dev ( struct pktgen_thread * t ,
2009-11-25 01:50:53 +03:00
const char * ifname , bool exact ) ;
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 ) ;
2008-11-11 03:48:03 +03:00
static void pktgen_reset_all_threads ( void ) ;
2005-04-17 02:20:36 +04:00
static void pktgen_stop_all_threads_ifs ( void ) ;
2009-08-27 17:55:10 +04:00
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 ) ;
2007-03-05 03:11:51 +03:00
2006-03-21 09:16:13 +03:00
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. */
2009-08-27 17:55:09 +04:00
static int pg_count_d __read_mostly = 1000 ;
static int pg_delay_d __read_mostly ;
static int pg_clone_skb_d __read_mostly ;
static int debug __read_mostly ;
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
{
2009-08-27 17:55:20 +04:00
seq_puts ( seq , version ) ;
2005-10-15 02:42:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-08-27 17:55:19 +04: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 ( ) ;
2008-11-11 03:48:03 +03:00
else if ( ! strcmp ( data , " reset " ) )
pktgen_reset_all_threads ( ) ;
2006-03-21 09:16:13 +03:00
else
2010-06-21 16:29:14 +04:00
pr_warning ( " Unknown command: %s \n " , data ) ;
2005-04-17 02:20:36 +04:00
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
{
2009-08-27 17:55:07 +04:00
const struct pktgen_dev * pkt_dev = seq - > private ;
2009-08-27 17:55:16 +04:00
ktime_t stopped ;
u64 idle ;
2006-03-21 09:16:13 +03:00
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 ,
2009-08-27 17:55:16 +04:00
" frags: %d delay: %llu clone_skb: %d ifname: %s \n " ,
pkt_dev - > nfrags , ( unsigned long long ) pkt_dev - > delay ,
2009-11-23 04:44:37 +03:00
pkt_dev - > clone_skb , pkt_dev - > odevname ) ;
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " flows: %u flowlen: %u \n " , pkt_dev - > cflows ,
pkt_dev - > lflow ) ;
2007-08-29 02:45:55 +04:00
seq_printf ( seq ,
" queue_map_min: %u queue_map_max: %u \n " ,
pkt_dev - > queue_map_min ,
pkt_dev - > queue_map_max ) ;
2010-11-16 22:12:28 +03:00
if ( pkt_dev - > skb_priority )
seq_printf ( seq , " skb_priority: %u \n " ,
pkt_dev - > skb_priority ) ;
2006-03-21 09:16:13 +03:00
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 ) ;
2009-08-27 17:55:19 +04:00
} else {
seq_printf ( seq ,
" dst_min: %s dst_max: %s \n " ,
pkt_dev - > dst_min , pkt_dev - > dst_max ) ;
2006-03-21 09:16:13 +03:00
seq_printf ( seq ,
2009-08-27 17:55:19 +04:00
" src_min: %s src_max: %s \n " ,
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
2008-10-28 01:59:26 +03:00
seq_printf ( seq , " %pM " ,
is_zero_ether_addr ( pkt_dev - > src_mac ) ?
pkt_dev - > odev - > dev_addr : pkt_dev - > src_mac ) ;
2006-03-21 09:16:13 +03:00
seq_printf ( seq , " dst_mac: " ) ;
2008-10-28 01:59:26 +03:00
seq_printf ( seq , " %pM \n " , pkt_dev - > dst_mac ) ;
2006-03-21 09:16:13 +03:00
seq_printf ( seq ,
2009-08-27 17:55:19 +04:00
" udp_src_min: %d udp_src_max: %d "
" udp_dst_min: %d udp_dst_max: %d \n " ,
2006-03-21 09:16:13 +03:00
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: " ) ;
2007-04-11 07:10:33 +04:00
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + )
2006-03-23 12:10:26 +03:00
seq_printf ( seq , " %08x%s " , ntohl ( pkt_dev - > labels [ i ] ) ,
i = = pkt_dev - > nr_labels - 1 ? " \n " : " , " ) ;
}
2009-08-27 17:55:19 +04:00
if ( pkt_dev - > vlan_id ! = 0xffff )
2006-09-28 03:30:44 +04:00
seq_printf ( seq , " vlan_id: %u vlan_p: %u vlan_cfi: %u \n " ,
2009-08-27 17:55:19 +04:00
pkt_dev - > vlan_id , pkt_dev - > vlan_p ,
pkt_dev - > vlan_cfi ) ;
2006-09-28 03:30:44 +04:00
2009-08-27 17:55:19 +04:00
if ( pkt_dev - > svlan_id ! = 0xffff )
2006-09-28 03:30:44 +04:00
seq_printf ( seq , " svlan_id: %u vlan_p: %u vlan_cfi: %u \n " ,
2009-08-27 17:55:19 +04:00
pkt_dev - > svlan_id , pkt_dev - > svlan_p ,
pkt_dev - > svlan_cfi ) ;
2006-09-28 03:30:44 +04:00
2009-08-27 17:55:19 +04:00
if ( pkt_dev - > tos )
2006-09-28 03:32:03 +04:00
seq_printf ( seq , " tos: 0x%02x \n " , pkt_dev - > tos ) ;
2009-08-27 17:55:19 +04:00
if ( pkt_dev - > traffic_class )
2006-09-28 03:32:03 +04:00
seq_printf ( seq , " traffic_class: 0x%02x \n " , pkt_dev - > traffic_class ) ;
2010-03-19 01:44:30 +03:00
if ( pkt_dev - > node > = 0 )
seq_printf ( seq , " node: %d \n " , pkt_dev - > node ) ;
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 " ) ;
2007-08-29 02:45:55 +04:00
if ( pkt_dev - > flags & F_QUEUE_MAP_RND )
seq_printf ( seq , " QUEUE_MAP_RND " ) ;
2008-08-07 13:23:01 +04:00
if ( pkt_dev - > flags & F_QUEUE_MAP_CPU )
seq_printf ( seq , " QUEUE_MAP_CPU " ) ;
2007-07-03 09:40:36 +04:00
if ( pkt_dev - > cflows ) {
if ( pkt_dev - > flags & F_FLOW_SEQ )
seq_printf ( seq , " FLOW_SEQ " ) ; /*in sequence flows*/
else
seq_printf ( seq , " FLOW_RND " ) ;
}
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
if ( pkt_dev - > flags & F_IPSEC_ON )
seq_printf ( seq , " IPSEC " ) ;
# endif
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 " ) ;
2010-03-19 01:44:30 +03:00
if ( pkt_dev - > flags & F_NODE )
seq_printf ( seq , " NODE_ALLOC " ) ;
2006-03-21 09:16:13 +03:00
seq_puts ( seq , " \n " ) ;
2009-08-27 17:55:16 +04:00
/* not really stopped, more like last-running-at */
stopped = pkt_dev - > running ? ktime_now ( ) : pkt_dev - > stopped_at ;
idle = pkt_dev - > idle_acc ;
do_div ( idle , NSEC_PER_USEC ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
seq_printf ( seq ,
2009-08-27 17:55:16 +04:00
" Current: \n pkts-sofar: %llu errors: %llu \n " ,
2006-03-21 09:16:13 +03:00
( unsigned long long ) pkt_dev - > sofar ,
2009-08-27 17:55:16 +04:00
( unsigned long long ) pkt_dev - > errors ) ;
seq_printf ( seq ,
" started: %lluus stopped: %lluus idle: %lluus \n " ,
( unsigned long long ) ktime_to_us ( pkt_dev - > started_at ) ,
( unsigned long long ) ktime_to_us ( stopped ) ,
( unsigned long long ) idle ) ;
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
2007-08-29 02:45:55 +04:00
seq_printf ( seq , " cur_queue_map: %u \n " , pkt_dev - > cur_queue_map ) ;
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
2009-08-27 17:55:19 +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 ;
2007-04-11 07:10:33 +04:00
for ( ; i < maxlen ; i + + ) {
2010-09-21 00:40:26 +04:00
int value ;
2006-03-23 12:10:26 +03:00
char c ;
* num < < = 4 ;
if ( get_user ( c , & user_buffer [ i ] ) )
return - EFAULT ;
2010-09-21 00:40:26 +04:00
value = hex_to_bin ( c ) ;
if ( value > = 0 )
* num | = value ;
2006-03-23 12:10:26 +03:00
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 ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
}
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
{
2010-10-18 16:14:44 +04:00
int i ;
2005-04-17 02:20:36 +04:00
* num = 0 ;
2006-03-21 09:16:13 +03:00
2010-10-18 16:14:44 +04:00
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 ;
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
{
2010-10-18 16:14:44 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2010-10-18 16:14:44 +04:00
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 ' ' :
goto done_str ;
break ;
default :
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
}
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 ;
2007-04-11 07:10:33 +04:00
} while ( c = = ' , ' ) ;
2006-03-23 12:10:26 +03:00
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
{
2010-07-12 14:50:23 +04:00
struct seq_file * seq = file - > private_data ;
2006-03-21 09:16:13 +03:00
struct pktgen_dev * pkt_dev = seq - > private ;
2010-10-18 16:14:44 +04:00
int i , max , len ;
2005-04-17 02:20:36 +04:00
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 ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " wrong command format \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-03-21 09:16:13 +03:00
2010-10-18 16:14:44 +04:00
max = count ;
tmp = count_trail_chars ( user_buffer , max ) ;
2006-03-21 09:16:13 +03:00
if ( tmp < 0 ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " illegal format \n " ) ;
2006-03-21 09:16:13 +03:00
return tmp ;
2005-04-17 02:20:36 +04:00
}
2010-10-18 16:14:44 +04:00
i = tmp ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
/* Read variable name */
len = strn_len ( & user_buffer [ i ] , sizeof ( name ) - 1 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) {
2010-11-06 23:11:38 +03:00
size_t copy = min_t ( size_t , count , 1023 ) ;
2010-10-28 22:31:07 +04:00
char tb [ copy + 1 ] ;
if ( copy_from_user ( tb , user_buffer , copy ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2010-10-28 22:31:07 +04:00
tb [ copy ] = 0 ;
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " 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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
2005-04-17 02:20:36 +04:00
i + = len ;
2009-08-27 17:55:16 +04:00
if ( value = = 0x7FFFFFFF )
pkt_dev - > delay = ULLONG_MAX ;
else
2009-10-03 05:39:18 +04:00
pkt_dev - > delay = ( u64 ) value ;
2009-08-27 17:55:16 +04:00
sprintf ( pg_result , " OK: delay=%llu " ,
( unsigned long long ) pkt_dev - > delay ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2010-06-10 02:49:57 +04:00
if ( ! strcmp ( name , " rate " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
if ( len < 0 )
return len ;
i + = len ;
if ( ! value )
return len ;
pkt_dev - > delay = pkt_dev - > min_pkt_size * 8 * NSEC_PER_USEC / value ;
if ( debug )
2010-06-21 16:29:14 +04:00
pr_info ( " Delay set at: %llu ns \n " , pkt_dev - > delay ) ;
2010-06-10 02:49:57 +04:00
sprintf ( pg_result , " OK: rate=%lu " , value ) ;
return count ;
}
if ( ! strcmp ( name , " ratep " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
if ( len < 0 )
return len ;
i + = len ;
if ( ! value )
return len ;
pkt_dev - > delay = NSEC_PER_SEC / value ;
if ( debug )
2010-06-21 16:29:14 +04:00
pr_info ( " Delay set at: %llu ns \n " , pkt_dev - > delay ) ;
2010-06-10 02:49:57 +04:00
sprintf ( pg_result , " OK: rate=%lu " , value ) ;
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ;
}
2010-03-19 01:44:30 +03:00
if ( ! strcmp ( name , " node " ) ) {
len = num_arg ( & user_buffer [ i ] , 10 , & value ) ;
if ( len < 0 )
return len ;
i + = len ;
if ( node_possible ( value ) ) {
pkt_dev - > node = value ;
sprintf ( pg_result , " OK: node=%d " , pkt_dev - > node ) ;
}
else
sprintf ( pg_result , " ERROR: node not possible " ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ;
2007-07-03 09:40:36 +04:00
else if ( strcmp ( f , " FLOW_SEQ " ) = = 0 )
pkt_dev - > flags | = F_FLOW_SEQ ;
2007-08-29 02:45:55 +04:00
else if ( strcmp ( f , " QUEUE_MAP_RND " ) = = 0 )
pkt_dev - > flags | = F_QUEUE_MAP_RND ;
else if ( strcmp ( f , " !QUEUE_MAP_RND " ) = = 0 )
pkt_dev - > flags & = ~ F_QUEUE_MAP_RND ;
2008-08-07 13:23:01 +04:00
else if ( strcmp ( f , " QUEUE_MAP_CPU " ) = = 0 )
pkt_dev - > flags | = F_QUEUE_MAP_CPU ;
else if ( strcmp ( f , " !QUEUE_MAP_CPU " ) = = 0 )
pkt_dev - > flags & = ~ F_QUEUE_MAP_CPU ;
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
else if ( strcmp ( f , " IPSEC " ) = = 0 )
pkt_dev - > flags | = F_IPSEC_ON ;
# endif
2006-09-28 03:32:03 +04:00
else if ( strcmp ( f , " !IPV6 " ) = = 0 )
pkt_dev - > flags & = ~ F_IPV6 ;
2010-03-19 01:44:30 +03:00
else if ( strcmp ( f , " NODE_ALLOC " ) = = 0 )
pkt_dev - > flags | = F_NODE ;
else if ( strcmp ( f , " !NODE_ALLOC " ) = = 0 )
pkt_dev - > flags & = ~ F_NODE ;
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, "
2010-03-19 01:44:30 +03:00
" MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, IPSEC, NODE_ALLOC \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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: dst_min set to: %s \n " ,
2006-03-21 09:16:13 +03:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: dst_max set to: %s \n " ,
2006-03-21 09:16:13 +03:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: dst6 set to: %s \n " , buf ) ;
2005-04-17 02:20:36 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: dst6_min set to: %s \n " , buf ) ;
2005-04-17 02:20:36 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: dst6_max set to: %s \n " , buf ) ;
2005-04-17 02:20:36 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " 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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +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 - > 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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: src_min set to: %s \n " ,
2006-03-21 09:16:13 +03:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +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 - > 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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: src_max set to: %s \n " ,
2006-03-21 09:16:13 +03:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 + + ) {
2010-07-23 07:18:10 +04:00
int value ;
value = hex_to_bin ( * v ) ;
if ( value > = 0 )
* m = * m * 16 + value ;
2005-04-17 02:20:36 +04:00
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 ;
2007-09-17 01:52:15 +04:00
unsigned char old_smac [ ETH_ALEN ] ;
2005-04-17 02:20:36 +04:00
unsigned char * m = pkt_dev - > src_mac ;
2007-09-17 01:52:15 +04:00
memcpy ( old_smac , pkt_dev - > src_mac , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
len = strn_len ( & user_buffer [ i ] , sizeof ( valstr ) - 1 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 + + ) {
2010-07-23 07:18:10 +04:00
int value ;
value = hex_to_bin ( * v ) ;
if ( value > = 0 )
* m = * m * 16 + value ;
2005-04-17 02:20:36 +04:00
if ( * v = = ' : ' ) {
m + + ;
* m = 0 ;
}
2006-03-21 09:16:13 +03:00
}
2005-04-17 02:20:36 +04:00
2007-09-17 01:52:15 +04:00
/* Set up Src MAC */
if ( compare_ether_addr ( old_smac , pkt_dev - > src_mac ) )
memcpy ( & ( pkt_dev - > hh [ 6 ] ) , pkt_dev - > src_mac , ETH_ALEN ) ;
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-03-21 09:16:13 +03:00
return len ;
2009-08-27 17:55:19 +04:00
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
2007-08-29 02:45:55 +04:00
if ( ! strcmp ( name , " queue_map_min " ) ) {
len = num_arg ( & user_buffer [ i ] , 5 , & value ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2007-08-29 02:45:55 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2007-08-29 02:45:55 +04:00
i + = len ;
pkt_dev - > queue_map_min = value ;
sprintf ( pg_result , " OK: queue_map_min=%u " , pkt_dev - > queue_map_min ) ;
return count ;
}
if ( ! strcmp ( name , " queue_map_max " ) ) {
len = num_arg ( & user_buffer [ i ] , 5 , & value ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2007-08-29 02:45:55 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2007-08-29 02:45:55 +04:00
i + = len ;
pkt_dev - > queue_map_max = value ;
sprintf ( pg_result , " OK: queue_map_max=%u " , pkt_dev - > queue_map_max ) ;
return count ;
}
2006-03-23 12:10:26 +03:00
if ( ! strcmp ( name , " mpls " ) ) {
2007-10-09 12:59:42 +04:00
unsigned n , cnt ;
2006-03-23 12:10:26 +03:00
len = get_labels ( & user_buffer [ i ] , pkt_dev ) ;
2007-10-09 12:59:42 +04:00
if ( len < 0 )
return len ;
2006-03-23 12:10:26 +03:00
i + = len ;
2007-10-09 12:59:42 +04:00
cnt = sprintf ( pg_result , " OK: mpls= " ) ;
2007-04-11 07:10:33 +04:00
for ( n = 0 ; n < pkt_dev - > nr_labels ; n + + )
2007-10-09 12:59:42 +04:00
cnt + = sprintf ( pg_result + cnt ,
" %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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: VLAN/SVLAN auto turned off \n " ) ;
2006-09-28 03:30:44 +04:00
}
return count ;
}
if ( ! strcmp ( name , " vlan_id " ) ) {
len = num_arg ( & user_buffer [ i ] , 4 , & value ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
i + = len ;
if ( value < = 4095 ) {
pkt_dev - > vlan_id = value ; /* turn on VLAN */
if ( debug )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: VLAN turned on \n " ) ;
2006-09-28 03:30:44 +04:00
if ( debug & & pkt_dev - > nr_labels )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: MPLS auto turned off \n " ) ;
2006-09-28 03:30:44 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: VLAN/SVLAN turned off \n " ) ;
2006-09-28 03:30:44 +04:00
}
return count ;
}
if ( ! strcmp ( name , " vlan_p " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
i + = len ;
if ( ( value < = 4095 ) & & ( ( pkt_dev - > vlan_id ! = 0xffff ) ) ) {
pkt_dev - > svlan_id = value ; /* turn on SVLAN */
if ( debug )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: SVLAN turned on \n " ) ;
2006-09-28 03:30:44 +04:00
if ( debug & & pkt_dev - > nr_labels )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: MPLS auto turned off \n " ) ;
2006-09-28 03:30:44 +04:00
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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " pktgen: VLAN/SVLAN turned off \n " ) ;
2006-09-28 03:30:44 +04:00
}
return count ;
}
if ( ! strcmp ( name , " svlan_p " ) ) {
len = num_arg ( & user_buffer [ i ] , 1 , & value ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:30:44 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:30:44 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:32:03 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:32:03 +04:00
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 ) ;
2009-08-27 17:55:19 +04:00
if ( len < 0 )
2006-09-28 03:32:03 +04:00
return len ;
2009-08-27 17:55:19 +04:00
2006-09-28 03:32:03 +04:00
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 ;
}
2010-11-16 22:12:28 +03:00
if ( ! strcmp ( name , " skb_priority " ) ) {
len = num_arg ( & user_buffer [ i ] , 9 , & value ) ;
if ( len < 0 )
return len ;
i + = len ;
pkt_dev - > skb_priority = value ;
sprintf ( pg_result , " OK: skb_priority=%i " ,
pkt_dev - > skb_priority ) ;
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 ;
2009-08-27 17:55:07 +04:00
const struct pktgen_dev * pkt_dev ;
2005-10-15 02:42:33 +04:00
BUG_ON ( ! t ) ;
2005-04-17 02:20:36 +04:00
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 )
2009-11-23 04:44:37 +03:00
seq_printf ( seq , " %s " , pkt_dev - > odevname ) ;
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 )
2009-11-23 04:44:37 +03:00
seq_printf ( seq , " %s " , pkt_dev - > odevname ) ;
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
{
2010-07-12 14:50:23 +04:00
struct seq_file * seq = file - > private_data ;
2006-03-21 09:16:13 +03:00
struct pktgen_thread * t = seq - > private ;
2010-10-18 16:14:44 +04:00
int i , max , len , ret ;
2005-04-17 02:20:36 +04:00
char name [ 40 ] ;
2006-03-21 09:16:13 +03:00
char * pg_result ;
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
2010-10-18 16:14:44 +04:00
max = count ;
len = count_trail_chars ( user_buffer , max ) ;
2006-03-21 09:16:13 +03:00
if ( len < 0 )
2005-10-15 02:42:33 +04:00
return len ;
2010-10-18 16:14:44 +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 )
2007-07-31 03:11:48 +04:00
printk ( KERN_DEBUG " 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 ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: No thread \n " ) ;
2005-04-17 02:20:36 +04:00
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 " ) ) {
2007-08-29 02:46:58 +04:00
sprintf ( pg_result , " OK: Note! max_before_softirq is obsoleted -- Do not use " ) ;
2006-03-21 09:16:13 +03:00
ret = count ;
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 ;
2009-11-25 01:50:53 +03:00
bool exact = ( remove = = FIND ) ;
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 ) {
2009-11-25 01:50:53 +03:00
pkt_dev = pktgen_find_dev ( t , ifname , exact ) ;
2005-04-17 02:20:36 +04:00
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
*/
2007-03-05 03:11:51 +03:00
static void 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 ;
2006-03-21 09:24:45 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
2010-06-21 16:29:14 +04:00
pr_debug ( " %s: marking %s for removal \n " , __func__ , ifname ) ;
2006-03-21 08:26:56 +03:00
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 ) ;
2010-06-21 16:29:14 +04:00
pr_debug ( " %s: waiting for %s to disappear.... \n " ,
__func__ , 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 ) {
2010-06-21 16:29:14 +04:00
pr_err ( " %s: timed out after waiting %d msec for device %s to be removed \n " ,
__func__ , msec_per_try * i , ifname ) ;
2006-03-21 08:26:56 +03:00
break ;
}
}
2006-03-21 09:24:45 +03:00
mutex_unlock ( & pktgen_thread_lock ) ;
2007-03-05 03:11:51 +03:00
}
2006-03-21 08:26:56 +03:00
2007-03-05 03:11:51 +03:00
static void pktgen_change_name ( struct net_device * dev )
{
struct pktgen_thread * t ;
list_for_each_entry ( t , & pktgen_threads , th_list ) {
struct pktgen_dev * pkt_dev ;
list_for_each_entry ( pkt_dev , & t - > if_list , list ) {
if ( pkt_dev - > odev ! = dev )
continue ;
remove_proc_entry ( pkt_dev - > entry - > name , pg_proc_dir ) ;
2009-08-29 10:34:43 +04:00
pkt_dev - > entry = proc_create_data ( dev - > name , 0600 ,
pg_proc_dir ,
& pktgen_if_fops ,
pkt_dev ) ;
2007-03-05 03:11:51 +03:00
if ( ! pkt_dev - > entry )
2010-06-21 16:29:14 +04:00
pr_err ( " can't move proc entry for '%s' \n " ,
dev - > name ) ;
2007-03-05 03:11:51 +03:00
break ;
}
}
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
{
2007-03-05 03:11:51 +03:00
struct net_device * dev = ptr ;
2005-04-17 02:20:36 +04:00
2008-07-20 09:34:43 +04:00
if ( ! net_eq ( dev_net ( dev ) , & init_net ) )
2007-09-12 15:02:17 +04:00
return NOTIFY_DONE ;
2005-04-17 02:20:36 +04:00
/* It is OK that we do not hold the group lock right now,
* as we run under the RTNL lock .
*/
switch ( event ) {
2007-03-05 03:11:51 +03:00
case NETDEV_CHANGENAME :
pktgen_change_name ( dev ) ;
2005-04-17 02:20:36 +04:00
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 ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
return NOTIFY_DONE ;
}
2009-08-27 17:55:19 +04:00
static struct net_device * pktgen_dev_get_by_name ( struct pktgen_dev * pkt_dev ,
const char * ifname )
2008-08-07 13:23:01 +04:00
{
char b [ IFNAMSIZ + 5 ] ;
2010-10-18 16:14:44 +04:00
int i ;
2008-08-07 13:23:01 +04:00
2009-08-27 17:55:19 +04:00
for ( i = 0 ; ifname [ i ] ! = ' @ ' ; i + + ) {
if ( i = = IFNAMSIZ )
2008-08-07 13:23:01 +04:00
break ;
b [ i ] = ifname [ i ] ;
}
b [ i ] = 0 ;
return dev_get_by_name ( & init_net , b ) ;
}
2005-04-17 02:20:36 +04:00
/* Associate pktgen_dev with a device. */
2007-03-05 03:11:51 +03:00
static int pktgen_setup_dev ( struct pktgen_dev * pkt_dev , const char * ifname )
2006-03-21 09:16:13 +03:00
{
2005-04-17 02:20:36 +04:00
struct net_device * odev ;
2007-03-05 03:11:51 +03:00
int err ;
2005-04-17 02:20:36 +04:00
/* 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
2008-08-07 13:23:01 +04:00
odev = pktgen_dev_get_by_name ( pkt_dev , ifname ) ;
2005-04-17 02:20:36 +04:00
if ( ! odev ) {
2010-06-21 16:29:14 +04:00
pr_err ( " no such netdevice: \" %s \" \n " , ifname ) ;
2007-03-05 03:11:51 +03:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2007-03-05 03:11:51 +03:00
2005-04-17 02:20:36 +04:00
if ( odev - > type ! = ARPHRD_ETHER ) {
2010-06-21 16:29:14 +04:00
pr_err ( " not an ethernet device: \" %s \" \n " , ifname ) ;
2007-03-05 03:11:51 +03:00
err = - EINVAL ;
} else if ( ! netif_running ( odev ) ) {
2010-06-21 16:29:14 +04:00
pr_err ( " device is down: \" %s \" \n " , ifname ) ;
2007-03-05 03:11:51 +03:00
err = - ENETDOWN ;
} else {
pkt_dev - > odev = odev ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
dev_put ( odev ) ;
2007-03-05 03:11:51 +03:00
return err ;
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 )
{
2008-08-14 02:16:00 +04:00
int ntxq ;
2006-03-21 09:16:13 +03:00
if ( ! pkt_dev - > odev ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: pkt_dev->odev == NULL in setup_inject \n " ) ;
2006-03-21 09:16:13 +03:00
sprintf ( pkt_dev - > result ,
" ERROR: pkt_dev->odev == NULL in setup_inject. \n " ) ;
return ;
}
2008-08-14 02:16:00 +04:00
/* make sure that we don't pick a non-existing transmit queue */
ntxq = pkt_dev - > odev - > real_num_tx_queues ;
2008-11-20 01:09:47 +03:00
2008-08-14 02:16:00 +04:00
if ( ntxq < = pkt_dev - > queue_map_min ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting \n " ,
pkt_dev - > queue_map_min , ( ntxq ? : 1 ) - 1 , ntxq ,
pkt_dev - > odevname ) ;
2008-08-14 02:16:00 +04:00
pkt_dev - > queue_map_min = ntxq - 1 ;
}
2008-10-28 23:21:51 +03:00
if ( pkt_dev - > queue_map_max > = ntxq ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting \n " ,
pkt_dev - > queue_map_max , ( ntxq ? : 1 ) - 1 , ntxq ,
pkt_dev - > odevname ) ;
2008-08-14 02:16:00 +04:00
pkt_dev - > queue_map_max = ntxq - 1 ;
}
2006-03-21 09:16:13 +03:00
/* 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 ( ) ;
2009-08-27 17:55:19 +04:00
idev = __in6_dev_get ( pkt_dev - > odev ) ;
if ( idev ) {
2005-04-17 02:20:36 +04:00
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 ) {
2009-11-30 03:55:45 +03:00
if ( ifp - > scope = = IFA_LINK & &
! ( ifp - > flags & IFA_F_TENTATIVE ) ) {
2006-03-21 09:16:13 +03:00
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 )
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: IPv6 link address not available \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 ;
}
2009-08-27 17:55:16 +04:00
static void spin ( struct pktgen_dev * pkt_dev , ktime_t spin_until )
{
2009-09-22 23:41:43 +04:00
ktime_t start_time , end_time ;
2009-10-01 20:29:45 +04:00
s64 remaining ;
2009-08-29 10:41:29 +04:00
struct hrtimer_sleeper t ;
hrtimer_init_on_stack ( & t . timer , CLOCK_MONOTONIC , HRTIMER_MODE_ABS ) ;
hrtimer_set_expires ( & t . timer , spin_until ) ;
2010-06-10 02:49:57 +04:00
remaining = ktime_to_ns ( hrtimer_expires_remaining ( & t . timer ) ) ;
2009-10-01 20:29:45 +04:00
if ( remaining < = 0 ) {
pkt_dev - > next_tx = ktime_add_ns ( spin_until , pkt_dev - > delay ) ;
2009-08-29 10:41:29 +04:00
return ;
2009-10-01 20:29:45 +04:00
}
2009-08-29 10:41:29 +04:00
2009-09-22 23:41:43 +04:00
start_time = ktime_now ( ) ;
2010-06-10 02:49:57 +04:00
if ( remaining < 100000 )
ndelay ( remaining ) ; /* really small just spin */
2009-08-29 10:41:29 +04:00
else {
/* see do_nanosleep */
hrtimer_init_sleeper ( & t , current ) ;
do {
set_current_state ( TASK_INTERRUPTIBLE ) ;
hrtimer_start_expires ( & t . timer , HRTIMER_MODE_ABS ) ;
if ( ! hrtimer_active ( & t . timer ) )
t . task = NULL ;
if ( likely ( t . task ) )
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2009-08-29 10:41:29 +04:00
hrtimer_cancel ( & t . timer ) ;
} while ( t . task & & pkt_dev - > running & & ! signal_pending ( current ) ) ;
__set_current_state ( TASK_RUNNING ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-22 23:41:43 +04:00
end_time = ktime_now ( ) ;
pkt_dev - > idle_acc + = ktime_to_ns ( ktime_sub ( end_time , start_time ) ) ;
2010-06-11 10:08:11 +04:00
pkt_dev - > next_tx = ktime_add_ns ( spin_until , pkt_dev - > delay ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-03 09:39:50 +04:00
static inline void set_pkt_overhead ( struct pktgen_dev * pkt_dev )
{
2007-07-03 09:41:59 +04:00
pkt_dev - > pkt_overhead = 0 ;
2007-07-03 09:39:50 +04:00
pkt_dev - > pkt_overhead + = pkt_dev - > nr_labels * sizeof ( u32 ) ;
pkt_dev - > pkt_overhead + = VLAN_TAG_SIZE ( pkt_dev ) ;
pkt_dev - > pkt_overhead + = SVLAN_TAG_SIZE ( pkt_dev ) ;
}
2009-08-27 17:55:07 +04:00
static inline int f_seen ( const struct pktgen_dev * pkt_dev , int flow )
2007-07-03 09:40:36 +04:00
{
2009-08-27 17:55:07 +04:00
return ! ! ( pkt_dev - > flows [ flow ] . flags & F_INIT ) ;
2007-07-03 09:40:36 +04:00
}
static inline int f_pick ( struct pktgen_dev * pkt_dev )
{
int flow = pkt_dev - > curfl ;
if ( pkt_dev - > flags & F_FLOW_SEQ ) {
if ( pkt_dev - > flows [ flow ] . count > = pkt_dev - > lflow ) {
/* reset time */
pkt_dev - > flows [ flow ] . count = 0 ;
2008-08-06 05:44:26 +04:00
pkt_dev - > flows [ flow ] . flags = 0 ;
2007-07-03 09:40:36 +04:00
pkt_dev - > curfl + = 1 ;
if ( pkt_dev - > curfl > = pkt_dev - > cflows )
pkt_dev - > curfl = 0 ; /*reset */
}
} else {
flow = random32 ( ) % pkt_dev - > cflows ;
2008-08-06 05:44:26 +04:00
pkt_dev - > curfl = flow ;
2007-07-03 09:40:36 +04:00
2008-08-06 05:44:26 +04:00
if ( pkt_dev - > flows [ flow ] . count > pkt_dev - > lflow ) {
2007-07-03 09:40:36 +04:00
pkt_dev - > flows [ flow ] . count = 0 ;
2008-08-06 05:44:26 +04:00
pkt_dev - > flows [ flow ] . flags = 0 ;
}
2007-07-03 09:40:36 +04:00
}
return pkt_dev - > curfl ;
}
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
/* If there was already an IPSEC SA, we keep it as is, else
* we go look for it . . .
*/
2010-02-23 03:20:22 +03:00
# define DUMMY_MARK 0
2007-07-31 05:04:09 +04:00
static void get_ipsec_sa ( struct pktgen_dev * pkt_dev , int flow )
2007-07-03 09:41:59 +04:00
{
struct xfrm_state * x = pkt_dev - > flows [ flow ] . x ;
if ( ! x ) {
/*slow path: we dont already have xfrm_state*/
2010-02-23 03:20:22 +03:00
x = xfrm_stateonly_find ( & init_net , DUMMY_MARK ,
2008-11-26 04:31:51 +03:00
( xfrm_address_t * ) & pkt_dev - > cur_daddr ,
2007-07-03 09:41:59 +04:00
( xfrm_address_t * ) & pkt_dev - > cur_saddr ,
AF_INET ,
pkt_dev - > ipsmode ,
pkt_dev - > ipsproto , 0 ) ;
if ( x ) {
pkt_dev - > flows [ flow ] . x = x ;
set_pkt_overhead ( pkt_dev ) ;
2009-08-27 17:55:19 +04:00
pkt_dev - > pkt_overhead + = x - > props . header_len ;
2007-07-03 09:41:59 +04:00
}
}
}
# endif
2008-07-17 12:56:23 +04:00
static void set_cur_queue_map ( struct pktgen_dev * pkt_dev )
{
2008-08-07 13:23:01 +04:00
if ( pkt_dev - > flags & F_QUEUE_MAP_CPU )
pkt_dev - > cur_queue_map = smp_processor_id ( ) ;
2009-10-03 00:24:59 +04:00
else if ( pkt_dev - > queue_map_min < = pkt_dev - > queue_map_max ) {
2008-07-17 12:56:23 +04:00
__u16 t ;
if ( pkt_dev - > flags & F_QUEUE_MAP_RND ) {
t = random32 ( ) %
( pkt_dev - > queue_map_max -
pkt_dev - > queue_map_min + 1 )
+ pkt_dev - > queue_map_min ;
} else {
t = pkt_dev - > cur_queue_map + 1 ;
if ( t > pkt_dev - > queue_map_max )
t = pkt_dev - > queue_map_min ;
}
pkt_dev - > cur_queue_map = t ;
}
2008-11-20 01:09:47 +03:00
pkt_dev - > cur_queue_map = pkt_dev - > cur_queue_map % pkt_dev - > odev - > real_num_tx_queues ;
2008-07-17 12:56:23 +04:00
}
2005-04-17 02:20:36 +04:00
/* 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
2007-07-03 09:40:36 +04:00
if ( pkt_dev - > cflows )
flow = f_pick ( pkt_dev ) ;
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 )
2007-03-05 03:07:28 +03:00
mc = random32 ( ) % pkt_dev - > src_mac_count ;
2006-03-21 09:16:13 +03:00
else {
mc = pkt_dev - > cur_src_mac_offset + + ;
2008-08-06 05:45:05 +04:00
if ( pkt_dev - > cur_src_mac_offset > =
2006-03-21 09:16:13 +03:00
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 )
2007-03-05 03:07:28 +03:00
mc = random32 ( ) % pkt_dev - > dst_mac_count ;
2006-03-21 09:16:13 +03:00
else {
mc = pkt_dev - > cur_dst_mac_offset + + ;
2008-08-06 05:45:05 +04:00
if ( pkt_dev - > cur_dst_mac_offset > =
2006-03-21 09:16:13 +03:00
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 ;
2007-04-11 07:10:33 +04:00
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + )
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > labels [ i ] & MPLS_STACK_BOTTOM )
pkt_dev - > labels [ i ] = MPLS_STACK_BOTTOM |
2007-03-05 03:07:28 +03:00
( ( __force __be32 ) random32 ( ) &
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 ) ) {
2007-03-05 03:07:28 +03:00
pkt_dev - > vlan_id = random32 ( ) & ( 4096 - 1 ) ;
2006-09-28 03:30:44 +04:00
}
if ( ( pkt_dev - > flags & F_SVID_RND ) & & ( pkt_dev - > svlan_id ! = 0xffff ) ) {
2007-03-05 03:07:28 +03:00
pkt_dev - > svlan_id = random32 ( ) & ( 4096 - 1 ) ;
2006-09-28 03:30:44 +04:00
}
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 )
2007-03-05 03:07:28 +03:00
pkt_dev - > cur_udp_src = random32 ( ) %
( pkt_dev - > udp_src_max - pkt_dev - > udp_src_min )
+ pkt_dev - > udp_src_min ;
2006-03-21 09:16:13 +03:00
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 ) {
2007-03-05 03:07:28 +03:00
pkt_dev - > cur_udp_dst = random32 ( ) %
( pkt_dev - > udp_dst_max - pkt_dev - > udp_dst_min )
+ pkt_dev - > udp_dst_min ;
2006-03-21 09:16:13 +03:00
} 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 ) ) {
2009-08-27 17:55:19 +04:00
imn = ntohl ( pkt_dev - > saddr_min ) ;
imx = ntohl ( pkt_dev - > saddr_max ) ;
if ( imn < imx ) {
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 )
2007-03-05 03:07:28 +03:00
t = random32 ( ) % ( imx - imn ) + imn ;
2005-04-17 02:20:36 +04:00
else {
t = ntohl ( pkt_dev - > cur_saddr ) ;
t + + ;
2009-08-27 17:55:19 +04:00
if ( t > imx )
2005-04-17 02:20:36 +04:00
t = imn ;
2009-08-27 17:55:19 +04:00
2005-04-17 02:20:36 +04:00
}
pkt_dev - > cur_saddr = htonl ( t ) ;
}
2006-03-21 09:16:13 +03:00
2007-07-03 09:40:36 +04:00
if ( pkt_dev - > cflows & & f_seen ( pkt_dev , flow ) ) {
2005-04-17 02:20:36 +04:00
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 ) {
2007-03-05 03:07:28 +03:00
t = random32 ( ) % ( imx - imn ) + imn ;
2006-11-15 07:48:11 +03:00
s = htonl ( t ) ;
2005-04-17 02:20:36 +04:00
2007-12-17 00:44:00 +03:00
while ( ipv4_is_loopback ( s ) | |
ipv4_is_multicast ( s ) | |
2008-01-21 14:18:08 +03:00
ipv4_is_lbcast ( s ) | |
2007-12-17 00:44:00 +03:00
ipv4_is_zeronet ( s ) | |
ipv4_is_local_multicast ( s ) ) {
2007-03-05 03:07:28 +03:00
t = random32 ( ) % ( imx - imn ) + imn ;
2006-11-15 07:48:11 +03:00
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 ) {
2007-07-03 09:40:36 +04:00
pkt_dev - > flows [ flow ] . flags | = F_INIT ;
2006-03-21 09:16:13 +03:00
pkt_dev - > flows [ flow ] . cur_daddr =
pkt_dev - > cur_daddr ;
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
if ( pkt_dev - > flags & F_IPSEC_ON )
get_ipsec_sa ( pkt_dev , flow ) ;
# endif
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 ] =
2007-03-05 03:07:28 +03:00
( ( ( __force __be32 ) random32 ( ) |
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 ) {
2007-03-05 03:07:28 +03:00
t = random32 ( ) %
( pkt_dev - > max_pkt_size - pkt_dev - > min_pkt_size )
+ pkt_dev - > min_pkt_size ;
2006-03-21 09:16:13 +03:00
} 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
2008-07-17 12:56:23 +04:00
set_cur_queue_map ( pkt_dev ) ;
2007-08-29 02:45:55 +04:00
2005-04-17 02:20:36 +04:00
pkt_dev - > flows [ flow ] . count + + ;
}
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
static int pktgen_output_ipsec ( struct sk_buff * skb , struct pktgen_dev * pkt_dev )
{
struct xfrm_state * x = pkt_dev - > flows [ pkt_dev - > curfl ] . x ;
int err = 0 ;
struct iphdr * iph ;
if ( ! x )
return 0 ;
/* XXX: we dont support tunnel mode for now until
* we resolve the dst issue */
if ( x - > props . mode ! = XFRM_MODE_TRANSPORT )
return 0 ;
spin_lock ( & x - > lock ) ;
iph = ip_hdr ( skb ) ;
2007-10-18 08:35:51 +04:00
err = x - > outer_mode - > output ( x , skb ) ;
2007-07-03 09:41:59 +04:00
if ( err )
goto error ;
err = x - > type - > output ( x , skb ) ;
if ( err )
goto error ;
2009-08-27 17:55:19 +04:00
x - > curlft . bytes + = skb - > len ;
2007-07-03 09:41:59 +04:00
x - > curlft . packets + + ;
error :
spin_unlock ( & x - > lock ) ;
return err ;
}
2009-08-27 17:55:08 +04:00
static void free_SAs ( struct pktgen_dev * pkt_dev )
2007-07-03 09:41:59 +04:00
{
if ( pkt_dev - > cflows ) {
/* let go of the SAs if we have them */
2010-10-18 16:14:44 +04:00
int i ;
for ( i = 0 ; i < pkt_dev - > cflows ; i + + ) {
2007-07-03 09:41:59 +04:00
struct xfrm_state * x = pkt_dev - > flows [ i ] . x ;
if ( x ) {
xfrm_state_put ( x ) ;
pkt_dev - > flows [ i ] . x = NULL ;
}
}
}
}
2009-08-27 17:55:08 +04:00
static int process_ipsec ( struct pktgen_dev * pkt_dev ,
2007-07-03 09:41:59 +04:00
struct sk_buff * skb , __be16 protocol )
{
if ( pkt_dev - > flags & F_IPSEC_ON ) {
struct xfrm_state * x = pkt_dev - > flows [ pkt_dev - > curfl ] . x ;
int nhead = 0 ;
if ( x ) {
int ret ;
__u8 * eth ;
nhead = x - > props . header_len - skb_headroom ( skb ) ;
2009-08-27 17:55:19 +04:00
if ( nhead > 0 ) {
2007-07-03 09:41:59 +04:00
ret = pskb_expand_head ( skb , nhead , 0 , GFP_ATOMIC ) ;
if ( ret < 0 ) {
2010-06-21 16:29:14 +04:00
pr_err ( " Error expanding ipsec packet %d \n " ,
ret ) ;
2008-10-14 05:43:59 +04:00
goto err ;
2007-07-03 09:41:59 +04:00
}
}
/* ipsec is not expecting ll header */
skb_pull ( skb , ETH_HLEN ) ;
ret = pktgen_output_ipsec ( skb , pkt_dev ) ;
if ( ret ) {
2010-06-21 16:29:14 +04:00
pr_err ( " Error creating ipsec packet %d \n " , ret ) ;
2008-10-14 05:43:59 +04:00
goto err ;
2007-07-03 09:41:59 +04:00
}
/* restore ll */
eth = ( __u8 * ) skb_push ( skb , ETH_HLEN ) ;
memcpy ( eth , pkt_dev - > hh , 12 ) ;
2009-08-27 17:55:19 +04:00
* ( u16 * ) & eth [ 12 ] = protocol ;
2007-07-03 09:41:59 +04:00
}
}
return 1 ;
2008-10-14 05:43:59 +04:00
err :
kfree_skb ( skb ) ;
return 0 ;
2007-07-03 09:41:59 +04:00
}
# endif
2006-03-23 12:10:26 +03:00
static void mpls_push ( __be32 * mpls , struct pktgen_dev * pkt_dev )
{
unsigned i ;
2009-08-27 17:55:19 +04:00
for ( i = 0 ; i < pkt_dev - > nr_labels ; i + + )
2006-03-23 12:10:26 +03:00
* mpls + + = pkt_dev - > labels [ i ] & ~ MPLS_STACK_BOTTOM ;
2009-08-27 17:55:19 +04:00
2006-03-23 12:10:26 +03:00
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 ;
2007-03-05 03:08:08 +03:00
__be16 protocol = htons ( ETH_P_IP ) ;
2006-03-23 12:10:26 +03:00
__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 */
2008-07-17 12:56:23 +04:00
u16 queue_map ;
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > nr_labels )
2007-03-05 03:08:08 +03:00
protocol = 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 )
2007-03-05 03:08:08 +03:00
protocol = htons ( ETH_P_8021Q ) ;
2006-09-28 03:30:44 +04:00
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 ) ;
2010-11-08 02:19:43 +03:00
queue_map = pkt_dev - > cur_queue_map ;
2005-06-27 02:27:10 +04:00
2006-01-19 01:19:10 +03:00
datalen = ( odev - > hard_header_len + 16 ) & ~ 0xf ;
2010-03-19 01:44:30 +03:00
if ( pkt_dev - > flags & F_NODE ) {
int node ;
if ( pkt_dev - > node > = 0 )
node = pkt_dev - > node ;
else
node = numa_node_id ( ) ;
skb = __alloc_skb ( NET_SKB_PAD + pkt_dev - > cur_pkt_size + 64
+ datalen + pkt_dev - > pkt_overhead , GFP_NOWAIT , 0 , node ) ;
if ( likely ( skb ) ) {
skb_reserve ( skb , NET_SKB_PAD ) ;
skb - > dev = odev ;
}
}
else
skb = __netdev_alloc_skb ( odev ,
pkt_dev - > cur_pkt_size + 64
+ datalen + pkt_dev - > pkt_overhead , GFP_NOWAIT ) ;
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 ) {
2007-04-11 07:10:33 +04:00
if ( pkt_dev - > svlan_id ! = 0xffff ) {
2006-09-28 03:30:44 +04:00
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 ) ) ;
2007-03-05 03:08:08 +03:00
* svlan_encapsulated_proto = htons ( ETH_P_8021Q ) ;
2006-09-28 03:30:44 +04:00
}
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 ) ) ;
2007-03-05 03:08:08 +03:00
* vlan_encapsulated_proto = htons ( ETH_P_IP ) ;
2006-09-28 03:30:44 +04:00
}
2007-04-20 07:29:13 +04:00
skb - > network_header = skb - > tail ;
2007-04-11 08:21:55 +04:00
skb - > transport_header = skb - > network_header + sizeof ( struct iphdr ) ;
2007-03-16 03:42:27 +03:00
skb_put ( skb , sizeof ( struct iphdr ) + sizeof ( struct udphdr ) ) ;
2008-07-17 12:56:23 +04:00
skb_set_queue_mapping ( skb , queue_map ) ;
2010-11-16 22:12:28 +03:00
skb - > priority = pkt_dev - > skb_priority ;
2007-03-16 03:42:27 +03:00
iph = ip_hdr ( skb ) ;
udph = udp_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
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 -
2007-07-03 09:39:50 +04:00
pkt_dev - > pkt_overhead ;
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 ;
2009-10-24 17:55:20 +04:00
iph - > id = htons ( pkt_dev - > ip_id ) ;
pkt_dev - > ip_id + + ;
2005-04-17 02:20:36 +04:00
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 ;
2007-04-11 08:21:55 +04:00
skb - > mac_header = ( skb - > network_header - ETH_HLEN -
2007-07-03 09:39:50 +04:00
pkt_dev - > pkt_overhead ) ;
2005-04-17 02:20:36 +04:00
skb - > dev = odev ;
skb - > pkt_type = PACKET_HOST ;
2009-10-24 17:55:20 +04:00
if ( pkt_dev - > nfrags < = 0 ) {
2006-03-21 09:16:13 +03:00
pgh = ( struct pktgen_hdr * ) skb_put ( skb , datalen ) ;
2009-10-24 17:55:20 +04:00
memset ( pgh + 1 , 0 , datalen - sizeof ( struct pktgen_hdr ) ) ;
} else {
2005-04-17 02:20:36 +04:00
int frags = pkt_dev - > nfrags ;
2009-10-24 17:55:20 +04:00
int i , len ;
2005-04-17 02:20:36 +04:00
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 ) {
2009-10-24 17:55:20 +04:00
len = datalen - frags * PAGE_SIZE ;
memset ( skb_put ( skb , len ) , 0 , len ) ;
2006-03-21 09:16:13 +03:00
datalen = frags * PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
}
i = 0 ;
while ( datalen > 0 ) {
2009-10-24 17:55:20 +04:00
struct page * page = alloc_pages ( GFP_KERNEL | __GFP_ZERO , 0 ) ;
2005-04-17 02:20:36 +04:00
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 ;
}
}
2009-08-27 17:55:19 +04:00
/* Stamp the time, and sequence number,
* convert them to network byte order
*/
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 ) ;
}
2005-04-17 02:20:36 +04:00
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
if ( ! process_ipsec ( pkt_dev , skb , protocol ) )
return NULL ;
# endif
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 ;
2007-10-09 12:59:42 +04:00
char * pos ;
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 + + ;
}
2007-10-09 12:59:42 +04:00
u = simple_strtoul ( s , & pos , 16 ) ;
i = pos - s ;
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 ;
2007-10-09 12:59:42 +04:00
u = simple_strtol ( s , & pos , 16 ) ;
i = pos - 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 ;
2007-03-05 03:08:08 +03:00
__be16 protocol = htons ( ETH_P_IPV6 ) ;
2006-03-23 12:10:26 +03:00
__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 */
2008-07-17 12:56:23 +04:00
u16 queue_map ;
2006-03-23 12:10:26 +03:00
if ( pkt_dev - > nr_labels )
2007-03-05 03:08:08 +03:00
protocol = 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 )
2007-03-05 03:08:08 +03:00
protocol = htons ( ETH_P_8021Q ) ;
2006-09-28 03:30:44 +04:00
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 ) ;
2010-11-08 02:19:43 +03:00
queue_map = pkt_dev - > cur_queue_map ;
2005-06-27 02:27:10 +04:00
2009-08-27 17:55:13 +04:00
skb = __netdev_alloc_skb ( odev ,
pkt_dev - > cur_pkt_size + 64
+ 16 + pkt_dev - > pkt_overhead , GFP_NOWAIT ) ;
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 ) {
2007-04-11 07:10:33 +04:00
if ( pkt_dev - > svlan_id ! = 0xffff ) {
2006-09-28 03:30:44 +04:00
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 ) ) ;
2007-03-05 03:08:08 +03:00
* svlan_encapsulated_proto = htons ( ETH_P_8021Q ) ;
2006-09-28 03:30:44 +04:00
}
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 ) ) ;
2007-03-05 03:08:08 +03:00
* vlan_encapsulated_proto = htons ( ETH_P_IPV6 ) ;
2006-09-28 03:30:44 +04:00
}
2007-04-20 07:29:13 +04:00
skb - > network_header = skb - > tail ;
2007-04-11 08:21:55 +04:00
skb - > transport_header = skb - > network_header + sizeof ( struct ipv6hdr ) ;
2007-03-16 03:42:27 +03:00
skb_put ( skb , sizeof ( struct ipv6hdr ) + sizeof ( struct udphdr ) ) ;
2008-07-17 12:56:23 +04:00
skb_set_queue_mapping ( skb , queue_map ) ;
2010-11-16 22:12:28 +03:00
skb - > priority = pkt_dev - > skb_priority ;
2007-03-16 03:42:27 +03:00
iph = ipv6_hdr ( skb ) ;
udph = udp_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
memcpy ( eth , pkt_dev - > hh , 12 ) ;
2009-08-27 17:55:19 +04: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 ) -
2007-07-03 09:39:50 +04:00
pkt_dev - > pkt_overhead ;
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 ( ) )
2010-06-21 16:29:14 +04:00
pr_info ( " 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
2007-03-05 03:08:08 +03:00
* ( __be32 * ) iph = 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 ) ;
2007-04-11 08:21:55 +04:00
skb - > mac_header = ( skb - > network_header - ETH_HLEN -
2007-07-03 09:39:50 +04:00
pkt_dev - > pkt_overhead ) ;
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-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 ;
}
}
2009-08-27 17:55:19 +04:00
/* Stamp the time, and sequence number,
* convert them to network byte order
* 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 ;
}
2009-08-27 17:55:08 +04:00
static 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 ;
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
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 ) {
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 ;
2009-08-27 17:55:16 +04:00
pkt_dev - > started_at =
pkt_dev - > next_tx = ktime_now ( ) ;
2007-07-03 09:39:50 +04:00
set_pkt_overhead ( pkt_dev ) ;
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
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
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
}
2009-08-27 17:55:07 +04:00
static int thread_is_running ( const struct pktgen_thread * t )
2005-04-17 02:20:36 +04:00
{
2009-08-27 17:55:07 +04:00
const struct pktgen_dev * pkt_dev ;
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 )
2009-08-27 17:55:07 +04:00
if ( pkt_dev - > running )
return 1 ;
return 0 ;
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
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
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_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
2009-08-27 17:55:19 +04:00
/* Propagate thread->control */
schedule_timeout_interruptible ( msecs_to_jiffies ( 125 ) ) ;
2006-03-21 09:16:13 +03:00
2005-04-17 02:20:36 +04:00
pktgen_wait_all_threads_run ( ) ;
}
2008-11-11 03:48:03 +03:00
static void pktgen_reset_all_threads ( void )
{
struct pktgen_thread * t ;
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
2008-11-11 03:48:03 +03:00
mutex_lock ( & pktgen_thread_lock ) ;
list_for_each_entry ( t , & pktgen_threads , th_list )
t - > control | = ( T_REMDEVALL ) ;
mutex_unlock ( & pktgen_thread_lock ) ;
2009-08-27 17:55:19 +04:00
/* Propagate thread->control */
schedule_timeout_interruptible ( msecs_to_jiffies ( 125 ) ) ;
2008-11-11 03:48:03 +03:00
pktgen_wait_all_threads_run ( ) ;
}
2005-04-17 02:20:36 +04:00
static void show_results ( struct pktgen_dev * pkt_dev , int nr_frags )
{
2009-08-27 17:55:16 +04:00
__u64 bps , mbps , pps ;
2006-03-21 09:16:13 +03:00
char * p = pkt_dev - > result ;
2009-08-27 17:55:16 +04:00
ktime_t elapsed = ktime_sub ( pkt_dev - > stopped_at ,
pkt_dev - > started_at ) ;
ktime_t idle = ns_to_ktime ( pkt_dev - > idle_acc ) ;
p + = sprintf ( p , " OK: %llu(c%llu+d%llu) nsec, %llu (%dbyte,%dfrags) \n " ,
( unsigned long long ) ktime_to_us ( elapsed ) ,
( unsigned long long ) ktime_to_us ( ktime_sub ( elapsed , idle ) ) ,
( unsigned long long ) ktime_to_us ( idle ) ,
2006-03-21 09:16:13 +03:00
( unsigned long long ) pkt_dev - > sofar ,
pkt_dev - > cur_pkt_size , nr_frags ) ;
2009-08-27 17:55:16 +04:00
pps = div64_u64 ( pkt_dev - > sofar * NSEC_PER_SEC ,
ktime_to_ns ( elapsed ) ) ;
2006-03-21 09:16:13 +03:00
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 ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " interface: %s is already stopped \n " ,
pkt_dev - > odevname ) ;
2006-03-21 09:16:13 +03:00
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2009-08-27 17:55:10 +04:00
kfree_skb ( pkt_dev - > skb ) ;
pkt_dev - > skb = NULL ;
2009-08-27 17:55:16 +04:00
pkt_dev - > stopped_at = ktime_now ( ) ;
2006-03-21 09:16:13 +03:00
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 ;
2009-08-27 17:55:16 +04:00
else if ( ktime_lt ( pkt_dev - > next_tx , best - > next_tx ) )
2006-03-21 09:18:16 +03:00
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
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
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 ) ;
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
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
2006-03-21 08:26:56 +03:00
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
2009-02-25 03:31:54 +03:00
kfree_skb ( cur - > skb ) ;
2006-03-21 08:26:56 +03:00
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
2010-06-21 16:29:14 +04:00
func_enter ( ) ;
2006-03-21 09:16:13 +03:00
/* Remove all devices, free mem */
2006-03-21 08:26:56 +03: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_safe ( q , n , & t - > if_list ) {
cur = list_entry ( q , struct pktgen_dev , list ) ;
2006-03-21 08:26:56 +03:00
2009-02-25 03:31:54 +03:00
kfree_skb ( cur - > skb ) ;
2006-03-21 08:26:56 +03:00
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
}
2009-09-22 23:41:43 +04:00
static void pktgen_resched ( struct pktgen_dev * pkt_dev )
2009-08-27 17:55:11 +04:00
{
2009-08-27 17:55:16 +04:00
ktime_t idle_start = ktime_now ( ) ;
2009-09-22 23:41:43 +04:00
schedule ( ) ;
pkt_dev - > idle_acc + = ktime_to_ns ( ktime_sub ( ktime_now ( ) , idle_start ) ) ;
}
2009-08-27 17:55:11 +04:00
2009-09-22 23:41:43 +04:00
static void pktgen_wait_for_skb ( struct pktgen_dev * pkt_dev )
{
ktime_t idle_start = ktime_now ( ) ;
2009-08-27 17:55:11 +04:00
2009-09-22 23:41:43 +04:00
while ( atomic_read ( & ( pkt_dev - > skb - > users ) ) ! = 1 ) {
if ( signal_pending ( current ) )
break ;
if ( need_resched ( ) )
pktgen_resched ( pkt_dev ) ;
else
cpu_relax ( ) ;
}
2009-08-27 17:55:16 +04:00
pkt_dev - > idle_acc + = ktime_to_ns ( ktime_sub ( ktime_now ( ) , idle_start ) ) ;
2009-08-27 17:55:11 +04:00
}
2009-08-27 17:55:08 +04:00
static void pktgen_xmit ( struct pktgen_dev * pkt_dev )
2005-04-17 02:20:36 +04:00
{
2008-11-21 07:14:53 +03:00
struct net_device * odev = pkt_dev - > odev ;
2009-08-31 23:50:41 +04:00
netdev_tx_t ( * xmit ) ( struct sk_buff * , struct net_device * )
2008-11-21 07:14:53 +03:00
= odev - > netdev_ops - > ndo_start_xmit ;
2008-07-17 12:56:23 +04:00
struct netdev_queue * txq ;
u16 queue_map ;
2005-04-17 02:20:36 +04:00
int ret ;
2009-09-22 23:41:43 +04:00
/* If device is offline, then don't send */
if ( unlikely ( ! netif_running ( odev ) | | ! netif_carrier_ok ( odev ) ) ) {
pktgen_stop_device ( pkt_dev ) ;
return ;
2008-07-17 12:56:23 +04:00
}
2009-09-22 23:41:43 +04:00
/* This is max DELAY, this has special meaning of
* " never transmit "
*/
if ( unlikely ( pkt_dev - > delay = = ULLONG_MAX ) ) {
pkt_dev - > next_tx = ktime_add_ns ( ktime_now ( ) , ULONG_MAX ) ;
2009-08-27 17:55:11 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:16:13 +03:00
2009-09-22 23:41:43 +04:00
/* If no skb or clone count exhausted then get new one */
2009-08-27 17:55:12 +04:00
if ( ! pkt_dev - > skb | | ( pkt_dev - > last_ok & &
+ + pkt_dev - > clone_count > = pkt_dev - > clone_skb ) ) {
/* build a new pkt */
kfree_skb ( pkt_dev - > skb ) ;
pkt_dev - > skb = fill_packet ( odev , pkt_dev ) ;
if ( pkt_dev - > skb = = NULL ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: couldn't allocate skb in fill_packet \n " ) ;
2009-08-27 17:55:12 +04:00
schedule ( ) ;
pkt_dev - > clone_count - - ; /* back out increment, OOM */
return ;
2005-04-17 02:20:36 +04:00
}
2009-11-06 08:04:32 +03:00
pkt_dev - > last_pkt_size = pkt_dev - > skb - > len ;
2009-08-27 17:55:12 +04:00
pkt_dev - > allocated_skbs + + ;
pkt_dev - > clone_count = 0 ; /* reset counter */
2005-04-17 02:20:36 +04:00
}
2006-03-21 08:26:56 +03:00
2009-09-22 23:41:43 +04:00
if ( pkt_dev - > delay & & pkt_dev - > last_ok )
spin ( pkt_dev , pkt_dev - > next_tx ) ;
2008-07-17 12:56:23 +04:00
queue_map = skb_get_queue_mapping ( pkt_dev - > skb ) ;
txq = netdev_get_tx_queue ( odev , queue_map ) ;
__netif_tx_lock_bh ( txq ) ;
2009-08-27 17:55:14 +04:00
2009-09-30 17:03:33 +04:00
if ( unlikely ( netif_tx_queue_stopped ( txq ) | | netif_tx_queue_frozen ( txq ) ) ) {
2009-09-22 23:41:43 +04:00
ret = NETDEV_TX_BUSY ;
2009-09-30 17:03:33 +04:00
pkt_dev - > last_ok = 0 ;
goto unlock ;
}
atomic_inc ( & ( pkt_dev - > skb - > users ) ) ;
ret = ( * xmit ) ( pkt_dev - > skb , odev ) ;
2009-09-22 23:41:43 +04:00
switch ( ret ) {
case NETDEV_TX_OK :
txq_trans_update ( txq ) ;
pkt_dev - > last_ok = 1 ;
pkt_dev - > sofar + + ;
pkt_dev - > seq_num + + ;
2009-11-06 08:04:32 +03:00
pkt_dev - > tx_bytes + = pkt_dev - > last_pkt_size ;
2009-09-22 23:41:43 +04:00
break ;
2009-12-24 09:02:57 +03:00
case NET_XMIT_DROP :
case NET_XMIT_CN :
case NET_XMIT_POLICED :
/* skb has been consumed */
pkt_dev - > errors + + ;
break ;
2009-09-22 23:41:43 +04:00
default : /* Drivers are not supposed to return other values! */
if ( net_ratelimit ( ) )
pr_info ( " pktgen: %s xmit error: %d \n " ,
2009-11-23 04:44:37 +03:00
pkt_dev - > odevname , ret ) ;
2009-09-22 23:41:43 +04:00
pkt_dev - > errors + + ;
/* fallthru */
case NETDEV_TX_LOCKED :
case NETDEV_TX_BUSY :
/* Retry it next time */
atomic_dec ( & ( pkt_dev - > skb - > users ) ) ;
pkt_dev - > last_ok = 0 ;
2006-03-21 09:16:13 +03:00
}
2009-09-30 17:03:33 +04:00
unlock :
2008-07-17 12:56:23 +04:00
__netif_tx_unlock_bh ( txq ) ;
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 ) ) {
2009-09-22 23:41:43 +04:00
pktgen_wait_for_skb ( pkt_dev ) ;
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 09:16:13 +03:00
}
}
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 ;
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 ) ;
2008-05-21 02:12:44 +04:00
complete ( & t - > start_done ) ;
2005-04-17 02:20:36 +04:00
2010-06-21 16:29:14 +04:00
pr_debug ( " starting pktgen/%d: pid=%d \n " , cpu , task_pid_nr ( current ) ) ;
2005-04-17 02:20:36 +04:00
2007-01-02 07:51:53 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-03-21 09:16:13 +03:00
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
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
2009-09-22 23:41:43 +04:00
if ( unlikely ( ! pkt_dev & & t - > control = = 0 ) ) {
2010-11-21 21:26:44 +03:00
if ( pktgen_exiting )
break ;
2009-09-22 23:41:43 +04:00
wait_event_interruptible_timeout ( t - > queue ,
t - > control ! = 0 ,
HZ / 10 ) ;
2010-02-05 01:00:41 +03:00
try_to_freeze ( ) ;
2009-09-22 23:41:43 +04:00
continue ;
2007-01-02 07:51:53 +03:00
}
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
2009-09-22 23:41:43 +04:00
if ( likely ( pkt_dev ) ) {
2005-04-17 02:20:36 +04:00
pktgen_xmit ( pkt_dev ) ;
2009-09-22 23:41:43 +04:00
if ( need_resched ( ) )
pktgen_resched ( pkt_dev ) ;
else
cpu_relax ( ) ;
}
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
2010-06-21 16:29:14 +04:00
pr_debug ( " %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
2010-06-21 16:29:14 +04:00
pr_debug ( " %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
2010-06-21 16:29:14 +04:00
pr_debug ( " %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
2010-11-21 21:26:44 +03:00
/* Wait for kthread_stop */
while ( ! kthread_should_stop ( ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule ( ) ;
}
__set_current_state ( TASK_RUNNING ) ;
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 ,
2009-11-25 01:50:53 +03:00
const char * ifname , bool exact )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:18:16 +03:00
struct pktgen_dev * p , * pkt_dev = NULL ;
2009-11-25 01:50:53 +03:00
size_t len = strlen ( ifname ) ;
2006-03-21 09:16:13 +03:00
2009-11-25 01:50:53 +03:00
if_lock ( t ) ;
2006-03-21 09:18:16 +03:00
list_for_each_entry ( p , & t - > if_list , list )
2009-11-25 01:50:53 +03:00
if ( strncmp ( p - > odevname , ifname , len ) = = 0 ) {
if ( p - > odevname [ len ] ) {
if ( exact | | p - > odevname [ len ] ! = ' @ ' )
continue ;
}
2006-03-21 09:18:16 +03:00
pkt_dev = p ;
2006-03-21 09:16:13 +03:00
break ;
}
if_unlock ( t ) ;
2010-06-21 16:29:14 +04:00
pr_debug ( " find_dev(%s) returning %p \n " , ifname , pkt_dev ) ;
2006-03-21 09:16:13 +03:00
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 ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: already assigned to a thread \n " ) ;
2006-03-21 09:16:13 +03:00
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 ;
2007-03-05 03:11:51 +03:00
int err ;
2009-11-29 11:44:33 +03:00
int node = cpu_to_node ( t - > cpu ) ;
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 ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: interface already used \n " ) ;
2006-03-21 09:16:13 +03:00
return - EBUSY ;
}
2005-10-15 02:42:33 +04:00
2009-11-29 11:44:33 +03:00
pkt_dev = kzalloc_node ( sizeof ( struct pktgen_dev ) , GFP_KERNEL , node ) ;
2005-10-15 02:42:33 +04:00
if ( ! pkt_dev )
return - ENOMEM ;
2009-11-23 04:44:37 +03:00
strcpy ( pkt_dev - > odevname , ifname ) ;
2009-11-29 11:44:33 +03:00
pkt_dev - > flows = vmalloc_node ( MAX_CFLOWS * sizeof ( struct flow_state ) ,
node ) ;
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 ;
2009-08-27 17:55:16 +04:00
pkt_dev - > delay = pg_delay_d ;
2005-10-15 02:42:33 +04:00
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 ;
2010-03-19 01:44:30 +03:00
pkt_dev - > node = - 1 ;
2006-09-28 03:30:44 +04:00
2007-03-05 03:11:51 +03:00
err = pktgen_setup_dev ( pkt_dev , ifname ) ;
if ( err )
goto out1 ;
2005-10-15 02:42:33 +04:00
2008-05-02 13:46:22 +04:00
pkt_dev - > entry = proc_create_data ( ifname , 0600 , pg_proc_dir ,
& pktgen_if_fops , pkt_dev ) ;
2007-03-05 03:11:51 +03:00
if ( ! pkt_dev - > entry ) {
2010-06-21 16:29:14 +04:00
pr_err ( " cannot create %s/%s procfs entry \n " ,
2005-10-15 02:42:33 +04:00
PG_PROC_DIR , ifname ) ;
2007-03-05 03:11:51 +03:00
err = - EINVAL ;
goto out2 ;
2005-10-15 02:42:33 +04:00
}
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
pkt_dev - > ipsmode = XFRM_MODE_TRANSPORT ;
pkt_dev - > ipsproto = IPPROTO_ESP ;
# endif
2005-10-15 02:42:33 +04:00
return add_dev_to_thread ( t , pkt_dev ) ;
2007-03-05 03:11:51 +03:00
out2 :
dev_put ( pkt_dev - > odev ) ;
out1 :
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
free_SAs ( pkt_dev ) ;
# endif
2009-06-08 11:40:35 +04:00
vfree ( pkt_dev - > flows ) ;
2007-03-05 03:11:51 +03:00
kfree ( pkt_dev ) ;
return err ;
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
2009-11-29 11:44:33 +03:00
t = kzalloc_node ( sizeof ( struct pktgen_thread ) , GFP_KERNEL ,
cpu_to_node ( cpu ) ) ;
2006-03-21 09:16:13 +03:00
if ( ! t ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: out of memory, can't create new thread \n " ) ;
2006-03-21 09:16:13 +03:00
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 ) ;
2008-05-21 02:12:44 +04:00
init_completion ( & t - > start_done ) ;
2007-01-02 07:51:53 +03:00
p = kthread_create ( pktgen_thread_worker , t , " kpktgend_%d " , cpu ) ;
if ( IS_ERR ( p ) ) {
2010-06-21 16:29:14 +04:00
pr_err ( " kernel_thread() failed for cpu %d \n " , t - > cpu ) ;
2007-01-02 07:51:53 +03:00
list_del ( & t - > th_list ) ;
kfree ( t ) ;
return PTR_ERR ( p ) ;
}
kthread_bind ( p , cpu ) ;
t - > tsk = p ;
2008-05-02 13:46:22 +04:00
pe = proc_create_data ( t - > tsk - > comm , 0600 , pg_proc_dir ,
& pktgen_thread_fops , t ) ;
2006-03-21 09:16:13 +03:00
if ( ! pe ) {
2010-06-21 16:29:14 +04:00
pr_err ( " 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
2007-01-02 07:51:53 +03:00
wake_up_process ( p ) ;
2008-05-21 02:12:44 +04:00
wait_for_completion ( & t - > start_done ) ;
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
{
2010-06-21 16:29:14 +04:00
pr_debug ( " remove_device pkt_dev=%p \n " , pkt_dev ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:16:13 +03:00
if ( pkt_dev - > running ) {
2010-06-21 16:29:14 +04:00
pr_warning ( " WARNING: trying to remove a running interface, stopping it now \n " ) ;
2006-03-21 09:16:13 +03:00
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 ) ;
2007-03-05 03:11:51 +03:00
if ( pkt_dev - > entry )
remove_proc_entry ( pkt_dev - > entry - > name , pg_proc_dir ) ;
2005-04-17 02:20:36 +04:00
2007-07-03 09:41:59 +04:00
# ifdef CONFIG_XFRM
free_SAs ( pkt_dev ) ;
# endif
2009-06-08 11:40:35 +04:00
vfree ( pkt_dev - > flows ) ;
2005-04-17 02:20:36 +04:00
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
2010-06-21 16:29:14 +04:00
pr_info ( " %s " , version ) ;
2005-04-17 02:20:36 +04:00
2007-09-12 14:01:34 +04:00
pg_proc_dir = proc_mkdir ( PG_PROC_DIR , init_net . proc_net ) ;
2005-10-15 02:42:33 +04:00
if ( ! pg_proc_dir )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-02-29 01:11:49 +03:00
pe = proc_create ( PGCTRL , 0600 , pg_proc_dir , & pktgen_fops ) ;
2006-03-21 09:16:13 +03:00
if ( pe = = NULL ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: cannot create %s procfs entry \n " , PGCTRL ) ;
2007-09-12 14:01:34 +04:00
proc_net_remove ( & init_net , PG_PROC_DIR ) ;
2006-03-21 09:16:13 +03:00
return - EINVAL ;
}
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 )
2010-06-21 16:29:14 +04:00
pr_warning ( " 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 ) ) {
2010-06-21 16:29:14 +04:00
pr_err ( " ERROR: Initialization failed for all threads \n " ) ;
2006-03-21 09:17:55 +03:00
unregister_netdevice_notifier ( & pktgen_notifier_block ) ;
remove_proc_entry ( PGCTRL , pg_proc_dir ) ;
2007-09-12 14:01:34 +04:00
proc_net_remove ( & init_net , PG_PROC_DIR ) ;
2006-03-21 09:17:55 +03:00
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
2006-03-21 09:16:13 +03:00
/* Stop all interfaces & threads */
2010-11-21 21:26:44 +03:00
pktgen_exiting = true ;
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 ) ;
2007-09-12 14:01:34 +04:00
proc_net_remove ( & init_net , PG_PROC_DIR ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( pg_init ) ;
module_exit ( pg_cleanup ) ;
2009-08-27 17:55:20 +04:00
MODULE_AUTHOR ( " Robert Olsson <robert.olsson@its.uu.se> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Packet Generator tool " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-08-27 17:55:20 +04:00
MODULE_VERSION ( VERSION ) ;
2005-04-17 02:20:36 +04:00
module_param ( pg_count_d , int , 0 ) ;
2009-08-27 17:55:20 +04:00
MODULE_PARM_DESC ( pg_count_d , " Default number of packets to inject " ) ;
2005-04-17 02:20:36 +04:00
module_param ( pg_delay_d , int , 0 ) ;
2009-08-27 17:55:20 +04:00
MODULE_PARM_DESC ( pg_delay_d , " Default delay between packets (nanoseconds) " ) ;
2005-04-17 02:20:36 +04:00
module_param ( pg_clone_skb_d , int , 0 ) ;
2009-08-27 17:55:20 +04:00
MODULE_PARM_DESC ( pg_clone_skb_d , " Default number of copies of the same packet " ) ;
2005-04-17 02:20:36 +04:00
module_param ( debug , int , 0 ) ;
2009-08-27 17:55:20 +04:00
MODULE_PARM_DESC ( debug , " Enable debugging of pktgen module " ) ;