xref: /openbmc/linux/net/core/pktgen.c (revision ce5d0b47f13f83dfb9fbb8ac91adad7120747aaf)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Authors:
31da177e4SLinus Torvalds  * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
41da177e4SLinus Torvalds  *                             Uppsala University and
51da177e4SLinus Torvalds  *                             Swedish University of Agricultural Sciences
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Alexey Kuznetsov  <kuznet@ms2.inr.ac.ru>
81da177e4SLinus Torvalds  * Ben Greear <greearb@candelatech.com>
91da177e4SLinus Torvalds  * Jens L��s <jens.laas@data.slu.se>
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
121da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
131da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
141da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  * A tool for loading the network with preconfigurated packets.
181da177e4SLinus Torvalds  * The tool is implemented as a linux module.  Parameters are output
191da177e4SLinus Torvalds  * device, delay (to hard_xmit), number of packets, and whether
201da177e4SLinus Torvalds  * to use multiple SKBs or just the same one.
211da177e4SLinus Torvalds  * pktgen uses the installed interface's output routine.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  * Additional hacking by:
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  * Jens.Laas@data.slu.se
261da177e4SLinus Torvalds  * Improved by ANK. 010120.
271da177e4SLinus Torvalds  * Improved by ANK even more. 010212.
281da177e4SLinus Torvalds  * MAC address typo fixed. 010417 --ro
291da177e4SLinus Torvalds  * Integrated.  020301 --DaveM
301da177e4SLinus Torvalds  * Added multiskb option 020301 --DaveM
311da177e4SLinus Torvalds  * Scaling of results. 020417--sigurdur@linpro.no
321da177e4SLinus Torvalds  * Significant re-work of the module:
331da177e4SLinus Torvalds  *   *  Convert to threaded model to more efficiently be able to transmit
341da177e4SLinus Torvalds  *       and receive on multiple interfaces at once.
351da177e4SLinus Torvalds  *   *  Converted many counters to __u64 to allow longer runs.
361da177e4SLinus Torvalds  *   *  Allow configuration of ranges, like min/max IP address, MACs,
371da177e4SLinus Torvalds  *       and UDP-ports, for both source and destination, and can
381da177e4SLinus Torvalds  *       set to use a random distribution or sequentially walk the range.
391da177e4SLinus Torvalds  *   *  Can now change most values after starting.
401da177e4SLinus Torvalds  *   *  Place 12-byte packet in UDP payload with magic number,
411da177e4SLinus Torvalds  *       sequence number, and timestamp.
421da177e4SLinus Torvalds  *   *  Add receiver code that detects dropped pkts, re-ordered pkts, and
431da177e4SLinus Torvalds  *       latencies (with micro-second) precision.
441da177e4SLinus Torvalds  *   *  Add IOCTL interface to easily get counters & configuration.
451da177e4SLinus Torvalds  *   --Ben Greear <greearb@candelatech.com>
461da177e4SLinus Torvalds  *
471da177e4SLinus Torvalds  * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
481da177e4SLinus Torvalds  * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
491da177e4SLinus Torvalds  * as a "fastpath" with a configurable number of clones after alloc's.
501da177e4SLinus Torvalds  * clone_skb=0 means all packets are allocated this also means ranges time
511da177e4SLinus Torvalds  * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
521da177e4SLinus Torvalds  * clones.
531da177e4SLinus Torvalds  *
541da177e4SLinus Torvalds  * Also moved to /proc/net/pktgen/
551da177e4SLinus Torvalds  * --ro
561da177e4SLinus Torvalds  *
571da177e4SLinus Torvalds  * Sept 10:  Fixed threading/locking.  Lots of bone-headed and more clever
581da177e4SLinus Torvalds  *    mistakes.  Also merged in DaveM's patch in the -pre6 patch.
591da177e4SLinus Torvalds  * --Ben Greear <greearb@candelatech.com>
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br)
621da177e4SLinus Torvalds  *
631da177e4SLinus Torvalds  *
641da177e4SLinus Torvalds  * 021124 Finished major redesign and rewrite for new functionality.
651da177e4SLinus Torvalds  * See Documentation/networking/pktgen.txt for how to use this.
661da177e4SLinus Torvalds  *
671da177e4SLinus Torvalds  * The new operation:
681da177e4SLinus Torvalds  * For each CPU one thread/process is created at start. This process checks
691da177e4SLinus Torvalds  * for running devices in the if_list and sends packets until count is 0 it
701da177e4SLinus Torvalds  * also the thread checks the thread->control which is used for inter-process
711da177e4SLinus Torvalds  * communication. controlling process "posts" operations to the threads this
721da177e4SLinus Torvalds  * way. The if_lock should be possible to remove when add/rem_device is merged
731da177e4SLinus Torvalds  * into this too.
741da177e4SLinus Torvalds  *
751da177e4SLinus Torvalds  * By design there should only be *one* "controlling" process. In practice
761da177e4SLinus Torvalds  * multiple write accesses gives unpredictable result. Understood by "write"
771da177e4SLinus Torvalds  * to /proc gives result code thats should be read be the "writer".
78b4099fabSStephen Hemminger  * For practical use this should be no problem.
791da177e4SLinus Torvalds  *
801da177e4SLinus Torvalds  * Note when adding devices to a specific CPU there good idea to also assign
811da177e4SLinus Torvalds  * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
821da177e4SLinus Torvalds  * --ro
831da177e4SLinus Torvalds  *
841da177e4SLinus Torvalds  * Fix refcount off by one if first packet fails, potential null deref,
851da177e4SLinus Torvalds  * memleak 030710- KJP
861da177e4SLinus Torvalds  *
871da177e4SLinus Torvalds  * First "ranges" functionality for ipv6 030726 --ro
881da177e4SLinus Torvalds  *
891da177e4SLinus Torvalds  * Included flow support. 030802 ANK.
901da177e4SLinus Torvalds  *
911da177e4SLinus Torvalds  * Fixed unaligned access on IA-64 Grant Grundler <grundler@parisc-linux.org>
921da177e4SLinus Torvalds  *
931da177e4SLinus Torvalds  * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
941da177e4SLinus Torvalds  * ia64 compilation fix from  Aron Griffis <aron@hp.com> 040604
951da177e4SLinus Torvalds  *
961da177e4SLinus Torvalds  * New xmit() return, do_div and misc clean up by Stephen Hemminger
971da177e4SLinus Torvalds  * <shemminger@osdl.org> 040923
981da177e4SLinus Torvalds  *
99b4099fabSStephen Hemminger  * Randy Dunlap fixed u64 printk compiler waring
1001da177e4SLinus Torvalds  *
1011da177e4SLinus Torvalds  * Remove FCS from BW calculation.  Lennert Buytenhek <buytenh@wantstofly.org>
1021da177e4SLinus Torvalds  * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213
1031da177e4SLinus Torvalds  *
1041da177e4SLinus Torvalds  * Corrections from Nikolai Malykh (nmalykh@bilim.com)
1051da177e4SLinus Torvalds  * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
1061da177e4SLinus Torvalds  *
1071da177e4SLinus Torvalds  * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
1081da177e4SLinus Torvalds  * 050103
109ca6549afSSteven Whitehouse  *
110ca6549afSSteven Whitehouse  * MPLS support by Steven Whitehouse <steve@chygwyn.com>
111ca6549afSSteven Whitehouse  *
11234954ddcSFrancesco Fondelli  * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <francesco.fondelli@gmail.com>
11334954ddcSFrancesco Fondelli  *
114*ce5d0b47SAdit Ranadive  * Fixed src_mac command to set source mac of packet to value specified in
115*ce5d0b47SAdit Ranadive  * command by Adit Ranadive <adit.262@gmail.com>
116*ce5d0b47SAdit Ranadive  *
1171da177e4SLinus Torvalds  */
1181da177e4SLinus Torvalds #include <linux/sys.h>
1191da177e4SLinus Torvalds #include <linux/types.h>
1201da177e4SLinus Torvalds #include <linux/module.h>
1211da177e4SLinus Torvalds #include <linux/moduleparam.h>
1221da177e4SLinus Torvalds #include <linux/kernel.h>
123222fa076SLuiz Capitulino #include <linux/mutex.h>
1241da177e4SLinus Torvalds #include <linux/sched.h>
1251da177e4SLinus Torvalds #include <linux/slab.h>
1261da177e4SLinus Torvalds #include <linux/vmalloc.h>
1271da177e4SLinus Torvalds #include <linux/unistd.h>
1281da177e4SLinus Torvalds #include <linux/string.h>
1291da177e4SLinus Torvalds #include <linux/ptrace.h>
1301da177e4SLinus Torvalds #include <linux/errno.h>
1311da177e4SLinus Torvalds #include <linux/ioport.h>
1321da177e4SLinus Torvalds #include <linux/interrupt.h>
1334fc268d2SRandy Dunlap #include <linux/capability.h>
13409fe3ef4SAndrew Morton #include <linux/freezer.h>
1351da177e4SLinus Torvalds #include <linux/delay.h>
1361da177e4SLinus Torvalds #include <linux/timer.h>
137cdcdbe0bSLuiz Capitulino #include <linux/list.h>
1381da177e4SLinus Torvalds #include <linux/init.h>
1391da177e4SLinus Torvalds #include <linux/skbuff.h>
1401da177e4SLinus Torvalds #include <linux/netdevice.h>
1411da177e4SLinus Torvalds #include <linux/inet.h>
1421da177e4SLinus Torvalds #include <linux/inetdevice.h>
1431da177e4SLinus Torvalds #include <linux/rtnetlink.h>
1441da177e4SLinus Torvalds #include <linux/if_arp.h>
14534954ddcSFrancesco Fondelli #include <linux/if_vlan.h>
1461da177e4SLinus Torvalds #include <linux/in.h>
1471da177e4SLinus Torvalds #include <linux/ip.h>
1481da177e4SLinus Torvalds #include <linux/ipv6.h>
1491da177e4SLinus Torvalds #include <linux/udp.h>
1501da177e4SLinus Torvalds #include <linux/proc_fs.h>
151d50a6b56SStephen Hemminger #include <linux/seq_file.h>
1521da177e4SLinus Torvalds #include <linux/wait.h>
153f404e9a6SKris Katterjohn #include <linux/etherdevice.h>
154ee74baa7SDavid S. Miller #include <linux/kthread.h>
1551da177e4SLinus Torvalds #include <net/checksum.h>
1561da177e4SLinus Torvalds #include <net/ipv6.h>
1571da177e4SLinus Torvalds #include <net/addrconf.h>
158a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
159a553e4a6SJamal Hadi Salim #include <net/xfrm.h>
160a553e4a6SJamal Hadi Salim #endif
1611da177e4SLinus Torvalds #include <asm/byteorder.h>
1621da177e4SLinus Torvalds #include <linux/rcupdate.h>
1631da177e4SLinus Torvalds #include <asm/bitops.h>
1641da177e4SLinus Torvalds #include <asm/io.h>
1651da177e4SLinus Torvalds #include <asm/dma.h>
1661da177e4SLinus Torvalds #include <asm/uaccess.h>
1671da177e4SLinus Torvalds #include <asm/div64.h>		/* do_div */
1681da177e4SLinus Torvalds #include <asm/timex.h>
1691da177e4SLinus Torvalds 
1701ca7768cSFrancesco Fondelli #define VERSION  "pktgen v2.68: Packet Generator for packet performance testing.\n"
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds /* The buckets are exponential in 'width' */
1731da177e4SLinus Torvalds #define LAT_BUCKETS_MAX 32
1741da177e4SLinus Torvalds #define IP_NAME_SZ 32
175ca6549afSSteven Whitehouse #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
176d5f1ce9aSStephen Hemminger #define MPLS_STACK_BOTTOM htonl(0x00000100)
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds /* Device flag bits */
1791da177e4SLinus Torvalds #define F_IPSRC_RND   (1<<0)	/* IP-Src Random  */
1801da177e4SLinus Torvalds #define F_IPDST_RND   (1<<1)	/* IP-Dst Random  */
1811da177e4SLinus Torvalds #define F_UDPSRC_RND  (1<<2)	/* UDP-Src Random */
1821da177e4SLinus Torvalds #define F_UDPDST_RND  (1<<3)	/* UDP-Dst Random */
1831da177e4SLinus Torvalds #define F_MACSRC_RND  (1<<4)	/* MAC-Src Random */
1841da177e4SLinus Torvalds #define F_MACDST_RND  (1<<5)	/* MAC-Dst Random */
1851da177e4SLinus Torvalds #define F_TXSIZE_RND  (1<<6)	/* Transmit size is random */
1861da177e4SLinus Torvalds #define F_IPV6        (1<<7)	/* Interface in IPV6 Mode */
187ca6549afSSteven Whitehouse #define F_MPLS_RND    (1<<8)	/* Random MPLS labels */
18834954ddcSFrancesco Fondelli #define F_VID_RND     (1<<9)	/* Random VLAN ID */
18934954ddcSFrancesco Fondelli #define F_SVID_RND    (1<<10)	/* Random SVLAN ID */
190007a531bSJamal Hadi Salim #define F_FLOW_SEQ    (1<<11)	/* Sequential flows */
191a553e4a6SJamal Hadi Salim #define F_IPSEC_ON    (1<<12)	/* ipsec on for flows */
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds /* Thread control flag bits */
1941da177e4SLinus Torvalds #define T_TERMINATE   (1<<0)
1951da177e4SLinus Torvalds #define T_STOP        (1<<1)	/* Stop run */
1961da177e4SLinus Torvalds #define T_RUN         (1<<2)	/* Start run */
19795ed63f7SArthur Kepner #define T_REMDEVALL   (1<<3)	/* Remove all devs */
19895ed63f7SArthur Kepner #define T_REMDEV      (1<<4)	/* Remove one dev */
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds /* If lock -- can be removed after some work */
2011da177e4SLinus Torvalds #define   if_lock(t)           spin_lock(&(t->if_lock));
2021da177e4SLinus Torvalds #define   if_unlock(t)           spin_unlock(&(t->if_lock));
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds /* Used to help with determining the pkts on receive */
2051da177e4SLinus Torvalds #define PKTGEN_MAGIC 0xbe9be955
206d50a6b56SStephen Hemminger #define PG_PROC_DIR "pktgen"
207d50a6b56SStephen Hemminger #define PGCTRL	    "pgctrl"
208d50a6b56SStephen Hemminger static struct proc_dir_entry *pg_proc_dir = NULL;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds #define MAX_CFLOWS  65536
2111da177e4SLinus Torvalds 
21234954ddcSFrancesco Fondelli #define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
21334954ddcSFrancesco Fondelli #define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
21434954ddcSFrancesco Fondelli 
215222f1806SLuiz Capitulino struct flow_state {
216252e3346SAl Viro 	__be32 cur_daddr;
2171da177e4SLinus Torvalds 	int count;
218a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
219a553e4a6SJamal Hadi Salim 	struct xfrm_state *x;
220a553e4a6SJamal Hadi Salim #endif
221007a531bSJamal Hadi Salim 	__u32 flags;
2221da177e4SLinus Torvalds };
2231da177e4SLinus Torvalds 
224007a531bSJamal Hadi Salim /* flow flag bits */
225007a531bSJamal Hadi Salim #define F_INIT   (1<<0)		/* flow has been initialized */
226007a531bSJamal Hadi Salim 
2271da177e4SLinus Torvalds struct pktgen_dev {
2281da177e4SLinus Torvalds 	/*
2291da177e4SLinus Torvalds 	 * Try to keep frequent/infrequent used vars. separated.
2301da177e4SLinus Torvalds 	 */
23139df232fSStephen Hemminger 	struct proc_dir_entry *entry;	/* proc file */
2321da177e4SLinus Torvalds 	struct pktgen_thread *pg_thread;/* the owner */
233c26a8016SLuiz Capitulino 	struct list_head list;		/* Used for chaining in the thread's run-queue */
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	int running;		/* if this changes to false, the test will stop */
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	/* If min != max, then we will either do a linear iteration, or
2381da177e4SLinus Torvalds 	 * we will do a random selection from within the range.
2391da177e4SLinus Torvalds 	 */
2401da177e4SLinus Torvalds 	__u32 flags;
24195ed63f7SArthur Kepner 	int removal_mark;	/* non-zero => the device is marked for
24295ed63f7SArthur Kepner 				 * removal by worker thread */
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	int min_pkt_size;	/* = ETH_ZLEN; */
2451da177e4SLinus Torvalds 	int max_pkt_size;	/* = ETH_ZLEN; */
24616dab72fSJamal Hadi Salim 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
2471da177e4SLinus Torvalds 	int nfrags;
2481da177e4SLinus Torvalds 	__u32 delay_us;		/* Default delay */
2491da177e4SLinus Torvalds 	__u32 delay_ns;
2501da177e4SLinus Torvalds 	__u64 count;		/* Default No packets to send */
2511da177e4SLinus Torvalds 	__u64 sofar;		/* How many pkts we've sent so far */
2521da177e4SLinus Torvalds 	__u64 tx_bytes;		/* How many bytes we've transmitted */
2531da177e4SLinus Torvalds 	__u64 errors;		/* Errors when trying to transmit, pkts will be re-sent */
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	/* runtime counters relating to clone_skb */
2561da177e4SLinus Torvalds 	__u64 next_tx_us;	/* timestamp of when to tx next */
2571da177e4SLinus Torvalds 	__u32 next_tx_ns;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	__u64 allocated_skbs;
2601da177e4SLinus Torvalds 	__u32 clone_count;
2611da177e4SLinus Torvalds 	int last_ok;		/* Was last skb sent?
2621da177e4SLinus Torvalds 				 * Or a failed transmit of some sort?  This will keep
2631da177e4SLinus Torvalds 				 * sequence numbers in order, for example.
2641da177e4SLinus Torvalds 				 */
2651da177e4SLinus Torvalds 	__u64 started_at;	/* micro-seconds */
2661da177e4SLinus Torvalds 	__u64 stopped_at;	/* micro-seconds */
2671da177e4SLinus Torvalds 	__u64 idle_acc;		/* micro-seconds */
2681da177e4SLinus Torvalds 	__u32 seq_num;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	int clone_skb;		/* Use multiple SKBs during packet gen.  If this number
271b4099fabSStephen Hemminger 				 * is greater than 1, then that many copies of the same
2721da177e4SLinus Torvalds 				 * packet will be sent before a new packet is allocated.
2731da177e4SLinus Torvalds 				 * For instance, if you want to send 1024 identical packets
2741da177e4SLinus Torvalds 				 * before creating a new packet, set clone_skb to 1024.
2751da177e4SLinus Torvalds 				 */
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	char dst_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2781da177e4SLinus Torvalds 	char dst_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2791da177e4SLinus Torvalds 	char src_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2801da177e4SLinus Torvalds 	char src_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	struct in6_addr in6_saddr;
2831da177e4SLinus Torvalds 	struct in6_addr in6_daddr;
2841da177e4SLinus Torvalds 	struct in6_addr cur_in6_daddr;
2851da177e4SLinus Torvalds 	struct in6_addr cur_in6_saddr;
2861da177e4SLinus Torvalds 	/* For ranges */
2871da177e4SLinus Torvalds 	struct in6_addr min_in6_daddr;
2881da177e4SLinus Torvalds 	struct in6_addr max_in6_daddr;
2891da177e4SLinus Torvalds 	struct in6_addr min_in6_saddr;
2901da177e4SLinus Torvalds 	struct in6_addr max_in6_saddr;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	/* If we're doing ranges, random or incremental, then this
2931da177e4SLinus Torvalds 	 * defines the min/max for those ranges.
2941da177e4SLinus Torvalds 	 */
295252e3346SAl Viro 	__be32 saddr_min;	/* inclusive, source IP address */
296252e3346SAl Viro 	__be32 saddr_max;	/* exclusive, source IP address */
297252e3346SAl Viro 	__be32 daddr_min;	/* inclusive, dest IP address */
298252e3346SAl Viro 	__be32 daddr_max;	/* exclusive, dest IP address */
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	__u16 udp_src_min;	/* inclusive, source UDP port */
3011da177e4SLinus Torvalds 	__u16 udp_src_max;	/* exclusive, source UDP port */
3021da177e4SLinus Torvalds 	__u16 udp_dst_min;	/* inclusive, dest UDP port */
3031da177e4SLinus Torvalds 	__u16 udp_dst_max;	/* exclusive, dest UDP port */
3041da177e4SLinus Torvalds 
3051ca7768cSFrancesco Fondelli 	/* DSCP + ECN */
3061ca7768cSFrancesco Fondelli 	__u8 tos;            /* six most significant bits of (former) IPv4 TOS are for dscp codepoint */
3071ca7768cSFrancesco Fondelli 	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6 (see RFC 3260, sec. 4) */
3081ca7768cSFrancesco Fondelli 
309ca6549afSSteven Whitehouse 	/* MPLS */
310ca6549afSSteven Whitehouse 	unsigned nr_labels;	/* Depth of stack, 0 = no MPLS */
311ca6549afSSteven Whitehouse 	__be32 labels[MAX_MPLS_LABELS];
312ca6549afSSteven Whitehouse 
31334954ddcSFrancesco Fondelli 	/* VLAN/SVLAN (802.1Q/Q-in-Q) */
31434954ddcSFrancesco Fondelli 	__u8  vlan_p;
31534954ddcSFrancesco Fondelli 	__u8  vlan_cfi;
31634954ddcSFrancesco Fondelli 	__u16 vlan_id;  /* 0xffff means no vlan tag */
31734954ddcSFrancesco Fondelli 
31834954ddcSFrancesco Fondelli 	__u8  svlan_p;
31934954ddcSFrancesco Fondelli 	__u8  svlan_cfi;
32034954ddcSFrancesco Fondelli 	__u16 svlan_id; /* 0xffff means no svlan tag */
32134954ddcSFrancesco Fondelli 
3221da177e4SLinus Torvalds 	__u32 src_mac_count;	/* How many MACs to iterate through */
3231da177e4SLinus Torvalds 	__u32 dst_mac_count;	/* How many MACs to iterate through */
3241da177e4SLinus Torvalds 
325f404e9a6SKris Katterjohn 	unsigned char dst_mac[ETH_ALEN];
326f404e9a6SKris Katterjohn 	unsigned char src_mac[ETH_ALEN];
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	__u32 cur_dst_mac_offset;
3291da177e4SLinus Torvalds 	__u32 cur_src_mac_offset;
330252e3346SAl Viro 	__be32 cur_saddr;
331252e3346SAl Viro 	__be32 cur_daddr;
3321da177e4SLinus Torvalds 	__u16 cur_udp_dst;
3331da177e4SLinus Torvalds 	__u16 cur_udp_src;
3341da177e4SLinus Torvalds 	__u32 cur_pkt_size;
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 	__u8 hh[14];
3371da177e4SLinus Torvalds 	/* = {
3381da177e4SLinus Torvalds 	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	   We fill in SRC address later
3411da177e4SLinus Torvalds 	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3421da177e4SLinus Torvalds 	   0x08, 0x00
3431da177e4SLinus Torvalds 	   };
3441da177e4SLinus Torvalds 	 */
3451da177e4SLinus Torvalds 	__u16 pad;		/* pad out the hh struct to an even 16 bytes */
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	struct sk_buff *skb;	/* skb we are to transmit next, mainly used for when we
3481da177e4SLinus Torvalds 				 * are transmitting the same one multiple times
3491da177e4SLinus Torvalds 				 */
3501da177e4SLinus Torvalds 	struct net_device *odev;	/* The out-going device.  Note that the device should
3511da177e4SLinus Torvalds 					 * have it's pg_info pointer pointing back to this
3521da177e4SLinus Torvalds 					 * device.  This will be set when the user specifies
3531da177e4SLinus Torvalds 					 * the out-going device name (not when the inject is
3541da177e4SLinus Torvalds 					 * started as it used to do.)
3551da177e4SLinus Torvalds 					 */
3561da177e4SLinus Torvalds 	struct flow_state *flows;
3571da177e4SLinus Torvalds 	unsigned cflows;	/* Concurrent flows (config) */
3581da177e4SLinus Torvalds 	unsigned lflow;		/* Flow length  (config) */
3591da177e4SLinus Torvalds 	unsigned nflows;	/* accumulated flows (stats) */
360007a531bSJamal Hadi Salim 	unsigned curfl;		/* current sequenced flow (state)*/
361a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
362a553e4a6SJamal Hadi Salim 	__u8	ipsmode;		/* IPSEC mode (config) */
363a553e4a6SJamal Hadi Salim 	__u8	ipsproto;		/* IPSEC type (config) */
364a553e4a6SJamal Hadi Salim #endif
36539df232fSStephen Hemminger 	char result[512];
3661da177e4SLinus Torvalds };
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds struct pktgen_hdr {
369252e3346SAl Viro 	__be32 pgh_magic;
370252e3346SAl Viro 	__be32 seq_num;
371252e3346SAl Viro 	__be32 tv_sec;
372252e3346SAl Viro 	__be32 tv_usec;
3731da177e4SLinus Torvalds };
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds struct pktgen_thread {
3761da177e4SLinus Torvalds 	spinlock_t if_lock;
377c26a8016SLuiz Capitulino 	struct list_head if_list;	/* All device here */
378cdcdbe0bSLuiz Capitulino 	struct list_head th_list;
379ee74baa7SDavid S. Miller 	struct task_struct *tsk;
3801da177e4SLinus Torvalds 	char result[512];
3811da177e4SLinus Torvalds 	u32 max_before_softirq;	/* We'll call do_softirq to prevent starvation. */
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 	/* Field for thread to receive "posted" events terminate, stop ifs etc. */
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	u32 control;
3861da177e4SLinus Torvalds 	int cpu;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	wait_queue_head_t queue;
3891da177e4SLinus Torvalds };
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds #define REMOVE 1
3921da177e4SLinus Torvalds #define FIND   0
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds /*  This code works around the fact that do_div cannot handle two 64-bit
3951da177e4SLinus Torvalds     numbers, and regular 64-bit division doesn't work on x86 kernels.
3961da177e4SLinus Torvalds     --Ben
3971da177e4SLinus Torvalds */
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds #define PG_DIV 0
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds /* This was emailed to LMKL by: Chris Caputo <ccaputo@alt.net>
4021da177e4SLinus Torvalds  * Function copied/adapted/optimized from:
4031da177e4SLinus Torvalds  *
4041da177e4SLinus Torvalds  *  nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html
4051da177e4SLinus Torvalds  *
4061da177e4SLinus Torvalds  * Copyright 1994, University of Cambridge Computer Laboratory
4071da177e4SLinus Torvalds  * All Rights Reserved.
4081da177e4SLinus Torvalds  *
4091da177e4SLinus Torvalds  */
41077933d72SJesper Juhl static inline s64 divremdi3(s64 x, s64 y, int type)
4111da177e4SLinus Torvalds {
4121da177e4SLinus Torvalds 	u64 a = (x < 0) ? -x : x;
4131da177e4SLinus Torvalds 	u64 b = (y < 0) ? -y : y;
4141da177e4SLinus Torvalds 	u64 res = 0, d = 1;
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	if (b > 0) {
4171da177e4SLinus Torvalds 		while (b < a) {
4181da177e4SLinus Torvalds 			b <<= 1;
4191da177e4SLinus Torvalds 			d <<= 1;
4201da177e4SLinus Torvalds 		}
4211da177e4SLinus Torvalds 	}
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	do {
4241da177e4SLinus Torvalds 		if (a >= b) {
4251da177e4SLinus Torvalds 			a -= b;
4261da177e4SLinus Torvalds 			res += d;
4271da177e4SLinus Torvalds 		}
4281da177e4SLinus Torvalds 		b >>= 1;
4291da177e4SLinus Torvalds 		d >>= 1;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 	while (d);
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	if (PG_DIV == type) {
4341da177e4SLinus Torvalds 		return (((x ^ y) & (1ll << 63)) == 0) ? res : -(s64) res;
435222f1806SLuiz Capitulino 	} else {
4361da177e4SLinus Torvalds 		return ((x & (1ll << 63)) == 0) ? a : -(s64) a;
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds /* End of hacks to deal with 64-bit math on x86 */
4411da177e4SLinus Torvalds 
442b4099fabSStephen Hemminger /** Convert to milliseconds */
4431da177e4SLinus Torvalds static inline __u64 tv_to_ms(const struct timeval *tv)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds 	__u64 ms = tv->tv_usec / 1000;
4461da177e4SLinus Torvalds 	ms += (__u64) tv->tv_sec * (__u64) 1000;
4471da177e4SLinus Torvalds 	return ms;
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds /** Convert to micro-seconds */
4511da177e4SLinus Torvalds static inline __u64 tv_to_us(const struct timeval *tv)
4521da177e4SLinus Torvalds {
4531da177e4SLinus Torvalds 	__u64 us = tv->tv_usec;
4541da177e4SLinus Torvalds 	us += (__u64) tv->tv_sec * (__u64) 1000000;
4551da177e4SLinus Torvalds 	return us;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
458222f1806SLuiz Capitulino static inline __u64 pg_div(__u64 n, __u32 base)
459222f1806SLuiz Capitulino {
4601da177e4SLinus Torvalds 	__u64 tmp = n;
4611da177e4SLinus Torvalds 	do_div(tmp, base);
4621da177e4SLinus Torvalds 	/* printk("pktgen: pg_div, n: %llu  base: %d  rv: %llu\n",
4631da177e4SLinus Torvalds 	   n, base, tmp); */
4641da177e4SLinus Torvalds 	return tmp;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds static inline __u64 pg_div64(__u64 n, __u64 base)
4681da177e4SLinus Torvalds {
4691da177e4SLinus Torvalds 	__u64 tmp = n;
4701da177e4SLinus Torvalds /*
471b4099fabSStephen Hemminger  * How do we know if the architecture we are running on
4721da177e4SLinus Torvalds  * supports division with 64 bit base?
4731da177e4SLinus Torvalds  *
4741da177e4SLinus Torvalds  */
4751da177e4SLinus Torvalds #if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds 	do_div(tmp, base);
4781da177e4SLinus Torvalds #else
4791da177e4SLinus Torvalds 	tmp = divremdi3(n, base, PG_DIV);
4801da177e4SLinus Torvalds #endif
4811da177e4SLinus Torvalds 	return tmp;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds static inline __u64 getCurMs(void)
4851da177e4SLinus Torvalds {
4861da177e4SLinus Torvalds 	struct timeval tv;
4871da177e4SLinus Torvalds 	do_gettimeofday(&tv);
4881da177e4SLinus Torvalds 	return tv_to_ms(&tv);
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds static inline __u64 getCurUs(void)
4921da177e4SLinus Torvalds {
4931da177e4SLinus Torvalds 	struct timeval tv;
4941da177e4SLinus Torvalds 	do_gettimeofday(&tv);
4951da177e4SLinus Torvalds 	return tv_to_us(&tv);
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds static inline __u64 tv_diff(const struct timeval *a, const struct timeval *b)
4991da177e4SLinus Torvalds {
5001da177e4SLinus Torvalds 	return tv_to_us(a) - tv_to_us(b);
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds /* old include end */
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds static char version[] __initdata = VERSION;
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
5081da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
509222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
510222f1806SLuiz Capitulino 					  const char *ifname);
5111da177e4SLinus Torvalds static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
5121da177e4SLinus Torvalds static void pktgen_run_all_threads(void);
5131da177e4SLinus Torvalds static void pktgen_stop_all_threads_ifs(void);
5141da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
5151da177e4SLinus Torvalds static void pktgen_stop(struct pktgen_thread *t);
5161da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
51739df232fSStephen Hemminger 
5181da177e4SLinus Torvalds static unsigned int scan_ip6(const char *s, char ip[16]);
5191da177e4SLinus Torvalds static unsigned int fmt_ip6(char *s, const char ip[16]);
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds /* Module parameters, defaults. */
5221da177e4SLinus Torvalds static int pg_count_d = 1000;	/* 1000 pkts by default */
523f34fbb97SJaco Kroon static int pg_delay_d;
524f34fbb97SJaco Kroon static int pg_clone_skb_d;
525f34fbb97SJaco Kroon static int debug;
5261da177e4SLinus Torvalds 
527222fa076SLuiz Capitulino static DEFINE_MUTEX(pktgen_thread_lock);
528cdcdbe0bSLuiz Capitulino static LIST_HEAD(pktgen_threads);
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds static struct notifier_block pktgen_notifier_block = {
5311da177e4SLinus Torvalds 	.notifier_call = pktgen_device_event,
5321da177e4SLinus Torvalds };
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds /*
5351da177e4SLinus Torvalds  * /proc handling functions
5361da177e4SLinus Torvalds  *
5371da177e4SLinus Torvalds  */
5381da177e4SLinus Torvalds 
539d50a6b56SStephen Hemminger static int pgctrl_show(struct seq_file *seq, void *v)
540d50a6b56SStephen Hemminger {
541d50a6b56SStephen Hemminger 	seq_puts(seq, VERSION);
542d50a6b56SStephen Hemminger 	return 0;
543d50a6b56SStephen Hemminger }
5441da177e4SLinus Torvalds 
545d50a6b56SStephen Hemminger static ssize_t pgctrl_write(struct file *file, const char __user * buf,
5461da177e4SLinus Torvalds 			    size_t count, loff_t * ppos)
5471da177e4SLinus Torvalds {
5481da177e4SLinus Torvalds 	int err = 0;
549d50a6b56SStephen Hemminger 	char data[128];
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	if (!capable(CAP_NET_ADMIN)) {
5521da177e4SLinus Torvalds 		err = -EPERM;
5531da177e4SLinus Torvalds 		goto out;
5541da177e4SLinus Torvalds 	}
5551da177e4SLinus Torvalds 
556d50a6b56SStephen Hemminger 	if (count > sizeof(data))
557d50a6b56SStephen Hemminger 		count = sizeof(data);
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	if (copy_from_user(data, buf, count)) {
5601da177e4SLinus Torvalds 		err = -EFAULT;
561d50a6b56SStephen Hemminger 		goto out;
5621da177e4SLinus Torvalds 	}
5631da177e4SLinus Torvalds 	data[count - 1] = 0;	/* Make string */
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	if (!strcmp(data, "stop"))
5661da177e4SLinus Torvalds 		pktgen_stop_all_threads_ifs();
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	else if (!strcmp(data, "start"))
5691da177e4SLinus Torvalds 		pktgen_run_all_threads();
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	else
57225a8b254SDavid S. Miller 		printk(KERN_WARNING "pktgen: Unknown command: %s\n", data);
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds 	err = count;
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds out:
5771da177e4SLinus Torvalds 	return err;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
580d50a6b56SStephen Hemminger static int pgctrl_open(struct inode *inode, struct file *file)
5811da177e4SLinus Torvalds {
582d50a6b56SStephen Hemminger 	return single_open(file, pgctrl_show, PDE(inode)->data);
583d50a6b56SStephen Hemminger }
584d50a6b56SStephen Hemminger 
5859a32144eSArjan van de Ven static const struct file_operations pktgen_fops = {
586d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
587d50a6b56SStephen Hemminger 	.open    = pgctrl_open,
588d50a6b56SStephen Hemminger 	.read    = seq_read,
589d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
590d50a6b56SStephen Hemminger 	.write   = pgctrl_write,
591d50a6b56SStephen Hemminger 	.release = single_release,
592d50a6b56SStephen Hemminger };
593d50a6b56SStephen Hemminger 
594d50a6b56SStephen Hemminger static int pktgen_if_show(struct seq_file *seq, void *v)
595d50a6b56SStephen Hemminger {
5961da177e4SLinus Torvalds 	int i;
597d50a6b56SStephen Hemminger 	struct pktgen_dev *pkt_dev = seq->private;
5981da177e4SLinus Torvalds 	__u64 sa;
5991da177e4SLinus Torvalds 	__u64 stopped;
6001da177e4SLinus Torvalds 	__u64 now = getCurUs();
6011da177e4SLinus Torvalds 
602222f1806SLuiz Capitulino 	seq_printf(seq,
603222f1806SLuiz Capitulino 		   "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n",
604222f1806SLuiz Capitulino 		   (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
605222f1806SLuiz Capitulino 		   pkt_dev->max_pkt_size);
6061da177e4SLinus Torvalds 
607222f1806SLuiz Capitulino 	seq_printf(seq,
608222f1806SLuiz Capitulino 		   "     frags: %d  delay: %u  clone_skb: %d  ifname: %s\n",
609222f1806SLuiz Capitulino 		   pkt_dev->nfrags,
610222f1806SLuiz Capitulino 		   1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
61139df232fSStephen Hemminger 		   pkt_dev->clone_skb, pkt_dev->odev->name);
6121da177e4SLinus Torvalds 
613222f1806SLuiz Capitulino 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
614222f1806SLuiz Capitulino 		   pkt_dev->lflow);
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
6171da177e4SLinus Torvalds 		char b1[128], b2[128], b3[128];
6181da177e4SLinus Torvalds 		fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
6191da177e4SLinus Torvalds 		fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
6201da177e4SLinus Torvalds 		fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
621222f1806SLuiz Capitulino 		seq_printf(seq,
622222f1806SLuiz Capitulino 			   "     saddr: %s  min_saddr: %s  max_saddr: %s\n", b1,
623222f1806SLuiz Capitulino 			   b2, b3);
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 		fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
6261da177e4SLinus Torvalds 		fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
6271da177e4SLinus Torvalds 		fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
628222f1806SLuiz Capitulino 		seq_printf(seq,
629222f1806SLuiz Capitulino 			   "     daddr: %s  min_daddr: %s  max_daddr: %s\n", b1,
630222f1806SLuiz Capitulino 			   b2, b3);
6311da177e4SLinus Torvalds 
632222f1806SLuiz Capitulino 	} else
633222f1806SLuiz Capitulino 		seq_printf(seq,
634222f1806SLuiz Capitulino 			   "     dst_min: %s  dst_max: %s\n     src_min: %s  src_max: %s\n",
635222f1806SLuiz Capitulino 			   pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min,
636222f1806SLuiz Capitulino 			   pkt_dev->src_max);
6371da177e4SLinus Torvalds 
638d50a6b56SStephen Hemminger 	seq_puts(seq, "     src_mac: ");
6391da177e4SLinus Torvalds 
640f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
6411da177e4SLinus Torvalds 		for (i = 0; i < 6; i++)
642222f1806SLuiz Capitulino 			seq_printf(seq, "%02X%s", pkt_dev->odev->dev_addr[i],
643222f1806SLuiz Capitulino 				   i == 5 ? "  " : ":");
6441da177e4SLinus Torvalds 	else
6451da177e4SLinus Torvalds 		for (i = 0; i < 6; i++)
646222f1806SLuiz Capitulino 			seq_printf(seq, "%02X%s", pkt_dev->src_mac[i],
647222f1806SLuiz Capitulino 				   i == 5 ? "  " : ":");
6481da177e4SLinus Torvalds 
649d50a6b56SStephen Hemminger 	seq_printf(seq, "dst_mac: ");
6501da177e4SLinus Torvalds 	for (i = 0; i < 6; i++)
651222f1806SLuiz Capitulino 		seq_printf(seq, "%02X%s", pkt_dev->dst_mac[i],
652222f1806SLuiz Capitulino 			   i == 5 ? "\n" : ":");
6531da177e4SLinus Torvalds 
654222f1806SLuiz Capitulino 	seq_printf(seq,
655222f1806SLuiz Capitulino 		   "     udp_src_min: %d  udp_src_max: %d  udp_dst_min: %d  udp_dst_max: %d\n",
656222f1806SLuiz Capitulino 		   pkt_dev->udp_src_min, pkt_dev->udp_src_max,
657222f1806SLuiz Capitulino 		   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
6581da177e4SLinus Torvalds 
659222f1806SLuiz Capitulino 	seq_printf(seq,
660ca6549afSSteven Whitehouse 		   "     src_mac_count: %d  dst_mac_count: %d\n",
6611da177e4SLinus Torvalds 		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
6621da177e4SLinus Torvalds 
663ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels) {
664ca6549afSSteven Whitehouse 		unsigned i;
665ca6549afSSteven Whitehouse 		seq_printf(seq, "     mpls: ");
666ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
667ca6549afSSteven Whitehouse 			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
668ca6549afSSteven Whitehouse 				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
669ca6549afSSteven Whitehouse 	}
670ca6549afSSteven Whitehouse 
67134954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
67234954ddcSFrancesco Fondelli 		seq_printf(seq, "     vlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
67334954ddcSFrancesco Fondelli 			   pkt_dev->vlan_id, pkt_dev->vlan_p, pkt_dev->vlan_cfi);
67434954ddcSFrancesco Fondelli 	}
67534954ddcSFrancesco Fondelli 
67634954ddcSFrancesco Fondelli 	if (pkt_dev->svlan_id != 0xffff) {
67734954ddcSFrancesco Fondelli 		seq_printf(seq, "     svlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
67834954ddcSFrancesco Fondelli 			   pkt_dev->svlan_id, pkt_dev->svlan_p, pkt_dev->svlan_cfi);
67934954ddcSFrancesco Fondelli 	}
68034954ddcSFrancesco Fondelli 
6811ca7768cSFrancesco Fondelli 	if (pkt_dev->tos) {
6821ca7768cSFrancesco Fondelli 		seq_printf(seq, "     tos: 0x%02x\n", pkt_dev->tos);
6831ca7768cSFrancesco Fondelli 	}
6841ca7768cSFrancesco Fondelli 
6851ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
6861ca7768cSFrancesco Fondelli 		seq_printf(seq, "     traffic_class: 0x%02x\n", pkt_dev->traffic_class);
6871ca7768cSFrancesco Fondelli 	}
6881ca7768cSFrancesco Fondelli 
689ca6549afSSteven Whitehouse 	seq_printf(seq, "     Flags: ");
690ca6549afSSteven Whitehouse 
6911da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
692d50a6b56SStephen Hemminger 		seq_printf(seq, "IPV6  ");
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPSRC_RND)
695d50a6b56SStephen Hemminger 		seq_printf(seq, "IPSRC_RND  ");
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPDST_RND)
698d50a6b56SStephen Hemminger 		seq_printf(seq, "IPDST_RND  ");
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	if (pkt_dev->flags & F_TXSIZE_RND)
701d50a6b56SStephen Hemminger 		seq_printf(seq, "TXSIZE_RND  ");
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPSRC_RND)
704d50a6b56SStephen Hemminger 		seq_printf(seq, "UDPSRC_RND  ");
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPDST_RND)
707d50a6b56SStephen Hemminger 		seq_printf(seq, "UDPDST_RND  ");
7081da177e4SLinus Torvalds 
709ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND)
710ca6549afSSteven Whitehouse 		seq_printf(seq,  "MPLS_RND  ");
711ca6549afSSteven Whitehouse 
712007a531bSJamal Hadi Salim 	if (pkt_dev->cflows) {
713007a531bSJamal Hadi Salim 		if (pkt_dev->flags & F_FLOW_SEQ)
714007a531bSJamal Hadi Salim 			seq_printf(seq,  "FLOW_SEQ  "); /*in sequence flows*/
715007a531bSJamal Hadi Salim 		else
716007a531bSJamal Hadi Salim 			seq_printf(seq,  "FLOW_RND  ");
717007a531bSJamal Hadi Salim 	}
718007a531bSJamal Hadi Salim 
719a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
720a553e4a6SJamal Hadi Salim 	if (pkt_dev->flags & F_IPSEC_ON)
721a553e4a6SJamal Hadi Salim 		seq_printf(seq,  "IPSEC  ");
722a553e4a6SJamal Hadi Salim #endif
723a553e4a6SJamal Hadi Salim 
7241da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACSRC_RND)
725d50a6b56SStephen Hemminger 		seq_printf(seq, "MACSRC_RND  ");
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACDST_RND)
728d50a6b56SStephen Hemminger 		seq_printf(seq, "MACDST_RND  ");
7291da177e4SLinus Torvalds 
73034954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_VID_RND)
73134954ddcSFrancesco Fondelli 		seq_printf(seq, "VID_RND  ");
73234954ddcSFrancesco Fondelli 
73334954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_SVID_RND)
73434954ddcSFrancesco Fondelli 		seq_printf(seq, "SVID_RND  ");
73534954ddcSFrancesco Fondelli 
736d50a6b56SStephen Hemminger 	seq_puts(seq, "\n");
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 	sa = pkt_dev->started_at;
7391da177e4SLinus Torvalds 	stopped = pkt_dev->stopped_at;
7401da177e4SLinus Torvalds 	if (pkt_dev->running)
7411da177e4SLinus Torvalds 		stopped = now;	/* not really stopped, more like last-running-at */
7421da177e4SLinus Torvalds 
743222f1806SLuiz Capitulino 	seq_printf(seq,
744222f1806SLuiz Capitulino 		   "Current:\n     pkts-sofar: %llu  errors: %llu\n     started: %lluus  stopped: %lluus idle: %lluus\n",
7451da177e4SLinus Torvalds 		   (unsigned long long)pkt_dev->sofar,
746222f1806SLuiz Capitulino 		   (unsigned long long)pkt_dev->errors, (unsigned long long)sa,
7471da177e4SLinus Torvalds 		   (unsigned long long)stopped,
7481da177e4SLinus Torvalds 		   (unsigned long long)pkt_dev->idle_acc);
7491da177e4SLinus Torvalds 
750222f1806SLuiz Capitulino 	seq_printf(seq,
751222f1806SLuiz Capitulino 		   "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
752d50a6b56SStephen Hemminger 		   pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
753d50a6b56SStephen Hemminger 		   pkt_dev->cur_src_mac_offset);
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
7561da177e4SLinus Torvalds 		char b1[128], b2[128];
7571da177e4SLinus Torvalds 		fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
7581da177e4SLinus Torvalds 		fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
759d50a6b56SStephen Hemminger 		seq_printf(seq, "     cur_saddr: %s  cur_daddr: %s\n", b2, b1);
760222f1806SLuiz Capitulino 	} else
761d50a6b56SStephen Hemminger 		seq_printf(seq, "     cur_saddr: 0x%x  cur_daddr: 0x%x\n",
7621da177e4SLinus Torvalds 			   pkt_dev->cur_saddr, pkt_dev->cur_daddr);
7631da177e4SLinus Torvalds 
764d50a6b56SStephen Hemminger 	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",
7651da177e4SLinus Torvalds 		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
7661da177e4SLinus Torvalds 
767d50a6b56SStephen Hemminger 	seq_printf(seq, "     flows: %u\n", pkt_dev->nflows);
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	if (pkt_dev->result[0])
770d50a6b56SStephen Hemminger 		seq_printf(seq, "Result: %s\n", pkt_dev->result);
7711da177e4SLinus Torvalds 	else
772d50a6b56SStephen Hemminger 		seq_printf(seq, "Result: Idle\n");
7731da177e4SLinus Torvalds 
774d50a6b56SStephen Hemminger 	return 0;
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds 
777ca6549afSSteven Whitehouse 
7781ca7768cSFrancesco Fondelli static int hex32_arg(const char __user *user_buffer, unsigned long maxlen, __u32 *num)
779ca6549afSSteven Whitehouse {
780ca6549afSSteven Whitehouse 	int i = 0;
781ca6549afSSteven Whitehouse 	*num = 0;
782ca6549afSSteven Whitehouse 
7831ca7768cSFrancesco Fondelli 	for (; i < maxlen; i++) {
784ca6549afSSteven Whitehouse 		char c;
785ca6549afSSteven Whitehouse 		*num <<= 4;
786ca6549afSSteven Whitehouse 		if (get_user(c, &user_buffer[i]))
787ca6549afSSteven Whitehouse 			return -EFAULT;
788ca6549afSSteven Whitehouse 		if ((c >= '0') && (c <= '9'))
789ca6549afSSteven Whitehouse 			*num |= c - '0';
790ca6549afSSteven Whitehouse 		else if ((c >= 'a') && (c <= 'f'))
791ca6549afSSteven Whitehouse 			*num |= c - 'a' + 10;
792ca6549afSSteven Whitehouse 		else if ((c >= 'A') && (c <= 'F'))
793ca6549afSSteven Whitehouse 			*num |= c - 'A' + 10;
794ca6549afSSteven Whitehouse 		else
795ca6549afSSteven Whitehouse 			break;
796ca6549afSSteven Whitehouse 	}
797ca6549afSSteven Whitehouse 	return i;
798ca6549afSSteven Whitehouse }
799ca6549afSSteven Whitehouse 
800222f1806SLuiz Capitulino static int count_trail_chars(const char __user * user_buffer,
801222f1806SLuiz Capitulino 			     unsigned int maxlen)
8021da177e4SLinus Torvalds {
8031da177e4SLinus Torvalds 	int i;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	for (i = 0; i < maxlen; i++) {
8061da177e4SLinus Torvalds 		char c;
8071da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8081da177e4SLinus Torvalds 			return -EFAULT;
8091da177e4SLinus Torvalds 		switch (c) {
8101da177e4SLinus Torvalds 		case '\"':
8111da177e4SLinus Torvalds 		case '\n':
8121da177e4SLinus Torvalds 		case '\r':
8131da177e4SLinus Torvalds 		case '\t':
8141da177e4SLinus Torvalds 		case ' ':
8151da177e4SLinus Torvalds 		case '=':
8161da177e4SLinus Torvalds 			break;
8171da177e4SLinus Torvalds 		default:
8181da177e4SLinus Torvalds 			goto done;
8193ff50b79SStephen Hemminger 		}
8201da177e4SLinus Torvalds 	}
8211da177e4SLinus Torvalds done:
8221da177e4SLinus Torvalds 	return i;
8231da177e4SLinus Torvalds }
8241da177e4SLinus Torvalds 
825222f1806SLuiz Capitulino static unsigned long num_arg(const char __user * user_buffer,
826222f1806SLuiz Capitulino 			     unsigned long maxlen, unsigned long *num)
8271da177e4SLinus Torvalds {
8281da177e4SLinus Torvalds 	int i = 0;
8291da177e4SLinus Torvalds 	*num = 0;
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 	for (; i < maxlen; i++) {
8321da177e4SLinus Torvalds 		char c;
8331da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8341da177e4SLinus Torvalds 			return -EFAULT;
8351da177e4SLinus Torvalds 		if ((c >= '0') && (c <= '9')) {
8361da177e4SLinus Torvalds 			*num *= 10;
8371da177e4SLinus Torvalds 			*num += c - '0';
8381da177e4SLinus Torvalds 		} else
8391da177e4SLinus Torvalds 			break;
8401da177e4SLinus Torvalds 	}
8411da177e4SLinus Torvalds 	return i;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds static int strn_len(const char __user * user_buffer, unsigned int maxlen)
8451da177e4SLinus Torvalds {
8461da177e4SLinus Torvalds 	int i = 0;
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 	for (; i < maxlen; i++) {
8491da177e4SLinus Torvalds 		char c;
8501da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8511da177e4SLinus Torvalds 			return -EFAULT;
8521da177e4SLinus Torvalds 		switch (c) {
8531da177e4SLinus Torvalds 		case '\"':
8541da177e4SLinus Torvalds 		case '\n':
8551da177e4SLinus Torvalds 		case '\r':
8561da177e4SLinus Torvalds 		case '\t':
8571da177e4SLinus Torvalds 		case ' ':
8581da177e4SLinus Torvalds 			goto done_str;
8591da177e4SLinus Torvalds 			break;
8601da177e4SLinus Torvalds 		default:
8611da177e4SLinus Torvalds 			break;
8623ff50b79SStephen Hemminger 		}
8631da177e4SLinus Torvalds 	}
8641da177e4SLinus Torvalds done_str:
8651da177e4SLinus Torvalds 	return i;
8661da177e4SLinus Torvalds }
8671da177e4SLinus Torvalds 
868ca6549afSSteven Whitehouse static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
869ca6549afSSteven Whitehouse {
870ca6549afSSteven Whitehouse 	unsigned n = 0;
871ca6549afSSteven Whitehouse 	char c;
872ca6549afSSteven Whitehouse 	ssize_t i = 0;
873ca6549afSSteven Whitehouse 	int len;
874ca6549afSSteven Whitehouse 
875ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = 0;
876ca6549afSSteven Whitehouse 	do {
877ca6549afSSteven Whitehouse 		__u32 tmp;
8781ca7768cSFrancesco Fondelli 		len = hex32_arg(&buffer[i], 8, &tmp);
879ca6549afSSteven Whitehouse 		if (len <= 0)
880ca6549afSSteven Whitehouse 			return len;
881ca6549afSSteven Whitehouse 		pkt_dev->labels[n] = htonl(tmp);
882ca6549afSSteven Whitehouse 		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
883ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
884ca6549afSSteven Whitehouse 		i += len;
885ca6549afSSteven Whitehouse 		if (get_user(c, &buffer[i]))
886ca6549afSSteven Whitehouse 			return -EFAULT;
887ca6549afSSteven Whitehouse 		i++;
888ca6549afSSteven Whitehouse 		n++;
889ca6549afSSteven Whitehouse 		if (n >= MAX_MPLS_LABELS)
890ca6549afSSteven Whitehouse 			return -E2BIG;
891ca6549afSSteven Whitehouse 	} while (c == ',');
892ca6549afSSteven Whitehouse 
893ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = n;
894ca6549afSSteven Whitehouse 	return i;
895ca6549afSSteven Whitehouse }
896ca6549afSSteven Whitehouse 
897222f1806SLuiz Capitulino static ssize_t pktgen_if_write(struct file *file,
898222f1806SLuiz Capitulino 			       const char __user * user_buffer, size_t count,
899222f1806SLuiz Capitulino 			       loff_t * offset)
9001da177e4SLinus Torvalds {
901d50a6b56SStephen Hemminger 	struct seq_file *seq = (struct seq_file *)file->private_data;
902d50a6b56SStephen Hemminger 	struct pktgen_dev *pkt_dev = seq->private;
9031da177e4SLinus Torvalds 	int i = 0, max, len;
9041da177e4SLinus Torvalds 	char name[16], valstr[32];
9051da177e4SLinus Torvalds 	unsigned long value = 0;
9061da177e4SLinus Torvalds 	char *pg_result = NULL;
9071da177e4SLinus Torvalds 	int tmp = 0;
9081da177e4SLinus Torvalds 	char buf[128];
9091da177e4SLinus Torvalds 
9101da177e4SLinus Torvalds 	pg_result = &(pkt_dev->result[0]);
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	if (count < 1) {
91325a8b254SDavid S. Miller 		printk(KERN_WARNING "pktgen: wrong command format\n");
9141da177e4SLinus Torvalds 		return -EINVAL;
9151da177e4SLinus Torvalds 	}
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	max = count - i;
9181da177e4SLinus Torvalds 	tmp = count_trail_chars(&user_buffer[i], max);
9191da177e4SLinus Torvalds 	if (tmp < 0) {
92025a8b254SDavid S. Miller 		printk(KERN_WARNING "pktgen: illegal format\n");
9211da177e4SLinus Torvalds 		return tmp;
9221da177e4SLinus Torvalds 	}
9231da177e4SLinus Torvalds 	i += tmp;
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds 	/* Read variable name */
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
928222f1806SLuiz Capitulino 	if (len < 0) {
929222f1806SLuiz Capitulino 		return len;
930222f1806SLuiz Capitulino 	}
9311da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
9321da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
9331da177e4SLinus Torvalds 		return -EFAULT;
9341da177e4SLinus Torvalds 	i += len;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	max = count - i;
9371da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
9381da177e4SLinus Torvalds 	if (len < 0)
9391da177e4SLinus Torvalds 		return len;
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	i += len;
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds 	if (debug) {
9441da177e4SLinus Torvalds 		char tb[count + 1];
9451da177e4SLinus Torvalds 		if (copy_from_user(tb, user_buffer, count))
9461da177e4SLinus Torvalds 			return -EFAULT;
9471da177e4SLinus Torvalds 		tb[count] = 0;
94825a8b254SDavid S. Miller 		printk(KERN_DEBUG "pktgen: %s,%lu  buffer -:%s:-\n", name,
949d50a6b56SStephen Hemminger 		       (unsigned long)count, tb);
9501da177e4SLinus Torvalds 	}
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	if (!strcmp(name, "min_pkt_size")) {
9531da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
954222f1806SLuiz Capitulino 		if (len < 0) {
955222f1806SLuiz Capitulino 			return len;
956222f1806SLuiz Capitulino 		}
9571da177e4SLinus Torvalds 		i += len;
9581da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9591da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9601da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9611da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9621da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9631da177e4SLinus Torvalds 		}
964222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: min_pkt_size=%u",
965222f1806SLuiz Capitulino 			pkt_dev->min_pkt_size);
9661da177e4SLinus Torvalds 		return count;
9671da177e4SLinus Torvalds 	}
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 	if (!strcmp(name, "max_pkt_size")) {
9701da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
971222f1806SLuiz Capitulino 		if (len < 0) {
972222f1806SLuiz Capitulino 			return len;
973222f1806SLuiz Capitulino 		}
9741da177e4SLinus Torvalds 		i += len;
9751da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9761da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9771da177e4SLinus Torvalds 		if (value != pkt_dev->max_pkt_size) {
9781da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9791da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9801da177e4SLinus Torvalds 		}
981222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: max_pkt_size=%u",
982222f1806SLuiz Capitulino 			pkt_dev->max_pkt_size);
9831da177e4SLinus Torvalds 		return count;
9841da177e4SLinus Torvalds 	}
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	/* Shortcut for min = max */
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds 	if (!strcmp(name, "pkt_size")) {
9891da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
990222f1806SLuiz Capitulino 		if (len < 0) {
991222f1806SLuiz Capitulino 			return len;
992222f1806SLuiz Capitulino 		}
9931da177e4SLinus Torvalds 		i += len;
9941da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9951da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9961da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9971da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9981da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9991da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
10001da177e4SLinus Torvalds 		}
10011da177e4SLinus Torvalds 		sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
10021da177e4SLinus Torvalds 		return count;
10031da177e4SLinus Torvalds 	}
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 	if (!strcmp(name, "debug")) {
10061da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1007222f1806SLuiz Capitulino 		if (len < 0) {
1008222f1806SLuiz Capitulino 			return len;
1009222f1806SLuiz Capitulino 		}
10101da177e4SLinus Torvalds 		i += len;
10111da177e4SLinus Torvalds 		debug = value;
10121da177e4SLinus Torvalds 		sprintf(pg_result, "OK: debug=%u", debug);
10131da177e4SLinus Torvalds 		return count;
10141da177e4SLinus Torvalds 	}
10151da177e4SLinus Torvalds 
10161da177e4SLinus Torvalds 	if (!strcmp(name, "frags")) {
10171da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1018222f1806SLuiz Capitulino 		if (len < 0) {
1019222f1806SLuiz Capitulino 			return len;
1020222f1806SLuiz Capitulino 		}
10211da177e4SLinus Torvalds 		i += len;
10221da177e4SLinus Torvalds 		pkt_dev->nfrags = value;
10231da177e4SLinus Torvalds 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
10241da177e4SLinus Torvalds 		return count;
10251da177e4SLinus Torvalds 	}
10261da177e4SLinus Torvalds 	if (!strcmp(name, "delay")) {
10271da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1028222f1806SLuiz Capitulino 		if (len < 0) {
1029222f1806SLuiz Capitulino 			return len;
1030222f1806SLuiz Capitulino 		}
10311da177e4SLinus Torvalds 		i += len;
10321da177e4SLinus Torvalds 		if (value == 0x7FFFFFFF) {
10331da177e4SLinus Torvalds 			pkt_dev->delay_us = 0x7FFFFFFF;
10341da177e4SLinus Torvalds 			pkt_dev->delay_ns = 0;
10351da177e4SLinus Torvalds 		} else {
10361da177e4SLinus Torvalds 			pkt_dev->delay_us = value / 1000;
10371da177e4SLinus Torvalds 			pkt_dev->delay_ns = value % 1000;
10381da177e4SLinus Torvalds 		}
1039222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: delay=%u",
1040222f1806SLuiz Capitulino 			1000 * pkt_dev->delay_us + pkt_dev->delay_ns);
10411da177e4SLinus Torvalds 		return count;
10421da177e4SLinus Torvalds 	}
10431da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_min")) {
10441da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1045222f1806SLuiz Capitulino 		if (len < 0) {
1046222f1806SLuiz Capitulino 			return len;
1047222f1806SLuiz Capitulino 		}
10481da177e4SLinus Torvalds 		i += len;
10491da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_min) {
10501da177e4SLinus Torvalds 			pkt_dev->udp_src_min = value;
10511da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10521da177e4SLinus Torvalds 		}
10531da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
10541da177e4SLinus Torvalds 		return count;
10551da177e4SLinus Torvalds 	}
10561da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_min")) {
10571da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1058222f1806SLuiz Capitulino 		if (len < 0) {
1059222f1806SLuiz Capitulino 			return len;
1060222f1806SLuiz Capitulino 		}
10611da177e4SLinus Torvalds 		i += len;
10621da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_min) {
10631da177e4SLinus Torvalds 			pkt_dev->udp_dst_min = value;
10641da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10651da177e4SLinus Torvalds 		}
10661da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
10671da177e4SLinus Torvalds 		return count;
10681da177e4SLinus Torvalds 	}
10691da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_max")) {
10701da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1071222f1806SLuiz Capitulino 		if (len < 0) {
1072222f1806SLuiz Capitulino 			return len;
1073222f1806SLuiz Capitulino 		}
10741da177e4SLinus Torvalds 		i += len;
10751da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_max) {
10761da177e4SLinus Torvalds 			pkt_dev->udp_src_max = value;
10771da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10781da177e4SLinus Torvalds 		}
10791da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
10801da177e4SLinus Torvalds 		return count;
10811da177e4SLinus Torvalds 	}
10821da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_max")) {
10831da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1084222f1806SLuiz Capitulino 		if (len < 0) {
1085222f1806SLuiz Capitulino 			return len;
1086222f1806SLuiz Capitulino 		}
10871da177e4SLinus Torvalds 		i += len;
10881da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_max) {
10891da177e4SLinus Torvalds 			pkt_dev->udp_dst_max = value;
10901da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10911da177e4SLinus Torvalds 		}
10921da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
10931da177e4SLinus Torvalds 		return count;
10941da177e4SLinus Torvalds 	}
10951da177e4SLinus Torvalds 	if (!strcmp(name, "clone_skb")) {
10961da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1097222f1806SLuiz Capitulino 		if (len < 0) {
1098222f1806SLuiz Capitulino 			return len;
1099222f1806SLuiz Capitulino 		}
11001da177e4SLinus Torvalds 		i += len;
11011da177e4SLinus Torvalds 		pkt_dev->clone_skb = value;
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 		sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
11041da177e4SLinus Torvalds 		return count;
11051da177e4SLinus Torvalds 	}
11061da177e4SLinus Torvalds 	if (!strcmp(name, "count")) {
11071da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1108222f1806SLuiz Capitulino 		if (len < 0) {
1109222f1806SLuiz Capitulino 			return len;
1110222f1806SLuiz Capitulino 		}
11111da177e4SLinus Torvalds 		i += len;
11121da177e4SLinus Torvalds 		pkt_dev->count = value;
11131da177e4SLinus Torvalds 		sprintf(pg_result, "OK: count=%llu",
11141da177e4SLinus Torvalds 			(unsigned long long)pkt_dev->count);
11151da177e4SLinus Torvalds 		return count;
11161da177e4SLinus Torvalds 	}
11171da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac_count")) {
11181da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1119222f1806SLuiz Capitulino 		if (len < 0) {
1120222f1806SLuiz Capitulino 			return len;
1121222f1806SLuiz Capitulino 		}
11221da177e4SLinus Torvalds 		i += len;
11231da177e4SLinus Torvalds 		if (pkt_dev->src_mac_count != value) {
11241da177e4SLinus Torvalds 			pkt_dev->src_mac_count = value;
11251da177e4SLinus Torvalds 			pkt_dev->cur_src_mac_offset = 0;
11261da177e4SLinus Torvalds 		}
1127222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: src_mac_count=%d",
1128222f1806SLuiz Capitulino 			pkt_dev->src_mac_count);
11291da177e4SLinus Torvalds 		return count;
11301da177e4SLinus Torvalds 	}
11311da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac_count")) {
11321da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1133222f1806SLuiz Capitulino 		if (len < 0) {
1134222f1806SLuiz Capitulino 			return len;
1135222f1806SLuiz Capitulino 		}
11361da177e4SLinus Torvalds 		i += len;
11371da177e4SLinus Torvalds 		if (pkt_dev->dst_mac_count != value) {
11381da177e4SLinus Torvalds 			pkt_dev->dst_mac_count = value;
11391da177e4SLinus Torvalds 			pkt_dev->cur_dst_mac_offset = 0;
11401da177e4SLinus Torvalds 		}
1141222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: dst_mac_count=%d",
1142222f1806SLuiz Capitulino 			pkt_dev->dst_mac_count);
11431da177e4SLinus Torvalds 		return count;
11441da177e4SLinus Torvalds 	}
11451da177e4SLinus Torvalds 	if (!strcmp(name, "flag")) {
11461da177e4SLinus Torvalds 		char f[32];
11471da177e4SLinus Torvalds 		memset(f, 0, 32);
11481da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
1149222f1806SLuiz Capitulino 		if (len < 0) {
1150222f1806SLuiz Capitulino 			return len;
1151222f1806SLuiz Capitulino 		}
11521da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
11531da177e4SLinus Torvalds 			return -EFAULT;
11541da177e4SLinus Torvalds 		i += len;
11551da177e4SLinus Torvalds 		if (strcmp(f, "IPSRC_RND") == 0)
11561da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPSRC_RND;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 		else if (strcmp(f, "!IPSRC_RND") == 0)
11591da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPSRC_RND;
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds 		else if (strcmp(f, "TXSIZE_RND") == 0)
11621da177e4SLinus Torvalds 			pkt_dev->flags |= F_TXSIZE_RND;
11631da177e4SLinus Torvalds 
11641da177e4SLinus Torvalds 		else if (strcmp(f, "!TXSIZE_RND") == 0)
11651da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_TXSIZE_RND;
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 		else if (strcmp(f, "IPDST_RND") == 0)
11681da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPDST_RND;
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 		else if (strcmp(f, "!IPDST_RND") == 0)
11711da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPDST_RND;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 		else if (strcmp(f, "UDPSRC_RND") == 0)
11741da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPSRC_RND;
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPSRC_RND") == 0)
11771da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPSRC_RND;
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds 		else if (strcmp(f, "UDPDST_RND") == 0)
11801da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPDST_RND;
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPDST_RND") == 0)
11831da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPDST_RND;
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds 		else if (strcmp(f, "MACSRC_RND") == 0)
11861da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACSRC_RND;
11871da177e4SLinus Torvalds 
11881da177e4SLinus Torvalds 		else if (strcmp(f, "!MACSRC_RND") == 0)
11891da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACSRC_RND;
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 		else if (strcmp(f, "MACDST_RND") == 0)
11921da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACDST_RND;
11931da177e4SLinus Torvalds 
11941da177e4SLinus Torvalds 		else if (strcmp(f, "!MACDST_RND") == 0)
11951da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACDST_RND;
11961da177e4SLinus Torvalds 
1197ca6549afSSteven Whitehouse 		else if (strcmp(f, "MPLS_RND") == 0)
1198ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
1199ca6549afSSteven Whitehouse 
1200ca6549afSSteven Whitehouse 		else if (strcmp(f, "!MPLS_RND") == 0)
1201ca6549afSSteven Whitehouse 			pkt_dev->flags &= ~F_MPLS_RND;
1202ca6549afSSteven Whitehouse 
120334954ddcSFrancesco Fondelli 		else if (strcmp(f, "VID_RND") == 0)
120434954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_VID_RND;
120534954ddcSFrancesco Fondelli 
120634954ddcSFrancesco Fondelli 		else if (strcmp(f, "!VID_RND") == 0)
120734954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_VID_RND;
120834954ddcSFrancesco Fondelli 
120934954ddcSFrancesco Fondelli 		else if (strcmp(f, "SVID_RND") == 0)
121034954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_SVID_RND;
121134954ddcSFrancesco Fondelli 
121234954ddcSFrancesco Fondelli 		else if (strcmp(f, "!SVID_RND") == 0)
121334954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_SVID_RND;
121434954ddcSFrancesco Fondelli 
1215007a531bSJamal Hadi Salim 		else if (strcmp(f, "FLOW_SEQ") == 0)
1216007a531bSJamal Hadi Salim 			pkt_dev->flags |= F_FLOW_SEQ;
1217007a531bSJamal Hadi Salim 
1218a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
1219a553e4a6SJamal Hadi Salim 		else if (strcmp(f, "IPSEC") == 0)
1220a553e4a6SJamal Hadi Salim 			pkt_dev->flags |= F_IPSEC_ON;
1221a553e4a6SJamal Hadi Salim #endif
1222a553e4a6SJamal Hadi Salim 
12231ca7768cSFrancesco Fondelli 		else if (strcmp(f, "!IPV6") == 0)
12241ca7768cSFrancesco Fondelli 			pkt_dev->flags &= ~F_IPV6;
12251ca7768cSFrancesco Fondelli 
12261da177e4SLinus Torvalds 		else {
1227222f1806SLuiz Capitulino 			sprintf(pg_result,
1228222f1806SLuiz Capitulino 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
12291da177e4SLinus Torvalds 				f,
12301ca7768cSFrancesco Fondelli 				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
1231a553e4a6SJamal Hadi Salim 				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, IPSEC\n");
12321da177e4SLinus Torvalds 			return count;
12331da177e4SLinus Torvalds 		}
12341da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
12351da177e4SLinus Torvalds 		return count;
12361da177e4SLinus Torvalds 	}
12371da177e4SLinus Torvalds 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
12381da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
1239222f1806SLuiz Capitulino 		if (len < 0) {
1240222f1806SLuiz Capitulino 			return len;
1241222f1806SLuiz Capitulino 		}
12421da177e4SLinus Torvalds 
12431da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
12441da177e4SLinus Torvalds 			return -EFAULT;
12451da177e4SLinus Torvalds 		buf[len] = 0;
12461da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
12471da177e4SLinus Torvalds 			memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
12481da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_min, buf, len);
12491da177e4SLinus Torvalds 			pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
12501da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_min;
12511da177e4SLinus Torvalds 		}
12521da177e4SLinus Torvalds 		if (debug)
125325a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: dst_min set to: %s\n",
1254222f1806SLuiz Capitulino 			       pkt_dev->dst_min);
12551da177e4SLinus Torvalds 		i += len;
12561da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
12571da177e4SLinus Torvalds 		return count;
12581da177e4SLinus Torvalds 	}
12591da177e4SLinus Torvalds 	if (!strcmp(name, "dst_max")) {
12601da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
1261222f1806SLuiz Capitulino 		if (len < 0) {
1262222f1806SLuiz Capitulino 			return len;
1263222f1806SLuiz Capitulino 		}
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
12661da177e4SLinus Torvalds 			return -EFAULT;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 		buf[len] = 0;
12691da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
12701da177e4SLinus Torvalds 			memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
12711da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_max, buf, len);
12721da177e4SLinus Torvalds 			pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
12731da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_max;
12741da177e4SLinus Torvalds 		}
12751da177e4SLinus Torvalds 		if (debug)
127625a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: dst_max set to: %s\n",
1277222f1806SLuiz Capitulino 			       pkt_dev->dst_max);
12781da177e4SLinus Torvalds 		i += len;
12791da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
12801da177e4SLinus Torvalds 		return count;
12811da177e4SLinus Torvalds 	}
12821da177e4SLinus Torvalds 	if (!strcmp(name, "dst6")) {
12831da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1284222f1806SLuiz Capitulino 		if (len < 0)
1285222f1806SLuiz Capitulino 			return len;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
12901da177e4SLinus Torvalds 			return -EFAULT;
12911da177e4SLinus Torvalds 		buf[len] = 0;
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 		scan_ip6(buf, pkt_dev->in6_daddr.s6_addr);
12941da177e4SLinus Torvalds 		fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 		ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr);
12971da177e4SLinus Torvalds 
12981da177e4SLinus Torvalds 		if (debug)
129925a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: dst6 set to: %s\n", buf);
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 		i += len;
13021da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6=%s", buf);
13031da177e4SLinus Torvalds 		return count;
13041da177e4SLinus Torvalds 	}
13051da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_min")) {
13061da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1307222f1806SLuiz Capitulino 		if (len < 0)
1308222f1806SLuiz Capitulino 			return len;
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13111da177e4SLinus Torvalds 
13121da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13131da177e4SLinus Torvalds 			return -EFAULT;
13141da177e4SLinus Torvalds 		buf[len] = 0;
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds 		scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
13171da177e4SLinus Torvalds 		fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
13181da177e4SLinus Torvalds 
1319222f1806SLuiz Capitulino 		ipv6_addr_copy(&pkt_dev->cur_in6_daddr,
1320222f1806SLuiz Capitulino 			       &pkt_dev->min_in6_daddr);
13211da177e4SLinus Torvalds 		if (debug)
132225a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: dst6_min set to: %s\n", buf);
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 		i += len;
13251da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_min=%s", buf);
13261da177e4SLinus Torvalds 		return count;
13271da177e4SLinus Torvalds 	}
13281da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_max")) {
13291da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1330222f1806SLuiz Capitulino 		if (len < 0)
1331222f1806SLuiz Capitulino 			return len;
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13361da177e4SLinus Torvalds 			return -EFAULT;
13371da177e4SLinus Torvalds 		buf[len] = 0;
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 		scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
13401da177e4SLinus Torvalds 		fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 		if (debug)
134325a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: dst6_max set to: %s\n", buf);
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 		i += len;
13461da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_max=%s", buf);
13471da177e4SLinus Torvalds 		return count;
13481da177e4SLinus Torvalds 	}
13491da177e4SLinus Torvalds 	if (!strcmp(name, "src6")) {
13501da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1351222f1806SLuiz Capitulino 		if (len < 0)
1352222f1806SLuiz Capitulino 			return len;
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13571da177e4SLinus Torvalds 			return -EFAULT;
13581da177e4SLinus Torvalds 		buf[len] = 0;
13591da177e4SLinus Torvalds 
13601da177e4SLinus Torvalds 		scan_ip6(buf, pkt_dev->in6_saddr.s6_addr);
13611da177e4SLinus Torvalds 		fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 		ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr);
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (debug)
136625a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: src6 set to: %s\n", buf);
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 		i += len;
13691da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src6=%s", buf);
13701da177e4SLinus Torvalds 		return count;
13711da177e4SLinus Torvalds 	}
13721da177e4SLinus Torvalds 	if (!strcmp(name, "src_min")) {
13731da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
1374222f1806SLuiz Capitulino 		if (len < 0) {
1375222f1806SLuiz Capitulino 			return len;
1376222f1806SLuiz Capitulino 		}
13771da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13781da177e4SLinus Torvalds 			return -EFAULT;
13791da177e4SLinus Torvalds 		buf[len] = 0;
13801da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_min) != 0) {
13811da177e4SLinus Torvalds 			memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
13821da177e4SLinus Torvalds 			strncpy(pkt_dev->src_min, buf, len);
13831da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
13841da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_min;
13851da177e4SLinus Torvalds 		}
13861da177e4SLinus Torvalds 		if (debug)
138725a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: src_min set to: %s\n",
1388222f1806SLuiz Capitulino 			       pkt_dev->src_min);
13891da177e4SLinus Torvalds 		i += len;
13901da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
13911da177e4SLinus Torvalds 		return count;
13921da177e4SLinus Torvalds 	}
13931da177e4SLinus Torvalds 	if (!strcmp(name, "src_max")) {
13941da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
1395222f1806SLuiz Capitulino 		if (len < 0) {
1396222f1806SLuiz Capitulino 			return len;
1397222f1806SLuiz Capitulino 		}
13981da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13991da177e4SLinus Torvalds 			return -EFAULT;
14001da177e4SLinus Torvalds 		buf[len] = 0;
14011da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_max) != 0) {
14021da177e4SLinus Torvalds 			memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
14031da177e4SLinus Torvalds 			strncpy(pkt_dev->src_max, buf, len);
14041da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
14051da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_max;
14061da177e4SLinus Torvalds 		}
14071da177e4SLinus Torvalds 		if (debug)
140825a8b254SDavid S. Miller 			printk(KERN_DEBUG "pktgen: src_max set to: %s\n",
1409222f1806SLuiz Capitulino 			       pkt_dev->src_max);
14101da177e4SLinus Torvalds 		i += len;
14111da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
14121da177e4SLinus Torvalds 		return count;
14131da177e4SLinus Torvalds 	}
14141da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac")) {
14151da177e4SLinus Torvalds 		char *v = valstr;
1416f404e9a6SKris Katterjohn 		unsigned char old_dmac[ETH_ALEN];
14171da177e4SLinus Torvalds 		unsigned char *m = pkt_dev->dst_mac;
1418f404e9a6SKris Katterjohn 		memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
14191da177e4SLinus Torvalds 
14201da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1421222f1806SLuiz Capitulino 		if (len < 0) {
1422222f1806SLuiz Capitulino 			return len;
1423222f1806SLuiz Capitulino 		}
14241da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
14251da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
14261da177e4SLinus Torvalds 			return -EFAULT;
14271da177e4SLinus Torvalds 		i += len;
14281da177e4SLinus Torvalds 
14291da177e4SLinus Torvalds 		for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) {
14301da177e4SLinus Torvalds 			if (*v >= '0' && *v <= '9') {
14311da177e4SLinus Torvalds 				*m *= 16;
14321da177e4SLinus Torvalds 				*m += *v - '0';
14331da177e4SLinus Torvalds 			}
14341da177e4SLinus Torvalds 			if (*v >= 'A' && *v <= 'F') {
14351da177e4SLinus Torvalds 				*m *= 16;
14361da177e4SLinus Torvalds 				*m += *v - 'A' + 10;
14371da177e4SLinus Torvalds 			}
14381da177e4SLinus Torvalds 			if (*v >= 'a' && *v <= 'f') {
14391da177e4SLinus Torvalds 				*m *= 16;
14401da177e4SLinus Torvalds 				*m += *v - 'a' + 10;
14411da177e4SLinus Torvalds 			}
14421da177e4SLinus Torvalds 			if (*v == ':') {
14431da177e4SLinus Torvalds 				m++;
14441da177e4SLinus Torvalds 				*m = 0;
14451da177e4SLinus Torvalds 			}
14461da177e4SLinus Torvalds 		}
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds 		/* Set up Dest MAC */
1449f404e9a6SKris Katterjohn 		if (compare_ether_addr(old_dmac, pkt_dev->dst_mac))
1450f404e9a6SKris Katterjohn 			memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dstmac");
14531da177e4SLinus Torvalds 		return count;
14541da177e4SLinus Torvalds 	}
14551da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac")) {
14561da177e4SLinus Torvalds 		char *v = valstr;
1457*ce5d0b47SAdit Ranadive 		unsigned char old_smac[ETH_ALEN];
14581da177e4SLinus Torvalds 		unsigned char *m = pkt_dev->src_mac;
14591da177e4SLinus Torvalds 
1460*ce5d0b47SAdit Ranadive 		memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN);
1461*ce5d0b47SAdit Ranadive 
14621da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1463222f1806SLuiz Capitulino 		if (len < 0) {
1464222f1806SLuiz Capitulino 			return len;
1465222f1806SLuiz Capitulino 		}
14661da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
14671da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
14681da177e4SLinus Torvalds 			return -EFAULT;
14691da177e4SLinus Torvalds 		i += len;
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds 		for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) {
14721da177e4SLinus Torvalds 			if (*v >= '0' && *v <= '9') {
14731da177e4SLinus Torvalds 				*m *= 16;
14741da177e4SLinus Torvalds 				*m += *v - '0';
14751da177e4SLinus Torvalds 			}
14761da177e4SLinus Torvalds 			if (*v >= 'A' && *v <= 'F') {
14771da177e4SLinus Torvalds 				*m *= 16;
14781da177e4SLinus Torvalds 				*m += *v - 'A' + 10;
14791da177e4SLinus Torvalds 			}
14801da177e4SLinus Torvalds 			if (*v >= 'a' && *v <= 'f') {
14811da177e4SLinus Torvalds 				*m *= 16;
14821da177e4SLinus Torvalds 				*m += *v - 'a' + 10;
14831da177e4SLinus Torvalds 			}
14841da177e4SLinus Torvalds 			if (*v == ':') {
14851da177e4SLinus Torvalds 				m++;
14861da177e4SLinus Torvalds 				*m = 0;
14871da177e4SLinus Torvalds 			}
14881da177e4SLinus Torvalds 		}
14891da177e4SLinus Torvalds 
1490*ce5d0b47SAdit Ranadive 		/* Set up Src MAC */
1491*ce5d0b47SAdit Ranadive 		if (compare_ether_addr(old_smac, pkt_dev->src_mac))
1492*ce5d0b47SAdit Ranadive 			memcpy(&(pkt_dev->hh[6]), pkt_dev->src_mac, ETH_ALEN);
1493*ce5d0b47SAdit Ranadive 
14941da177e4SLinus Torvalds 		sprintf(pg_result, "OK: srcmac");
14951da177e4SLinus Torvalds 		return count;
14961da177e4SLinus Torvalds 	}
14971da177e4SLinus Torvalds 
14981da177e4SLinus Torvalds 	if (!strcmp(name, "clear_counters")) {
14991da177e4SLinus Torvalds 		pktgen_clear_counters(pkt_dev);
15001da177e4SLinus Torvalds 		sprintf(pg_result, "OK: Clearing counters.\n");
15011da177e4SLinus Torvalds 		return count;
15021da177e4SLinus Torvalds 	}
15031da177e4SLinus Torvalds 
15041da177e4SLinus Torvalds 	if (!strcmp(name, "flows")) {
15051da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1506222f1806SLuiz Capitulino 		if (len < 0) {
1507222f1806SLuiz Capitulino 			return len;
1508222f1806SLuiz Capitulino 		}
15091da177e4SLinus Torvalds 		i += len;
15101da177e4SLinus Torvalds 		if (value > MAX_CFLOWS)
15111da177e4SLinus Torvalds 			value = MAX_CFLOWS;
15121da177e4SLinus Torvalds 
15131da177e4SLinus Torvalds 		pkt_dev->cflows = value;
15141da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
15151da177e4SLinus Torvalds 		return count;
15161da177e4SLinus Torvalds 	}
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds 	if (!strcmp(name, "flowlen")) {
15191da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
1520222f1806SLuiz Capitulino 		if (len < 0) {
1521222f1806SLuiz Capitulino 			return len;
1522222f1806SLuiz Capitulino 		}
15231da177e4SLinus Torvalds 		i += len;
15241da177e4SLinus Torvalds 		pkt_dev->lflow = value;
15251da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
15261da177e4SLinus Torvalds 		return count;
15271da177e4SLinus Torvalds 	}
15281da177e4SLinus Torvalds 
1529ca6549afSSteven Whitehouse 	if (!strcmp(name, "mpls")) {
1530ca6549afSSteven Whitehouse 		unsigned n, offset;
1531ca6549afSSteven Whitehouse 		len = get_labels(&user_buffer[i], pkt_dev);
1532ca6549afSSteven Whitehouse 		if (len < 0) { return len; }
1533ca6549afSSteven Whitehouse 		i += len;
1534ca6549afSSteven Whitehouse 		offset = sprintf(pg_result, "OK: mpls=");
1535ca6549afSSteven Whitehouse 		for (n = 0; n < pkt_dev->nr_labels; n++)
1536ca6549afSSteven Whitehouse 			offset += sprintf(pg_result + offset,
1537ca6549afSSteven Whitehouse 					  "%08x%s", ntohl(pkt_dev->labels[n]),
1538ca6549afSSteven Whitehouse 					  n == pkt_dev->nr_labels-1 ? "" : ",");
153934954ddcSFrancesco Fondelli 
154034954ddcSFrancesco Fondelli 		if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
154134954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
154234954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
154334954ddcSFrancesco Fondelli 
154434954ddcSFrancesco Fondelli 			if (debug)
154525a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: VLAN/SVLAN auto turned off\n");
154634954ddcSFrancesco Fondelli 		}
154734954ddcSFrancesco Fondelli 		return count;
154834954ddcSFrancesco Fondelli 	}
154934954ddcSFrancesco Fondelli 
155034954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_id")) {
155134954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
155234954ddcSFrancesco Fondelli 		if (len < 0) {
155334954ddcSFrancesco Fondelli 			return len;
155434954ddcSFrancesco Fondelli 		}
155534954ddcSFrancesco Fondelli 		i += len;
155634954ddcSFrancesco Fondelli 		if (value <= 4095) {
155734954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = value;  /* turn on VLAN */
155834954ddcSFrancesco Fondelli 
155934954ddcSFrancesco Fondelli 			if (debug)
156025a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: VLAN turned on\n");
156134954ddcSFrancesco Fondelli 
156234954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
156325a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: MPLS auto turned off\n");
156434954ddcSFrancesco Fondelli 
156534954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
156634954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
156734954ddcSFrancesco Fondelli 		} else {
156834954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
156934954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
157034954ddcSFrancesco Fondelli 
157134954ddcSFrancesco Fondelli 			if (debug)
157225a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: VLAN/SVLAN turned off\n");
157334954ddcSFrancesco Fondelli 		}
157434954ddcSFrancesco Fondelli 		return count;
157534954ddcSFrancesco Fondelli 	}
157634954ddcSFrancesco Fondelli 
157734954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_p")) {
157834954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
157934954ddcSFrancesco Fondelli 		if (len < 0) {
158034954ddcSFrancesco Fondelli 			return len;
158134954ddcSFrancesco Fondelli 		}
158234954ddcSFrancesco Fondelli 		i += len;
158334954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
158434954ddcSFrancesco Fondelli 			pkt_dev->vlan_p = value;
158534954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
158634954ddcSFrancesco Fondelli 		} else {
158734954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_p must be 0-7");
158834954ddcSFrancesco Fondelli 		}
158934954ddcSFrancesco Fondelli 		return count;
159034954ddcSFrancesco Fondelli 	}
159134954ddcSFrancesco Fondelli 
159234954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_cfi")) {
159334954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
159434954ddcSFrancesco Fondelli 		if (len < 0) {
159534954ddcSFrancesco Fondelli 			return len;
159634954ddcSFrancesco Fondelli 		}
159734954ddcSFrancesco Fondelli 		i += len;
159834954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
159934954ddcSFrancesco Fondelli 			pkt_dev->vlan_cfi = value;
160034954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
160134954ddcSFrancesco Fondelli 		} else {
160234954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
160334954ddcSFrancesco Fondelli 		}
160434954ddcSFrancesco Fondelli 		return count;
160534954ddcSFrancesco Fondelli 	}
160634954ddcSFrancesco Fondelli 
160734954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_id")) {
160834954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
160934954ddcSFrancesco Fondelli 		if (len < 0) {
161034954ddcSFrancesco Fondelli 			return len;
161134954ddcSFrancesco Fondelli 		}
161234954ddcSFrancesco Fondelli 		i += len;
161334954ddcSFrancesco Fondelli 		if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
161434954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = value;  /* turn on SVLAN */
161534954ddcSFrancesco Fondelli 
161634954ddcSFrancesco Fondelli 			if (debug)
161725a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: SVLAN turned on\n");
161834954ddcSFrancesco Fondelli 
161934954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
162025a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: MPLS auto turned off\n");
162134954ddcSFrancesco Fondelli 
162234954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
162334954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
162434954ddcSFrancesco Fondelli 		} else {
162534954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
162634954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
162734954ddcSFrancesco Fondelli 
162834954ddcSFrancesco Fondelli 			if (debug)
162925a8b254SDavid S. Miller 				printk(KERN_DEBUG "pktgen: VLAN/SVLAN turned off\n");
163034954ddcSFrancesco Fondelli 		}
163134954ddcSFrancesco Fondelli 		return count;
163234954ddcSFrancesco Fondelli 	}
163334954ddcSFrancesco Fondelli 
163434954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_p")) {
163534954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
163634954ddcSFrancesco Fondelli 		if (len < 0) {
163734954ddcSFrancesco Fondelli 			return len;
163834954ddcSFrancesco Fondelli 		}
163934954ddcSFrancesco Fondelli 		i += len;
164034954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
164134954ddcSFrancesco Fondelli 			pkt_dev->svlan_p = value;
164234954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
164334954ddcSFrancesco Fondelli 		} else {
164434954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_p must be 0-7");
164534954ddcSFrancesco Fondelli 		}
164634954ddcSFrancesco Fondelli 		return count;
164734954ddcSFrancesco Fondelli 	}
164834954ddcSFrancesco Fondelli 
164934954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_cfi")) {
165034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
165134954ddcSFrancesco Fondelli 		if (len < 0) {
165234954ddcSFrancesco Fondelli 			return len;
165334954ddcSFrancesco Fondelli 		}
165434954ddcSFrancesco Fondelli 		i += len;
165534954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
165634954ddcSFrancesco Fondelli 			pkt_dev->svlan_cfi = value;
165734954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
165834954ddcSFrancesco Fondelli 		} else {
165934954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
166034954ddcSFrancesco Fondelli 		}
1661ca6549afSSteven Whitehouse 		return count;
1662ca6549afSSteven Whitehouse 	}
1663ca6549afSSteven Whitehouse 
16641ca7768cSFrancesco Fondelli 	if (!strcmp(name, "tos")) {
16651ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
16661ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
16671ca7768cSFrancesco Fondelli 		if (len < 0) {
16681ca7768cSFrancesco Fondelli 			return len;
16691ca7768cSFrancesco Fondelli 		}
16701ca7768cSFrancesco Fondelli 		i += len;
16711ca7768cSFrancesco Fondelli 		if (len == 2) {
16721ca7768cSFrancesco Fondelli 			pkt_dev->tos = tmp_value;
16731ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
16741ca7768cSFrancesco Fondelli 		} else {
16751ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: tos must be 00-ff");
16761ca7768cSFrancesco Fondelli 		}
16771ca7768cSFrancesco Fondelli 		return count;
16781ca7768cSFrancesco Fondelli 	}
16791ca7768cSFrancesco Fondelli 
16801ca7768cSFrancesco Fondelli 	if (!strcmp(name, "traffic_class")) {
16811ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
16821ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
16831ca7768cSFrancesco Fondelli 		if (len < 0) {
16841ca7768cSFrancesco Fondelli 			return len;
16851ca7768cSFrancesco Fondelli 		}
16861ca7768cSFrancesco Fondelli 		i += len;
16871ca7768cSFrancesco Fondelli 		if (len == 2) {
16881ca7768cSFrancesco Fondelli 			pkt_dev->traffic_class = tmp_value;
16891ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
16901ca7768cSFrancesco Fondelli 		} else {
16911ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
16921ca7768cSFrancesco Fondelli 		}
16931ca7768cSFrancesco Fondelli 		return count;
16941ca7768cSFrancesco Fondelli 	}
16951ca7768cSFrancesco Fondelli 
16961da177e4SLinus Torvalds 	sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
16971da177e4SLinus Torvalds 	return -EINVAL;
16981da177e4SLinus Torvalds }
16991da177e4SLinus Torvalds 
1700d50a6b56SStephen Hemminger static int pktgen_if_open(struct inode *inode, struct file *file)
17011da177e4SLinus Torvalds {
1702d50a6b56SStephen Hemminger 	return single_open(file, pktgen_if_show, PDE(inode)->data);
17031da177e4SLinus Torvalds }
17041da177e4SLinus Torvalds 
17059a32144eSArjan van de Ven static const struct file_operations pktgen_if_fops = {
1706d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1707d50a6b56SStephen Hemminger 	.open    = pktgen_if_open,
1708d50a6b56SStephen Hemminger 	.read    = seq_read,
1709d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1710d50a6b56SStephen Hemminger 	.write   = pktgen_if_write,
1711d50a6b56SStephen Hemminger 	.release = single_release,
1712d50a6b56SStephen Hemminger };
1713d50a6b56SStephen Hemminger 
1714d50a6b56SStephen Hemminger static int pktgen_thread_show(struct seq_file *seq, void *v)
1715d50a6b56SStephen Hemminger {
1716d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1717c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
1718d50a6b56SStephen Hemminger 
1719d50a6b56SStephen Hemminger 	BUG_ON(!t);
1720d50a6b56SStephen Hemminger 
1721d50a6b56SStephen Hemminger 	seq_printf(seq, "Name: %s  max_before_softirq: %d\n",
1722ee74baa7SDavid S. Miller 		   t->tsk->comm, t->max_before_softirq);
17231da177e4SLinus Torvalds 
1724d50a6b56SStephen Hemminger 	seq_printf(seq, "Running: ");
17251da177e4SLinus Torvalds 
17261da177e4SLinus Torvalds 	if_lock(t);
1727c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list)
17281da177e4SLinus Torvalds 		if (pkt_dev->running)
172939df232fSStephen Hemminger 			seq_printf(seq, "%s ", pkt_dev->odev->name);
17301da177e4SLinus Torvalds 
1731d50a6b56SStephen Hemminger 	seq_printf(seq, "\nStopped: ");
17321da177e4SLinus Torvalds 
1733c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list)
17341da177e4SLinus Torvalds 		if (!pkt_dev->running)
173539df232fSStephen Hemminger 			seq_printf(seq, "%s ", pkt_dev->odev->name);
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds 	if (t->result[0])
1738d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: %s\n", t->result);
17391da177e4SLinus Torvalds 	else
1740d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: NA\n");
17411da177e4SLinus Torvalds 
17421da177e4SLinus Torvalds 	if_unlock(t);
17431da177e4SLinus Torvalds 
1744d50a6b56SStephen Hemminger 	return 0;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds 
1747d50a6b56SStephen Hemminger static ssize_t pktgen_thread_write(struct file *file,
1748d50a6b56SStephen Hemminger 				   const char __user * user_buffer,
1749d50a6b56SStephen Hemminger 				   size_t count, loff_t * offset)
17501da177e4SLinus Torvalds {
1751d50a6b56SStephen Hemminger 	struct seq_file *seq = (struct seq_file *)file->private_data;
1752d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
17531da177e4SLinus Torvalds 	int i = 0, max, len, ret;
17541da177e4SLinus Torvalds 	char name[40];
17551da177e4SLinus Torvalds 	char *pg_result;
17561da177e4SLinus Torvalds 	unsigned long value = 0;
17571da177e4SLinus Torvalds 
17581da177e4SLinus Torvalds 	if (count < 1) {
17591da177e4SLinus Torvalds 		//      sprintf(pg_result, "Wrong command format");
17601da177e4SLinus Torvalds 		return -EINVAL;
17611da177e4SLinus Torvalds 	}
17621da177e4SLinus Torvalds 
17631da177e4SLinus Torvalds 	max = count - i;
17641da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
17651da177e4SLinus Torvalds 	if (len < 0)
17661da177e4SLinus Torvalds 		return len;
17671da177e4SLinus Torvalds 
17681da177e4SLinus Torvalds 	i += len;
17691da177e4SLinus Torvalds 
17701da177e4SLinus Torvalds 	/* Read variable name */
17711da177e4SLinus Torvalds 
17721da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
17731da177e4SLinus Torvalds 	if (len < 0)
17741da177e4SLinus Torvalds 		return len;
17751da177e4SLinus Torvalds 
17761da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
17771da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
17781da177e4SLinus Torvalds 		return -EFAULT;
17791da177e4SLinus Torvalds 	i += len;
17801da177e4SLinus Torvalds 
17811da177e4SLinus Torvalds 	max = count - i;
17821da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
17831da177e4SLinus Torvalds 	if (len < 0)
17841da177e4SLinus Torvalds 		return len;
17851da177e4SLinus Torvalds 
17861da177e4SLinus Torvalds 	i += len;
17871da177e4SLinus Torvalds 
17881da177e4SLinus Torvalds 	if (debug)
178925a8b254SDavid S. Miller 		printk(KERN_DEBUG "pktgen: t=%s, count=%lu\n",
179025a8b254SDavid S. Miller 		       name, (unsigned long)count);
17911da177e4SLinus Torvalds 
17921da177e4SLinus Torvalds 	if (!t) {
179325a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: No thread\n");
17941da177e4SLinus Torvalds 		ret = -EINVAL;
17951da177e4SLinus Torvalds 		goto out;
17961da177e4SLinus Torvalds 	}
17971da177e4SLinus Torvalds 
17981da177e4SLinus Torvalds 	pg_result = &(t->result[0]);
17991da177e4SLinus Torvalds 
18001da177e4SLinus Torvalds 	if (!strcmp(name, "add_device")) {
18011da177e4SLinus Torvalds 		char f[32];
18021da177e4SLinus Torvalds 		memset(f, 0, 32);
18031da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
18041da177e4SLinus Torvalds 		if (len < 0) {
18051da177e4SLinus Torvalds 			ret = len;
18061da177e4SLinus Torvalds 			goto out;
18071da177e4SLinus Torvalds 		}
18081da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
18091da177e4SLinus Torvalds 			return -EFAULT;
18101da177e4SLinus Torvalds 		i += len;
18116146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
18121da177e4SLinus Torvalds 		pktgen_add_device(t, f);
18136146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
18141da177e4SLinus Torvalds 		ret = count;
18151da177e4SLinus Torvalds 		sprintf(pg_result, "OK: add_device=%s", f);
18161da177e4SLinus Torvalds 		goto out;
18171da177e4SLinus Torvalds 	}
18181da177e4SLinus Torvalds 
18191da177e4SLinus Torvalds 	if (!strcmp(name, "rem_device_all")) {
18206146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
182195ed63f7SArthur Kepner 		t->control |= T_REMDEVALL;
18226146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1823121caf57SNishanth Aravamudan 		schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
18241da177e4SLinus Torvalds 		ret = count;
18251da177e4SLinus Torvalds 		sprintf(pg_result, "OK: rem_device_all");
18261da177e4SLinus Torvalds 		goto out;
18271da177e4SLinus Torvalds 	}
18281da177e4SLinus Torvalds 
18291da177e4SLinus Torvalds 	if (!strcmp(name, "max_before_softirq")) {
18301da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
18316146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
18321da177e4SLinus Torvalds 		t->max_before_softirq = value;
18336146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
18341da177e4SLinus Torvalds 		ret = count;
18351da177e4SLinus Torvalds 		sprintf(pg_result, "OK: max_before_softirq=%lu", value);
18361da177e4SLinus Torvalds 		goto out;
18371da177e4SLinus Torvalds 	}
18381da177e4SLinus Torvalds 
18391da177e4SLinus Torvalds 	ret = -EINVAL;
18401da177e4SLinus Torvalds out:
18411da177e4SLinus Torvalds 	return ret;
18421da177e4SLinus Torvalds }
18431da177e4SLinus Torvalds 
1844d50a6b56SStephen Hemminger static int pktgen_thread_open(struct inode *inode, struct file *file)
18451da177e4SLinus Torvalds {
1846d50a6b56SStephen Hemminger 	return single_open(file, pktgen_thread_show, PDE(inode)->data);
18471da177e4SLinus Torvalds }
18481da177e4SLinus Torvalds 
18499a32144eSArjan van de Ven static const struct file_operations pktgen_thread_fops = {
1850d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1851d50a6b56SStephen Hemminger 	.open    = pktgen_thread_open,
1852d50a6b56SStephen Hemminger 	.read    = seq_read,
1853d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1854d50a6b56SStephen Hemminger 	.write   = pktgen_thread_write,
1855d50a6b56SStephen Hemminger 	.release = single_release,
1856d50a6b56SStephen Hemminger };
18571da177e4SLinus Torvalds 
18581da177e4SLinus Torvalds /* Think find or remove for NN */
18591da177e4SLinus Torvalds static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
18601da177e4SLinus Torvalds {
18611da177e4SLinus Torvalds 	struct pktgen_thread *t;
18621da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
18631da177e4SLinus Torvalds 
1864cdcdbe0bSLuiz Capitulino 	list_for_each_entry(t, &pktgen_threads, th_list) {
18651da177e4SLinus Torvalds 		pkt_dev = pktgen_find_dev(t, ifname);
18661da177e4SLinus Torvalds 		if (pkt_dev) {
18671da177e4SLinus Torvalds 			if (remove) {
18681da177e4SLinus Torvalds 				if_lock(t);
186995ed63f7SArthur Kepner 				pkt_dev->removal_mark = 1;
187095ed63f7SArthur Kepner 				t->control |= T_REMDEV;
18711da177e4SLinus Torvalds 				if_unlock(t);
18721da177e4SLinus Torvalds 			}
18731da177e4SLinus Torvalds 			break;
18741da177e4SLinus Torvalds 		}
18751da177e4SLinus Torvalds 	}
18761da177e4SLinus Torvalds 	return pkt_dev;
18771da177e4SLinus Torvalds }
18781da177e4SLinus Torvalds 
187995ed63f7SArthur Kepner /*
188095ed63f7SArthur Kepner  * mark a device for removal
188195ed63f7SArthur Kepner  */
188239df232fSStephen Hemminger static void pktgen_mark_device(const char *ifname)
18831da177e4SLinus Torvalds {
18841da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
188595ed63f7SArthur Kepner 	const int max_tries = 10, msec_per_try = 125;
188695ed63f7SArthur Kepner 	int i = 0;
188795ed63f7SArthur Kepner 
18886146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
188925c4e53aSStephen Hemminger 	pr_debug("pktgen: pktgen_mark_device marking %s for removal\n", ifname);
189095ed63f7SArthur Kepner 
189195ed63f7SArthur Kepner 	while (1) {
189295ed63f7SArthur Kepner 
189395ed63f7SArthur Kepner 		pkt_dev = __pktgen_NN_threads(ifname, REMOVE);
1894222f1806SLuiz Capitulino 		if (pkt_dev == NULL)
1895222f1806SLuiz Capitulino 			break;	/* success */
189695ed63f7SArthur Kepner 
18976146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
189825c4e53aSStephen Hemminger 		pr_debug("pktgen: pktgen_mark_device waiting for %s "
189925c4e53aSStephen Hemminger 				"to disappear....\n", ifname);
190095ed63f7SArthur Kepner 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
19016146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
190295ed63f7SArthur Kepner 
190395ed63f7SArthur Kepner 		if (++i >= max_tries) {
190425a8b254SDavid S. Miller 			printk(KERN_ERR "pktgen_mark_device: timed out after "
190525a8b254SDavid S. Miller 			       "waiting %d msec for device %s to be removed\n",
190695ed63f7SArthur Kepner 			       msec_per_try * i, ifname);
190795ed63f7SArthur Kepner 			break;
190895ed63f7SArthur Kepner 		}
190995ed63f7SArthur Kepner 
191095ed63f7SArthur Kepner 	}
191195ed63f7SArthur Kepner 
19126146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
191339df232fSStephen Hemminger }
191495ed63f7SArthur Kepner 
191539df232fSStephen Hemminger static void pktgen_change_name(struct net_device *dev)
191639df232fSStephen Hemminger {
191739df232fSStephen Hemminger 	struct pktgen_thread *t;
191839df232fSStephen Hemminger 
191939df232fSStephen Hemminger 	list_for_each_entry(t, &pktgen_threads, th_list) {
192039df232fSStephen Hemminger 		struct pktgen_dev *pkt_dev;
192139df232fSStephen Hemminger 
192239df232fSStephen Hemminger 		list_for_each_entry(pkt_dev, &t->if_list, list) {
192339df232fSStephen Hemminger 			if (pkt_dev->odev != dev)
192439df232fSStephen Hemminger 				continue;
192539df232fSStephen Hemminger 
192639df232fSStephen Hemminger 			remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
192739df232fSStephen Hemminger 
192839df232fSStephen Hemminger 			pkt_dev->entry = create_proc_entry(dev->name, 0600,
192939df232fSStephen Hemminger 							   pg_proc_dir);
193039df232fSStephen Hemminger 			if (!pkt_dev->entry)
193139df232fSStephen Hemminger 				printk(KERN_ERR "pktgen: can't move proc "
193239df232fSStephen Hemminger 				       " entry for '%s'\n", dev->name);
193339df232fSStephen Hemminger 			break;
193439df232fSStephen Hemminger 		}
193539df232fSStephen Hemminger 	}
19361da177e4SLinus Torvalds }
19371da177e4SLinus Torvalds 
1938222f1806SLuiz Capitulino static int pktgen_device_event(struct notifier_block *unused,
1939222f1806SLuiz Capitulino 			       unsigned long event, void *ptr)
19401da177e4SLinus Torvalds {
194139df232fSStephen Hemminger 	struct net_device *dev = ptr;
19421da177e4SLinus Torvalds 
19431da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
19441da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
19451da177e4SLinus Torvalds 	 */
19461da177e4SLinus Torvalds 
19471da177e4SLinus Torvalds 	switch (event) {
194839df232fSStephen Hemminger 	case NETDEV_CHANGENAME:
194939df232fSStephen Hemminger 		pktgen_change_name(dev);
19501da177e4SLinus Torvalds 		break;
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
195395ed63f7SArthur Kepner 		pktgen_mark_device(dev->name);
19541da177e4SLinus Torvalds 		break;
19553ff50b79SStephen Hemminger 	}
19561da177e4SLinus Torvalds 
19571da177e4SLinus Torvalds 	return NOTIFY_DONE;
19581da177e4SLinus Torvalds }
19591da177e4SLinus Torvalds 
19601da177e4SLinus Torvalds /* Associate pktgen_dev with a device. */
19611da177e4SLinus Torvalds 
196239df232fSStephen Hemminger static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname)
1963222f1806SLuiz Capitulino {
19641da177e4SLinus Torvalds 	struct net_device *odev;
196539df232fSStephen Hemminger 	int err;
19661da177e4SLinus Torvalds 
19671da177e4SLinus Torvalds 	/* Clean old setups */
19681da177e4SLinus Torvalds 	if (pkt_dev->odev) {
19691da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
19701da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
19711da177e4SLinus Torvalds 	}
19721da177e4SLinus Torvalds 
197339df232fSStephen Hemminger 	odev = dev_get_by_name(ifname);
19741da177e4SLinus Torvalds 	if (!odev) {
197525a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: no such netdevice: \"%s\"\n", ifname);
197639df232fSStephen Hemminger 		return -ENODEV;
19771da177e4SLinus Torvalds 	}
197839df232fSStephen Hemminger 
19791da177e4SLinus Torvalds 	if (odev->type != ARPHRD_ETHER) {
198025a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: not an ethernet device: \"%s\"\n", ifname);
198139df232fSStephen Hemminger 		err = -EINVAL;
198239df232fSStephen Hemminger 	} else if (!netif_running(odev)) {
198325a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: device is down: \"%s\"\n", ifname);
198439df232fSStephen Hemminger 		err = -ENETDOWN;
198539df232fSStephen Hemminger 	} else {
19861da177e4SLinus Torvalds 		pkt_dev->odev = odev;
198739df232fSStephen Hemminger 		return 0;
198839df232fSStephen Hemminger 	}
19891da177e4SLinus Torvalds 
19901da177e4SLinus Torvalds 	dev_put(odev);
199139df232fSStephen Hemminger 	return err;
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds 
19941da177e4SLinus Torvalds /* Read pkt_dev from the interface and set up internal pktgen_dev
19951da177e4SLinus Torvalds  * structure to have the right information to create/send packets
19961da177e4SLinus Torvalds  */
19971da177e4SLinus Torvalds static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	if (!pkt_dev->odev) {
200025a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: pkt_dev->odev == NULL in "
200125a8b254SDavid S. Miller 		       "setup_inject.\n");
2002222f1806SLuiz Capitulino 		sprintf(pkt_dev->result,
2003222f1806SLuiz Capitulino 			"ERROR: pkt_dev->odev == NULL in setup_inject.\n");
20041da177e4SLinus Torvalds 		return;
20051da177e4SLinus Torvalds 	}
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 	/* Default to the interface's mac if not explicitly set. */
20081da177e4SLinus Torvalds 
2009f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
2010f404e9a6SKris Katterjohn 		memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN);
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds 	/* Set up Dest MAC */
2013f404e9a6SKris Katterjohn 	memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
20141da177e4SLinus Torvalds 
20151da177e4SLinus Torvalds 	/* Set up pkt size */
20161da177e4SLinus Torvalds 	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
20171da177e4SLinus Torvalds 
20181da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
20191da177e4SLinus Torvalds 		/*
20201da177e4SLinus Torvalds 		 * Skip this automatic address setting until locks or functions
20211da177e4SLinus Torvalds 		 * gets exported
20221da177e4SLinus Torvalds 		 */
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds #ifdef NOTNOW
20251da177e4SLinus Torvalds 		int i, set = 0, err = 1;
20261da177e4SLinus Torvalds 		struct inet6_dev *idev;
20271da177e4SLinus Torvalds 
20281da177e4SLinus Torvalds 		for (i = 0; i < IN6_ADDR_HSIZE; i++)
20291da177e4SLinus Torvalds 			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
20301da177e4SLinus Torvalds 				set = 1;
20311da177e4SLinus Torvalds 				break;
20321da177e4SLinus Torvalds 			}
20331da177e4SLinus Torvalds 
20341da177e4SLinus Torvalds 		if (!set) {
20351da177e4SLinus Torvalds 
20361da177e4SLinus Torvalds 			/*
20371da177e4SLinus Torvalds 			 * Use linklevel address if unconfigured.
20381da177e4SLinus Torvalds 			 *
20391da177e4SLinus Torvalds 			 * use ipv6_get_lladdr if/when it's get exported
20401da177e4SLinus Torvalds 			 */
20411da177e4SLinus Torvalds 
20428814c4b5SYOSHIFUJI Hideaki 			rcu_read_lock();
20431da177e4SLinus Torvalds 			if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) {
20441da177e4SLinus Torvalds 				struct inet6_ifaddr *ifp;
20451da177e4SLinus Torvalds 
20461da177e4SLinus Torvalds 				read_lock_bh(&idev->lock);
2047222f1806SLuiz Capitulino 				for (ifp = idev->addr_list; ifp;
2048222f1806SLuiz Capitulino 				     ifp = ifp->if_next) {
2049222f1806SLuiz Capitulino 					if (ifp->scope == IFA_LINK
2050222f1806SLuiz Capitulino 					    && !(ifp->
2051222f1806SLuiz Capitulino 						 flags & IFA_F_TENTATIVE)) {
2052222f1806SLuiz Capitulino 						ipv6_addr_copy(&pkt_dev->
2053222f1806SLuiz Capitulino 							       cur_in6_saddr,
2054222f1806SLuiz Capitulino 							       &ifp->addr);
20551da177e4SLinus Torvalds 						err = 0;
20561da177e4SLinus Torvalds 						break;
20571da177e4SLinus Torvalds 					}
20581da177e4SLinus Torvalds 				}
20591da177e4SLinus Torvalds 				read_unlock_bh(&idev->lock);
20601da177e4SLinus Torvalds 			}
20618814c4b5SYOSHIFUJI Hideaki 			rcu_read_unlock();
2062222f1806SLuiz Capitulino 			if (err)
206325a8b254SDavid S. Miller 				printk(KERN_ERR "pktgen: ERROR: IPv6 link "
206425a8b254SDavid S. Miller 				       "address not availble.\n");
20651da177e4SLinus Torvalds 		}
20661da177e4SLinus Torvalds #endif
2067222f1806SLuiz Capitulino 	} else {
20681da177e4SLinus Torvalds 		pkt_dev->saddr_min = 0;
20691da177e4SLinus Torvalds 		pkt_dev->saddr_max = 0;
20701da177e4SLinus Torvalds 		if (strlen(pkt_dev->src_min) == 0) {
20711da177e4SLinus Torvalds 
20721da177e4SLinus Torvalds 			struct in_device *in_dev;
20731da177e4SLinus Torvalds 
20741da177e4SLinus Torvalds 			rcu_read_lock();
2075e5ed6399SHerbert Xu 			in_dev = __in_dev_get_rcu(pkt_dev->odev);
20761da177e4SLinus Torvalds 			if (in_dev) {
20771da177e4SLinus Torvalds 				if (in_dev->ifa_list) {
2078222f1806SLuiz Capitulino 					pkt_dev->saddr_min =
2079222f1806SLuiz Capitulino 					    in_dev->ifa_list->ifa_address;
20801da177e4SLinus Torvalds 					pkt_dev->saddr_max = pkt_dev->saddr_min;
20811da177e4SLinus Torvalds 				}
20821da177e4SLinus Torvalds 			}
20831da177e4SLinus Torvalds 			rcu_read_unlock();
2084222f1806SLuiz Capitulino 		} else {
20851da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
20861da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
20871da177e4SLinus Torvalds 		}
20881da177e4SLinus Torvalds 
20891da177e4SLinus Torvalds 		pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
20901da177e4SLinus Torvalds 		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
20911da177e4SLinus Torvalds 	}
20921da177e4SLinus Torvalds 	/* Initialize current values. */
20931da177e4SLinus Torvalds 	pkt_dev->cur_dst_mac_offset = 0;
20941da177e4SLinus Torvalds 	pkt_dev->cur_src_mac_offset = 0;
20951da177e4SLinus Torvalds 	pkt_dev->cur_saddr = pkt_dev->saddr_min;
20961da177e4SLinus Torvalds 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
20971da177e4SLinus Torvalds 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
20981da177e4SLinus Torvalds 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
20991da177e4SLinus Torvalds 	pkt_dev->nflows = 0;
21001da177e4SLinus Torvalds }
21011da177e4SLinus Torvalds 
21021da177e4SLinus Torvalds static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
21031da177e4SLinus Torvalds {
21041da177e4SLinus Torvalds 	__u64 start;
21051da177e4SLinus Torvalds 	__u64 now;
21061da177e4SLinus Torvalds 
21071da177e4SLinus Torvalds 	start = now = getCurUs();
21081da177e4SLinus Torvalds 	printk(KERN_INFO "sleeping for %d\n", (int)(spin_until_us - now));
21091da177e4SLinus Torvalds 	while (now < spin_until_us) {
2110b4099fabSStephen Hemminger 		/* TODO: optimize sleeping behavior */
2111121caf57SNishanth Aravamudan 		if (spin_until_us - now > jiffies_to_usecs(1) + 1)
2112121caf57SNishanth Aravamudan 			schedule_timeout_interruptible(1);
2113121caf57SNishanth Aravamudan 		else if (spin_until_us - now > 100) {
21141da177e4SLinus Torvalds 			do_softirq();
21151da177e4SLinus Torvalds 			if (!pkt_dev->running)
21161da177e4SLinus Torvalds 				return;
21171da177e4SLinus Torvalds 			if (need_resched())
21181da177e4SLinus Torvalds 				schedule();
21191da177e4SLinus Torvalds 		}
21201da177e4SLinus Torvalds 
21211da177e4SLinus Torvalds 		now = getCurUs();
21221da177e4SLinus Torvalds 	}
21231da177e4SLinus Torvalds 
21241da177e4SLinus Torvalds 	pkt_dev->idle_acc += now - start;
21251da177e4SLinus Torvalds }
21261da177e4SLinus Torvalds 
212716dab72fSJamal Hadi Salim static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
212816dab72fSJamal Hadi Salim {
2129a553e4a6SJamal Hadi Salim 	pkt_dev->pkt_overhead = 0;
213016dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
213116dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
213216dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
213316dab72fSJamal Hadi Salim }
213416dab72fSJamal Hadi Salim 
2135007a531bSJamal Hadi Salim static inline int f_seen(struct pktgen_dev *pkt_dev, int flow)
2136007a531bSJamal Hadi Salim {
2137007a531bSJamal Hadi Salim 
2138007a531bSJamal Hadi Salim 	if (pkt_dev->flows[flow].flags & F_INIT)
2139007a531bSJamal Hadi Salim 		return 1;
2140007a531bSJamal Hadi Salim 	else
2141007a531bSJamal Hadi Salim 		return 0;
2142007a531bSJamal Hadi Salim }
2143007a531bSJamal Hadi Salim 
2144007a531bSJamal Hadi Salim static inline int f_pick(struct pktgen_dev *pkt_dev)
2145007a531bSJamal Hadi Salim {
2146007a531bSJamal Hadi Salim 	int flow = pkt_dev->curfl;
2147007a531bSJamal Hadi Salim 
2148007a531bSJamal Hadi Salim 	if (pkt_dev->flags & F_FLOW_SEQ) {
2149007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2150007a531bSJamal Hadi Salim 			/* reset time */
2151007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
2152007a531bSJamal Hadi Salim 			pkt_dev->curfl += 1;
2153007a531bSJamal Hadi Salim 			if (pkt_dev->curfl >= pkt_dev->cflows)
2154007a531bSJamal Hadi Salim 				pkt_dev->curfl = 0; /*reset */
2155007a531bSJamal Hadi Salim 		}
2156007a531bSJamal Hadi Salim 	} else {
2157007a531bSJamal Hadi Salim 		flow = random32() % pkt_dev->cflows;
2158007a531bSJamal Hadi Salim 
2159007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count > pkt_dev->lflow)
2160007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
2161007a531bSJamal Hadi Salim 	}
2162007a531bSJamal Hadi Salim 
2163007a531bSJamal Hadi Salim 	return pkt_dev->curfl;
2164007a531bSJamal Hadi Salim }
2165007a531bSJamal Hadi Salim 
2166a553e4a6SJamal Hadi Salim 
2167a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2168a553e4a6SJamal Hadi Salim /* If there was already an IPSEC SA, we keep it as is, else
2169a553e4a6SJamal Hadi Salim  * we go look for it ...
2170a553e4a6SJamal Hadi Salim */
2171fea1ab0fSAdrian Bunk static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
2172a553e4a6SJamal Hadi Salim {
2173a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[flow].x;
2174a553e4a6SJamal Hadi Salim 	if (!x) {
2175a553e4a6SJamal Hadi Salim 		/*slow path: we dont already have xfrm_state*/
2176a553e4a6SJamal Hadi Salim 		x = xfrm_stateonly_find((xfrm_address_t *)&pkt_dev->cur_daddr,
2177a553e4a6SJamal Hadi Salim 					(xfrm_address_t *)&pkt_dev->cur_saddr,
2178a553e4a6SJamal Hadi Salim 					AF_INET,
2179a553e4a6SJamal Hadi Salim 					pkt_dev->ipsmode,
2180a553e4a6SJamal Hadi Salim 					pkt_dev->ipsproto, 0);
2181a553e4a6SJamal Hadi Salim 		if (x) {
2182a553e4a6SJamal Hadi Salim 			pkt_dev->flows[flow].x = x;
2183a553e4a6SJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
2184a553e4a6SJamal Hadi Salim 			pkt_dev->pkt_overhead+=x->props.header_len;
2185a553e4a6SJamal Hadi Salim 		}
2186a553e4a6SJamal Hadi Salim 
2187a553e4a6SJamal Hadi Salim 	}
2188a553e4a6SJamal Hadi Salim }
2189a553e4a6SJamal Hadi Salim #endif
21901da177e4SLinus Torvalds /* Increment/randomize headers according to flags and current values
21911da177e4SLinus Torvalds  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
21921da177e4SLinus Torvalds  */
2193222f1806SLuiz Capitulino static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2194222f1806SLuiz Capitulino {
21951da177e4SLinus Torvalds 	__u32 imn;
21961da177e4SLinus Torvalds 	__u32 imx;
21971da177e4SLinus Torvalds 	int flow = 0;
21981da177e4SLinus Torvalds 
2199007a531bSJamal Hadi Salim 	if (pkt_dev->cflows)
2200007a531bSJamal Hadi Salim 		flow = f_pick(pkt_dev);
22011da177e4SLinus Torvalds 
22021da177e4SLinus Torvalds 	/*  Deal with source MAC */
22031da177e4SLinus Torvalds 	if (pkt_dev->src_mac_count > 1) {
22041da177e4SLinus Torvalds 		__u32 mc;
22051da177e4SLinus Torvalds 		__u32 tmp;
22061da177e4SLinus Torvalds 
22071da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACSRC_RND)
22085fa6fc76SStephen Hemminger 			mc = random32() % pkt_dev->src_mac_count;
22091da177e4SLinus Torvalds 		else {
22101da177e4SLinus Torvalds 			mc = pkt_dev->cur_src_mac_offset++;
2211222f1806SLuiz Capitulino 			if (pkt_dev->cur_src_mac_offset >
2212222f1806SLuiz Capitulino 			    pkt_dev->src_mac_count)
22131da177e4SLinus Torvalds 				pkt_dev->cur_src_mac_offset = 0;
22141da177e4SLinus Torvalds 		}
22151da177e4SLinus Torvalds 
22161da177e4SLinus Torvalds 		tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
22171da177e4SLinus Torvalds 		pkt_dev->hh[11] = tmp;
22181da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
22191da177e4SLinus Torvalds 		pkt_dev->hh[10] = tmp;
22201da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
22211da177e4SLinus Torvalds 		pkt_dev->hh[9] = tmp;
22221da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
22231da177e4SLinus Torvalds 		pkt_dev->hh[8] = tmp;
22241da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
22251da177e4SLinus Torvalds 		pkt_dev->hh[7] = tmp;
22261da177e4SLinus Torvalds 	}
22271da177e4SLinus Torvalds 
22281da177e4SLinus Torvalds 	/*  Deal with Destination MAC */
22291da177e4SLinus Torvalds 	if (pkt_dev->dst_mac_count > 1) {
22301da177e4SLinus Torvalds 		__u32 mc;
22311da177e4SLinus Torvalds 		__u32 tmp;
22321da177e4SLinus Torvalds 
22331da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACDST_RND)
22345fa6fc76SStephen Hemminger 			mc = random32() % pkt_dev->dst_mac_count;
22351da177e4SLinus Torvalds 
22361da177e4SLinus Torvalds 		else {
22371da177e4SLinus Torvalds 			mc = pkt_dev->cur_dst_mac_offset++;
2238222f1806SLuiz Capitulino 			if (pkt_dev->cur_dst_mac_offset >
2239222f1806SLuiz Capitulino 			    pkt_dev->dst_mac_count) {
22401da177e4SLinus Torvalds 				pkt_dev->cur_dst_mac_offset = 0;
22411da177e4SLinus Torvalds 			}
22421da177e4SLinus Torvalds 		}
22431da177e4SLinus Torvalds 
22441da177e4SLinus Torvalds 		tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
22451da177e4SLinus Torvalds 		pkt_dev->hh[5] = tmp;
22461da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
22471da177e4SLinus Torvalds 		pkt_dev->hh[4] = tmp;
22481da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
22491da177e4SLinus Torvalds 		pkt_dev->hh[3] = tmp;
22501da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
22511da177e4SLinus Torvalds 		pkt_dev->hh[2] = tmp;
22521da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
22531da177e4SLinus Torvalds 		pkt_dev->hh[1] = tmp;
22541da177e4SLinus Torvalds 	}
22551da177e4SLinus Torvalds 
2256ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND) {
2257ca6549afSSteven Whitehouse 		unsigned i;
2258ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
2259ca6549afSSteven Whitehouse 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2260ca6549afSSteven Whitehouse 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
22615fa6fc76SStephen Hemminger 					     ((__force __be32)random32() &
2262ca6549afSSteven Whitehouse 						      htonl(0x000fffff));
2263ca6549afSSteven Whitehouse 	}
2264ca6549afSSteven Whitehouse 
226534954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
22665fa6fc76SStephen Hemminger 		pkt_dev->vlan_id = random32() & (4096-1);
226734954ddcSFrancesco Fondelli 	}
226834954ddcSFrancesco Fondelli 
226934954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
22705fa6fc76SStephen Hemminger 		pkt_dev->svlan_id = random32() & (4096 - 1);
227134954ddcSFrancesco Fondelli 	}
227234954ddcSFrancesco Fondelli 
22731da177e4SLinus Torvalds 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
22741da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPSRC_RND)
22755fa6fc76SStephen Hemminger 			pkt_dev->cur_udp_src = random32() %
22765fa6fc76SStephen Hemminger 				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)
22775fa6fc76SStephen Hemminger 				+ pkt_dev->udp_src_min;
22781da177e4SLinus Torvalds 
22791da177e4SLinus Torvalds 		else {
22801da177e4SLinus Torvalds 			pkt_dev->cur_udp_src++;
22811da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
22821da177e4SLinus Torvalds 				pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
22831da177e4SLinus Torvalds 		}
22841da177e4SLinus Torvalds 	}
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
22871da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPDST_RND) {
22885fa6fc76SStephen Hemminger 			pkt_dev->cur_udp_dst = random32() %
22895fa6fc76SStephen Hemminger 				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
22905fa6fc76SStephen Hemminger 				+ pkt_dev->udp_dst_min;
2291222f1806SLuiz Capitulino 		} else {
22921da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst++;
22931da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
22941da177e4SLinus Torvalds 				pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
22951da177e4SLinus Torvalds 		}
22961da177e4SLinus Torvalds 	}
22971da177e4SLinus Torvalds 
22981da177e4SLinus Torvalds 	if (!(pkt_dev->flags & F_IPV6)) {
22991da177e4SLinus Torvalds 
2300222f1806SLuiz Capitulino 		if ((imn = ntohl(pkt_dev->saddr_min)) < (imx =
2301222f1806SLuiz Capitulino 							 ntohl(pkt_dev->
2302222f1806SLuiz Capitulino 							       saddr_max))) {
23031da177e4SLinus Torvalds 			__u32 t;
23041da177e4SLinus Torvalds 			if (pkt_dev->flags & F_IPSRC_RND)
23055fa6fc76SStephen Hemminger 				t = random32() % (imx - imn) + imn;
23061da177e4SLinus Torvalds 			else {
23071da177e4SLinus Torvalds 				t = ntohl(pkt_dev->cur_saddr);
23081da177e4SLinus Torvalds 				t++;
23091da177e4SLinus Torvalds 				if (t > imx) {
23101da177e4SLinus Torvalds 					t = imn;
23111da177e4SLinus Torvalds 				}
23121da177e4SLinus Torvalds 			}
23131da177e4SLinus Torvalds 			pkt_dev->cur_saddr = htonl(t);
23141da177e4SLinus Torvalds 		}
23151da177e4SLinus Torvalds 
2316007a531bSJamal Hadi Salim 		if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
23171da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
23181da177e4SLinus Torvalds 		} else {
2319252e3346SAl Viro 			imn = ntohl(pkt_dev->daddr_min);
2320252e3346SAl Viro 			imx = ntohl(pkt_dev->daddr_max);
2321252e3346SAl Viro 			if (imn < imx) {
23221da177e4SLinus Torvalds 				__u32 t;
2323252e3346SAl Viro 				__be32 s;
23241da177e4SLinus Torvalds 				if (pkt_dev->flags & F_IPDST_RND) {
23251da177e4SLinus Torvalds 
23265fa6fc76SStephen Hemminger 					t = random32() % (imx - imn) + imn;
2327252e3346SAl Viro 					s = htonl(t);
23281da177e4SLinus Torvalds 
2329252e3346SAl Viro 					while (LOOPBACK(s) || MULTICAST(s)
2330252e3346SAl Viro 					       || BADCLASS(s) || ZERONET(s)
2331252e3346SAl Viro 					       || LOCAL_MCAST(s)) {
23325fa6fc76SStephen Hemminger 						t = random32() % (imx - imn) + imn;
2333252e3346SAl Viro 						s = htonl(t);
23341da177e4SLinus Torvalds 					}
2335252e3346SAl Viro 					pkt_dev->cur_daddr = s;
2336252e3346SAl Viro 				} else {
23371da177e4SLinus Torvalds 					t = ntohl(pkt_dev->cur_daddr);
23381da177e4SLinus Torvalds 					t++;
23391da177e4SLinus Torvalds 					if (t > imx) {
23401da177e4SLinus Torvalds 						t = imn;
23411da177e4SLinus Torvalds 					}
23421da177e4SLinus Torvalds 					pkt_dev->cur_daddr = htonl(t);
23431da177e4SLinus Torvalds 				}
23441da177e4SLinus Torvalds 			}
23451da177e4SLinus Torvalds 			if (pkt_dev->cflows) {
2346007a531bSJamal Hadi Salim 				pkt_dev->flows[flow].flags |= F_INIT;
2347222f1806SLuiz Capitulino 				pkt_dev->flows[flow].cur_daddr =
2348222f1806SLuiz Capitulino 				    pkt_dev->cur_daddr;
2349a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2350a553e4a6SJamal Hadi Salim 				if (pkt_dev->flags & F_IPSEC_ON)
2351a553e4a6SJamal Hadi Salim 					get_ipsec_sa(pkt_dev, flow);
2352a553e4a6SJamal Hadi Salim #endif
23531da177e4SLinus Torvalds 				pkt_dev->nflows++;
23541da177e4SLinus Torvalds 			}
23551da177e4SLinus Torvalds 		}
2356222f1806SLuiz Capitulino 	} else {		/* IPV6 * */
2357222f1806SLuiz Capitulino 
23581da177e4SLinus Torvalds 		if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
23591da177e4SLinus Torvalds 		    pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
23601da177e4SLinus Torvalds 		    pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
23611da177e4SLinus Torvalds 		    pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ;
23621da177e4SLinus Torvalds 		else {
23631da177e4SLinus Torvalds 			int i;
23641da177e4SLinus Torvalds 
23651da177e4SLinus Torvalds 			/* Only random destinations yet */
23661da177e4SLinus Torvalds 
23671da177e4SLinus Torvalds 			for (i = 0; i < 4; i++) {
23681da177e4SLinus Torvalds 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
23695fa6fc76SStephen Hemminger 				    (((__force __be32)random32() |
23701da177e4SLinus Torvalds 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
23711da177e4SLinus Torvalds 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
23721da177e4SLinus Torvalds 			}
23731da177e4SLinus Torvalds 		}
23741da177e4SLinus Torvalds 	}
23751da177e4SLinus Torvalds 
23761da177e4SLinus Torvalds 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
23771da177e4SLinus Torvalds 		__u32 t;
23781da177e4SLinus Torvalds 		if (pkt_dev->flags & F_TXSIZE_RND) {
23795fa6fc76SStephen Hemminger 			t = random32() %
23805fa6fc76SStephen Hemminger 				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
23815fa6fc76SStephen Hemminger 				+ pkt_dev->min_pkt_size;
2382222f1806SLuiz Capitulino 		} else {
23831da177e4SLinus Torvalds 			t = pkt_dev->cur_pkt_size + 1;
23841da177e4SLinus Torvalds 			if (t > pkt_dev->max_pkt_size)
23851da177e4SLinus Torvalds 				t = pkt_dev->min_pkt_size;
23861da177e4SLinus Torvalds 		}
23871da177e4SLinus Torvalds 		pkt_dev->cur_pkt_size = t;
23881da177e4SLinus Torvalds 	}
23891da177e4SLinus Torvalds 
23901da177e4SLinus Torvalds 	pkt_dev->flows[flow].count++;
23911da177e4SLinus Torvalds }
23921da177e4SLinus Torvalds 
2393a553e4a6SJamal Hadi Salim 
2394a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2395a553e4a6SJamal Hadi Salim static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
2396a553e4a6SJamal Hadi Salim {
2397a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2398a553e4a6SJamal Hadi Salim 	int err = 0;
2399a553e4a6SJamal Hadi Salim 	struct iphdr *iph;
2400a553e4a6SJamal Hadi Salim 
2401a553e4a6SJamal Hadi Salim 	if (!x)
2402a553e4a6SJamal Hadi Salim 		return 0;
2403a553e4a6SJamal Hadi Salim 	/* XXX: we dont support tunnel mode for now until
2404a553e4a6SJamal Hadi Salim 	 * we resolve the dst issue */
2405a553e4a6SJamal Hadi Salim 	if (x->props.mode != XFRM_MODE_TRANSPORT)
2406a553e4a6SJamal Hadi Salim 		return 0;
2407a553e4a6SJamal Hadi Salim 
2408a553e4a6SJamal Hadi Salim 	spin_lock(&x->lock);
2409a553e4a6SJamal Hadi Salim 	iph = ip_hdr(skb);
2410a553e4a6SJamal Hadi Salim 
2411a553e4a6SJamal Hadi Salim 	err = x->mode->output(x, skb);
2412a553e4a6SJamal Hadi Salim 	if (err)
2413a553e4a6SJamal Hadi Salim 		goto error;
2414a553e4a6SJamal Hadi Salim 	err = x->type->output(x, skb);
2415a553e4a6SJamal Hadi Salim 	if (err)
2416a553e4a6SJamal Hadi Salim 		goto error;
2417a553e4a6SJamal Hadi Salim 
2418a553e4a6SJamal Hadi Salim 	x->curlft.bytes +=skb->len;
2419a553e4a6SJamal Hadi Salim 	x->curlft.packets++;
2420a553e4a6SJamal Hadi Salim 	spin_unlock(&x->lock);
2421a553e4a6SJamal Hadi Salim 
2422a553e4a6SJamal Hadi Salim error:
2423a553e4a6SJamal Hadi Salim 	spin_unlock(&x->lock);
2424a553e4a6SJamal Hadi Salim 	return err;
2425a553e4a6SJamal Hadi Salim }
2426a553e4a6SJamal Hadi Salim 
2427a553e4a6SJamal Hadi Salim static inline void free_SAs(struct pktgen_dev *pkt_dev)
2428a553e4a6SJamal Hadi Salim {
2429a553e4a6SJamal Hadi Salim 	if (pkt_dev->cflows) {
2430a553e4a6SJamal Hadi Salim 		/* let go of the SAs if we have them */
2431a553e4a6SJamal Hadi Salim 		int i = 0;
2432a553e4a6SJamal Hadi Salim 		for (;  i < pkt_dev->nflows; i++){
2433a553e4a6SJamal Hadi Salim 			struct xfrm_state *x = pkt_dev->flows[i].x;
2434a553e4a6SJamal Hadi Salim 			if (x) {
2435a553e4a6SJamal Hadi Salim 				xfrm_state_put(x);
2436a553e4a6SJamal Hadi Salim 				pkt_dev->flows[i].x = NULL;
2437a553e4a6SJamal Hadi Salim 			}
2438a553e4a6SJamal Hadi Salim 		}
2439a553e4a6SJamal Hadi Salim 	}
2440a553e4a6SJamal Hadi Salim }
2441a553e4a6SJamal Hadi Salim 
2442a553e4a6SJamal Hadi Salim static inline int process_ipsec(struct pktgen_dev *pkt_dev,
2443a553e4a6SJamal Hadi Salim 			      struct sk_buff *skb, __be16 protocol)
2444a553e4a6SJamal Hadi Salim {
2445a553e4a6SJamal Hadi Salim 	if (pkt_dev->flags & F_IPSEC_ON) {
2446a553e4a6SJamal Hadi Salim 		struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2447a553e4a6SJamal Hadi Salim 		int nhead = 0;
2448a553e4a6SJamal Hadi Salim 		if (x) {
2449a553e4a6SJamal Hadi Salim 			int ret;
2450a553e4a6SJamal Hadi Salim 			__u8 *eth;
2451a553e4a6SJamal Hadi Salim 			nhead = x->props.header_len - skb_headroom(skb);
2452a553e4a6SJamal Hadi Salim 			if (nhead >0) {
2453a553e4a6SJamal Hadi Salim 				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
2454a553e4a6SJamal Hadi Salim 				if (ret < 0) {
245525a8b254SDavid S. Miller 					printk(KERN_ERR "Error expanding "
245625a8b254SDavid S. Miller 					       "ipsec packet %d\n",ret);
2457a553e4a6SJamal Hadi Salim 					return 0;
2458a553e4a6SJamal Hadi Salim 				}
2459a553e4a6SJamal Hadi Salim 			}
2460a553e4a6SJamal Hadi Salim 
2461a553e4a6SJamal Hadi Salim 			/* ipsec is not expecting ll header */
2462a553e4a6SJamal Hadi Salim 			skb_pull(skb, ETH_HLEN);
2463a553e4a6SJamal Hadi Salim 			ret = pktgen_output_ipsec(skb, pkt_dev);
2464a553e4a6SJamal Hadi Salim 			if (ret) {
246525a8b254SDavid S. Miller 				printk(KERN_ERR "Error creating ipsec "
246625a8b254SDavid S. Miller 				       "packet %d\n",ret);
2467a553e4a6SJamal Hadi Salim 				kfree_skb(skb);
2468a553e4a6SJamal Hadi Salim 				return 0;
2469a553e4a6SJamal Hadi Salim 			}
2470a553e4a6SJamal Hadi Salim 			/* restore ll */
2471a553e4a6SJamal Hadi Salim 			eth = (__u8 *) skb_push(skb, ETH_HLEN);
2472a553e4a6SJamal Hadi Salim 			memcpy(eth, pkt_dev->hh, 12);
2473a553e4a6SJamal Hadi Salim 			*(u16 *) & eth[12] = protocol;
2474a553e4a6SJamal Hadi Salim 		}
2475a553e4a6SJamal Hadi Salim 	}
2476a553e4a6SJamal Hadi Salim 	return 1;
2477a553e4a6SJamal Hadi Salim }
2478a553e4a6SJamal Hadi Salim #endif
2479a553e4a6SJamal Hadi Salim 
2480ca6549afSSteven Whitehouse static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2481ca6549afSSteven Whitehouse {
2482ca6549afSSteven Whitehouse 	unsigned i;
2483ca6549afSSteven Whitehouse 	for (i = 0; i < pkt_dev->nr_labels; i++) {
2484ca6549afSSteven Whitehouse 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
2485ca6549afSSteven Whitehouse 	}
2486ca6549afSSteven Whitehouse 	mpls--;
2487ca6549afSSteven Whitehouse 	*mpls |= MPLS_STACK_BOTTOM;
2488ca6549afSSteven Whitehouse }
2489ca6549afSSteven Whitehouse 
24900f37c605SAl Viro static inline __be16 build_tci(unsigned int id, unsigned int cfi,
24910f37c605SAl Viro 			       unsigned int prio)
24920f37c605SAl Viro {
24930f37c605SAl Viro 	return htons(id | (cfi << 12) | (prio << 13));
24940f37c605SAl Viro }
24950f37c605SAl Viro 
24961da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
24971da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
24981da177e4SLinus Torvalds {
24991da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
25001da177e4SLinus Torvalds 	__u8 *eth;
25011da177e4SLinus Torvalds 	struct udphdr *udph;
25021da177e4SLinus Torvalds 	int datalen, iplen;
25031da177e4SLinus Torvalds 	struct iphdr *iph;
25041da177e4SLinus Torvalds 	struct pktgen_hdr *pgh = NULL;
2505d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IP);
2506ca6549afSSteven Whitehouse 	__be32 *mpls;
250734954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
250834954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
250934954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
251034954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
251134954ddcSFrancesco Fondelli 
2512ca6549afSSteven Whitehouse 
2513ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2514d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
25151da177e4SLinus Torvalds 
251634954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2517d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
251834954ddcSFrancesco Fondelli 
251964053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
252064053beeSRobert Olsson 	 * fields.
252164053beeSRobert Olsson 	 */
252264053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
252364053beeSRobert Olsson 
25247ac5459eSDavid S. Miller 	datalen = (odev->hard_header_len + 16) & ~0xf;
2525ca6549afSSteven Whitehouse 	skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen +
252616dab72fSJamal Hadi Salim 			pkt_dev->pkt_overhead, GFP_ATOMIC);
25271da177e4SLinus Torvalds 	if (!skb) {
25281da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
25291da177e4SLinus Torvalds 		return NULL;
25301da177e4SLinus Torvalds 	}
25311da177e4SLinus Torvalds 
25327ac5459eSDavid S. Miller 	skb_reserve(skb, datalen);
25331da177e4SLinus Torvalds 
25341da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
25351da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2536ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2537ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2538ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
253934954ddcSFrancesco Fondelli 
254034954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
254134954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
254234954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
25430f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
25440f37c605SAl Viro 					       pkt_dev->svlan_cfi,
25450f37c605SAl Viro 					       pkt_dev->svlan_p);
254634954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2547d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
254834954ddcSFrancesco Fondelli 		}
254934954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
25500f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
25510f37c605SAl Viro 				      pkt_dev->vlan_cfi,
25520f37c605SAl Viro 				      pkt_dev->vlan_p);
255334954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2554d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IP);
255534954ddcSFrancesco Fondelli 	}
255634954ddcSFrancesco Fondelli 
255727a884dcSArnaldo Carvalho de Melo 	skb->network_header = skb->tail;
2558b0e380b1SArnaldo Carvalho de Melo 	skb->transport_header = skb->network_header + sizeof(struct iphdr);
2559ddc7b8e3SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr));
2560ddc7b8e3SArnaldo Carvalho de Melo 
2561ddc7b8e3SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
2562ddc7b8e3SArnaldo Carvalho de Melo 	udph = udp_hdr(skb);
25631da177e4SLinus Torvalds 
25641da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2565252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
25661da177e4SLinus Torvalds 
2567ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2568ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
256916dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
25701da177e4SLinus Torvalds 	if (datalen < sizeof(struct pktgen_hdr))
25711da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
25721da177e4SLinus Torvalds 
25731da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
25741da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
25751da177e4SLinus Torvalds 	udph->len = htons(datalen + 8);	/* DATA + udphdr */
25761da177e4SLinus Torvalds 	udph->check = 0;	/* No checksum */
25771da177e4SLinus Torvalds 
25781da177e4SLinus Torvalds 	iph->ihl = 5;
25791da177e4SLinus Torvalds 	iph->version = 4;
25801da177e4SLinus Torvalds 	iph->ttl = 32;
25811ca7768cSFrancesco Fondelli 	iph->tos = pkt_dev->tos;
25821da177e4SLinus Torvalds 	iph->protocol = IPPROTO_UDP;	/* UDP */
25831da177e4SLinus Torvalds 	iph->saddr = pkt_dev->cur_saddr;
25841da177e4SLinus Torvalds 	iph->daddr = pkt_dev->cur_daddr;
25851da177e4SLinus Torvalds 	iph->frag_off = 0;
25861da177e4SLinus Torvalds 	iplen = 20 + 8 + datalen;
25871da177e4SLinus Torvalds 	iph->tot_len = htons(iplen);
25881da177e4SLinus Torvalds 	iph->check = 0;
25891da177e4SLinus Torvalds 	iph->check = ip_fast_csum((void *)iph, iph->ihl);
2590ca6549afSSteven Whitehouse 	skb->protocol = protocol;
2591b0e380b1SArnaldo Carvalho de Melo 	skb->mac_header = (skb->network_header - ETH_HLEN -
259216dab72fSJamal Hadi Salim 			   pkt_dev->pkt_overhead);
25931da177e4SLinus Torvalds 	skb->dev = odev;
25941da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
25951da177e4SLinus Torvalds 
25961da177e4SLinus Torvalds 	if (pkt_dev->nfrags <= 0)
25971da177e4SLinus Torvalds 		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
25981da177e4SLinus Torvalds 	else {
25991da177e4SLinus Torvalds 		int frags = pkt_dev->nfrags;
26001da177e4SLinus Torvalds 		int i;
26011da177e4SLinus Torvalds 
26021da177e4SLinus Torvalds 		pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
26031da177e4SLinus Torvalds 
26041da177e4SLinus Torvalds 		if (frags > MAX_SKB_FRAGS)
26051da177e4SLinus Torvalds 			frags = MAX_SKB_FRAGS;
26061da177e4SLinus Torvalds 		if (datalen > frags * PAGE_SIZE) {
26071da177e4SLinus Torvalds 			skb_put(skb, datalen - frags * PAGE_SIZE);
26081da177e4SLinus Torvalds 			datalen = frags * PAGE_SIZE;
26091da177e4SLinus Torvalds 		}
26101da177e4SLinus Torvalds 
26111da177e4SLinus Torvalds 		i = 0;
26121da177e4SLinus Torvalds 		while (datalen > 0) {
26131da177e4SLinus Torvalds 			struct page *page = alloc_pages(GFP_KERNEL, 0);
26141da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].page = page;
26151da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].page_offset = 0;
26161da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].size =
26171da177e4SLinus Torvalds 			    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
26181da177e4SLinus Torvalds 			datalen -= skb_shinfo(skb)->frags[i].size;
26191da177e4SLinus Torvalds 			skb->len += skb_shinfo(skb)->frags[i].size;
26201da177e4SLinus Torvalds 			skb->data_len += skb_shinfo(skb)->frags[i].size;
26211da177e4SLinus Torvalds 			i++;
26221da177e4SLinus Torvalds 			skb_shinfo(skb)->nr_frags = i;
26231da177e4SLinus Torvalds 		}
26241da177e4SLinus Torvalds 
26251da177e4SLinus Torvalds 		while (i < frags) {
26261da177e4SLinus Torvalds 			int rem;
26271da177e4SLinus Torvalds 
26281da177e4SLinus Torvalds 			if (i == 0)
26291da177e4SLinus Torvalds 				break;
26301da177e4SLinus Torvalds 
26311da177e4SLinus Torvalds 			rem = skb_shinfo(skb)->frags[i - 1].size / 2;
26321da177e4SLinus Torvalds 			if (rem == 0)
26331da177e4SLinus Torvalds 				break;
26341da177e4SLinus Torvalds 
26351da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i - 1].size -= rem;
26361da177e4SLinus Torvalds 
2637222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i] =
2638222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1];
26391da177e4SLinus Torvalds 			get_page(skb_shinfo(skb)->frags[i].page);
2640222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i].page =
2641222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1].page;
2642222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i].page_offset +=
2643222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1].size;
26441da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].size = rem;
26451da177e4SLinus Torvalds 			i++;
26461da177e4SLinus Torvalds 			skb_shinfo(skb)->nr_frags = i;
26471da177e4SLinus Torvalds 		}
26481da177e4SLinus Torvalds 	}
26491da177e4SLinus Torvalds 
26501da177e4SLinus Torvalds 	/* Stamp the time, and sequence number, convert them to network byte order */
26511da177e4SLinus Torvalds 
26521da177e4SLinus Torvalds 	if (pgh) {
26531da177e4SLinus Torvalds 		struct timeval timestamp;
26541da177e4SLinus Torvalds 
26551da177e4SLinus Torvalds 		pgh->pgh_magic = htonl(PKTGEN_MAGIC);
26561da177e4SLinus Torvalds 		pgh->seq_num = htonl(pkt_dev->seq_num);
26571da177e4SLinus Torvalds 
26581da177e4SLinus Torvalds 		do_gettimeofday(&timestamp);
26591da177e4SLinus Torvalds 		pgh->tv_sec = htonl(timestamp.tv_sec);
26601da177e4SLinus Torvalds 		pgh->tv_usec = htonl(timestamp.tv_usec);
26611da177e4SLinus Torvalds 	}
26621da177e4SLinus Torvalds 
2663a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2664a553e4a6SJamal Hadi Salim 	if (!process_ipsec(pkt_dev, skb, protocol))
2665a553e4a6SJamal Hadi Salim 		return NULL;
2666a553e4a6SJamal Hadi Salim #endif
2667a553e4a6SJamal Hadi Salim 
26681da177e4SLinus Torvalds 	return skb;
26691da177e4SLinus Torvalds }
26701da177e4SLinus Torvalds 
26711da177e4SLinus Torvalds /*
26721da177e4SLinus Torvalds  * scan_ip6, fmt_ip taken from dietlibc-0.21
26731da177e4SLinus Torvalds  * Author Felix von Leitner <felix-dietlibc@fefe.de>
26741da177e4SLinus Torvalds  *
26751da177e4SLinus Torvalds  * Slightly modified for kernel.
26761da177e4SLinus Torvalds  * Should be candidate for net/ipv4/utils.c
26771da177e4SLinus Torvalds  * --ro
26781da177e4SLinus Torvalds  */
26791da177e4SLinus Torvalds 
26801da177e4SLinus Torvalds static unsigned int scan_ip6(const char *s, char ip[16])
26811da177e4SLinus Torvalds {
26821da177e4SLinus Torvalds 	unsigned int i;
26831da177e4SLinus Torvalds 	unsigned int len = 0;
26841da177e4SLinus Torvalds 	unsigned long u;
26851da177e4SLinus Torvalds 	char suffix[16];
26861da177e4SLinus Torvalds 	unsigned int prefixlen = 0;
26871da177e4SLinus Torvalds 	unsigned int suffixlen = 0;
2688252e3346SAl Viro 	__be32 tmp;
26891da177e4SLinus Torvalds 
2690222f1806SLuiz Capitulino 	for (i = 0; i < 16; i++)
2691222f1806SLuiz Capitulino 		ip[i] = 0;
26921da177e4SLinus Torvalds 
26931da177e4SLinus Torvalds 	for (;;) {
26941da177e4SLinus Torvalds 		if (*s == ':') {
26951da177e4SLinus Torvalds 			len++;
26961da177e4SLinus Torvalds 			if (s[1] == ':') {	/* Found "::", skip to part 2 */
26971da177e4SLinus Torvalds 				s += 2;
26981da177e4SLinus Torvalds 				len++;
26991da177e4SLinus Torvalds 				break;
27001da177e4SLinus Torvalds 			}
27011da177e4SLinus Torvalds 			s++;
27021da177e4SLinus Torvalds 		}
27031da177e4SLinus Torvalds 		{
27041da177e4SLinus Torvalds 			char *tmp;
27051da177e4SLinus Torvalds 			u = simple_strtoul(s, &tmp, 16);
27061da177e4SLinus Torvalds 			i = tmp - s;
27071da177e4SLinus Torvalds 		}
27081da177e4SLinus Torvalds 
2709222f1806SLuiz Capitulino 		if (!i)
2710222f1806SLuiz Capitulino 			return 0;
27111da177e4SLinus Torvalds 		if (prefixlen == 12 && s[i] == '.') {
27121da177e4SLinus Torvalds 
27131da177e4SLinus Torvalds 			/* the last 4 bytes may be written as IPv4 address */
27141da177e4SLinus Torvalds 
27151da177e4SLinus Torvalds 			tmp = in_aton(s);
27161da177e4SLinus Torvalds 			memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp));
27171da177e4SLinus Torvalds 			return i + len;
27181da177e4SLinus Torvalds 		}
27191da177e4SLinus Torvalds 		ip[prefixlen++] = (u >> 8);
27201da177e4SLinus Torvalds 		ip[prefixlen++] = (u & 255);
2721222f1806SLuiz Capitulino 		s += i;
2722222f1806SLuiz Capitulino 		len += i;
27231da177e4SLinus Torvalds 		if (prefixlen == 16)
27241da177e4SLinus Torvalds 			return len;
27251da177e4SLinus Torvalds 	}
27261da177e4SLinus Torvalds 
27271da177e4SLinus Torvalds /* part 2, after "::" */
27281da177e4SLinus Torvalds 	for (;;) {
27291da177e4SLinus Torvalds 		if (*s == ':') {
27301da177e4SLinus Torvalds 			if (suffixlen == 0)
27311da177e4SLinus Torvalds 				break;
27321da177e4SLinus Torvalds 			s++;
27331da177e4SLinus Torvalds 			len++;
27341da177e4SLinus Torvalds 		} else if (suffixlen != 0)
27351da177e4SLinus Torvalds 			break;
27361da177e4SLinus Torvalds 		{
27371da177e4SLinus Torvalds 			char *tmp;
27381da177e4SLinus Torvalds 			u = simple_strtol(s, &tmp, 16);
27391da177e4SLinus Torvalds 			i = tmp - s;
27401da177e4SLinus Torvalds 		}
27411da177e4SLinus Torvalds 		if (!i) {
2742222f1806SLuiz Capitulino 			if (*s)
2743222f1806SLuiz Capitulino 				len--;
27441da177e4SLinus Torvalds 			break;
27451da177e4SLinus Torvalds 		}
27461da177e4SLinus Torvalds 		if (suffixlen + prefixlen <= 12 && s[i] == '.') {
27471da177e4SLinus Torvalds 			tmp = in_aton(s);
2748222f1806SLuiz Capitulino 			memcpy((struct in_addr *)(suffix + suffixlen), &tmp,
2749222f1806SLuiz Capitulino 			       sizeof(tmp));
27501da177e4SLinus Torvalds 			suffixlen += 4;
27511da177e4SLinus Torvalds 			len += strlen(s);
27521da177e4SLinus Torvalds 			break;
27531da177e4SLinus Torvalds 		}
27541da177e4SLinus Torvalds 		suffix[suffixlen++] = (u >> 8);
27551da177e4SLinus Torvalds 		suffix[suffixlen++] = (u & 255);
2756222f1806SLuiz Capitulino 		s += i;
2757222f1806SLuiz Capitulino 		len += i;
27581da177e4SLinus Torvalds 		if (prefixlen + suffixlen == 16)
27591da177e4SLinus Torvalds 			break;
27601da177e4SLinus Torvalds 	}
27611da177e4SLinus Torvalds 	for (i = 0; i < suffixlen; i++)
27621da177e4SLinus Torvalds 		ip[16 - suffixlen + i] = suffix[i];
27631da177e4SLinus Torvalds 	return len;
27641da177e4SLinus Torvalds }
27651da177e4SLinus Torvalds 
2766222f1806SLuiz Capitulino static char tohex(char hexdigit)
2767222f1806SLuiz Capitulino {
27681da177e4SLinus Torvalds 	return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
27691da177e4SLinus Torvalds }
27701da177e4SLinus Torvalds 
2771222f1806SLuiz Capitulino static int fmt_xlong(char *s, unsigned int i)
2772222f1806SLuiz Capitulino {
27731da177e4SLinus Torvalds 	char *bak = s;
2774222f1806SLuiz Capitulino 	*s = tohex((i >> 12) & 0xf);
2775222f1806SLuiz Capitulino 	if (s != bak || *s != '0')
2776222f1806SLuiz Capitulino 		++s;
2777222f1806SLuiz Capitulino 	*s = tohex((i >> 8) & 0xf);
2778222f1806SLuiz Capitulino 	if (s != bak || *s != '0')
2779222f1806SLuiz Capitulino 		++s;
2780222f1806SLuiz Capitulino 	*s = tohex((i >> 4) & 0xf);
2781222f1806SLuiz Capitulino 	if (s != bak || *s != '0')
2782222f1806SLuiz Capitulino 		++s;
27831da177e4SLinus Torvalds 	*s = tohex(i & 0xf);
27841da177e4SLinus Torvalds 	return s - bak + 1;
27851da177e4SLinus Torvalds }
27861da177e4SLinus Torvalds 
2787222f1806SLuiz Capitulino static unsigned int fmt_ip6(char *s, const char ip[16])
2788222f1806SLuiz Capitulino {
27891da177e4SLinus Torvalds 	unsigned int len;
27901da177e4SLinus Torvalds 	unsigned int i;
27911da177e4SLinus Torvalds 	unsigned int temp;
27921da177e4SLinus Torvalds 	unsigned int compressing;
27931da177e4SLinus Torvalds 	int j;
27941da177e4SLinus Torvalds 
2795222f1806SLuiz Capitulino 	len = 0;
2796222f1806SLuiz Capitulino 	compressing = 0;
27971da177e4SLinus Torvalds 	for (j = 0; j < 16; j += 2) {
27981da177e4SLinus Torvalds 
27991da177e4SLinus Torvalds #ifdef V4MAPPEDPREFIX
28001da177e4SLinus Torvalds 		if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) {
28011da177e4SLinus Torvalds 			inet_ntoa_r(*(struct in_addr *)(ip + 12), s);
28021da177e4SLinus Torvalds 			temp = strlen(s);
28031da177e4SLinus Torvalds 			return len + temp;
28041da177e4SLinus Torvalds 		}
28051da177e4SLinus Torvalds #endif
28061da177e4SLinus Torvalds 		temp = ((unsigned long)(unsigned char)ip[j] << 8) +
28071da177e4SLinus Torvalds 		    (unsigned long)(unsigned char)ip[j + 1];
28081da177e4SLinus Torvalds 		if (temp == 0) {
28091da177e4SLinus Torvalds 			if (!compressing) {
28101da177e4SLinus Torvalds 				compressing = 1;
28111da177e4SLinus Torvalds 				if (j == 0) {
2812222f1806SLuiz Capitulino 					*s++ = ':';
2813222f1806SLuiz Capitulino 					++len;
28141da177e4SLinus Torvalds 				}
28151da177e4SLinus Torvalds 			}
28161da177e4SLinus Torvalds 		} else {
28171da177e4SLinus Torvalds 			if (compressing) {
28181da177e4SLinus Torvalds 				compressing = 0;
2819222f1806SLuiz Capitulino 				*s++ = ':';
2820222f1806SLuiz Capitulino 				++len;
28211da177e4SLinus Torvalds 			}
2822222f1806SLuiz Capitulino 			i = fmt_xlong(s, temp);
2823222f1806SLuiz Capitulino 			len += i;
2824222f1806SLuiz Capitulino 			s += i;
28251da177e4SLinus Torvalds 			if (j < 14) {
28261da177e4SLinus Torvalds 				*s++ = ':';
28271da177e4SLinus Torvalds 				++len;
28281da177e4SLinus Torvalds 			}
28291da177e4SLinus Torvalds 		}
28301da177e4SLinus Torvalds 	}
28311da177e4SLinus Torvalds 	if (compressing) {
2832222f1806SLuiz Capitulino 		*s++ = ':';
2833222f1806SLuiz Capitulino 		++len;
28341da177e4SLinus Torvalds 	}
28351da177e4SLinus Torvalds 	*s = 0;
28361da177e4SLinus Torvalds 	return len;
28371da177e4SLinus Torvalds }
28381da177e4SLinus Torvalds 
28391da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
28401da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
28411da177e4SLinus Torvalds {
28421da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
28431da177e4SLinus Torvalds 	__u8 *eth;
28441da177e4SLinus Torvalds 	struct udphdr *udph;
28451da177e4SLinus Torvalds 	int datalen;
28461da177e4SLinus Torvalds 	struct ipv6hdr *iph;
28471da177e4SLinus Torvalds 	struct pktgen_hdr *pgh = NULL;
2848d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IPV6);
2849ca6549afSSteven Whitehouse 	__be32 *mpls;
285034954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
285134954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
285234954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
285334954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2854ca6549afSSteven Whitehouse 
2855ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2856d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
28571da177e4SLinus Torvalds 
285834954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2859d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
286034954ddcSFrancesco Fondelli 
286164053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
286264053beeSRobert Olsson 	 * fields.
286364053beeSRobert Olsson 	 */
286464053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
286564053beeSRobert Olsson 
2866ca6549afSSteven Whitehouse 	skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 +
286716dab72fSJamal Hadi Salim 			pkt_dev->pkt_overhead, GFP_ATOMIC);
28681da177e4SLinus Torvalds 	if (!skb) {
28691da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
28701da177e4SLinus Torvalds 		return NULL;
28711da177e4SLinus Torvalds 	}
28721da177e4SLinus Torvalds 
28731da177e4SLinus Torvalds 	skb_reserve(skb, 16);
28741da177e4SLinus Torvalds 
28751da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
28761da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2877ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2878ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2879ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
288034954ddcSFrancesco Fondelli 
288134954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
288234954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
288334954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28840f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
28850f37c605SAl Viro 					       pkt_dev->svlan_cfi,
28860f37c605SAl Viro 					       pkt_dev->svlan_p);
288734954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2888d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
288934954ddcSFrancesco Fondelli 		}
289034954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28910f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
28920f37c605SAl Viro 				      pkt_dev->vlan_cfi,
28930f37c605SAl Viro 				      pkt_dev->vlan_p);
289434954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2895d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
289634954ddcSFrancesco Fondelli 	}
289734954ddcSFrancesco Fondelli 
289827a884dcSArnaldo Carvalho de Melo 	skb->network_header = skb->tail;
2899b0e380b1SArnaldo Carvalho de Melo 	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
2900ddc7b8e3SArnaldo Carvalho de Melo 	skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr));
2901ddc7b8e3SArnaldo Carvalho de Melo 
2902ddc7b8e3SArnaldo Carvalho de Melo 	iph = ipv6_hdr(skb);
2903ddc7b8e3SArnaldo Carvalho de Melo 	udph = udp_hdr(skb);
29041da177e4SLinus Torvalds 
29051da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2906252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
29071da177e4SLinus Torvalds 
2908ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2909ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 -
2910ca6549afSSteven Whitehouse 		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
291116dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
29121da177e4SLinus Torvalds 
29131da177e4SLinus Torvalds 	if (datalen < sizeof(struct pktgen_hdr)) {
29141da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
29151da177e4SLinus Torvalds 		if (net_ratelimit())
2916222f1806SLuiz Capitulino 			printk(KERN_INFO "pktgen: increased datalen to %d\n",
2917222f1806SLuiz Capitulino 			       datalen);
29181da177e4SLinus Torvalds 	}
29191da177e4SLinus Torvalds 
29201da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
29211da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
29221da177e4SLinus Torvalds 	udph->len = htons(datalen + sizeof(struct udphdr));
29231da177e4SLinus Torvalds 	udph->check = 0;	/* No checksum */
29241da177e4SLinus Torvalds 
2925d5f1ce9aSStephen Hemminger 	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
29261da177e4SLinus Torvalds 
29271ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
29281ca7768cSFrancesco Fondelli 		/* Version + traffic class + flow (0) */
2929252e3346SAl Viro 		*(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
29301ca7768cSFrancesco Fondelli 	}
29311ca7768cSFrancesco Fondelli 
29321da177e4SLinus Torvalds 	iph->hop_limit = 32;
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds 	iph->payload_len = htons(sizeof(struct udphdr) + datalen);
29351da177e4SLinus Torvalds 	iph->nexthdr = IPPROTO_UDP;
29361da177e4SLinus Torvalds 
29371da177e4SLinus Torvalds 	ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
29381da177e4SLinus Torvalds 	ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
29391da177e4SLinus Torvalds 
2940b0e380b1SArnaldo Carvalho de Melo 	skb->mac_header = (skb->network_header - ETH_HLEN -
294116dab72fSJamal Hadi Salim 			   pkt_dev->pkt_overhead);
2942ca6549afSSteven Whitehouse 	skb->protocol = protocol;
29431da177e4SLinus Torvalds 	skb->dev = odev;
29441da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
29451da177e4SLinus Torvalds 
29461da177e4SLinus Torvalds 	if (pkt_dev->nfrags <= 0)
29471da177e4SLinus Torvalds 		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
29481da177e4SLinus Torvalds 	else {
29491da177e4SLinus Torvalds 		int frags = pkt_dev->nfrags;
29501da177e4SLinus Torvalds 		int i;
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds 		pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
29531da177e4SLinus Torvalds 
29541da177e4SLinus Torvalds 		if (frags > MAX_SKB_FRAGS)
29551da177e4SLinus Torvalds 			frags = MAX_SKB_FRAGS;
29561da177e4SLinus Torvalds 		if (datalen > frags * PAGE_SIZE) {
29571da177e4SLinus Torvalds 			skb_put(skb, datalen - frags * PAGE_SIZE);
29581da177e4SLinus Torvalds 			datalen = frags * PAGE_SIZE;
29591da177e4SLinus Torvalds 		}
29601da177e4SLinus Torvalds 
29611da177e4SLinus Torvalds 		i = 0;
29621da177e4SLinus Torvalds 		while (datalen > 0) {
29631da177e4SLinus Torvalds 			struct page *page = alloc_pages(GFP_KERNEL, 0);
29641da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].page = page;
29651da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].page_offset = 0;
29661da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].size =
29671da177e4SLinus Torvalds 			    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
29681da177e4SLinus Torvalds 			datalen -= skb_shinfo(skb)->frags[i].size;
29691da177e4SLinus Torvalds 			skb->len += skb_shinfo(skb)->frags[i].size;
29701da177e4SLinus Torvalds 			skb->data_len += skb_shinfo(skb)->frags[i].size;
29711da177e4SLinus Torvalds 			i++;
29721da177e4SLinus Torvalds 			skb_shinfo(skb)->nr_frags = i;
29731da177e4SLinus Torvalds 		}
29741da177e4SLinus Torvalds 
29751da177e4SLinus Torvalds 		while (i < frags) {
29761da177e4SLinus Torvalds 			int rem;
29771da177e4SLinus Torvalds 
29781da177e4SLinus Torvalds 			if (i == 0)
29791da177e4SLinus Torvalds 				break;
29801da177e4SLinus Torvalds 
29811da177e4SLinus Torvalds 			rem = skb_shinfo(skb)->frags[i - 1].size / 2;
29821da177e4SLinus Torvalds 			if (rem == 0)
29831da177e4SLinus Torvalds 				break;
29841da177e4SLinus Torvalds 
29851da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i - 1].size -= rem;
29861da177e4SLinus Torvalds 
2987222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i] =
2988222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1];
29891da177e4SLinus Torvalds 			get_page(skb_shinfo(skb)->frags[i].page);
2990222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i].page =
2991222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1].page;
2992222f1806SLuiz Capitulino 			skb_shinfo(skb)->frags[i].page_offset +=
2993222f1806SLuiz Capitulino 			    skb_shinfo(skb)->frags[i - 1].size;
29941da177e4SLinus Torvalds 			skb_shinfo(skb)->frags[i].size = rem;
29951da177e4SLinus Torvalds 			i++;
29961da177e4SLinus Torvalds 			skb_shinfo(skb)->nr_frags = i;
29971da177e4SLinus Torvalds 		}
29981da177e4SLinus Torvalds 	}
29991da177e4SLinus Torvalds 
30001da177e4SLinus Torvalds 	/* Stamp the time, and sequence number, convert them to network byte order */
30011da177e4SLinus Torvalds 	/* should we update cloned packets too ? */
30021da177e4SLinus Torvalds 	if (pgh) {
30031da177e4SLinus Torvalds 		struct timeval timestamp;
30041da177e4SLinus Torvalds 
30051da177e4SLinus Torvalds 		pgh->pgh_magic = htonl(PKTGEN_MAGIC);
30061da177e4SLinus Torvalds 		pgh->seq_num = htonl(pkt_dev->seq_num);
30071da177e4SLinus Torvalds 
30081da177e4SLinus Torvalds 		do_gettimeofday(&timestamp);
30091da177e4SLinus Torvalds 		pgh->tv_sec = htonl(timestamp.tv_sec);
30101da177e4SLinus Torvalds 		pgh->tv_usec = htonl(timestamp.tv_usec);
30111da177e4SLinus Torvalds 	}
301234954ddcSFrancesco Fondelli 	/* pkt_dev->seq_num++; FF: you really mean this? */
30131da177e4SLinus Torvalds 
30141da177e4SLinus Torvalds 	return skb;
30151da177e4SLinus Torvalds }
30161da177e4SLinus Torvalds 
30171da177e4SLinus Torvalds static inline struct sk_buff *fill_packet(struct net_device *odev,
30181da177e4SLinus Torvalds 					  struct pktgen_dev *pkt_dev)
30191da177e4SLinus Torvalds {
30201da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
30211da177e4SLinus Torvalds 		return fill_packet_ipv6(odev, pkt_dev);
30221da177e4SLinus Torvalds 	else
30231da177e4SLinus Torvalds 		return fill_packet_ipv4(odev, pkt_dev);
30241da177e4SLinus Torvalds }
30251da177e4SLinus Torvalds 
30261da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
30271da177e4SLinus Torvalds {
30281da177e4SLinus Torvalds 	pkt_dev->seq_num = 1;
30291da177e4SLinus Torvalds 	pkt_dev->idle_acc = 0;
30301da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
30311da177e4SLinus Torvalds 	pkt_dev->tx_bytes = 0;
30321da177e4SLinus Torvalds 	pkt_dev->errors = 0;
30331da177e4SLinus Torvalds }
30341da177e4SLinus Torvalds 
30351da177e4SLinus Torvalds /* Set up structure for sending pkts, clear counters */
30361da177e4SLinus Torvalds 
30371da177e4SLinus Torvalds static void pktgen_run(struct pktgen_thread *t)
30381da177e4SLinus Torvalds {
3039c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
30401da177e4SLinus Torvalds 	int started = 0;
30411da177e4SLinus Torvalds 
304225c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_run. %p\n", t);
30431da177e4SLinus Torvalds 
30441da177e4SLinus Torvalds 	if_lock(t);
3045c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list) {
30461da177e4SLinus Torvalds 
30471da177e4SLinus Torvalds 		/*
30481da177e4SLinus Torvalds 		 * setup odev and create initial packet.
30491da177e4SLinus Torvalds 		 */
30501da177e4SLinus Torvalds 		pktgen_setup_inject(pkt_dev);
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds 		if (pkt_dev->odev) {
30531da177e4SLinus Torvalds 			pktgen_clear_counters(pkt_dev);
30541da177e4SLinus Torvalds 			pkt_dev->running = 1;	/* Cranke yeself! */
30551da177e4SLinus Torvalds 			pkt_dev->skb = NULL;
30561da177e4SLinus Torvalds 			pkt_dev->started_at = getCurUs();
30571da177e4SLinus Torvalds 			pkt_dev->next_tx_us = getCurUs();	/* Transmit immediately */
30581da177e4SLinus Torvalds 			pkt_dev->next_tx_ns = 0;
305916dab72fSJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
30601da177e4SLinus Torvalds 
30611da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Starting");
30621da177e4SLinus Torvalds 			started++;
3063222f1806SLuiz Capitulino 		} else
30641da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Error starting");
30651da177e4SLinus Torvalds 	}
30661da177e4SLinus Torvalds 	if_unlock(t);
3067222f1806SLuiz Capitulino 	if (started)
3068222f1806SLuiz Capitulino 		t->control &= ~(T_STOP);
30691da177e4SLinus Torvalds }
30701da177e4SLinus Torvalds 
30711da177e4SLinus Torvalds static void pktgen_stop_all_threads_ifs(void)
30721da177e4SLinus Torvalds {
3073cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
30741da177e4SLinus Torvalds 
307525c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_stop_all_threads_ifs.\n");
30761da177e4SLinus Torvalds 
30776146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3078cdcdbe0bSLuiz Capitulino 
3079cdcdbe0bSLuiz Capitulino 	list_for_each_entry(t, &pktgen_threads, th_list)
308095ed63f7SArthur Kepner 		t->control |= T_STOP;
3081cdcdbe0bSLuiz Capitulino 
30826146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
30831da177e4SLinus Torvalds }
30841da177e4SLinus Torvalds 
30851da177e4SLinus Torvalds static int thread_is_running(struct pktgen_thread *t)
30861da177e4SLinus Torvalds {
3087c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
30881da177e4SLinus Torvalds 	int res = 0;
30891da177e4SLinus Torvalds 
3090c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list)
3091c26a8016SLuiz Capitulino 		if (pkt_dev->running) {
30921da177e4SLinus Torvalds 			res = 1;
30931da177e4SLinus Torvalds 			break;
30941da177e4SLinus Torvalds 		}
30951da177e4SLinus Torvalds 	return res;
30961da177e4SLinus Torvalds }
30971da177e4SLinus Torvalds 
30981da177e4SLinus Torvalds static int pktgen_wait_thread_run(struct pktgen_thread *t)
30991da177e4SLinus Torvalds {
31001da177e4SLinus Torvalds 	if_lock(t);
31011da177e4SLinus Torvalds 
31021da177e4SLinus Torvalds 	while (thread_is_running(t)) {
31031da177e4SLinus Torvalds 
31041da177e4SLinus Torvalds 		if_unlock(t);
31051da177e4SLinus Torvalds 
31061da177e4SLinus Torvalds 		msleep_interruptible(100);
31071da177e4SLinus Torvalds 
31081da177e4SLinus Torvalds 		if (signal_pending(current))
31091da177e4SLinus Torvalds 			goto signal;
31101da177e4SLinus Torvalds 		if_lock(t);
31111da177e4SLinus Torvalds 	}
31121da177e4SLinus Torvalds 	if_unlock(t);
31131da177e4SLinus Torvalds 	return 1;
31141da177e4SLinus Torvalds signal:
31151da177e4SLinus Torvalds 	return 0;
31161da177e4SLinus Torvalds }
31171da177e4SLinus Torvalds 
31181da177e4SLinus Torvalds static int pktgen_wait_all_threads_run(void)
31191da177e4SLinus Torvalds {
3120cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31211da177e4SLinus Torvalds 	int sig = 1;
31221da177e4SLinus Torvalds 
31236146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3124cdcdbe0bSLuiz Capitulino 
3125cdcdbe0bSLuiz Capitulino 	list_for_each_entry(t, &pktgen_threads, th_list) {
31261da177e4SLinus Torvalds 		sig = pktgen_wait_thread_run(t);
3127222f1806SLuiz Capitulino 		if (sig == 0)
3128222f1806SLuiz Capitulino 			break;
31291da177e4SLinus Torvalds 	}
3130cdcdbe0bSLuiz Capitulino 
3131cdcdbe0bSLuiz Capitulino 	if (sig == 0)
3132cdcdbe0bSLuiz Capitulino 		list_for_each_entry(t, &pktgen_threads, th_list)
31331da177e4SLinus Torvalds 			t->control |= (T_STOP);
3134cdcdbe0bSLuiz Capitulino 
31356146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31361da177e4SLinus Torvalds 	return sig;
31371da177e4SLinus Torvalds }
31381da177e4SLinus Torvalds 
31391da177e4SLinus Torvalds static void pktgen_run_all_threads(void)
31401da177e4SLinus Torvalds {
3141cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31421da177e4SLinus Torvalds 
314325c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_run_all_threads.\n");
31441da177e4SLinus Torvalds 
31456146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
31461da177e4SLinus Torvalds 
3147cdcdbe0bSLuiz Capitulino 	list_for_each_entry(t, &pktgen_threads, th_list)
31481da177e4SLinus Torvalds 		t->control |= (T_RUN);
3149cdcdbe0bSLuiz Capitulino 
31506146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31511da177e4SLinus Torvalds 
3152121caf57SNishanth Aravamudan 	schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
31531da177e4SLinus Torvalds 
31541da177e4SLinus Torvalds 	pktgen_wait_all_threads_run();
31551da177e4SLinus Torvalds }
31561da177e4SLinus Torvalds 
31571da177e4SLinus Torvalds static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
31581da177e4SLinus Torvalds {
31591da177e4SLinus Torvalds 	__u64 total_us, bps, mbps, pps, idle;
31601da177e4SLinus Torvalds 	char *p = pkt_dev->result;
31611da177e4SLinus Torvalds 
31621da177e4SLinus Torvalds 	total_us = pkt_dev->stopped_at - pkt_dev->started_at;
31631da177e4SLinus Torvalds 
31641da177e4SLinus Torvalds 	idle = pkt_dev->idle_acc;
31651da177e4SLinus Torvalds 
31661da177e4SLinus Torvalds 	p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
31671da177e4SLinus Torvalds 		     (unsigned long long)total_us,
31681da177e4SLinus Torvalds 		     (unsigned long long)(total_us - idle),
31691da177e4SLinus Torvalds 		     (unsigned long long)idle,
31701da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->sofar,
31711da177e4SLinus Torvalds 		     pkt_dev->cur_pkt_size, nr_frags);
31721da177e4SLinus Torvalds 
31731da177e4SLinus Torvalds 	pps = pkt_dev->sofar * USEC_PER_SEC;
31741da177e4SLinus Torvalds 
31751da177e4SLinus Torvalds 	while ((total_us >> 32) != 0) {
31761da177e4SLinus Torvalds 		pps >>= 1;
31771da177e4SLinus Torvalds 		total_us >>= 1;
31781da177e4SLinus Torvalds 	}
31791da177e4SLinus Torvalds 
31801da177e4SLinus Torvalds 	do_div(pps, total_us);
31811da177e4SLinus Torvalds 
31821da177e4SLinus Torvalds 	bps = pps * 8 * pkt_dev->cur_pkt_size;
31831da177e4SLinus Torvalds 
31841da177e4SLinus Torvalds 	mbps = bps;
31851da177e4SLinus Torvalds 	do_div(mbps, 1000000);
31861da177e4SLinus Torvalds 	p += sprintf(p, "  %llupps %lluMb/sec (%llubps) errors: %llu",
31871da177e4SLinus Torvalds 		     (unsigned long long)pps,
31881da177e4SLinus Torvalds 		     (unsigned long long)mbps,
31891da177e4SLinus Torvalds 		     (unsigned long long)bps,
31901da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->errors);
31911da177e4SLinus Torvalds }
31921da177e4SLinus Torvalds 
31931da177e4SLinus Torvalds /* Set stopped-at timer, remove from running list, do counters & statistics */
31941da177e4SLinus Torvalds 
31951da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
31961da177e4SLinus Torvalds {
3197222f1806SLuiz Capitulino 	int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
31981da177e4SLinus Torvalds 
31991da177e4SLinus Torvalds 	if (!pkt_dev->running) {
320025a8b254SDavid S. Miller 		printk(KERN_WARNING "pktgen: interface: %s is already "
320125a8b254SDavid S. Miller 		       "stopped\n", pkt_dev->odev->name);
32021da177e4SLinus Torvalds 		return -EINVAL;
32031da177e4SLinus Torvalds 	}
32041da177e4SLinus Torvalds 
32051da177e4SLinus Torvalds 	pkt_dev->stopped_at = getCurUs();
32061da177e4SLinus Torvalds 	pkt_dev->running = 0;
32071da177e4SLinus Torvalds 
320895ed63f7SArthur Kepner 	show_results(pkt_dev, nr_frags);
32091da177e4SLinus Torvalds 
32101da177e4SLinus Torvalds 	return 0;
32111da177e4SLinus Torvalds }
32121da177e4SLinus Torvalds 
32131da177e4SLinus Torvalds static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
32141da177e4SLinus Torvalds {
3215c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev, *best = NULL;
32161da177e4SLinus Torvalds 
32171da177e4SLinus Torvalds 	if_lock(t);
32181da177e4SLinus Torvalds 
3219c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list) {
3220c26a8016SLuiz Capitulino 		if (!pkt_dev->running)
3221222f1806SLuiz Capitulino 			continue;
3222222f1806SLuiz Capitulino 		if (best == NULL)
3223c26a8016SLuiz Capitulino 			best = pkt_dev;
3224c26a8016SLuiz Capitulino 		else if (pkt_dev->next_tx_us < best->next_tx_us)
3225c26a8016SLuiz Capitulino 			best = pkt_dev;
32261da177e4SLinus Torvalds 	}
32271da177e4SLinus Torvalds 	if_unlock(t);
32281da177e4SLinus Torvalds 	return best;
32291da177e4SLinus Torvalds }
32301da177e4SLinus Torvalds 
3231222f1806SLuiz Capitulino static void pktgen_stop(struct pktgen_thread *t)
3232222f1806SLuiz Capitulino {
3233c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
32341da177e4SLinus Torvalds 
323525c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_stop\n");
32361da177e4SLinus Torvalds 
32371da177e4SLinus Torvalds 	if_lock(t);
32381da177e4SLinus Torvalds 
3239c26a8016SLuiz Capitulino 	list_for_each_entry(pkt_dev, &t->if_list, list) {
3240c26a8016SLuiz Capitulino 		pktgen_stop_device(pkt_dev);
3241c26a8016SLuiz Capitulino 		if (pkt_dev->skb)
3242c26a8016SLuiz Capitulino 			kfree_skb(pkt_dev->skb);
324395ed63f7SArthur Kepner 
3244c26a8016SLuiz Capitulino 		pkt_dev->skb = NULL;
324595ed63f7SArthur Kepner 	}
324695ed63f7SArthur Kepner 
324795ed63f7SArthur Kepner 	if_unlock(t);
324895ed63f7SArthur Kepner }
324995ed63f7SArthur Kepner 
325095ed63f7SArthur Kepner /*
325195ed63f7SArthur Kepner  * one of our devices needs to be removed - find it
325295ed63f7SArthur Kepner  * and remove it
325395ed63f7SArthur Kepner  */
325495ed63f7SArthur Kepner static void pktgen_rem_one_if(struct pktgen_thread *t)
325595ed63f7SArthur Kepner {
3256c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3257c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
325895ed63f7SArthur Kepner 
325925c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_rem_one_if\n");
326095ed63f7SArthur Kepner 
326195ed63f7SArthur Kepner 	if_lock(t);
326295ed63f7SArthur Kepner 
3263c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3264c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
326595ed63f7SArthur Kepner 
3266222f1806SLuiz Capitulino 		if (!cur->removal_mark)
3267222f1806SLuiz Capitulino 			continue;
326895ed63f7SArthur Kepner 
326995ed63f7SArthur Kepner 		if (cur->skb)
327095ed63f7SArthur Kepner 			kfree_skb(cur->skb);
327195ed63f7SArthur Kepner 		cur->skb = NULL;
327295ed63f7SArthur Kepner 
327395ed63f7SArthur Kepner 		pktgen_remove_device(t, cur);
327495ed63f7SArthur Kepner 
327595ed63f7SArthur Kepner 		break;
327695ed63f7SArthur Kepner 	}
32771da177e4SLinus Torvalds 
32781da177e4SLinus Torvalds 	if_unlock(t);
32791da177e4SLinus Torvalds }
32801da177e4SLinus Torvalds 
32811da177e4SLinus Torvalds static void pktgen_rem_all_ifs(struct pktgen_thread *t)
32821da177e4SLinus Torvalds {
3283c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3284c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
32851da177e4SLinus Torvalds 
32861da177e4SLinus Torvalds 	/* Remove all devices, free mem */
32871da177e4SLinus Torvalds 
328825c4e53aSStephen Hemminger 	pr_debug("pktgen: entering pktgen_rem_all_ifs\n");
32891da177e4SLinus Torvalds 	if_lock(t);
32901da177e4SLinus Torvalds 
3291c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3292c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
329395ed63f7SArthur Kepner 
329495ed63f7SArthur Kepner 		if (cur->skb)
329595ed63f7SArthur Kepner 			kfree_skb(cur->skb);
329695ed63f7SArthur Kepner 		cur->skb = NULL;
329795ed63f7SArthur Kepner 
32981da177e4SLinus Torvalds 		pktgen_remove_device(t, cur);
32991da177e4SLinus Torvalds 	}
33001da177e4SLinus Torvalds 
33011da177e4SLinus Torvalds 	if_unlock(t);
33021da177e4SLinus Torvalds }
33031da177e4SLinus Torvalds 
33041da177e4SLinus Torvalds static void pktgen_rem_thread(struct pktgen_thread *t)
33051da177e4SLinus Torvalds {
33061da177e4SLinus Torvalds 	/* Remove from the thread list */
33071da177e4SLinus Torvalds 
3308ee74baa7SDavid S. Miller 	remove_proc_entry(t->tsk->comm, pg_proc_dir);
33091da177e4SLinus Torvalds 
33106146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
33111da177e4SLinus Torvalds 
3312cdcdbe0bSLuiz Capitulino 	list_del(&t->th_list);
3313cdcdbe0bSLuiz Capitulino 
33146146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
33151da177e4SLinus Torvalds }
33161da177e4SLinus Torvalds 
33171da177e4SLinus Torvalds static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
33181da177e4SLinus Torvalds {
33191da177e4SLinus Torvalds 	struct net_device *odev = NULL;
33201da177e4SLinus Torvalds 	__u64 idle_start = 0;
33211da177e4SLinus Torvalds 	int ret;
33221da177e4SLinus Torvalds 
33231da177e4SLinus Torvalds 	odev = pkt_dev->odev;
33241da177e4SLinus Torvalds 
33251da177e4SLinus Torvalds 	if (pkt_dev->delay_us || pkt_dev->delay_ns) {
33261da177e4SLinus Torvalds 		u64 now;
33271da177e4SLinus Torvalds 
33281da177e4SLinus Torvalds 		now = getCurUs();
33291da177e4SLinus Torvalds 		if (now < pkt_dev->next_tx_us)
33301da177e4SLinus Torvalds 			spin(pkt_dev, pkt_dev->next_tx_us);
33311da177e4SLinus Torvalds 
33321da177e4SLinus Torvalds 		/* This is max DELAY, this has special meaning of
33331da177e4SLinus Torvalds 		 * "never transmit"
33341da177e4SLinus Torvalds 		 */
33351da177e4SLinus Torvalds 		if (pkt_dev->delay_us == 0x7FFFFFFF) {
33361da177e4SLinus Torvalds 			pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us;
33371da177e4SLinus Torvalds 			pkt_dev->next_tx_ns = pkt_dev->delay_ns;
33381da177e4SLinus Torvalds 			goto out;
33391da177e4SLinus Torvalds 		}
33401da177e4SLinus Torvalds 	}
33411da177e4SLinus Torvalds 
3342f25f4e44SPeter P Waskiewicz Jr 	if ((netif_queue_stopped(odev) ||
3343378be2c0SRobert Olsson 	     (pkt_dev->skb &&
3344378be2c0SRobert Olsson 	      netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping))) ||
3345f25f4e44SPeter P Waskiewicz Jr 	    need_resched()) {
33461da177e4SLinus Torvalds 		idle_start = getCurUs();
33471da177e4SLinus Torvalds 
33481da177e4SLinus Torvalds 		if (!netif_running(odev)) {
33491da177e4SLinus Torvalds 			pktgen_stop_device(pkt_dev);
335095ed63f7SArthur Kepner 			if (pkt_dev->skb)
335195ed63f7SArthur Kepner 				kfree_skb(pkt_dev->skb);
335295ed63f7SArthur Kepner 			pkt_dev->skb = NULL;
33531da177e4SLinus Torvalds 			goto out;
33541da177e4SLinus Torvalds 		}
33551da177e4SLinus Torvalds 		if (need_resched())
33561da177e4SLinus Torvalds 			schedule();
33571da177e4SLinus Torvalds 
33581da177e4SLinus Torvalds 		pkt_dev->idle_acc += getCurUs() - idle_start;
33591da177e4SLinus Torvalds 
3360f25f4e44SPeter P Waskiewicz Jr 		if (netif_queue_stopped(odev) ||
3361f25f4e44SPeter P Waskiewicz Jr 		    netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
33621da177e4SLinus Torvalds 			pkt_dev->next_tx_us = getCurUs();	/* TODO */
33631da177e4SLinus Torvalds 			pkt_dev->next_tx_ns = 0;
33641da177e4SLinus Torvalds 			goto out;	/* Try the next interface */
33651da177e4SLinus Torvalds 		}
33661da177e4SLinus Torvalds 	}
33671da177e4SLinus Torvalds 
33681da177e4SLinus Torvalds 	if (pkt_dev->last_ok || !pkt_dev->skb) {
3369222f1806SLuiz Capitulino 		if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
3370222f1806SLuiz Capitulino 		    || (!pkt_dev->skb)) {
33711da177e4SLinus Torvalds 			/* build a new pkt */
33721da177e4SLinus Torvalds 			if (pkt_dev->skb)
33731da177e4SLinus Torvalds 				kfree_skb(pkt_dev->skb);
33741da177e4SLinus Torvalds 
33751da177e4SLinus Torvalds 			pkt_dev->skb = fill_packet(odev, pkt_dev);
33761da177e4SLinus Torvalds 			if (pkt_dev->skb == NULL) {
337725a8b254SDavid S. Miller 				printk(KERN_ERR "pktgen: ERROR: couldn't "
337825a8b254SDavid S. Miller 				       "allocate skb in fill_packet.\n");
33791da177e4SLinus Torvalds 				schedule();
33801da177e4SLinus Torvalds 				pkt_dev->clone_count--;	/* back out increment, OOM */
33811da177e4SLinus Torvalds 				goto out;
33821da177e4SLinus Torvalds 			}
33831da177e4SLinus Torvalds 			pkt_dev->allocated_skbs++;
33841da177e4SLinus Torvalds 			pkt_dev->clone_count = 0;	/* reset counter */
33851da177e4SLinus Torvalds 		}
33861da177e4SLinus Torvalds 	}
33871da177e4SLinus Torvalds 
3388932ff279SHerbert Xu 	netif_tx_lock_bh(odev);
3389f25f4e44SPeter P Waskiewicz Jr 	if (!netif_queue_stopped(odev) &&
3390f25f4e44SPeter P Waskiewicz Jr 	    !netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
33911da177e4SLinus Torvalds 
33921da177e4SLinus Torvalds 		atomic_inc(&(pkt_dev->skb->users));
33931da177e4SLinus Torvalds 	      retry_now:
33941da177e4SLinus Torvalds 		ret = odev->hard_start_xmit(pkt_dev->skb, odev);
33951da177e4SLinus Torvalds 		if (likely(ret == NETDEV_TX_OK)) {
33961da177e4SLinus Torvalds 			pkt_dev->last_ok = 1;
33971da177e4SLinus Torvalds 			pkt_dev->sofar++;
33981da177e4SLinus Torvalds 			pkt_dev->seq_num++;
33991da177e4SLinus Torvalds 			pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
34001da177e4SLinus Torvalds 
34011da177e4SLinus Torvalds 		} else if (ret == NETDEV_TX_LOCKED
34021da177e4SLinus Torvalds 			   && (odev->features & NETIF_F_LLTX)) {
34031da177e4SLinus Torvalds 			cpu_relax();
34041da177e4SLinus Torvalds 			goto retry_now;
34051da177e4SLinus Torvalds 		} else {	/* Retry it next time */
34061da177e4SLinus Torvalds 
34071da177e4SLinus Torvalds 			atomic_dec(&(pkt_dev->skb->users));
34081da177e4SLinus Torvalds 
34091da177e4SLinus Torvalds 			if (debug && net_ratelimit())
34101da177e4SLinus Torvalds 				printk(KERN_INFO "pktgen: Hard xmit error\n");
34111da177e4SLinus Torvalds 
34121da177e4SLinus Torvalds 			pkt_dev->errors++;
34131da177e4SLinus Torvalds 			pkt_dev->last_ok = 0;
34141da177e4SLinus Torvalds 		}
34151da177e4SLinus Torvalds 
34161da177e4SLinus Torvalds 		pkt_dev->next_tx_us = getCurUs();
34171da177e4SLinus Torvalds 		pkt_dev->next_tx_ns = 0;
34181da177e4SLinus Torvalds 
34191da177e4SLinus Torvalds 		pkt_dev->next_tx_us += pkt_dev->delay_us;
34201da177e4SLinus Torvalds 		pkt_dev->next_tx_ns += pkt_dev->delay_ns;
34211da177e4SLinus Torvalds 
34221da177e4SLinus Torvalds 		if (pkt_dev->next_tx_ns > 1000) {
34231da177e4SLinus Torvalds 			pkt_dev->next_tx_us++;
34241da177e4SLinus Torvalds 			pkt_dev->next_tx_ns -= 1000;
34251da177e4SLinus Torvalds 		}
34261da177e4SLinus Torvalds 	}
34271da177e4SLinus Torvalds 
34281da177e4SLinus Torvalds 	else {			/* Retry it next time */
34291da177e4SLinus Torvalds 		pkt_dev->last_ok = 0;
34301da177e4SLinus Torvalds 		pkt_dev->next_tx_us = getCurUs();	/* TODO */
34311da177e4SLinus Torvalds 		pkt_dev->next_tx_ns = 0;
34321da177e4SLinus Torvalds 	}
34331da177e4SLinus Torvalds 
3434932ff279SHerbert Xu 	netif_tx_unlock_bh(odev);
34351da177e4SLinus Torvalds 
34361da177e4SLinus Torvalds 	/* If pkt_dev->count is zero, then run forever */
34371da177e4SLinus Torvalds 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
34381da177e4SLinus Torvalds 		if (atomic_read(&(pkt_dev->skb->users)) != 1) {
34391da177e4SLinus Torvalds 			idle_start = getCurUs();
34401da177e4SLinus Torvalds 			while (atomic_read(&(pkt_dev->skb->users)) != 1) {
34411da177e4SLinus Torvalds 				if (signal_pending(current)) {
34421da177e4SLinus Torvalds 					break;
34431da177e4SLinus Torvalds 				}
34441da177e4SLinus Torvalds 				schedule();
34451da177e4SLinus Torvalds 			}
34461da177e4SLinus Torvalds 			pkt_dev->idle_acc += getCurUs() - idle_start;
34471da177e4SLinus Torvalds 		}
34481da177e4SLinus Torvalds 
34491da177e4SLinus Torvalds 		/* Done with this */
34501da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
345195ed63f7SArthur Kepner 		if (pkt_dev->skb)
345295ed63f7SArthur Kepner 			kfree_skb(pkt_dev->skb);
345395ed63f7SArthur Kepner 		pkt_dev->skb = NULL;
34541da177e4SLinus Torvalds 	}
34551da177e4SLinus Torvalds out:;
34561da177e4SLinus Torvalds }
34571da177e4SLinus Torvalds 
34581da177e4SLinus Torvalds /*
34591da177e4SLinus Torvalds  * Main loop of the thread goes here
34601da177e4SLinus Torvalds  */
34611da177e4SLinus Torvalds 
3462ee74baa7SDavid S. Miller static int pktgen_thread_worker(void *arg)
34631da177e4SLinus Torvalds {
34641da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
3465ee74baa7SDavid S. Miller 	struct pktgen_thread *t = arg;
34661da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
34671da177e4SLinus Torvalds 	int cpu = t->cpu;
34681da177e4SLinus Torvalds 	u32 max_before_softirq;
34691da177e4SLinus Torvalds 	u32 tx_since_softirq = 0;
34701da177e4SLinus Torvalds 
3471ee74baa7SDavid S. Miller 	BUG_ON(smp_processor_id() != cpu);
34721da177e4SLinus Torvalds 
34731da177e4SLinus Torvalds 	init_waitqueue_head(&t->queue);
34741da177e4SLinus Torvalds 
347525c4e53aSStephen Hemminger 	pr_debug("pktgen: starting pktgen/%d:  pid=%d\n", cpu, current->pid);
34761da177e4SLinus Torvalds 
34771da177e4SLinus Torvalds 	max_before_softirq = t->max_before_softirq;
34781da177e4SLinus Torvalds 
3479ee74baa7SDavid S. Miller 	set_current_state(TASK_INTERRUPTIBLE);
34801da177e4SLinus Torvalds 
348183144186SRafael J. Wysocki 	set_freezable();
348283144186SRafael J. Wysocki 
3483ee74baa7SDavid S. Miller 	while (!kthread_should_stop()) {
3484ee74baa7SDavid S. Miller 		pkt_dev = next_to_run(t);
3485ee74baa7SDavid S. Miller 
3486ee74baa7SDavid S. Miller 		if (!pkt_dev &&
3487ee74baa7SDavid S. Miller 		    (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV))
3488ee74baa7SDavid S. Miller 		    == 0) {
3489ee74baa7SDavid S. Miller 			prepare_to_wait(&(t->queue), &wait,
3490ee74baa7SDavid S. Miller 					TASK_INTERRUPTIBLE);
3491ee74baa7SDavid S. Miller 			schedule_timeout(HZ / 10);
3492ee74baa7SDavid S. Miller 			finish_wait(&(t->queue), &wait);
3493ee74baa7SDavid S. Miller 		}
34941da177e4SLinus Torvalds 
34951da177e4SLinus Torvalds 		__set_current_state(TASK_RUNNING);
34961da177e4SLinus Torvalds 
34971da177e4SLinus Torvalds 		if (pkt_dev) {
34981da177e4SLinus Torvalds 
34991da177e4SLinus Torvalds 			pktgen_xmit(pkt_dev);
35001da177e4SLinus Torvalds 
35011da177e4SLinus Torvalds 			/*
35021da177e4SLinus Torvalds 			 * We like to stay RUNNING but must also give
35031da177e4SLinus Torvalds 			 * others fair share.
35041da177e4SLinus Torvalds 			 */
35051da177e4SLinus Torvalds 
35061da177e4SLinus Torvalds 			tx_since_softirq += pkt_dev->last_ok;
35071da177e4SLinus Torvalds 
35081da177e4SLinus Torvalds 			if (tx_since_softirq > max_before_softirq) {
35091da177e4SLinus Torvalds 				if (local_softirq_pending())
35101da177e4SLinus Torvalds 					do_softirq();
35111da177e4SLinus Torvalds 				tx_since_softirq = 0;
35121da177e4SLinus Torvalds 			}
35131da177e4SLinus Torvalds 		}
35141da177e4SLinus Torvalds 
35151da177e4SLinus Torvalds 		if (t->control & T_STOP) {
35161da177e4SLinus Torvalds 			pktgen_stop(t);
35171da177e4SLinus Torvalds 			t->control &= ~(T_STOP);
35181da177e4SLinus Torvalds 		}
35191da177e4SLinus Torvalds 
35201da177e4SLinus Torvalds 		if (t->control & T_RUN) {
35211da177e4SLinus Torvalds 			pktgen_run(t);
35221da177e4SLinus Torvalds 			t->control &= ~(T_RUN);
35231da177e4SLinus Torvalds 		}
35241da177e4SLinus Torvalds 
352595ed63f7SArthur Kepner 		if (t->control & T_REMDEVALL) {
35261da177e4SLinus Torvalds 			pktgen_rem_all_ifs(t);
352795ed63f7SArthur Kepner 			t->control &= ~(T_REMDEVALL);
352895ed63f7SArthur Kepner 		}
352995ed63f7SArthur Kepner 
353095ed63f7SArthur Kepner 		if (t->control & T_REMDEV) {
353195ed63f7SArthur Kepner 			pktgen_rem_one_if(t);
35321da177e4SLinus Torvalds 			t->control &= ~(T_REMDEV);
35331da177e4SLinus Torvalds 		}
35341da177e4SLinus Torvalds 
353509fe3ef4SAndrew Morton 		try_to_freeze();
353609fe3ef4SAndrew Morton 
3537ee74baa7SDavid S. Miller 		set_current_state(TASK_INTERRUPTIBLE);
35381da177e4SLinus Torvalds 	}
35391da177e4SLinus Torvalds 
354025c4e53aSStephen Hemminger 	pr_debug("pktgen: %s stopping all device\n", t->tsk->comm);
35411da177e4SLinus Torvalds 	pktgen_stop(t);
35421da177e4SLinus Torvalds 
354325c4e53aSStephen Hemminger 	pr_debug("pktgen: %s removing all device\n", t->tsk->comm);
35441da177e4SLinus Torvalds 	pktgen_rem_all_ifs(t);
35451da177e4SLinus Torvalds 
354625c4e53aSStephen Hemminger 	pr_debug("pktgen: %s removing thread.\n", t->tsk->comm);
35471da177e4SLinus Torvalds 	pktgen_rem_thread(t);
3548cdcdbe0bSLuiz Capitulino 
3549ee74baa7SDavid S. Miller 	return 0;
35501da177e4SLinus Torvalds }
35511da177e4SLinus Torvalds 
3552222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
3553222f1806SLuiz Capitulino 					  const char *ifname)
35541da177e4SLinus Torvalds {
3555c26a8016SLuiz Capitulino 	struct pktgen_dev *p, *pkt_dev = NULL;
35561da177e4SLinus Torvalds 	if_lock(t);
35571da177e4SLinus Torvalds 
3558c26a8016SLuiz Capitulino 	list_for_each_entry(p, &t->if_list, list)
355939df232fSStephen Hemminger 		if (strncmp(p->odev->name, ifname, IFNAMSIZ) == 0) {
3560c26a8016SLuiz Capitulino 			pkt_dev = p;
35611da177e4SLinus Torvalds 			break;
35621da177e4SLinus Torvalds 		}
35631da177e4SLinus Torvalds 
35641da177e4SLinus Torvalds 	if_unlock(t);
356525c4e53aSStephen Hemminger 	pr_debug("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev);
35661da177e4SLinus Torvalds 	return pkt_dev;
35671da177e4SLinus Torvalds }
35681da177e4SLinus Torvalds 
35691da177e4SLinus Torvalds /*
35701da177e4SLinus Torvalds  * Adds a dev at front of if_list.
35711da177e4SLinus Torvalds  */
35721da177e4SLinus Torvalds 
3573222f1806SLuiz Capitulino static int add_dev_to_thread(struct pktgen_thread *t,
3574222f1806SLuiz Capitulino 			     struct pktgen_dev *pkt_dev)
35751da177e4SLinus Torvalds {
35761da177e4SLinus Torvalds 	int rv = 0;
35771da177e4SLinus Torvalds 
35781da177e4SLinus Torvalds 	if_lock(t);
35791da177e4SLinus Torvalds 
35801da177e4SLinus Torvalds 	if (pkt_dev->pg_thread) {
358125a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: already assigned "
358225a8b254SDavid S. Miller 		       "to a thread.\n");
35831da177e4SLinus Torvalds 		rv = -EBUSY;
35841da177e4SLinus Torvalds 		goto out;
35851da177e4SLinus Torvalds 	}
3586c26a8016SLuiz Capitulino 
3587c26a8016SLuiz Capitulino 	list_add(&pkt_dev->list, &t->if_list);
35881da177e4SLinus Torvalds 	pkt_dev->pg_thread = t;
35891da177e4SLinus Torvalds 	pkt_dev->running = 0;
35901da177e4SLinus Torvalds 
35911da177e4SLinus Torvalds out:
35921da177e4SLinus Torvalds 	if_unlock(t);
35931da177e4SLinus Torvalds 	return rv;
35941da177e4SLinus Torvalds }
35951da177e4SLinus Torvalds 
35961da177e4SLinus Torvalds /* Called under thread lock */
35971da177e4SLinus Torvalds 
35981da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
35991da177e4SLinus Torvalds {
36001da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev;
360139df232fSStephen Hemminger 	int err;
36021da177e4SLinus Torvalds 
36031da177e4SLinus Torvalds 	/* We don't allow a device to be on several threads */
36041da177e4SLinus Torvalds 
3605d50a6b56SStephen Hemminger 	pkt_dev = __pktgen_NN_threads(ifname, FIND);
3606d50a6b56SStephen Hemminger 	if (pkt_dev) {
360725a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: interface already used.\n");
3608d50a6b56SStephen Hemminger 		return -EBUSY;
3609d50a6b56SStephen Hemminger 	}
36101da177e4SLinus Torvalds 
36112845b63bSStephen Hemminger 	pkt_dev = kzalloc(sizeof(struct pktgen_dev), GFP_KERNEL);
36121da177e4SLinus Torvalds 	if (!pkt_dev)
36131da177e4SLinus Torvalds 		return -ENOMEM;
36141da177e4SLinus Torvalds 
36151da177e4SLinus Torvalds 	pkt_dev->flows = vmalloc(MAX_CFLOWS * sizeof(struct flow_state));
36161da177e4SLinus Torvalds 	if (pkt_dev->flows == NULL) {
36171da177e4SLinus Torvalds 		kfree(pkt_dev);
36181da177e4SLinus Torvalds 		return -ENOMEM;
36191da177e4SLinus Torvalds 	}
36201da177e4SLinus Torvalds 	memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
36211da177e4SLinus Torvalds 
362295ed63f7SArthur Kepner 	pkt_dev->removal_mark = 0;
36231da177e4SLinus Torvalds 	pkt_dev->min_pkt_size = ETH_ZLEN;
36241da177e4SLinus Torvalds 	pkt_dev->max_pkt_size = ETH_ZLEN;
36251da177e4SLinus Torvalds 	pkt_dev->nfrags = 0;
36261da177e4SLinus Torvalds 	pkt_dev->clone_skb = pg_clone_skb_d;
36271da177e4SLinus Torvalds 	pkt_dev->delay_us = pg_delay_d / 1000;
36281da177e4SLinus Torvalds 	pkt_dev->delay_ns = pg_delay_d % 1000;
36291da177e4SLinus Torvalds 	pkt_dev->count = pg_count_d;
36301da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
36311da177e4SLinus Torvalds 	pkt_dev->udp_src_min = 9;	/* sink port */
36321da177e4SLinus Torvalds 	pkt_dev->udp_src_max = 9;
36331da177e4SLinus Torvalds 	pkt_dev->udp_dst_min = 9;
36341da177e4SLinus Torvalds 	pkt_dev->udp_dst_max = 9;
36351da177e4SLinus Torvalds 
363634954ddcSFrancesco Fondelli 	pkt_dev->vlan_p = 0;
363734954ddcSFrancesco Fondelli 	pkt_dev->vlan_cfi = 0;
363834954ddcSFrancesco Fondelli 	pkt_dev->vlan_id = 0xffff;
363934954ddcSFrancesco Fondelli 	pkt_dev->svlan_p = 0;
364034954ddcSFrancesco Fondelli 	pkt_dev->svlan_cfi = 0;
364134954ddcSFrancesco Fondelli 	pkt_dev->svlan_id = 0xffff;
364234954ddcSFrancesco Fondelli 
364339df232fSStephen Hemminger 	err = pktgen_setup_dev(pkt_dev, ifname);
364439df232fSStephen Hemminger 	if (err)
364539df232fSStephen Hemminger 		goto out1;
36461da177e4SLinus Torvalds 
364739df232fSStephen Hemminger 	pkt_dev->entry = create_proc_entry(ifname, 0600, pg_proc_dir);
364839df232fSStephen Hemminger 	if (!pkt_dev->entry) {
364925a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: cannot create %s/%s procfs entry.\n",
3650d50a6b56SStephen Hemminger 		       PG_PROC_DIR, ifname);
365139df232fSStephen Hemminger 		err = -EINVAL;
365239df232fSStephen Hemminger 		goto out2;
365339df232fSStephen Hemminger 	}
365439df232fSStephen Hemminger 	pkt_dev->entry->proc_fops = &pktgen_if_fops;
365539df232fSStephen Hemminger 	pkt_dev->entry->data = pkt_dev;
3656a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3657a553e4a6SJamal Hadi Salim 	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;
3658a553e4a6SJamal Hadi Salim 	pkt_dev->ipsproto = IPPROTO_ESP;
3659a553e4a6SJamal Hadi Salim #endif
366039df232fSStephen Hemminger 
366139df232fSStephen Hemminger 	return add_dev_to_thread(t, pkt_dev);
366239df232fSStephen Hemminger out2:
366339df232fSStephen Hemminger 	dev_put(pkt_dev->odev);
366439df232fSStephen Hemminger out1:
3665a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3666a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3667a553e4a6SJamal Hadi Salim #endif
36681da177e4SLinus Torvalds 	if (pkt_dev->flows)
36691da177e4SLinus Torvalds 		vfree(pkt_dev->flows);
36701da177e4SLinus Torvalds 	kfree(pkt_dev);
367139df232fSStephen Hemminger 	return err;
36721da177e4SLinus Torvalds }
36731da177e4SLinus Torvalds 
3674ee74baa7SDavid S. Miller static int __init pktgen_create_thread(int cpu)
36751da177e4SLinus Torvalds {
3676cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3677d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3678ee74baa7SDavid S. Miller 	struct task_struct *p;
36791da177e4SLinus Torvalds 
36802845b63bSStephen Hemminger 	t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
36811da177e4SLinus Torvalds 	if (!t) {
368225a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: out of memory, can't "
368325a8b254SDavid S. Miller 		       "create new thread.\n");
36841da177e4SLinus Torvalds 		return -ENOMEM;
36851da177e4SLinus Torvalds 	}
36861da177e4SLinus Torvalds 
36871da177e4SLinus Torvalds 	spin_lock_init(&t->if_lock);
36881da177e4SLinus Torvalds 	t->cpu = cpu;
36891da177e4SLinus Torvalds 
3690ee74baa7SDavid S. Miller 	INIT_LIST_HEAD(&t->if_list);
3691ee74baa7SDavid S. Miller 
3692ee74baa7SDavid S. Miller 	list_add_tail(&t->th_list, &pktgen_threads);
3693ee74baa7SDavid S. Miller 
3694ee74baa7SDavid S. Miller 	p = kthread_create(pktgen_thread_worker, t, "kpktgend_%d", cpu);
3695ee74baa7SDavid S. Miller 	if (IS_ERR(p)) {
369625a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: kernel_thread() failed "
369725a8b254SDavid S. Miller 		       "for cpu %d\n", t->cpu);
3698ee74baa7SDavid S. Miller 		list_del(&t->th_list);
3699ee74baa7SDavid S. Miller 		kfree(t);
3700ee74baa7SDavid S. Miller 		return PTR_ERR(p);
3701ee74baa7SDavid S. Miller 	}
3702ee74baa7SDavid S. Miller 	kthread_bind(p, cpu);
3703ee74baa7SDavid S. Miller 	t->tsk = p;
3704ee74baa7SDavid S. Miller 
3705ee74baa7SDavid S. Miller 	pe = create_proc_entry(t->tsk->comm, 0600, pg_proc_dir);
3706d50a6b56SStephen Hemminger 	if (!pe) {
370725a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: cannot create %s/%s procfs entry.\n",
3708ee74baa7SDavid S. Miller 		       PG_PROC_DIR, t->tsk->comm);
3709ee74baa7SDavid S. Miller 		kthread_stop(p);
3710ee74baa7SDavid S. Miller 		list_del(&t->th_list);
37111da177e4SLinus Torvalds 		kfree(t);
37121da177e4SLinus Torvalds 		return -EINVAL;
37131da177e4SLinus Torvalds 	}
3714d50a6b56SStephen Hemminger 
3715d50a6b56SStephen Hemminger 	pe->proc_fops = &pktgen_thread_fops;
3716d50a6b56SStephen Hemminger 	pe->data = t;
37171da177e4SLinus Torvalds 
3718ee74baa7SDavid S. Miller 	wake_up_process(p);
37191da177e4SLinus Torvalds 
37201da177e4SLinus Torvalds 	return 0;
37211da177e4SLinus Torvalds }
37221da177e4SLinus Torvalds 
37231da177e4SLinus Torvalds /*
37241da177e4SLinus Torvalds  * Removes a device from the thread if_list.
37251da177e4SLinus Torvalds  */
3726222f1806SLuiz Capitulino static void _rem_dev_from_if_list(struct pktgen_thread *t,
3727222f1806SLuiz Capitulino 				  struct pktgen_dev *pkt_dev)
37281da177e4SLinus Torvalds {
3729c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3730c26a8016SLuiz Capitulino 	struct pktgen_dev *p;
37311da177e4SLinus Torvalds 
3732c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3733c26a8016SLuiz Capitulino 		p = list_entry(q, struct pktgen_dev, list);
3734c26a8016SLuiz Capitulino 		if (p == pkt_dev)
3735c26a8016SLuiz Capitulino 			list_del(&p->list);
37361da177e4SLinus Torvalds 	}
37371da177e4SLinus Torvalds }
37381da177e4SLinus Torvalds 
3739222f1806SLuiz Capitulino static int pktgen_remove_device(struct pktgen_thread *t,
3740222f1806SLuiz Capitulino 				struct pktgen_dev *pkt_dev)
37411da177e4SLinus Torvalds {
37421da177e4SLinus Torvalds 
374325c4e53aSStephen Hemminger 	pr_debug("pktgen: remove_device pkt_dev=%p\n", pkt_dev);
37441da177e4SLinus Torvalds 
37451da177e4SLinus Torvalds 	if (pkt_dev->running) {
374625a8b254SDavid S. Miller 		printk(KERN_WARNING "pktgen: WARNING: trying to remove a "
374725a8b254SDavid S. Miller 		       "running interface, stopping it now.\n");
37481da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
37491da177e4SLinus Torvalds 	}
37501da177e4SLinus Torvalds 
37511da177e4SLinus Torvalds 	/* Dis-associate from the interface */
37521da177e4SLinus Torvalds 
37531da177e4SLinus Torvalds 	if (pkt_dev->odev) {
37541da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
37551da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
37561da177e4SLinus Torvalds 	}
37571da177e4SLinus Torvalds 
37581da177e4SLinus Torvalds 	/* And update the thread if_list */
37591da177e4SLinus Torvalds 
37601da177e4SLinus Torvalds 	_rem_dev_from_if_list(t, pkt_dev);
37611da177e4SLinus Torvalds 
376239df232fSStephen Hemminger 	if (pkt_dev->entry)
376339df232fSStephen Hemminger 		remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
37641da177e4SLinus Torvalds 
3765a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3766a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3767a553e4a6SJamal Hadi Salim #endif
37681da177e4SLinus Torvalds 	if (pkt_dev->flows)
37691da177e4SLinus Torvalds 		vfree(pkt_dev->flows);
37701da177e4SLinus Torvalds 	kfree(pkt_dev);
37711da177e4SLinus Torvalds 	return 0;
37721da177e4SLinus Torvalds }
37731da177e4SLinus Torvalds 
37741da177e4SLinus Torvalds static int __init pg_init(void)
37751da177e4SLinus Torvalds {
37761da177e4SLinus Torvalds 	int cpu;
3777d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3778d50a6b56SStephen Hemminger 
377925a8b254SDavid S. Miller 	printk(KERN_INFO "%s", version);
37801da177e4SLinus Torvalds 
3781d50a6b56SStephen Hemminger 	pg_proc_dir = proc_mkdir(PG_PROC_DIR, proc_net);
3782d50a6b56SStephen Hemminger 	if (!pg_proc_dir)
3783d50a6b56SStephen Hemminger 		return -ENODEV;
3784d50a6b56SStephen Hemminger 	pg_proc_dir->owner = THIS_MODULE;
37851da177e4SLinus Torvalds 
3786d50a6b56SStephen Hemminger 	pe = create_proc_entry(PGCTRL, 0600, pg_proc_dir);
3787d50a6b56SStephen Hemminger 	if (pe == NULL) {
378825a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: cannot create %s "
378925a8b254SDavid S. Miller 		       "procfs entry.\n", PGCTRL);
3790d50a6b56SStephen Hemminger 		proc_net_remove(PG_PROC_DIR);
37911da177e4SLinus Torvalds 		return -EINVAL;
37921da177e4SLinus Torvalds 	}
37931da177e4SLinus Torvalds 
3794d50a6b56SStephen Hemminger 	pe->proc_fops = &pktgen_fops;
3795d50a6b56SStephen Hemminger 	pe->data = NULL;
37961da177e4SLinus Torvalds 
37971da177e4SLinus Torvalds 	/* Register us to receive netdevice events */
37981da177e4SLinus Torvalds 	register_netdevice_notifier(&pktgen_notifier_block);
37991da177e4SLinus Torvalds 
3800670c02c2SJohn Hawkes 	for_each_online_cpu(cpu) {
38018024bb24SLuiz Capitulino 		int err;
38021da177e4SLinus Torvalds 
3803ee74baa7SDavid S. Miller 		err = pktgen_create_thread(cpu);
38048024bb24SLuiz Capitulino 		if (err)
380525a8b254SDavid S. Miller 			printk(KERN_WARNING "pktgen: WARNING: Cannot create "
380625a8b254SDavid S. Miller 			       "thread for cpu %d (%d)\n", cpu, err);
38071da177e4SLinus Torvalds 	}
38088024bb24SLuiz Capitulino 
38098024bb24SLuiz Capitulino 	if (list_empty(&pktgen_threads)) {
381025a8b254SDavid S. Miller 		printk(KERN_ERR "pktgen: ERROR: Initialization failed for "
381125a8b254SDavid S. Miller 		       "all threads\n");
38128024bb24SLuiz Capitulino 		unregister_netdevice_notifier(&pktgen_notifier_block);
38138024bb24SLuiz Capitulino 		remove_proc_entry(PGCTRL, pg_proc_dir);
38148024bb24SLuiz Capitulino 		proc_net_remove(PG_PROC_DIR);
38158024bb24SLuiz Capitulino 		return -ENODEV;
38168024bb24SLuiz Capitulino 	}
38178024bb24SLuiz Capitulino 
38181da177e4SLinus Torvalds 	return 0;
38191da177e4SLinus Torvalds }
38201da177e4SLinus Torvalds 
38211da177e4SLinus Torvalds static void __exit pg_cleanup(void)
38221da177e4SLinus Torvalds {
3823cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3824cdcdbe0bSLuiz Capitulino 	struct list_head *q, *n;
38251da177e4SLinus Torvalds 	wait_queue_head_t queue;
38261da177e4SLinus Torvalds 	init_waitqueue_head(&queue);
38271da177e4SLinus Torvalds 
38281da177e4SLinus Torvalds 	/* Stop all interfaces & threads */
38291da177e4SLinus Torvalds 
3830cdcdbe0bSLuiz Capitulino 	list_for_each_safe(q, n, &pktgen_threads) {
3831cdcdbe0bSLuiz Capitulino 		t = list_entry(q, struct pktgen_thread, th_list);
3832ee74baa7SDavid S. Miller 		kthread_stop(t->tsk);
3833ee74baa7SDavid S. Miller 		kfree(t);
38341da177e4SLinus Torvalds 	}
38351da177e4SLinus Torvalds 
38361da177e4SLinus Torvalds 	/* Un-register us from receiving netdevice events */
38371da177e4SLinus Torvalds 	unregister_netdevice_notifier(&pktgen_notifier_block);
38381da177e4SLinus Torvalds 
38391da177e4SLinus Torvalds 	/* Clean up proc file system */
3840d50a6b56SStephen Hemminger 	remove_proc_entry(PGCTRL, pg_proc_dir);
3841d50a6b56SStephen Hemminger 	proc_net_remove(PG_PROC_DIR);
38421da177e4SLinus Torvalds }
38431da177e4SLinus Torvalds 
38441da177e4SLinus Torvalds module_init(pg_init);
38451da177e4SLinus Torvalds module_exit(pg_cleanup);
38461da177e4SLinus Torvalds 
38471da177e4SLinus Torvalds MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
38481da177e4SLinus Torvalds MODULE_DESCRIPTION("Packet Generator tool");
38491da177e4SLinus Torvalds MODULE_LICENSE("GPL");
38501da177e4SLinus Torvalds module_param(pg_count_d, int, 0);
38511da177e4SLinus Torvalds module_param(pg_delay_d, int, 0);
38521da177e4SLinus Torvalds module_param(pg_clone_skb_d, int, 0);
38531da177e4SLinus Torvalds module_param(debug, int, 0);
3854