xref: /openbmc/linux/net/core/pktgen.c (revision 294a0b7f3148e2a4e916965a6d14838e08143ba8)
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>
996de0e25SJan Engelhardt  * 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
728788370aSJesper Dangaard Brouer  * way.
738788370aSJesper Dangaard Brouer  * The if_list is RCU protected, and the if_lock remains to protect updating
748788370aSJesper Dangaard Brouer  * of if_list, from "add_device" as it invoked from userspace (via proc write).
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  * By design there should only be *one* "controlling" process. In practice
771da177e4SLinus Torvalds  * multiple write accesses gives unpredictable result. Understood by "write"
781da177e4SLinus Torvalds  * to /proc gives result code thats should be read be the "writer".
79b4099fabSStephen Hemminger  * For practical use this should be no problem.
801da177e4SLinus Torvalds  *
811da177e4SLinus Torvalds  * Note when adding devices to a specific CPU there good idea to also assign
821da177e4SLinus Torvalds  * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
831da177e4SLinus Torvalds  * --ro
841da177e4SLinus Torvalds  *
851da177e4SLinus Torvalds  * Fix refcount off by one if first packet fails, potential null deref,
861da177e4SLinus Torvalds  * memleak 030710- KJP
871da177e4SLinus Torvalds  *
881da177e4SLinus Torvalds  * First "ranges" functionality for ipv6 030726 --ro
891da177e4SLinus Torvalds  *
901da177e4SLinus Torvalds  * Included flow support. 030802 ANK.
911da177e4SLinus Torvalds  *
921da177e4SLinus Torvalds  * Fixed unaligned access on IA-64 Grant Grundler <grundler@parisc-linux.org>
931da177e4SLinus Torvalds  *
941da177e4SLinus Torvalds  * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
951da177e4SLinus Torvalds  * ia64 compilation fix from  Aron Griffis <aron@hp.com> 040604
961da177e4SLinus Torvalds  *
971da177e4SLinus Torvalds  * New xmit() return, do_div and misc clean up by Stephen Hemminger
981da177e4SLinus Torvalds  * <shemminger@osdl.org> 040923
991da177e4SLinus Torvalds  *
100b4099fabSStephen Hemminger  * Randy Dunlap fixed u64 printk compiler waring
1011da177e4SLinus Torvalds  *
1021da177e4SLinus Torvalds  * Remove FCS from BW calculation.  Lennert Buytenhek <buytenh@wantstofly.org>
1031da177e4SLinus Torvalds  * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213
1041da177e4SLinus Torvalds  *
1051da177e4SLinus Torvalds  * Corrections from Nikolai Malykh (nmalykh@bilim.com)
1061da177e4SLinus Torvalds  * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
1071da177e4SLinus Torvalds  *
1081da177e4SLinus Torvalds  * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
1091da177e4SLinus Torvalds  * 050103
110ca6549afSSteven Whitehouse  *
111ca6549afSSteven Whitehouse  * MPLS support by Steven Whitehouse <steve@chygwyn.com>
112ca6549afSSteven Whitehouse  *
11334954ddcSFrancesco Fondelli  * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <francesco.fondelli@gmail.com>
11434954ddcSFrancesco Fondelli  *
115ce5d0b47SAdit Ranadive  * Fixed src_mac command to set source mac of packet to value specified in
116ce5d0b47SAdit Ranadive  * command by Adit Ranadive <adit.262@gmail.com>
117ce5d0b47SAdit Ranadive  *
1181da177e4SLinus Torvalds  */
119f9467eaeSJoe Perches 
120f9467eaeSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
121f9467eaeSJoe Perches 
1221da177e4SLinus Torvalds #include <linux/sys.h>
1231da177e4SLinus Torvalds #include <linux/types.h>
1241da177e4SLinus Torvalds #include <linux/module.h>
1251da177e4SLinus Torvalds #include <linux/moduleparam.h>
1261da177e4SLinus Torvalds #include <linux/kernel.h>
127222fa076SLuiz Capitulino #include <linux/mutex.h>
1281da177e4SLinus Torvalds #include <linux/sched.h>
1291da177e4SLinus Torvalds #include <linux/slab.h>
1301da177e4SLinus Torvalds #include <linux/vmalloc.h>
1311da177e4SLinus Torvalds #include <linux/unistd.h>
1321da177e4SLinus Torvalds #include <linux/string.h>
1331da177e4SLinus Torvalds #include <linux/ptrace.h>
1341da177e4SLinus Torvalds #include <linux/errno.h>
1351da177e4SLinus Torvalds #include <linux/ioport.h>
1361da177e4SLinus Torvalds #include <linux/interrupt.h>
1374fc268d2SRandy Dunlap #include <linux/capability.h>
1382bc481cfSStephen Hemminger #include <linux/hrtimer.h>
13909fe3ef4SAndrew Morton #include <linux/freezer.h>
1401da177e4SLinus Torvalds #include <linux/delay.h>
1411da177e4SLinus Torvalds #include <linux/timer.h>
142cdcdbe0bSLuiz Capitulino #include <linux/list.h>
1431da177e4SLinus Torvalds #include <linux/init.h>
1441da177e4SLinus Torvalds #include <linux/skbuff.h>
1451da177e4SLinus Torvalds #include <linux/netdevice.h>
1461da177e4SLinus Torvalds #include <linux/inet.h>
1471da177e4SLinus Torvalds #include <linux/inetdevice.h>
1481da177e4SLinus Torvalds #include <linux/rtnetlink.h>
1491da177e4SLinus Torvalds #include <linux/if_arp.h>
15034954ddcSFrancesco Fondelli #include <linux/if_vlan.h>
1511da177e4SLinus Torvalds #include <linux/in.h>
1521da177e4SLinus Torvalds #include <linux/ip.h>
1531da177e4SLinus Torvalds #include <linux/ipv6.h>
1541da177e4SLinus Torvalds #include <linux/udp.h>
1551da177e4SLinus Torvalds #include <linux/proc_fs.h>
156d50a6b56SStephen Hemminger #include <linux/seq_file.h>
1571da177e4SLinus Torvalds #include <linux/wait.h>
158f404e9a6SKris Katterjohn #include <linux/etherdevice.h>
159ee74baa7SDavid S. Miller #include <linux/kthread.h>
160268bb0ceSLinus Torvalds #include <linux/prefetch.h>
161457c4cbcSEric W. Biederman #include <net/net_namespace.h>
1621da177e4SLinus Torvalds #include <net/checksum.h>
1631da177e4SLinus Torvalds #include <net/ipv6.h>
164c26bf4a5SThomas Graf #include <net/udp.h>
16573d94e94SStephen Rothwell #include <net/ip6_checksum.h>
1661da177e4SLinus Torvalds #include <net/addrconf.h>
167a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
168a553e4a6SJamal Hadi Salim #include <net/xfrm.h>
169a553e4a6SJamal Hadi Salim #endif
1704e58a027SCong Wang #include <net/netns/generic.h>
1711da177e4SLinus Torvalds #include <asm/byteorder.h>
1721da177e4SLinus Torvalds #include <linux/rcupdate.h>
1731977f032SJiri Slaby #include <linux/bitops.h>
17463adc6fbSStephen Hemminger #include <linux/io.h>
17563adc6fbSStephen Hemminger #include <linux/timex.h>
17663adc6fbSStephen Hemminger #include <linux/uaccess.h>
1771da177e4SLinus Torvalds #include <asm/dma.h>
1781da177e4SLinus Torvalds #include <asm/div64.h>		/* do_div */
1791da177e4SLinus Torvalds 
18043d28b65SDaniel Turull #define VERSION	"2.74"
1811da177e4SLinus Torvalds #define IP_NAME_SZ 32
182ca6549afSSteven Whitehouse #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
183d5f1ce9aSStephen Hemminger #define MPLS_STACK_BOTTOM htonl(0x00000100)
1841da177e4SLinus Torvalds 
185f9467eaeSJoe Perches #define func_enter() pr_debug("entering %s\n", __func__);
186f9467eaeSJoe Perches 
1871da177e4SLinus Torvalds /* Device flag bits */
1881da177e4SLinus Torvalds #define F_IPSRC_RND   (1<<0)	/* IP-Src Random  */
1891da177e4SLinus Torvalds #define F_IPDST_RND   (1<<1)	/* IP-Dst Random  */
1901da177e4SLinus Torvalds #define F_UDPSRC_RND  (1<<2)	/* UDP-Src Random */
1911da177e4SLinus Torvalds #define F_UDPDST_RND  (1<<3)	/* UDP-Dst Random */
1921da177e4SLinus Torvalds #define F_MACSRC_RND  (1<<4)	/* MAC-Src Random */
1931da177e4SLinus Torvalds #define F_MACDST_RND  (1<<5)	/* MAC-Dst Random */
1941da177e4SLinus Torvalds #define F_TXSIZE_RND  (1<<6)	/* Transmit size is random */
1951da177e4SLinus Torvalds #define F_IPV6        (1<<7)	/* Interface in IPV6 Mode */
196ca6549afSSteven Whitehouse #define F_MPLS_RND    (1<<8)	/* Random MPLS labels */
19734954ddcSFrancesco Fondelli #define F_VID_RND     (1<<9)	/* Random VLAN ID */
19834954ddcSFrancesco Fondelli #define F_SVID_RND    (1<<10)	/* Random SVLAN ID */
199007a531bSJamal Hadi Salim #define F_FLOW_SEQ    (1<<11)	/* Sequential flows */
200a553e4a6SJamal Hadi Salim #define F_IPSEC_ON    (1<<12)	/* ipsec on for flows */
20145b270f8SRobert Olsson #define F_QUEUE_MAP_RND (1<<13)	/* queue map Random */
202e6fce5b9SRobert Olsson #define F_QUEUE_MAP_CPU (1<<14)	/* queue map mirrors smp_processor_id() */
203e99b99b4SRobert Olsson #define F_NODE          (1<<15)	/* Node memory alloc*/
204c26bf4a5SThomas Graf #define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
205afb84b62SJesper Dangaard Brouer #define F_NO_TIMESTAMP  (1<<17)	/* Don't timestamp packets (default TS) */
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds /* Thread control flag bits */
2086b80d6a6SStephen Hemminger #define T_STOP        (1<<0)	/* Stop run */
2096b80d6a6SStephen Hemminger #define T_RUN         (1<<1)	/* Start run */
2106b80d6a6SStephen Hemminger #define T_REMDEVALL   (1<<2)	/* Remove all devs */
2116b80d6a6SStephen Hemminger #define T_REMDEV      (1<<3)	/* Remove one dev */
2121da177e4SLinus Torvalds 
2138788370aSJesper Dangaard Brouer /* If lock -- protects updating of if_list */
2141da177e4SLinus Torvalds #define   if_lock(t)           spin_lock(&(t->if_lock));
2151da177e4SLinus Torvalds #define   if_unlock(t)           spin_unlock(&(t->if_lock));
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Used to help with determining the pkts on receive */
2181da177e4SLinus Torvalds #define PKTGEN_MAGIC 0xbe9be955
219d50a6b56SStephen Hemminger #define PG_PROC_DIR "pktgen"
220d50a6b56SStephen Hemminger #define PGCTRL	    "pgctrl"
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds #define MAX_CFLOWS  65536
2231da177e4SLinus Torvalds 
22434954ddcSFrancesco Fondelli #define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
22534954ddcSFrancesco Fondelli #define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
22634954ddcSFrancesco Fondelli 
227222f1806SLuiz Capitulino struct flow_state {
228252e3346SAl Viro 	__be32 cur_daddr;
2291da177e4SLinus Torvalds 	int count;
230a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
231a553e4a6SJamal Hadi Salim 	struct xfrm_state *x;
232a553e4a6SJamal Hadi Salim #endif
233007a531bSJamal Hadi Salim 	__u32 flags;
2341da177e4SLinus Torvalds };
2351da177e4SLinus Torvalds 
236007a531bSJamal Hadi Salim /* flow flag bits */
237007a531bSJamal Hadi Salim #define F_INIT   (1<<0)		/* flow has been initialized */
238007a531bSJamal Hadi Salim 
2391da177e4SLinus Torvalds struct pktgen_dev {
2401da177e4SLinus Torvalds 	/*
2411da177e4SLinus Torvalds 	 * Try to keep frequent/infrequent used vars. separated.
2421da177e4SLinus Torvalds 	 */
24339df232fSStephen Hemminger 	struct proc_dir_entry *entry;	/* proc file */
2441da177e4SLinus Torvalds 	struct pktgen_thread *pg_thread;/* the owner */
24563adc6fbSStephen Hemminger 	struct list_head list;		/* chaining in the thread's run-queue */
2468788370aSJesper Dangaard Brouer 	struct rcu_head	 rcu;		/* freed by RCU */
2471da177e4SLinus Torvalds 
24863adc6fbSStephen Hemminger 	int running;		/* if false, the test will stop */
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	/* If min != max, then we will either do a linear iteration, or
2511da177e4SLinus Torvalds 	 * we will do a random selection from within the range.
2521da177e4SLinus Torvalds 	 */
2531da177e4SLinus Torvalds 	__u32 flags;
25495ed63f7SArthur Kepner 	int removal_mark;	/* non-zero => the device is marked for
25595ed63f7SArthur Kepner 				 * removal by worker thread */
2561da177e4SLinus Torvalds 
25768bf9f0bSAmerigo Wang 	int min_pkt_size;
25868bf9f0bSAmerigo Wang 	int max_pkt_size;
25916dab72fSJamal Hadi Salim 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
2601da177e4SLinus Torvalds 	int nfrags;
26126ad7879SEric Dumazet 	struct page *page;
262fd29cf72SStephen Hemminger 	u64 delay;		/* nano-seconds */
263fd29cf72SStephen Hemminger 
2641da177e4SLinus Torvalds 	__u64 count;		/* Default No packets to send */
2651da177e4SLinus Torvalds 	__u64 sofar;		/* How many pkts we've sent so far */
2661da177e4SLinus Torvalds 	__u64 tx_bytes;		/* How many bytes we've transmitted */
267f466dba1SJohn Fastabend 	__u64 errors;		/* Errors when trying to transmit, */
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	/* runtime counters relating to clone_skb */
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	__u64 allocated_skbs;
2721da177e4SLinus Torvalds 	__u32 clone_count;
2731da177e4SLinus Torvalds 	int last_ok;		/* Was last skb sent?
27463adc6fbSStephen Hemminger 				 * Or a failed transmit of some sort?
27563adc6fbSStephen Hemminger 				 * This will keep sequence numbers in order
2761da177e4SLinus Torvalds 				 */
277fd29cf72SStephen Hemminger 	ktime_t next_tx;
278fd29cf72SStephen Hemminger 	ktime_t started_at;
279fd29cf72SStephen Hemminger 	ktime_t stopped_at;
280fd29cf72SStephen Hemminger 	u64	idle_acc;	/* nano-seconds */
281fd29cf72SStephen Hemminger 
2821da177e4SLinus Torvalds 	__u32 seq_num;
2831da177e4SLinus Torvalds 
28463adc6fbSStephen Hemminger 	int clone_skb;		/*
28563adc6fbSStephen Hemminger 				 * Use multiple SKBs during packet gen.
28663adc6fbSStephen Hemminger 				 * If this number is greater than 1, then
28763adc6fbSStephen Hemminger 				 * that many copies of the same packet will be
28863adc6fbSStephen Hemminger 				 * sent before a new packet is allocated.
28963adc6fbSStephen Hemminger 				 * If you want to send 1024 identical packets
29063adc6fbSStephen Hemminger 				 * before creating a new packet,
29163adc6fbSStephen Hemminger 				 * set clone_skb to 1024.
2921da177e4SLinus Torvalds 				 */
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	char dst_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2951da177e4SLinus Torvalds 	char dst_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2961da177e4SLinus Torvalds 	char src_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2971da177e4SLinus Torvalds 	char src_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	struct in6_addr in6_saddr;
3001da177e4SLinus Torvalds 	struct in6_addr in6_daddr;
3011da177e4SLinus Torvalds 	struct in6_addr cur_in6_daddr;
3021da177e4SLinus Torvalds 	struct in6_addr cur_in6_saddr;
3031da177e4SLinus Torvalds 	/* For ranges */
3041da177e4SLinus Torvalds 	struct in6_addr min_in6_daddr;
3051da177e4SLinus Torvalds 	struct in6_addr max_in6_daddr;
3061da177e4SLinus Torvalds 	struct in6_addr min_in6_saddr;
3071da177e4SLinus Torvalds 	struct in6_addr max_in6_saddr;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	/* If we're doing ranges, random or incremental, then this
3101da177e4SLinus Torvalds 	 * defines the min/max for those ranges.
3111da177e4SLinus Torvalds 	 */
312252e3346SAl Viro 	__be32 saddr_min;	/* inclusive, source IP address */
313252e3346SAl Viro 	__be32 saddr_max;	/* exclusive, source IP address */
314252e3346SAl Viro 	__be32 daddr_min;	/* inclusive, dest IP address */
315252e3346SAl Viro 	__be32 daddr_max;	/* exclusive, dest IP address */
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	__u16 udp_src_min;	/* inclusive, source UDP port */
3181da177e4SLinus Torvalds 	__u16 udp_src_max;	/* exclusive, source UDP port */
3191da177e4SLinus Torvalds 	__u16 udp_dst_min;	/* inclusive, dest UDP port */
3201da177e4SLinus Torvalds 	__u16 udp_dst_max;	/* exclusive, dest UDP port */
3211da177e4SLinus Torvalds 
3221ca7768cSFrancesco Fondelli 	/* DSCP + ECN */
32363adc6fbSStephen Hemminger 	__u8 tos;            /* six MSB of (former) IPv4 TOS
32463adc6fbSStephen Hemminger 				are for dscp codepoint */
32563adc6fbSStephen Hemminger 	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6
32663adc6fbSStephen Hemminger 				(see RFC 3260, sec. 4) */
3271ca7768cSFrancesco Fondelli 
328ca6549afSSteven Whitehouse 	/* MPLS */
32995c96174SEric Dumazet 	unsigned int nr_labels;	/* Depth of stack, 0 = no MPLS */
330ca6549afSSteven Whitehouse 	__be32 labels[MAX_MPLS_LABELS];
331ca6549afSSteven Whitehouse 
33234954ddcSFrancesco Fondelli 	/* VLAN/SVLAN (802.1Q/Q-in-Q) */
33334954ddcSFrancesco Fondelli 	__u8  vlan_p;
33434954ddcSFrancesco Fondelli 	__u8  vlan_cfi;
33534954ddcSFrancesco Fondelli 	__u16 vlan_id;  /* 0xffff means no vlan tag */
33634954ddcSFrancesco Fondelli 
33734954ddcSFrancesco Fondelli 	__u8  svlan_p;
33834954ddcSFrancesco Fondelli 	__u8  svlan_cfi;
33934954ddcSFrancesco Fondelli 	__u16 svlan_id; /* 0xffff means no svlan tag */
34034954ddcSFrancesco Fondelli 
3411da177e4SLinus Torvalds 	__u32 src_mac_count;	/* How many MACs to iterate through */
3421da177e4SLinus Torvalds 	__u32 dst_mac_count;	/* How many MACs to iterate through */
3431da177e4SLinus Torvalds 
344f404e9a6SKris Katterjohn 	unsigned char dst_mac[ETH_ALEN];
345f404e9a6SKris Katterjohn 	unsigned char src_mac[ETH_ALEN];
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	__u32 cur_dst_mac_offset;
3481da177e4SLinus Torvalds 	__u32 cur_src_mac_offset;
349252e3346SAl Viro 	__be32 cur_saddr;
350252e3346SAl Viro 	__be32 cur_daddr;
35166ed1e5eSEric Dumazet 	__u16 ip_id;
3521da177e4SLinus Torvalds 	__u16 cur_udp_dst;
3531da177e4SLinus Torvalds 	__u16 cur_udp_src;
35445b270f8SRobert Olsson 	__u16 cur_queue_map;
3551da177e4SLinus Torvalds 	__u32 cur_pkt_size;
356baac8564SEric Dumazet 	__u32 last_pkt_size;
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	__u8 hh[14];
3591da177e4SLinus Torvalds 	/* = {
3601da177e4SLinus Torvalds 	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	   We fill in SRC address later
3631da177e4SLinus Torvalds 	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3641da177e4SLinus Torvalds 	   0x08, 0x00
3651da177e4SLinus Torvalds 	   };
3661da177e4SLinus Torvalds 	 */
3671da177e4SLinus Torvalds 	__u16 pad;		/* pad out the hh struct to an even 16 bytes */
3681da177e4SLinus Torvalds 
36963adc6fbSStephen Hemminger 	struct sk_buff *skb;	/* skb we are to transmit next, used for when we
3701da177e4SLinus Torvalds 				 * are transmitting the same one multiple times
3711da177e4SLinus Torvalds 				 */
37263adc6fbSStephen Hemminger 	struct net_device *odev; /* The out-going device.
37363adc6fbSStephen Hemminger 				  * Note that the device should have it's
37463adc6fbSStephen Hemminger 				  * pg_info pointer pointing back to this
37563adc6fbSStephen Hemminger 				  * device.
37663adc6fbSStephen Hemminger 				  * Set when the user specifies the out-going
37763adc6fbSStephen Hemminger 				  * device name (not when the inject is
3781da177e4SLinus Torvalds 				  * started as it used to do.)
3791da177e4SLinus Torvalds 				  */
380593f63b0SEric Dumazet 	char odevname[32];
3811da177e4SLinus Torvalds 	struct flow_state *flows;
38295c96174SEric Dumazet 	unsigned int cflows;	/* Concurrent flows (config) */
38395c96174SEric Dumazet 	unsigned int lflow;		/* Flow length  (config) */
38495c96174SEric Dumazet 	unsigned int nflows;	/* accumulated flows (stats) */
38595c96174SEric Dumazet 	unsigned int curfl;		/* current sequenced flow (state)*/
38645b270f8SRobert Olsson 
38745b270f8SRobert Olsson 	u16 queue_map_min;
38845b270f8SRobert Olsson 	u16 queue_map_max;
3899e50e3acSJohn Fastabend 	__u32 skb_priority;	/* skb priority field */
390e99b99b4SRobert Olsson 	int node;               /* Memory node */
39145b270f8SRobert Olsson 
392a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
393a553e4a6SJamal Hadi Salim 	__u8	ipsmode;		/* IPSEC mode (config) */
394a553e4a6SJamal Hadi Salim 	__u8	ipsproto;		/* IPSEC type (config) */
395de4aee7dSFan Du 	__u32	spi;
396cf93d47eSFan Du 	struct dst_entry dst;
397cf93d47eSFan Du 	struct dst_ops dstops;
398a553e4a6SJamal Hadi Salim #endif
39939df232fSStephen Hemminger 	char result[512];
4001da177e4SLinus Torvalds };
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds struct pktgen_hdr {
403252e3346SAl Viro 	__be32 pgh_magic;
404252e3346SAl Viro 	__be32 seq_num;
405252e3346SAl Viro 	__be32 tv_sec;
406252e3346SAl Viro 	__be32 tv_usec;
4071da177e4SLinus Torvalds };
4081da177e4SLinus Torvalds 
4094e58a027SCong Wang 
4104e58a027SCong Wang static int pg_net_id __read_mostly;
4114e58a027SCong Wang 
4124e58a027SCong Wang struct pktgen_net {
4134e58a027SCong Wang 	struct net		*net;
4144e58a027SCong Wang 	struct proc_dir_entry	*proc_dir;
4154e58a027SCong Wang 	struct list_head	pktgen_threads;
4164e58a027SCong Wang 	bool			pktgen_exiting;
4174e58a027SCong Wang };
418551eaff1SEric Dumazet 
4191da177e4SLinus Torvalds struct pktgen_thread {
42063adc6fbSStephen Hemminger 	spinlock_t if_lock;		/* for list of devices */
421c26a8016SLuiz Capitulino 	struct list_head if_list;	/* All device here */
422cdcdbe0bSLuiz Capitulino 	struct list_head th_list;
423ee74baa7SDavid S. Miller 	struct task_struct *tsk;
4241da177e4SLinus Torvalds 	char result[512];
4251da177e4SLinus Torvalds 
42663adc6fbSStephen Hemminger 	/* Field for thread to receive "posted" events terminate,
42763adc6fbSStephen Hemminger 	   stop ifs etc. */
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 	u32 control;
4301da177e4SLinus Torvalds 	int cpu;
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	wait_queue_head_t queue;
433d3ede327SDenis V. Lunev 	struct completion start_done;
4344e58a027SCong Wang 	struct pktgen_net *net;
4351da177e4SLinus Torvalds };
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds #define REMOVE 1
4381da177e4SLinus Torvalds #define FIND   0
4391da177e4SLinus Torvalds 
440c3d2f52dSStephen Hemminger static const char version[] =
441f9467eaeSJoe Perches 	"Packet Generator for packet performance testing. "
442f9467eaeSJoe Perches 	"Version: " VERSION "\n";
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
4451da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
446222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
4473e984840SEric Dumazet 					  const char *ifname, bool exact);
4481da177e4SLinus Torvalds static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
4494e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn);
4504e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn);
4514e58a027SCong Wang static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn);
4523bda06a3SStephen Hemminger 
4531da177e4SLinus Torvalds static void pktgen_stop(struct pktgen_thread *t);
4541da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
45539df232fSStephen Hemminger 
4561da177e4SLinus Torvalds /* Module parameters, defaults. */
45765c5b786SStephen Hemminger static int pg_count_d __read_mostly = 1000;
45865c5b786SStephen Hemminger static int pg_delay_d __read_mostly;
45965c5b786SStephen Hemminger static int pg_clone_skb_d  __read_mostly;
46065c5b786SStephen Hemminger static int debug  __read_mostly;
4611da177e4SLinus Torvalds 
462222fa076SLuiz Capitulino static DEFINE_MUTEX(pktgen_thread_lock);
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds static struct notifier_block pktgen_notifier_block = {
4651da177e4SLinus Torvalds 	.notifier_call = pktgen_device_event,
4661da177e4SLinus Torvalds };
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds /*
4691da177e4SLinus Torvalds  * /proc handling functions
4701da177e4SLinus Torvalds  *
4711da177e4SLinus Torvalds  */
4721da177e4SLinus Torvalds 
473d50a6b56SStephen Hemminger static int pgctrl_show(struct seq_file *seq, void *v)
474d50a6b56SStephen Hemminger {
475c3d2f52dSStephen Hemminger 	seq_puts(seq, version);
476d50a6b56SStephen Hemminger 	return 0;
477d50a6b56SStephen Hemminger }
4781da177e4SLinus Torvalds 
479d50a6b56SStephen Hemminger static ssize_t pgctrl_write(struct file *file, const char __user *buf,
4801da177e4SLinus Torvalds 			    size_t count, loff_t *ppos)
4811da177e4SLinus Torvalds {
482d50a6b56SStephen Hemminger 	char data[128];
4834e58a027SCong Wang 	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
4841da177e4SLinus Torvalds 
48509455747SMathias Krause 	if (!capable(CAP_NET_ADMIN))
48609455747SMathias Krause 		return -EPERM;
4871da177e4SLinus Torvalds 
48820b0c718SMathias Krause 	if (count == 0)
48920b0c718SMathias Krause 		return -EINVAL;
49020b0c718SMathias Krause 
491d50a6b56SStephen Hemminger 	if (count > sizeof(data))
492d50a6b56SStephen Hemminger 		count = sizeof(data);
4931da177e4SLinus Torvalds 
49409455747SMathias Krause 	if (copy_from_user(data, buf, count))
49509455747SMathias Krause 		return -EFAULT;
49609455747SMathias Krause 
49720b0c718SMathias Krause 	data[count - 1] = 0;	/* Strip trailing '\n' and terminate string */
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	if (!strcmp(data, "stop"))
5004e58a027SCong Wang 		pktgen_stop_all_threads_ifs(pn);
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	else if (!strcmp(data, "start"))
5034e58a027SCong Wang 		pktgen_run_all_threads(pn);
5041da177e4SLinus Torvalds 
505eb37b41cSJesse Brandeburg 	else if (!strcmp(data, "reset"))
5064e58a027SCong Wang 		pktgen_reset_all_threads(pn);
507eb37b41cSJesse Brandeburg 
5081da177e4SLinus Torvalds 	else
509*294a0b7fSJoe Perches 		pr_warn("Unknown command: %s\n", data);
5101da177e4SLinus Torvalds 
51109455747SMathias Krause 	return count;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds 
514d50a6b56SStephen Hemminger static int pgctrl_open(struct inode *inode, struct file *file)
5151da177e4SLinus Torvalds {
516d9dda78bSAl Viro 	return single_open(file, pgctrl_show, PDE_DATA(inode));
517d50a6b56SStephen Hemminger }
518d50a6b56SStephen Hemminger 
5199a32144eSArjan van de Ven static const struct file_operations pktgen_fops = {
520d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
521d50a6b56SStephen Hemminger 	.open    = pgctrl_open,
522d50a6b56SStephen Hemminger 	.read    = seq_read,
523d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
524d50a6b56SStephen Hemminger 	.write   = pgctrl_write,
525d50a6b56SStephen Hemminger 	.release = single_release,
526d50a6b56SStephen Hemminger };
527d50a6b56SStephen Hemminger 
528d50a6b56SStephen Hemminger static int pktgen_if_show(struct seq_file *seq, void *v)
529d50a6b56SStephen Hemminger {
530648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev = seq->private;
531fd29cf72SStephen Hemminger 	ktime_t stopped;
532fd29cf72SStephen Hemminger 	u64 idle;
5331da177e4SLinus Torvalds 
534222f1806SLuiz Capitulino 	seq_printf(seq,
535222f1806SLuiz Capitulino 		   "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n",
536222f1806SLuiz Capitulino 		   (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
537222f1806SLuiz Capitulino 		   pkt_dev->max_pkt_size);
5381da177e4SLinus Torvalds 
539222f1806SLuiz Capitulino 	seq_printf(seq,
540fd29cf72SStephen Hemminger 		   "     frags: %d  delay: %llu  clone_skb: %d  ifname: %s\n",
541fd29cf72SStephen Hemminger 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
542593f63b0SEric Dumazet 		   pkt_dev->clone_skb, pkt_dev->odevname);
5431da177e4SLinus Torvalds 
544222f1806SLuiz Capitulino 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
545222f1806SLuiz Capitulino 		   pkt_dev->lflow);
5461da177e4SLinus Torvalds 
54745b270f8SRobert Olsson 	seq_printf(seq,
54845b270f8SRobert Olsson 		   "     queue_map_min: %u  queue_map_max: %u\n",
54945b270f8SRobert Olsson 		   pkt_dev->queue_map_min,
55045b270f8SRobert Olsson 		   pkt_dev->queue_map_max);
55145b270f8SRobert Olsson 
5529e50e3acSJohn Fastabend 	if (pkt_dev->skb_priority)
5539e50e3acSJohn Fastabend 		seq_printf(seq, "     skb_priority: %u\n",
5549e50e3acSJohn Fastabend 			   pkt_dev->skb_priority);
5559e50e3acSJohn Fastabend 
5561da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
557222f1806SLuiz Capitulino 		seq_printf(seq,
55847a0200dSAlexey Dobriyan 			   "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n"
55947a0200dSAlexey Dobriyan 			   "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n",
56047a0200dSAlexey Dobriyan 			   &pkt_dev->in6_saddr,
56147a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr,
56247a0200dSAlexey Dobriyan 			   &pkt_dev->in6_daddr,
56347a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);
56463adc6fbSStephen Hemminger 	} else {
565222f1806SLuiz Capitulino 		seq_printf(seq,
56663adc6fbSStephen Hemminger 			   "     dst_min: %s  dst_max: %s\n",
56763adc6fbSStephen Hemminger 			   pkt_dev->dst_min, pkt_dev->dst_max);
56863adc6fbSStephen Hemminger 		seq_printf(seq,
56963adc6fbSStephen Hemminger 			   "        src_min: %s  src_max: %s\n",
57063adc6fbSStephen Hemminger 			   pkt_dev->src_min, pkt_dev->src_max);
57163adc6fbSStephen Hemminger 	}
5721da177e4SLinus Torvalds 
573d50a6b56SStephen Hemminger 	seq_puts(seq, "     src_mac: ");
5741da177e4SLinus Torvalds 
575e174961cSJohannes Berg 	seq_printf(seq, "%pM ",
576e174961cSJohannes Berg 		   is_zero_ether_addr(pkt_dev->src_mac) ?
577e174961cSJohannes Berg 			     pkt_dev->odev->dev_addr : pkt_dev->src_mac);
5781da177e4SLinus Torvalds 
57997dc48e2SThomas Graf 	seq_puts(seq, "dst_mac: ");
580e174961cSJohannes Berg 	seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
5811da177e4SLinus Torvalds 
582222f1806SLuiz Capitulino 	seq_printf(seq,
58363adc6fbSStephen Hemminger 		   "     udp_src_min: %d  udp_src_max: %d"
58463adc6fbSStephen Hemminger 		   "  udp_dst_min: %d  udp_dst_max: %d\n",
585222f1806SLuiz Capitulino 		   pkt_dev->udp_src_min, pkt_dev->udp_src_max,
586222f1806SLuiz Capitulino 		   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
5871da177e4SLinus Torvalds 
588222f1806SLuiz Capitulino 	seq_printf(seq,
589ca6549afSSteven Whitehouse 		   "     src_mac_count: %d  dst_mac_count: %d\n",
5901da177e4SLinus Torvalds 		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
5911da177e4SLinus Torvalds 
592ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels) {
59395c96174SEric Dumazet 		unsigned int i;
59497dc48e2SThomas Graf 		seq_puts(seq, "     mpls: ");
595ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
596ca6549afSSteven Whitehouse 			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
597ca6549afSSteven Whitehouse 				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
598ca6549afSSteven Whitehouse 	}
599ca6549afSSteven Whitehouse 
60063adc6fbSStephen Hemminger 	if (pkt_dev->vlan_id != 0xffff)
60134954ddcSFrancesco Fondelli 		seq_printf(seq, "     vlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
60263adc6fbSStephen Hemminger 			   pkt_dev->vlan_id, pkt_dev->vlan_p,
60363adc6fbSStephen Hemminger 			   pkt_dev->vlan_cfi);
60434954ddcSFrancesco Fondelli 
60563adc6fbSStephen Hemminger 	if (pkt_dev->svlan_id != 0xffff)
60634954ddcSFrancesco Fondelli 		seq_printf(seq, "     svlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
60763adc6fbSStephen Hemminger 			   pkt_dev->svlan_id, pkt_dev->svlan_p,
60863adc6fbSStephen Hemminger 			   pkt_dev->svlan_cfi);
60934954ddcSFrancesco Fondelli 
61063adc6fbSStephen Hemminger 	if (pkt_dev->tos)
6111ca7768cSFrancesco Fondelli 		seq_printf(seq, "     tos: 0x%02x\n", pkt_dev->tos);
6121ca7768cSFrancesco Fondelli 
61363adc6fbSStephen Hemminger 	if (pkt_dev->traffic_class)
6141ca7768cSFrancesco Fondelli 		seq_printf(seq, "     traffic_class: 0x%02x\n", pkt_dev->traffic_class);
6151ca7768cSFrancesco Fondelli 
616e99b99b4SRobert Olsson 	if (pkt_dev->node >= 0)
617e99b99b4SRobert Olsson 		seq_printf(seq, "     node: %d\n", pkt_dev->node);
618e99b99b4SRobert Olsson 
61997dc48e2SThomas Graf 	seq_puts(seq, "     Flags: ");
620ca6549afSSteven Whitehouse 
6211da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
62297dc48e2SThomas Graf 		seq_puts(seq, "IPV6  ");
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPSRC_RND)
62597dc48e2SThomas Graf 		seq_puts(seq, "IPSRC_RND  ");
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPDST_RND)
62897dc48e2SThomas Graf 		seq_puts(seq, "IPDST_RND  ");
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	if (pkt_dev->flags & F_TXSIZE_RND)
63197dc48e2SThomas Graf 		seq_puts(seq, "TXSIZE_RND  ");
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPSRC_RND)
63497dc48e2SThomas Graf 		seq_puts(seq, "UDPSRC_RND  ");
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPDST_RND)
63797dc48e2SThomas Graf 		seq_puts(seq, "UDPDST_RND  ");
6381da177e4SLinus Torvalds 
639c26bf4a5SThomas Graf 	if (pkt_dev->flags & F_UDPCSUM)
64097dc48e2SThomas Graf 		seq_puts(seq, "UDPCSUM  ");
641c26bf4a5SThomas Graf 
642afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP)
643afb84b62SJesper Dangaard Brouer 		seq_puts(seq, "NO_TIMESTAMP  ");
644afb84b62SJesper Dangaard Brouer 
645ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND)
64697dc48e2SThomas Graf 		seq_puts(seq,  "MPLS_RND  ");
647ca6549afSSteven Whitehouse 
64845b270f8SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_RND)
64997dc48e2SThomas Graf 		seq_puts(seq,  "QUEUE_MAP_RND  ");
65045b270f8SRobert Olsson 
651e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
65297dc48e2SThomas Graf 		seq_puts(seq,  "QUEUE_MAP_CPU  ");
653e6fce5b9SRobert Olsson 
654007a531bSJamal Hadi Salim 	if (pkt_dev->cflows) {
655007a531bSJamal Hadi Salim 		if (pkt_dev->flags & F_FLOW_SEQ)
65697dc48e2SThomas Graf 			seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/
657007a531bSJamal Hadi Salim 		else
65897dc48e2SThomas Graf 			seq_puts(seq,  "FLOW_RND  ");
659007a531bSJamal Hadi Salim 	}
660007a531bSJamal Hadi Salim 
661a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
6628101328bSFan Du 	if (pkt_dev->flags & F_IPSEC_ON) {
66397dc48e2SThomas Graf 		seq_puts(seq,  "IPSEC  ");
6648101328bSFan Du 		if (pkt_dev->spi)
6658101328bSFan Du 			seq_printf(seq, "spi:%u", pkt_dev->spi);
6668101328bSFan Du 	}
667a553e4a6SJamal Hadi Salim #endif
668a553e4a6SJamal Hadi Salim 
6691da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACSRC_RND)
67097dc48e2SThomas Graf 		seq_puts(seq, "MACSRC_RND  ");
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACDST_RND)
67397dc48e2SThomas Graf 		seq_puts(seq, "MACDST_RND  ");
6741da177e4SLinus Torvalds 
67534954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_VID_RND)
67697dc48e2SThomas Graf 		seq_puts(seq, "VID_RND  ");
67734954ddcSFrancesco Fondelli 
67834954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_SVID_RND)
67997dc48e2SThomas Graf 		seq_puts(seq, "SVID_RND  ");
68034954ddcSFrancesco Fondelli 
681e99b99b4SRobert Olsson 	if (pkt_dev->flags & F_NODE)
68297dc48e2SThomas Graf 		seq_puts(seq, "NODE_ALLOC  ");
683e99b99b4SRobert Olsson 
684d50a6b56SStephen Hemminger 	seq_puts(seq, "\n");
6851da177e4SLinus Torvalds 
686fd29cf72SStephen Hemminger 	/* not really stopped, more like last-running-at */
687398f382cSDaniel Borkmann 	stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
688fd29cf72SStephen Hemminger 	idle = pkt_dev->idle_acc;
689fd29cf72SStephen Hemminger 	do_div(idle, NSEC_PER_USEC);
6901da177e4SLinus Torvalds 
691222f1806SLuiz Capitulino 	seq_printf(seq,
692fd29cf72SStephen Hemminger 		   "Current:\n     pkts-sofar: %llu  errors: %llu\n",
6931da177e4SLinus Torvalds 		   (unsigned long long)pkt_dev->sofar,
694fd29cf72SStephen Hemminger 		   (unsigned long long)pkt_dev->errors);
695fd29cf72SStephen Hemminger 
696fd29cf72SStephen Hemminger 	seq_printf(seq,
697fd29cf72SStephen Hemminger 		   "     started: %lluus  stopped: %lluus idle: %lluus\n",
698fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(pkt_dev->started_at),
699fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(stopped),
700fd29cf72SStephen Hemminger 		   (unsigned long long) idle);
7011da177e4SLinus Torvalds 
702222f1806SLuiz Capitulino 	seq_printf(seq,
703222f1806SLuiz Capitulino 		   "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
704d50a6b56SStephen Hemminger 		   pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
705d50a6b56SStephen Hemminger 		   pkt_dev->cur_src_mac_offset);
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
70847a0200dSAlexey Dobriyan 		seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n",
70947a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_saddr,
71047a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_daddr);
711222f1806SLuiz Capitulino 	} else
7120373a946SAmerigo Wang 		seq_printf(seq, "     cur_saddr: %pI4  cur_daddr: %pI4\n",
7130373a946SAmerigo Wang 			   &pkt_dev->cur_saddr, &pkt_dev->cur_daddr);
7141da177e4SLinus Torvalds 
715d50a6b56SStephen Hemminger 	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",
7161da177e4SLinus Torvalds 		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
7171da177e4SLinus Torvalds 
71845b270f8SRobert Olsson 	seq_printf(seq, "     cur_queue_map: %u\n", pkt_dev->cur_queue_map);
71945b270f8SRobert Olsson 
720d50a6b56SStephen Hemminger 	seq_printf(seq, "     flows: %u\n", pkt_dev->nflows);
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	if (pkt_dev->result[0])
723d50a6b56SStephen Hemminger 		seq_printf(seq, "Result: %s\n", pkt_dev->result);
7241da177e4SLinus Torvalds 	else
72597dc48e2SThomas Graf 		seq_puts(seq, "Result: Idle\n");
7261da177e4SLinus Torvalds 
727d50a6b56SStephen Hemminger 	return 0;
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
730ca6549afSSteven Whitehouse 
73163adc6fbSStephen Hemminger static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
73263adc6fbSStephen Hemminger 		     __u32 *num)
733ca6549afSSteven Whitehouse {
734ca6549afSSteven Whitehouse 	int i = 0;
735ca6549afSSteven Whitehouse 	*num = 0;
736ca6549afSSteven Whitehouse 
7371ca7768cSFrancesco Fondelli 	for (; i < maxlen; i++) {
73882fd5b5dSAndy Shevchenko 		int value;
739ca6549afSSteven Whitehouse 		char c;
740ca6549afSSteven Whitehouse 		*num <<= 4;
741ca6549afSSteven Whitehouse 		if (get_user(c, &user_buffer[i]))
742ca6549afSSteven Whitehouse 			return -EFAULT;
74382fd5b5dSAndy Shevchenko 		value = hex_to_bin(c);
74482fd5b5dSAndy Shevchenko 		if (value >= 0)
74582fd5b5dSAndy Shevchenko 			*num |= value;
746ca6549afSSteven Whitehouse 		else
747ca6549afSSteven Whitehouse 			break;
748ca6549afSSteven Whitehouse 	}
749ca6549afSSteven Whitehouse 	return i;
750ca6549afSSteven Whitehouse }
751ca6549afSSteven Whitehouse 
752222f1806SLuiz Capitulino static int count_trail_chars(const char __user * user_buffer,
753222f1806SLuiz Capitulino 			     unsigned int maxlen)
7541da177e4SLinus Torvalds {
7551da177e4SLinus Torvalds 	int i;
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds 	for (i = 0; i < maxlen; i++) {
7581da177e4SLinus Torvalds 		char c;
7591da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
7601da177e4SLinus Torvalds 			return -EFAULT;
7611da177e4SLinus Torvalds 		switch (c) {
7621da177e4SLinus Torvalds 		case '\"':
7631da177e4SLinus Torvalds 		case '\n':
7641da177e4SLinus Torvalds 		case '\r':
7651da177e4SLinus Torvalds 		case '\t':
7661da177e4SLinus Torvalds 		case ' ':
7671da177e4SLinus Torvalds 		case '=':
7681da177e4SLinus Torvalds 			break;
7691da177e4SLinus Torvalds 		default:
7701da177e4SLinus Torvalds 			goto done;
7713ff50b79SStephen Hemminger 		}
7721da177e4SLinus Torvalds 	}
7731da177e4SLinus Torvalds done:
7741da177e4SLinus Torvalds 	return i;
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds 
777bf0813bdSPaul Gortmaker static long num_arg(const char __user *user_buffer, unsigned long maxlen,
778bf0813bdSPaul Gortmaker 				unsigned long *num)
7791da177e4SLinus Torvalds {
780d6182223SPaul Gortmaker 	int i;
7811da177e4SLinus Torvalds 	*num = 0;
7821da177e4SLinus Torvalds 
783d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
7841da177e4SLinus Torvalds 		char c;
7851da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
7861da177e4SLinus Torvalds 			return -EFAULT;
7871da177e4SLinus Torvalds 		if ((c >= '0') && (c <= '9')) {
7881da177e4SLinus Torvalds 			*num *= 10;
7891da177e4SLinus Torvalds 			*num += c - '0';
7901da177e4SLinus Torvalds 		} else
7911da177e4SLinus Torvalds 			break;
7921da177e4SLinus Torvalds 	}
7931da177e4SLinus Torvalds 	return i;
7941da177e4SLinus Torvalds }
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds static int strn_len(const char __user * user_buffer, unsigned int maxlen)
7971da177e4SLinus Torvalds {
798d6182223SPaul Gortmaker 	int i;
7991da177e4SLinus Torvalds 
800d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
8011da177e4SLinus Torvalds 		char c;
8021da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8031da177e4SLinus Torvalds 			return -EFAULT;
8041da177e4SLinus Torvalds 		switch (c) {
8051da177e4SLinus Torvalds 		case '\"':
8061da177e4SLinus Torvalds 		case '\n':
8071da177e4SLinus Torvalds 		case '\r':
8081da177e4SLinus Torvalds 		case '\t':
8091da177e4SLinus Torvalds 		case ' ':
8101da177e4SLinus Torvalds 			goto done_str;
8111da177e4SLinus Torvalds 		default:
8121da177e4SLinus Torvalds 			break;
8133ff50b79SStephen Hemminger 		}
8141da177e4SLinus Torvalds 	}
8151da177e4SLinus Torvalds done_str:
8161da177e4SLinus Torvalds 	return i;
8171da177e4SLinus Torvalds }
8181da177e4SLinus Torvalds 
819ca6549afSSteven Whitehouse static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
820ca6549afSSteven Whitehouse {
82195c96174SEric Dumazet 	unsigned int n = 0;
822ca6549afSSteven Whitehouse 	char c;
823ca6549afSSteven Whitehouse 	ssize_t i = 0;
824ca6549afSSteven Whitehouse 	int len;
825ca6549afSSteven Whitehouse 
826ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = 0;
827ca6549afSSteven Whitehouse 	do {
828ca6549afSSteven Whitehouse 		__u32 tmp;
8291ca7768cSFrancesco Fondelli 		len = hex32_arg(&buffer[i], 8, &tmp);
830ca6549afSSteven Whitehouse 		if (len <= 0)
831ca6549afSSteven Whitehouse 			return len;
832ca6549afSSteven Whitehouse 		pkt_dev->labels[n] = htonl(tmp);
833ca6549afSSteven Whitehouse 		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
834ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
835ca6549afSSteven Whitehouse 		i += len;
836ca6549afSSteven Whitehouse 		if (get_user(c, &buffer[i]))
837ca6549afSSteven Whitehouse 			return -EFAULT;
838ca6549afSSteven Whitehouse 		i++;
839ca6549afSSteven Whitehouse 		n++;
840ca6549afSSteven Whitehouse 		if (n >= MAX_MPLS_LABELS)
841ca6549afSSteven Whitehouse 			return -E2BIG;
842ca6549afSSteven Whitehouse 	} while (c == ',');
843ca6549afSSteven Whitehouse 
844ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = n;
845ca6549afSSteven Whitehouse 	return i;
846ca6549afSSteven Whitehouse }
847ca6549afSSteven Whitehouse 
848222f1806SLuiz Capitulino static ssize_t pktgen_if_write(struct file *file,
849222f1806SLuiz Capitulino 			       const char __user * user_buffer, size_t count,
850222f1806SLuiz Capitulino 			       loff_t * offset)
8511da177e4SLinus Torvalds {
8528a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
853d50a6b56SStephen Hemminger 	struct pktgen_dev *pkt_dev = seq->private;
854d6182223SPaul Gortmaker 	int i, max, len;
8551da177e4SLinus Torvalds 	char name[16], valstr[32];
8561da177e4SLinus Torvalds 	unsigned long value = 0;
8571da177e4SLinus Torvalds 	char *pg_result = NULL;
8581da177e4SLinus Torvalds 	int tmp = 0;
8591da177e4SLinus Torvalds 	char buf[128];
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds 	pg_result = &(pkt_dev->result[0]);
8621da177e4SLinus Torvalds 
8631da177e4SLinus Torvalds 	if (count < 1) {
864*294a0b7fSJoe Perches 		pr_warn("wrong command format\n");
8651da177e4SLinus Torvalds 		return -EINVAL;
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 
868d6182223SPaul Gortmaker 	max = count;
869d6182223SPaul Gortmaker 	tmp = count_trail_chars(user_buffer, max);
8701da177e4SLinus Torvalds 	if (tmp < 0) {
871*294a0b7fSJoe Perches 		pr_warn("illegal format\n");
8721da177e4SLinus Torvalds 		return tmp;
8731da177e4SLinus Torvalds 	}
874d6182223SPaul Gortmaker 	i = tmp;
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 	/* Read variable name */
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
87963adc6fbSStephen Hemminger 	if (len < 0)
880222f1806SLuiz Capitulino 		return len;
88163adc6fbSStephen Hemminger 
8821da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
8831da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
8841da177e4SLinus Torvalds 		return -EFAULT;
8851da177e4SLinus Torvalds 	i += len;
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 	max = count - i;
8881da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
8891da177e4SLinus Torvalds 	if (len < 0)
8901da177e4SLinus Torvalds 		return len;
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 	i += len;
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	if (debug) {
89586c2c0a8SDmitry Torokhov 		size_t copy = min_t(size_t, count, 1023);
896448d7b5dSNelson Elhage 		char tb[copy + 1];
897448d7b5dSNelson Elhage 		if (copy_from_user(tb, user_buffer, copy))
8981da177e4SLinus Torvalds 			return -EFAULT;
899448d7b5dSNelson Elhage 		tb[copy] = 0;
900f342cda7SJoe Perches 		pr_debug("%s,%lu  buffer -:%s:-\n",
901f342cda7SJoe Perches 			 name, (unsigned long)count, tb);
9021da177e4SLinus Torvalds 	}
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	if (!strcmp(name, "min_pkt_size")) {
9051da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
90663adc6fbSStephen Hemminger 		if (len < 0)
907222f1806SLuiz Capitulino 			return len;
90863adc6fbSStephen Hemminger 
9091da177e4SLinus Torvalds 		i += len;
9101da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9111da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9121da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9131da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9141da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9151da177e4SLinus Torvalds 		}
916222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: min_pkt_size=%u",
917222f1806SLuiz Capitulino 			pkt_dev->min_pkt_size);
9181da177e4SLinus Torvalds 		return count;
9191da177e4SLinus Torvalds 	}
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	if (!strcmp(name, "max_pkt_size")) {
9221da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
92363adc6fbSStephen Hemminger 		if (len < 0)
924222f1806SLuiz Capitulino 			return len;
92563adc6fbSStephen Hemminger 
9261da177e4SLinus Torvalds 		i += len;
9271da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9281da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9291da177e4SLinus Torvalds 		if (value != pkt_dev->max_pkt_size) {
9301da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9311da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9321da177e4SLinus Torvalds 		}
933222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: max_pkt_size=%u",
934222f1806SLuiz Capitulino 			pkt_dev->max_pkt_size);
9351da177e4SLinus Torvalds 		return count;
9361da177e4SLinus Torvalds 	}
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	/* Shortcut for min = max */
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds 	if (!strcmp(name, "pkt_size")) {
9411da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
94263adc6fbSStephen Hemminger 		if (len < 0)
943222f1806SLuiz Capitulino 			return len;
94463adc6fbSStephen Hemminger 
9451da177e4SLinus Torvalds 		i += len;
9461da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9471da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9481da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9491da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9501da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9511da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9521da177e4SLinus Torvalds 		}
9531da177e4SLinus Torvalds 		sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
9541da177e4SLinus Torvalds 		return count;
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds 
9571da177e4SLinus Torvalds 	if (!strcmp(name, "debug")) {
9581da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
95963adc6fbSStephen Hemminger 		if (len < 0)
960222f1806SLuiz Capitulino 			return len;
96163adc6fbSStephen Hemminger 
9621da177e4SLinus Torvalds 		i += len;
9631da177e4SLinus Torvalds 		debug = value;
9641da177e4SLinus Torvalds 		sprintf(pg_result, "OK: debug=%u", debug);
9651da177e4SLinus Torvalds 		return count;
9661da177e4SLinus Torvalds 	}
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 	if (!strcmp(name, "frags")) {
9691da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
97063adc6fbSStephen Hemminger 		if (len < 0)
971222f1806SLuiz Capitulino 			return len;
97263adc6fbSStephen Hemminger 
9731da177e4SLinus Torvalds 		i += len;
9741da177e4SLinus Torvalds 		pkt_dev->nfrags = value;
9751da177e4SLinus Torvalds 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
9761da177e4SLinus Torvalds 		return count;
9771da177e4SLinus Torvalds 	}
9781da177e4SLinus Torvalds 	if (!strcmp(name, "delay")) {
9791da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
98063adc6fbSStephen Hemminger 		if (len < 0)
981222f1806SLuiz Capitulino 			return len;
98263adc6fbSStephen Hemminger 
9831da177e4SLinus Torvalds 		i += len;
984fd29cf72SStephen Hemminger 		if (value == 0x7FFFFFFF)
985fd29cf72SStephen Hemminger 			pkt_dev->delay = ULLONG_MAX;
986fd29cf72SStephen Hemminger 		else
9879240d715SEric Dumazet 			pkt_dev->delay = (u64)value;
988fd29cf72SStephen Hemminger 
989fd29cf72SStephen Hemminger 		sprintf(pg_result, "OK: delay=%llu",
990fd29cf72SStephen Hemminger 			(unsigned long long) pkt_dev->delay);
9911da177e4SLinus Torvalds 		return count;
9921da177e4SLinus Torvalds 	}
99343d28b65SDaniel Turull 	if (!strcmp(name, "rate")) {
99443d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
99543d28b65SDaniel Turull 		if (len < 0)
99643d28b65SDaniel Turull 			return len;
99743d28b65SDaniel Turull 
99843d28b65SDaniel Turull 		i += len;
99943d28b65SDaniel Turull 		if (!value)
100043d28b65SDaniel Turull 			return len;
100143d28b65SDaniel Turull 		pkt_dev->delay = pkt_dev->min_pkt_size*8*NSEC_PER_USEC/value;
100243d28b65SDaniel Turull 		if (debug)
1003f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
100443d28b65SDaniel Turull 
100543d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
100643d28b65SDaniel Turull 		return count;
100743d28b65SDaniel Turull 	}
100843d28b65SDaniel Turull 	if (!strcmp(name, "ratep")) {
100943d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
101043d28b65SDaniel Turull 		if (len < 0)
101143d28b65SDaniel Turull 			return len;
101243d28b65SDaniel Turull 
101343d28b65SDaniel Turull 		i += len;
101443d28b65SDaniel Turull 		if (!value)
101543d28b65SDaniel Turull 			return len;
101643d28b65SDaniel Turull 		pkt_dev->delay = NSEC_PER_SEC/value;
101743d28b65SDaniel Turull 		if (debug)
1018f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
101943d28b65SDaniel Turull 
102043d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
102143d28b65SDaniel Turull 		return count;
102243d28b65SDaniel Turull 	}
10231da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_min")) {
10241da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
102563adc6fbSStephen Hemminger 		if (len < 0)
1026222f1806SLuiz Capitulino 			return len;
102763adc6fbSStephen Hemminger 
10281da177e4SLinus Torvalds 		i += len;
10291da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_min) {
10301da177e4SLinus Torvalds 			pkt_dev->udp_src_min = value;
10311da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10321da177e4SLinus Torvalds 		}
10331da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
10341da177e4SLinus Torvalds 		return count;
10351da177e4SLinus Torvalds 	}
10361da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_min")) {
10371da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
103863adc6fbSStephen Hemminger 		if (len < 0)
1039222f1806SLuiz Capitulino 			return len;
104063adc6fbSStephen Hemminger 
10411da177e4SLinus Torvalds 		i += len;
10421da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_min) {
10431da177e4SLinus Torvalds 			pkt_dev->udp_dst_min = value;
10441da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10451da177e4SLinus Torvalds 		}
10461da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
10471da177e4SLinus Torvalds 		return count;
10481da177e4SLinus Torvalds 	}
10491da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_max")) {
10501da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
105163adc6fbSStephen Hemminger 		if (len < 0)
1052222f1806SLuiz Capitulino 			return len;
105363adc6fbSStephen Hemminger 
10541da177e4SLinus Torvalds 		i += len;
10551da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_max) {
10561da177e4SLinus Torvalds 			pkt_dev->udp_src_max = value;
10571da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10581da177e4SLinus Torvalds 		}
10591da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
10601da177e4SLinus Torvalds 		return count;
10611da177e4SLinus Torvalds 	}
10621da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_max")) {
10631da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
106463adc6fbSStephen Hemminger 		if (len < 0)
1065222f1806SLuiz Capitulino 			return len;
106663adc6fbSStephen Hemminger 
10671da177e4SLinus Torvalds 		i += len;
10681da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_max) {
10691da177e4SLinus Torvalds 			pkt_dev->udp_dst_max = value;
10701da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10711da177e4SLinus Torvalds 		}
10721da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
10731da177e4SLinus Torvalds 		return count;
10741da177e4SLinus Torvalds 	}
10751da177e4SLinus Torvalds 	if (!strcmp(name, "clone_skb")) {
10761da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
107763adc6fbSStephen Hemminger 		if (len < 0)
1078222f1806SLuiz Capitulino 			return len;
1079d8873315SNeil Horman 		if ((value > 0) &&
1080d8873315SNeil Horman 		    (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
1081d8873315SNeil Horman 			return -ENOTSUPP;
10821da177e4SLinus Torvalds 		i += len;
10831da177e4SLinus Torvalds 		pkt_dev->clone_skb = value;
10841da177e4SLinus Torvalds 
10851da177e4SLinus Torvalds 		sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
10861da177e4SLinus Torvalds 		return count;
10871da177e4SLinus Torvalds 	}
10881da177e4SLinus Torvalds 	if (!strcmp(name, "count")) {
10891da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
109063adc6fbSStephen Hemminger 		if (len < 0)
1091222f1806SLuiz Capitulino 			return len;
109263adc6fbSStephen Hemminger 
10931da177e4SLinus Torvalds 		i += len;
10941da177e4SLinus Torvalds 		pkt_dev->count = value;
10951da177e4SLinus Torvalds 		sprintf(pg_result, "OK: count=%llu",
10961da177e4SLinus Torvalds 			(unsigned long long)pkt_dev->count);
10971da177e4SLinus Torvalds 		return count;
10981da177e4SLinus Torvalds 	}
10991da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac_count")) {
11001da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
110163adc6fbSStephen Hemminger 		if (len < 0)
1102222f1806SLuiz Capitulino 			return len;
110363adc6fbSStephen Hemminger 
11041da177e4SLinus Torvalds 		i += len;
11051da177e4SLinus Torvalds 		if (pkt_dev->src_mac_count != value) {
11061da177e4SLinus Torvalds 			pkt_dev->src_mac_count = value;
11071da177e4SLinus Torvalds 			pkt_dev->cur_src_mac_offset = 0;
11081da177e4SLinus Torvalds 		}
1109222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: src_mac_count=%d",
1110222f1806SLuiz Capitulino 			pkt_dev->src_mac_count);
11111da177e4SLinus Torvalds 		return count;
11121da177e4SLinus Torvalds 	}
11131da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac_count")) {
11141da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
111563adc6fbSStephen Hemminger 		if (len < 0)
1116222f1806SLuiz Capitulino 			return len;
111763adc6fbSStephen Hemminger 
11181da177e4SLinus Torvalds 		i += len;
11191da177e4SLinus Torvalds 		if (pkt_dev->dst_mac_count != value) {
11201da177e4SLinus Torvalds 			pkt_dev->dst_mac_count = value;
11211da177e4SLinus Torvalds 			pkt_dev->cur_dst_mac_offset = 0;
11221da177e4SLinus Torvalds 		}
1123222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: dst_mac_count=%d",
1124222f1806SLuiz Capitulino 			pkt_dev->dst_mac_count);
11251da177e4SLinus Torvalds 		return count;
11261da177e4SLinus Torvalds 	}
1127e99b99b4SRobert Olsson 	if (!strcmp(name, "node")) {
1128e99b99b4SRobert Olsson 		len = num_arg(&user_buffer[i], 10, &value);
1129e99b99b4SRobert Olsson 		if (len < 0)
1130e99b99b4SRobert Olsson 			return len;
1131e99b99b4SRobert Olsson 
1132e99b99b4SRobert Olsson 		i += len;
1133e99b99b4SRobert Olsson 
1134e99b99b4SRobert Olsson 		if (node_possible(value)) {
1135e99b99b4SRobert Olsson 			pkt_dev->node = value;
1136e99b99b4SRobert Olsson 			sprintf(pg_result, "OK: node=%d", pkt_dev->node);
113726ad7879SEric Dumazet 			if (pkt_dev->page) {
113826ad7879SEric Dumazet 				put_page(pkt_dev->page);
113926ad7879SEric Dumazet 				pkt_dev->page = NULL;
114026ad7879SEric Dumazet 			}
1141e99b99b4SRobert Olsson 		}
1142e99b99b4SRobert Olsson 		else
1143e99b99b4SRobert Olsson 			sprintf(pg_result, "ERROR: node not possible");
1144e99b99b4SRobert Olsson 		return count;
1145e99b99b4SRobert Olsson 	}
11461da177e4SLinus Torvalds 	if (!strcmp(name, "flag")) {
11471da177e4SLinus Torvalds 		char f[32];
11481da177e4SLinus Torvalds 		memset(f, 0, 32);
11491da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
115063adc6fbSStephen Hemminger 		if (len < 0)
1151222f1806SLuiz Capitulino 			return len;
115263adc6fbSStephen Hemminger 
11531da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
11541da177e4SLinus Torvalds 			return -EFAULT;
11551da177e4SLinus Torvalds 		i += len;
11561da177e4SLinus Torvalds 		if (strcmp(f, "IPSRC_RND") == 0)
11571da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPSRC_RND;
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds 		else if (strcmp(f, "!IPSRC_RND") == 0)
11601da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPSRC_RND;
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 		else if (strcmp(f, "TXSIZE_RND") == 0)
11631da177e4SLinus Torvalds 			pkt_dev->flags |= F_TXSIZE_RND;
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds 		else if (strcmp(f, "!TXSIZE_RND") == 0)
11661da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_TXSIZE_RND;
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds 		else if (strcmp(f, "IPDST_RND") == 0)
11691da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPDST_RND;
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 		else if (strcmp(f, "!IPDST_RND") == 0)
11721da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPDST_RND;
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 		else if (strcmp(f, "UDPSRC_RND") == 0)
11751da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPSRC_RND;
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPSRC_RND") == 0)
11781da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPSRC_RND;
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 		else if (strcmp(f, "UDPDST_RND") == 0)
11811da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPDST_RND;
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPDST_RND") == 0)
11841da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPDST_RND;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 		else if (strcmp(f, "MACSRC_RND") == 0)
11871da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACSRC_RND;
11881da177e4SLinus Torvalds 
11891da177e4SLinus Torvalds 		else if (strcmp(f, "!MACSRC_RND") == 0)
11901da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACSRC_RND;
11911da177e4SLinus Torvalds 
11921da177e4SLinus Torvalds 		else if (strcmp(f, "MACDST_RND") == 0)
11931da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACDST_RND;
11941da177e4SLinus Torvalds 
11951da177e4SLinus Torvalds 		else if (strcmp(f, "!MACDST_RND") == 0)
11961da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACDST_RND;
11971da177e4SLinus Torvalds 
1198ca6549afSSteven Whitehouse 		else if (strcmp(f, "MPLS_RND") == 0)
1199ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
1200ca6549afSSteven Whitehouse 
1201ca6549afSSteven Whitehouse 		else if (strcmp(f, "!MPLS_RND") == 0)
1202ca6549afSSteven Whitehouse 			pkt_dev->flags &= ~F_MPLS_RND;
1203ca6549afSSteven Whitehouse 
120434954ddcSFrancesco Fondelli 		else if (strcmp(f, "VID_RND") == 0)
120534954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_VID_RND;
120634954ddcSFrancesco Fondelli 
120734954ddcSFrancesco Fondelli 		else if (strcmp(f, "!VID_RND") == 0)
120834954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_VID_RND;
120934954ddcSFrancesco Fondelli 
121034954ddcSFrancesco Fondelli 		else if (strcmp(f, "SVID_RND") == 0)
121134954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_SVID_RND;
121234954ddcSFrancesco Fondelli 
121334954ddcSFrancesco Fondelli 		else if (strcmp(f, "!SVID_RND") == 0)
121434954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_SVID_RND;
121534954ddcSFrancesco Fondelli 
1216007a531bSJamal Hadi Salim 		else if (strcmp(f, "FLOW_SEQ") == 0)
1217007a531bSJamal Hadi Salim 			pkt_dev->flags |= F_FLOW_SEQ;
1218007a531bSJamal Hadi Salim 
121945b270f8SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_RND") == 0)
122045b270f8SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_RND;
122145b270f8SRobert Olsson 
122245b270f8SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_RND") == 0)
122345b270f8SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_RND;
1224e6fce5b9SRobert Olsson 
1225e6fce5b9SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_CPU") == 0)
1226e6fce5b9SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_CPU;
1227e6fce5b9SRobert Olsson 
1228e6fce5b9SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_CPU") == 0)
1229e6fce5b9SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_CPU;
1230a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
1231a553e4a6SJamal Hadi Salim 		else if (strcmp(f, "IPSEC") == 0)
1232a553e4a6SJamal Hadi Salim 			pkt_dev->flags |= F_IPSEC_ON;
1233a553e4a6SJamal Hadi Salim #endif
1234a553e4a6SJamal Hadi Salim 
12351ca7768cSFrancesco Fondelli 		else if (strcmp(f, "!IPV6") == 0)
12361ca7768cSFrancesco Fondelli 			pkt_dev->flags &= ~F_IPV6;
12371ca7768cSFrancesco Fondelli 
1238e99b99b4SRobert Olsson 		else if (strcmp(f, "NODE_ALLOC") == 0)
1239e99b99b4SRobert Olsson 			pkt_dev->flags |= F_NODE;
1240e99b99b4SRobert Olsson 
1241e99b99b4SRobert Olsson 		else if (strcmp(f, "!NODE_ALLOC") == 0)
1242e99b99b4SRobert Olsson 			pkt_dev->flags &= ~F_NODE;
1243e99b99b4SRobert Olsson 
1244c26bf4a5SThomas Graf 		else if (strcmp(f, "UDPCSUM") == 0)
1245c26bf4a5SThomas Graf 			pkt_dev->flags |= F_UDPCSUM;
1246c26bf4a5SThomas Graf 
1247c26bf4a5SThomas Graf 		else if (strcmp(f, "!UDPCSUM") == 0)
1248c26bf4a5SThomas Graf 			pkt_dev->flags &= ~F_UDPCSUM;
1249c26bf4a5SThomas Graf 
1250afb84b62SJesper Dangaard Brouer 		else if (strcmp(f, "NO_TIMESTAMP") == 0)
1251afb84b62SJesper Dangaard Brouer 			pkt_dev->flags |= F_NO_TIMESTAMP;
1252afb84b62SJesper Dangaard Brouer 
12531da177e4SLinus Torvalds 		else {
1254222f1806SLuiz Capitulino 			sprintf(pg_result,
1255222f1806SLuiz Capitulino 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
12561da177e4SLinus Torvalds 				f,
12571ca7768cSFrancesco Fondelli 				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
125872f8e06fSMathias Krause 				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, "
125972f8e06fSMathias Krause 				"MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, "
126072f8e06fSMathias Krause 				"QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, "
1261afb84b62SJesper Dangaard Brouer 				"NO_TIMESTAMP, "
126272f8e06fSMathias Krause #ifdef CONFIG_XFRM
126372f8e06fSMathias Krause 				"IPSEC, "
126472f8e06fSMathias Krause #endif
126572f8e06fSMathias Krause 				"NODE_ALLOC\n");
12661da177e4SLinus Torvalds 			return count;
12671da177e4SLinus Torvalds 		}
12681da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
12691da177e4SLinus Torvalds 		return count;
12701da177e4SLinus Torvalds 	}
12711da177e4SLinus Torvalds 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
12721da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
127363adc6fbSStephen Hemminger 		if (len < 0)
1274222f1806SLuiz Capitulino 			return len;
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
12771da177e4SLinus Torvalds 			return -EFAULT;
12781da177e4SLinus Torvalds 		buf[len] = 0;
12791da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
12801da177e4SLinus Torvalds 			memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
12811da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_min, buf, len);
12821da177e4SLinus Torvalds 			pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
12831da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_min;
12841da177e4SLinus Torvalds 		}
12851da177e4SLinus Torvalds 		if (debug)
1286f342cda7SJoe Perches 			pr_debug("dst_min set to: %s\n", pkt_dev->dst_min);
12871da177e4SLinus Torvalds 		i += len;
12881da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
12891da177e4SLinus Torvalds 		return count;
12901da177e4SLinus Torvalds 	}
12911da177e4SLinus Torvalds 	if (!strcmp(name, "dst_max")) {
12921da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
129363adc6fbSStephen Hemminger 		if (len < 0)
1294222f1806SLuiz Capitulino 			return len;
129563adc6fbSStephen Hemminger 
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
12981da177e4SLinus Torvalds 			return -EFAULT;
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 		buf[len] = 0;
13011da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
13021da177e4SLinus Torvalds 			memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
13031da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_max, buf, len);
13041da177e4SLinus Torvalds 			pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
13051da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_max;
13061da177e4SLinus Torvalds 		}
13071da177e4SLinus Torvalds 		if (debug)
1308f342cda7SJoe Perches 			pr_debug("dst_max set to: %s\n", pkt_dev->dst_max);
13091da177e4SLinus Torvalds 		i += len;
13101da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
13111da177e4SLinus Torvalds 		return count;
13121da177e4SLinus Torvalds 	}
13131da177e4SLinus Torvalds 	if (!strcmp(name, "dst6")) {
13141da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1315222f1806SLuiz Capitulino 		if (len < 0)
1316222f1806SLuiz Capitulino 			return len;
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13191da177e4SLinus Torvalds 
13201da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13211da177e4SLinus Torvalds 			return -EFAULT;
13221da177e4SLinus Torvalds 		buf[len] = 0;
13231da177e4SLinus Torvalds 
1324c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL);
132547a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);
13261da177e4SLinus Torvalds 
13274e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->in6_daddr;
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 		if (debug)
1330f342cda7SJoe Perches 			pr_debug("dst6 set to: %s\n", buf);
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 		i += len;
13331da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6=%s", buf);
13341da177e4SLinus Torvalds 		return count;
13351da177e4SLinus Torvalds 	}
13361da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_min")) {
13371da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1338222f1806SLuiz Capitulino 		if (len < 0)
1339222f1806SLuiz Capitulino 			return len;
13401da177e4SLinus Torvalds 
13411da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13421da177e4SLinus Torvalds 
13431da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13441da177e4SLinus Torvalds 			return -EFAULT;
13451da177e4SLinus Torvalds 		buf[len] = 0;
13461da177e4SLinus Torvalds 
1347c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL);
134847a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);
13491da177e4SLinus Torvalds 
13504e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->min_in6_daddr;
13511da177e4SLinus Torvalds 		if (debug)
1352f342cda7SJoe Perches 			pr_debug("dst6_min set to: %s\n", buf);
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds 		i += len;
13551da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_min=%s", buf);
13561da177e4SLinus Torvalds 		return count;
13571da177e4SLinus Torvalds 	}
13581da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_max")) {
13591da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1360222f1806SLuiz Capitulino 		if (len < 0)
1361222f1806SLuiz Capitulino 			return len;
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13661da177e4SLinus Torvalds 			return -EFAULT;
13671da177e4SLinus Torvalds 		buf[len] = 0;
13681da177e4SLinus Torvalds 
1369c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL);
137047a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 		if (debug)
1373f342cda7SJoe Perches 			pr_debug("dst6_max set to: %s\n", buf);
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds 		i += len;
13761da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_max=%s", buf);
13771da177e4SLinus Torvalds 		return count;
13781da177e4SLinus Torvalds 	}
13791da177e4SLinus Torvalds 	if (!strcmp(name, "src6")) {
13801da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1381222f1806SLuiz Capitulino 		if (len < 0)
1382222f1806SLuiz Capitulino 			return len;
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13851da177e4SLinus Torvalds 
13861da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13871da177e4SLinus Torvalds 			return -EFAULT;
13881da177e4SLinus Torvalds 		buf[len] = 0;
13891da177e4SLinus Torvalds 
1390c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL);
139147a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);
13921da177e4SLinus Torvalds 
13934e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_saddr = pkt_dev->in6_saddr;
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 		if (debug)
1396f342cda7SJoe Perches 			pr_debug("src6 set to: %s\n", buf);
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds 		i += len;
13991da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src6=%s", buf);
14001da177e4SLinus Torvalds 		return count;
14011da177e4SLinus Torvalds 	}
14021da177e4SLinus Torvalds 	if (!strcmp(name, "src_min")) {
14031da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
140463adc6fbSStephen Hemminger 		if (len < 0)
1405222f1806SLuiz Capitulino 			return len;
140663adc6fbSStephen Hemminger 
14071da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14081da177e4SLinus Torvalds 			return -EFAULT;
14091da177e4SLinus Torvalds 		buf[len] = 0;
14101da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_min) != 0) {
14111da177e4SLinus Torvalds 			memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
14121da177e4SLinus Torvalds 			strncpy(pkt_dev->src_min, buf, len);
14131da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
14141da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_min;
14151da177e4SLinus Torvalds 		}
14161da177e4SLinus Torvalds 		if (debug)
1417f342cda7SJoe Perches 			pr_debug("src_min set to: %s\n", pkt_dev->src_min);
14181da177e4SLinus Torvalds 		i += len;
14191da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
14201da177e4SLinus Torvalds 		return count;
14211da177e4SLinus Torvalds 	}
14221da177e4SLinus Torvalds 	if (!strcmp(name, "src_max")) {
14231da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
142463adc6fbSStephen Hemminger 		if (len < 0)
1425222f1806SLuiz Capitulino 			return len;
142663adc6fbSStephen Hemminger 
14271da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14281da177e4SLinus Torvalds 			return -EFAULT;
14291da177e4SLinus Torvalds 		buf[len] = 0;
14301da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_max) != 0) {
14311da177e4SLinus Torvalds 			memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
14321da177e4SLinus Torvalds 			strncpy(pkt_dev->src_max, buf, len);
14331da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
14341da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_max;
14351da177e4SLinus Torvalds 		}
14361da177e4SLinus Torvalds 		if (debug)
1437f342cda7SJoe Perches 			pr_debug("src_max set to: %s\n", pkt_dev->src_max);
14381da177e4SLinus Torvalds 		i += len;
14391da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
14401da177e4SLinus Torvalds 		return count;
14411da177e4SLinus Torvalds 	}
14421da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac")) {
14431da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
144463adc6fbSStephen Hemminger 		if (len < 0)
1445222f1806SLuiz Capitulino 			return len;
144663adc6fbSStephen Hemminger 
14471da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
14481da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
14491da177e4SLinus Torvalds 			return -EFAULT;
14501da177e4SLinus Torvalds 
14514940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->dst_mac))
14524940fc88SAlexey Dobriyan 			return -EINVAL;
14531da177e4SLinus Torvalds 		/* Set up Dest MAC */
14549ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[0], pkt_dev->dst_mac);
14551da177e4SLinus Torvalds 
14564940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);
14571da177e4SLinus Torvalds 		return count;
14581da177e4SLinus Torvalds 	}
14591da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac")) {
14601da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
146163adc6fbSStephen Hemminger 		if (len < 0)
1462222f1806SLuiz Capitulino 			return len;
146363adc6fbSStephen Hemminger 
14641da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
14651da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
14661da177e4SLinus Torvalds 			return -EFAULT;
14671da177e4SLinus Torvalds 
14684940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->src_mac))
14694940fc88SAlexey Dobriyan 			return -EINVAL;
1470ce5d0b47SAdit Ranadive 		/* Set up Src MAC */
14719ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[6], pkt_dev->src_mac);
1472ce5d0b47SAdit Ranadive 
14734940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);
14741da177e4SLinus Torvalds 		return count;
14751da177e4SLinus Torvalds 	}
14761da177e4SLinus Torvalds 
14771da177e4SLinus Torvalds 	if (!strcmp(name, "clear_counters")) {
14781da177e4SLinus Torvalds 		pktgen_clear_counters(pkt_dev);
14791da177e4SLinus Torvalds 		sprintf(pg_result, "OK: Clearing counters.\n");
14801da177e4SLinus Torvalds 		return count;
14811da177e4SLinus Torvalds 	}
14821da177e4SLinus Torvalds 
14831da177e4SLinus Torvalds 	if (!strcmp(name, "flows")) {
14841da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
148563adc6fbSStephen Hemminger 		if (len < 0)
1486222f1806SLuiz Capitulino 			return len;
148763adc6fbSStephen Hemminger 
14881da177e4SLinus Torvalds 		i += len;
14891da177e4SLinus Torvalds 		if (value > MAX_CFLOWS)
14901da177e4SLinus Torvalds 			value = MAX_CFLOWS;
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 		pkt_dev->cflows = value;
14931da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
14941da177e4SLinus Torvalds 		return count;
14951da177e4SLinus Torvalds 	}
14966bae9190SFan Du #ifdef CONFIG_XFRM
1497de4aee7dSFan Du 	if (!strcmp(name, "spi")) {
1498de4aee7dSFan Du 		len = num_arg(&user_buffer[i], 10, &value);
1499de4aee7dSFan Du 		if (len < 0)
1500de4aee7dSFan Du 			return len;
1501de4aee7dSFan Du 
1502de4aee7dSFan Du 		i += len;
1503de4aee7dSFan Du 		pkt_dev->spi = value;
1504de4aee7dSFan Du 		sprintf(pg_result, "OK: spi=%u", pkt_dev->spi);
1505de4aee7dSFan Du 		return count;
1506de4aee7dSFan Du 	}
15076bae9190SFan Du #endif
15081da177e4SLinus Torvalds 	if (!strcmp(name, "flowlen")) {
15091da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
151063adc6fbSStephen Hemminger 		if (len < 0)
1511222f1806SLuiz Capitulino 			return len;
151263adc6fbSStephen Hemminger 
15131da177e4SLinus Torvalds 		i += len;
15141da177e4SLinus Torvalds 		pkt_dev->lflow = value;
15151da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
15161da177e4SLinus Torvalds 		return count;
15171da177e4SLinus Torvalds 	}
15181da177e4SLinus Torvalds 
151945b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_min")) {
152045b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
152163adc6fbSStephen Hemminger 		if (len < 0)
152245b270f8SRobert Olsson 			return len;
152363adc6fbSStephen Hemminger 
152445b270f8SRobert Olsson 		i += len;
152545b270f8SRobert Olsson 		pkt_dev->queue_map_min = value;
152645b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min);
152745b270f8SRobert Olsson 		return count;
152845b270f8SRobert Olsson 	}
152945b270f8SRobert Olsson 
153045b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_max")) {
153145b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
153263adc6fbSStephen Hemminger 		if (len < 0)
153345b270f8SRobert Olsson 			return len;
153463adc6fbSStephen Hemminger 
153545b270f8SRobert Olsson 		i += len;
153645b270f8SRobert Olsson 		pkt_dev->queue_map_max = value;
153745b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max);
153845b270f8SRobert Olsson 		return count;
153945b270f8SRobert Olsson 	}
154045b270f8SRobert Olsson 
1541ca6549afSSteven Whitehouse 	if (!strcmp(name, "mpls")) {
154295c96174SEric Dumazet 		unsigned int n, cnt;
1543cfcabdccSStephen Hemminger 
1544ca6549afSSteven Whitehouse 		len = get_labels(&user_buffer[i], pkt_dev);
1545cfcabdccSStephen Hemminger 		if (len < 0)
1546cfcabdccSStephen Hemminger 			return len;
1547ca6549afSSteven Whitehouse 		i += len;
1548cfcabdccSStephen Hemminger 		cnt = sprintf(pg_result, "OK: mpls=");
1549ca6549afSSteven Whitehouse 		for (n = 0; n < pkt_dev->nr_labels; n++)
1550cfcabdccSStephen Hemminger 			cnt += sprintf(pg_result + cnt,
1551ca6549afSSteven Whitehouse 				       "%08x%s", ntohl(pkt_dev->labels[n]),
1552ca6549afSSteven Whitehouse 				       n == pkt_dev->nr_labels-1 ? "" : ",");
155334954ddcSFrancesco Fondelli 
155434954ddcSFrancesco Fondelli 		if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
155534954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
155634954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
155734954ddcSFrancesco Fondelli 
155834954ddcSFrancesco Fondelli 			if (debug)
1559f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN auto turned off\n");
156034954ddcSFrancesco Fondelli 		}
156134954ddcSFrancesco Fondelli 		return count;
156234954ddcSFrancesco Fondelli 	}
156334954ddcSFrancesco Fondelli 
156434954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_id")) {
156534954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
156663adc6fbSStephen Hemminger 		if (len < 0)
156734954ddcSFrancesco Fondelli 			return len;
156863adc6fbSStephen Hemminger 
156934954ddcSFrancesco Fondelli 		i += len;
157034954ddcSFrancesco Fondelli 		if (value <= 4095) {
157134954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = value;  /* turn on VLAN */
157234954ddcSFrancesco Fondelli 
157334954ddcSFrancesco Fondelli 			if (debug)
1574f342cda7SJoe Perches 				pr_debug("VLAN turned on\n");
157534954ddcSFrancesco Fondelli 
157634954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1577f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
157834954ddcSFrancesco Fondelli 
157934954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
158034954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
158134954ddcSFrancesco Fondelli 		} else {
158234954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
158334954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
158434954ddcSFrancesco Fondelli 
158534954ddcSFrancesco Fondelli 			if (debug)
1586f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
158734954ddcSFrancesco Fondelli 		}
158834954ddcSFrancesco Fondelli 		return count;
158934954ddcSFrancesco Fondelli 	}
159034954ddcSFrancesco Fondelli 
159134954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_p")) {
159234954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
159363adc6fbSStephen Hemminger 		if (len < 0)
159434954ddcSFrancesco Fondelli 			return len;
159563adc6fbSStephen Hemminger 
159634954ddcSFrancesco Fondelli 		i += len;
159734954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
159834954ddcSFrancesco Fondelli 			pkt_dev->vlan_p = value;
159934954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
160034954ddcSFrancesco Fondelli 		} else {
160134954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_p must be 0-7");
160234954ddcSFrancesco Fondelli 		}
160334954ddcSFrancesco Fondelli 		return count;
160434954ddcSFrancesco Fondelli 	}
160534954ddcSFrancesco Fondelli 
160634954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_cfi")) {
160734954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
160863adc6fbSStephen Hemminger 		if (len < 0)
160934954ddcSFrancesco Fondelli 			return len;
161063adc6fbSStephen Hemminger 
161134954ddcSFrancesco Fondelli 		i += len;
161234954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
161334954ddcSFrancesco Fondelli 			pkt_dev->vlan_cfi = value;
161434954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
161534954ddcSFrancesco Fondelli 		} else {
161634954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
161734954ddcSFrancesco Fondelli 		}
161834954ddcSFrancesco Fondelli 		return count;
161934954ddcSFrancesco Fondelli 	}
162034954ddcSFrancesco Fondelli 
162134954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_id")) {
162234954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
162363adc6fbSStephen Hemminger 		if (len < 0)
162434954ddcSFrancesco Fondelli 			return len;
162563adc6fbSStephen Hemminger 
162634954ddcSFrancesco Fondelli 		i += len;
162734954ddcSFrancesco Fondelli 		if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
162834954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = value;  /* turn on SVLAN */
162934954ddcSFrancesco Fondelli 
163034954ddcSFrancesco Fondelli 			if (debug)
1631f342cda7SJoe Perches 				pr_debug("SVLAN turned on\n");
163234954ddcSFrancesco Fondelli 
163334954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1634f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
163534954ddcSFrancesco Fondelli 
163634954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
163734954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
163834954ddcSFrancesco Fondelli 		} else {
163934954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
164034954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
164134954ddcSFrancesco Fondelli 
164234954ddcSFrancesco Fondelli 			if (debug)
1643f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
164434954ddcSFrancesco Fondelli 		}
164534954ddcSFrancesco Fondelli 		return count;
164634954ddcSFrancesco Fondelli 	}
164734954ddcSFrancesco Fondelli 
164834954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_p")) {
164934954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
165063adc6fbSStephen Hemminger 		if (len < 0)
165134954ddcSFrancesco Fondelli 			return len;
165263adc6fbSStephen Hemminger 
165334954ddcSFrancesco Fondelli 		i += len;
165434954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
165534954ddcSFrancesco Fondelli 			pkt_dev->svlan_p = value;
165634954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
165734954ddcSFrancesco Fondelli 		} else {
165834954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_p must be 0-7");
165934954ddcSFrancesco Fondelli 		}
166034954ddcSFrancesco Fondelli 		return count;
166134954ddcSFrancesco Fondelli 	}
166234954ddcSFrancesco Fondelli 
166334954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_cfi")) {
166434954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
166563adc6fbSStephen Hemminger 		if (len < 0)
166634954ddcSFrancesco Fondelli 			return len;
166763adc6fbSStephen Hemminger 
166834954ddcSFrancesco Fondelli 		i += len;
166934954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
167034954ddcSFrancesco Fondelli 			pkt_dev->svlan_cfi = value;
167134954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
167234954ddcSFrancesco Fondelli 		} else {
167334954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
167434954ddcSFrancesco Fondelli 		}
1675ca6549afSSteven Whitehouse 		return count;
1676ca6549afSSteven Whitehouse 	}
1677ca6549afSSteven Whitehouse 
16781ca7768cSFrancesco Fondelli 	if (!strcmp(name, "tos")) {
16791ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
16801ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
168163adc6fbSStephen Hemminger 		if (len < 0)
16821ca7768cSFrancesco Fondelli 			return len;
168363adc6fbSStephen Hemminger 
16841ca7768cSFrancesco Fondelli 		i += len;
16851ca7768cSFrancesco Fondelli 		if (len == 2) {
16861ca7768cSFrancesco Fondelli 			pkt_dev->tos = tmp_value;
16871ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
16881ca7768cSFrancesco Fondelli 		} else {
16891ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: tos must be 00-ff");
16901ca7768cSFrancesco Fondelli 		}
16911ca7768cSFrancesco Fondelli 		return count;
16921ca7768cSFrancesco Fondelli 	}
16931ca7768cSFrancesco Fondelli 
16941ca7768cSFrancesco Fondelli 	if (!strcmp(name, "traffic_class")) {
16951ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
16961ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
169763adc6fbSStephen Hemminger 		if (len < 0)
16981ca7768cSFrancesco Fondelli 			return len;
169963adc6fbSStephen Hemminger 
17001ca7768cSFrancesco Fondelli 		i += len;
17011ca7768cSFrancesco Fondelli 		if (len == 2) {
17021ca7768cSFrancesco Fondelli 			pkt_dev->traffic_class = tmp_value;
17031ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
17041ca7768cSFrancesco Fondelli 		} else {
17051ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
17061ca7768cSFrancesco Fondelli 		}
17071ca7768cSFrancesco Fondelli 		return count;
17081ca7768cSFrancesco Fondelli 	}
17091ca7768cSFrancesco Fondelli 
17109e50e3acSJohn Fastabend 	if (!strcmp(name, "skb_priority")) {
17119e50e3acSJohn Fastabend 		len = num_arg(&user_buffer[i], 9, &value);
17129e50e3acSJohn Fastabend 		if (len < 0)
17139e50e3acSJohn Fastabend 			return len;
17149e50e3acSJohn Fastabend 
17159e50e3acSJohn Fastabend 		i += len;
17169e50e3acSJohn Fastabend 		pkt_dev->skb_priority = value;
17179e50e3acSJohn Fastabend 		sprintf(pg_result, "OK: skb_priority=%i",
17189e50e3acSJohn Fastabend 			pkt_dev->skb_priority);
17199e50e3acSJohn Fastabend 		return count;
17209e50e3acSJohn Fastabend 	}
17219e50e3acSJohn Fastabend 
17221da177e4SLinus Torvalds 	sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
17231da177e4SLinus Torvalds 	return -EINVAL;
17241da177e4SLinus Torvalds }
17251da177e4SLinus Torvalds 
1726d50a6b56SStephen Hemminger static int pktgen_if_open(struct inode *inode, struct file *file)
17271da177e4SLinus Torvalds {
1728d9dda78bSAl Viro 	return single_open(file, pktgen_if_show, PDE_DATA(inode));
17291da177e4SLinus Torvalds }
17301da177e4SLinus Torvalds 
17319a32144eSArjan van de Ven static const struct file_operations pktgen_if_fops = {
1732d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1733d50a6b56SStephen Hemminger 	.open    = pktgen_if_open,
1734d50a6b56SStephen Hemminger 	.read    = seq_read,
1735d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1736d50a6b56SStephen Hemminger 	.write   = pktgen_if_write,
1737d50a6b56SStephen Hemminger 	.release = single_release,
1738d50a6b56SStephen Hemminger };
1739d50a6b56SStephen Hemminger 
1740d50a6b56SStephen Hemminger static int pktgen_thread_show(struct seq_file *seq, void *v)
1741d50a6b56SStephen Hemminger {
1742d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1743648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
1744d50a6b56SStephen Hemminger 
1745d50a6b56SStephen Hemminger 	BUG_ON(!t);
1746d50a6b56SStephen Hemminger 
174797dc48e2SThomas Graf 	seq_puts(seq, "Running: ");
17481da177e4SLinus Torvalds 
17498788370aSJesper Dangaard Brouer 	rcu_read_lock();
17508788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
17511da177e4SLinus Torvalds 		if (pkt_dev->running)
1752593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
17531da177e4SLinus Torvalds 
175497dc48e2SThomas Graf 	seq_puts(seq, "\nStopped: ");
17551da177e4SLinus Torvalds 
17568788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
17571da177e4SLinus Torvalds 		if (!pkt_dev->running)
1758593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
17591da177e4SLinus Torvalds 
17601da177e4SLinus Torvalds 	if (t->result[0])
1761d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: %s\n", t->result);
17621da177e4SLinus Torvalds 	else
176397dc48e2SThomas Graf 		seq_puts(seq, "\nResult: NA\n");
17641da177e4SLinus Torvalds 
17658788370aSJesper Dangaard Brouer 	rcu_read_unlock();
17661da177e4SLinus Torvalds 
1767d50a6b56SStephen Hemminger 	return 0;
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds 
1770d50a6b56SStephen Hemminger static ssize_t pktgen_thread_write(struct file *file,
1771d50a6b56SStephen Hemminger 				   const char __user * user_buffer,
1772d50a6b56SStephen Hemminger 				   size_t count, loff_t * offset)
17731da177e4SLinus Torvalds {
17748a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
1775d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1776d6182223SPaul Gortmaker 	int i, max, len, ret;
17771da177e4SLinus Torvalds 	char name[40];
17781da177e4SLinus Torvalds 	char *pg_result;
17791da177e4SLinus Torvalds 
17801da177e4SLinus Torvalds 	if (count < 1) {
17811da177e4SLinus Torvalds 		//      sprintf(pg_result, "Wrong command format");
17821da177e4SLinus Torvalds 		return -EINVAL;
17831da177e4SLinus Torvalds 	}
17841da177e4SLinus Torvalds 
1785d6182223SPaul Gortmaker 	max = count;
1786d6182223SPaul Gortmaker 	len = count_trail_chars(user_buffer, max);
17871da177e4SLinus Torvalds 	if (len < 0)
17881da177e4SLinus Torvalds 		return len;
17891da177e4SLinus Torvalds 
1790d6182223SPaul Gortmaker 	i = len;
17911da177e4SLinus Torvalds 
17921da177e4SLinus Torvalds 	/* Read variable name */
17931da177e4SLinus Torvalds 
17941da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
17951da177e4SLinus Torvalds 	if (len < 0)
17961da177e4SLinus Torvalds 		return len;
17971da177e4SLinus Torvalds 
17981da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
17991da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
18001da177e4SLinus Torvalds 		return -EFAULT;
18011da177e4SLinus Torvalds 	i += len;
18021da177e4SLinus Torvalds 
18031da177e4SLinus Torvalds 	max = count - i;
18041da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
18051da177e4SLinus Torvalds 	if (len < 0)
18061da177e4SLinus Torvalds 		return len;
18071da177e4SLinus Torvalds 
18081da177e4SLinus Torvalds 	i += len;
18091da177e4SLinus Torvalds 
18101da177e4SLinus Torvalds 	if (debug)
1811f342cda7SJoe Perches 		pr_debug("t=%s, count=%lu\n", name, (unsigned long)count);
18121da177e4SLinus Torvalds 
18131da177e4SLinus Torvalds 	if (!t) {
1814f9467eaeSJoe Perches 		pr_err("ERROR: No thread\n");
18151da177e4SLinus Torvalds 		ret = -EINVAL;
18161da177e4SLinus Torvalds 		goto out;
18171da177e4SLinus Torvalds 	}
18181da177e4SLinus Torvalds 
18191da177e4SLinus Torvalds 	pg_result = &(t->result[0]);
18201da177e4SLinus Torvalds 
18211da177e4SLinus Torvalds 	if (!strcmp(name, "add_device")) {
18221da177e4SLinus Torvalds 		char f[32];
18231da177e4SLinus Torvalds 		memset(f, 0, 32);
18241da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
18251da177e4SLinus Torvalds 		if (len < 0) {
18261da177e4SLinus Torvalds 			ret = len;
18271da177e4SLinus Torvalds 			goto out;
18281da177e4SLinus Torvalds 		}
18291da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
18301da177e4SLinus Torvalds 			return -EFAULT;
18311da177e4SLinus Torvalds 		i += len;
18326146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
1833604dfd6eSCong Wang 		ret = pktgen_add_device(t, f);
18346146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1835604dfd6eSCong Wang 		if (!ret) {
18361da177e4SLinus Torvalds 			ret = count;
18371da177e4SLinus Torvalds 			sprintf(pg_result, "OK: add_device=%s", f);
1838604dfd6eSCong Wang 		} else
1839604dfd6eSCong Wang 			sprintf(pg_result, "ERROR: can not add device %s", f);
18401da177e4SLinus Torvalds 		goto out;
18411da177e4SLinus Torvalds 	}
18421da177e4SLinus Torvalds 
18431da177e4SLinus Torvalds 	if (!strcmp(name, "rem_device_all")) {
18446146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
184595ed63f7SArthur Kepner 		t->control |= T_REMDEVALL;
18466146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1847121caf57SNishanth Aravamudan 		schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
18481da177e4SLinus Torvalds 		ret = count;
18491da177e4SLinus Torvalds 		sprintf(pg_result, "OK: rem_device_all");
18501da177e4SLinus Torvalds 		goto out;
18511da177e4SLinus Torvalds 	}
18521da177e4SLinus Torvalds 
18531da177e4SLinus Torvalds 	if (!strcmp(name, "max_before_softirq")) {
1854b163911fSRobert Olsson 		sprintf(pg_result, "OK: Note! max_before_softirq is obsoleted -- Do not use");
18551da177e4SLinus Torvalds 		ret = count;
18561da177e4SLinus Torvalds 		goto out;
18571da177e4SLinus Torvalds 	}
18581da177e4SLinus Torvalds 
18591da177e4SLinus Torvalds 	ret = -EINVAL;
18601da177e4SLinus Torvalds out:
18611da177e4SLinus Torvalds 	return ret;
18621da177e4SLinus Torvalds }
18631da177e4SLinus Torvalds 
1864d50a6b56SStephen Hemminger static int pktgen_thread_open(struct inode *inode, struct file *file)
18651da177e4SLinus Torvalds {
1866d9dda78bSAl Viro 	return single_open(file, pktgen_thread_show, PDE_DATA(inode));
18671da177e4SLinus Torvalds }
18681da177e4SLinus Torvalds 
18699a32144eSArjan van de Ven static const struct file_operations pktgen_thread_fops = {
1870d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1871d50a6b56SStephen Hemminger 	.open    = pktgen_thread_open,
1872d50a6b56SStephen Hemminger 	.read    = seq_read,
1873d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1874d50a6b56SStephen Hemminger 	.write   = pktgen_thread_write,
1875d50a6b56SStephen Hemminger 	.release = single_release,
1876d50a6b56SStephen Hemminger };
18771da177e4SLinus Torvalds 
18781da177e4SLinus Torvalds /* Think find or remove for NN */
18794e58a027SCong Wang static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
18804e58a027SCong Wang 					      const char *ifname, int remove)
18811da177e4SLinus Torvalds {
18821da177e4SLinus Torvalds 	struct pktgen_thread *t;
18831da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
18843e984840SEric Dumazet 	bool exact = (remove == FIND);
18851da177e4SLinus Torvalds 
18864e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
18873e984840SEric Dumazet 		pkt_dev = pktgen_find_dev(t, ifname, exact);
18881da177e4SLinus Torvalds 		if (pkt_dev) {
18891da177e4SLinus Torvalds 			if (remove) {
189095ed63f7SArthur Kepner 				pkt_dev->removal_mark = 1;
189195ed63f7SArthur Kepner 				t->control |= T_REMDEV;
18921da177e4SLinus Torvalds 			}
18931da177e4SLinus Torvalds 			break;
18941da177e4SLinus Torvalds 		}
18951da177e4SLinus Torvalds 	}
18961da177e4SLinus Torvalds 	return pkt_dev;
18971da177e4SLinus Torvalds }
18981da177e4SLinus Torvalds 
189995ed63f7SArthur Kepner /*
190095ed63f7SArthur Kepner  * mark a device for removal
190195ed63f7SArthur Kepner  */
19024e58a027SCong Wang static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)
19031da177e4SLinus Torvalds {
19041da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
190595ed63f7SArthur Kepner 	const int max_tries = 10, msec_per_try = 125;
190695ed63f7SArthur Kepner 	int i = 0;
190795ed63f7SArthur Kepner 
19086146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
1909f9467eaeSJoe Perches 	pr_debug("%s: marking %s for removal\n", __func__, ifname);
191095ed63f7SArthur Kepner 
191195ed63f7SArthur Kepner 	while (1) {
191295ed63f7SArthur Kepner 
19134e58a027SCong Wang 		pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE);
1914222f1806SLuiz Capitulino 		if (pkt_dev == NULL)
1915222f1806SLuiz Capitulino 			break;	/* success */
191695ed63f7SArthur Kepner 
19176146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1918f9467eaeSJoe Perches 		pr_debug("%s: waiting for %s to disappear....\n",
1919f9467eaeSJoe Perches 			 __func__, ifname);
192095ed63f7SArthur Kepner 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
19216146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
192295ed63f7SArthur Kepner 
192395ed63f7SArthur Kepner 		if (++i >= max_tries) {
1924f9467eaeSJoe Perches 			pr_err("%s: timed out after waiting %d msec for device %s to be removed\n",
1925f9467eaeSJoe Perches 			       __func__, msec_per_try * i, ifname);
192695ed63f7SArthur Kepner 			break;
192795ed63f7SArthur Kepner 		}
192895ed63f7SArthur Kepner 
192995ed63f7SArthur Kepner 	}
193095ed63f7SArthur Kepner 
19316146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
193239df232fSStephen Hemminger }
193395ed63f7SArthur Kepner 
19344e58a027SCong Wang static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)
193539df232fSStephen Hemminger {
193639df232fSStephen Hemminger 	struct pktgen_thread *t;
193739df232fSStephen Hemminger 
19384e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
193939df232fSStephen Hemminger 		struct pktgen_dev *pkt_dev;
194039df232fSStephen Hemminger 
19418788370aSJesper Dangaard Brouer 		rcu_read_lock();
19428788370aSJesper Dangaard Brouer 		list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
194339df232fSStephen Hemminger 			if (pkt_dev->odev != dev)
194439df232fSStephen Hemminger 				continue;
194539df232fSStephen Hemminger 
1946a8ca16eaSDavid Howells 			proc_remove(pkt_dev->entry);
194739df232fSStephen Hemminger 
19482975315bSAlexey Dobriyan 			pkt_dev->entry = proc_create_data(dev->name, 0600,
19494e58a027SCong Wang 							  pn->proc_dir,
19502975315bSAlexey Dobriyan 							  &pktgen_if_fops,
19512975315bSAlexey Dobriyan 							  pkt_dev);
195239df232fSStephen Hemminger 			if (!pkt_dev->entry)
1953f9467eaeSJoe Perches 				pr_err("can't move proc entry for '%s'\n",
1954f9467eaeSJoe Perches 				       dev->name);
195539df232fSStephen Hemminger 			break;
195639df232fSStephen Hemminger 		}
19578788370aSJesper Dangaard Brouer 		rcu_read_unlock();
195839df232fSStephen Hemminger 	}
19591da177e4SLinus Torvalds }
19601da177e4SLinus Torvalds 
1961222f1806SLuiz Capitulino static int pktgen_device_event(struct notifier_block *unused,
1962222f1806SLuiz Capitulino 			       unsigned long event, void *ptr)
19631da177e4SLinus Torvalds {
1964351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
19654e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id);
19661da177e4SLinus Torvalds 
19674e58a027SCong Wang 	if (pn->pktgen_exiting)
1968e9dc8653SEric W. Biederman 		return NOTIFY_DONE;
1969e9dc8653SEric W. Biederman 
19701da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
19711da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
19721da177e4SLinus Torvalds 	 */
19731da177e4SLinus Torvalds 
19741da177e4SLinus Torvalds 	switch (event) {
197539df232fSStephen Hemminger 	case NETDEV_CHANGENAME:
19764e58a027SCong Wang 		pktgen_change_name(pn, dev);
19771da177e4SLinus Torvalds 		break;
19781da177e4SLinus Torvalds 
19791da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
19804e58a027SCong Wang 		pktgen_mark_device(pn, dev->name);
19811da177e4SLinus Torvalds 		break;
19823ff50b79SStephen Hemminger 	}
19831da177e4SLinus Torvalds 
19841da177e4SLinus Torvalds 	return NOTIFY_DONE;
19851da177e4SLinus Torvalds }
19861da177e4SLinus Torvalds 
19874e58a027SCong Wang static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn,
19884e58a027SCong Wang 						 struct pktgen_dev *pkt_dev,
198963adc6fbSStephen Hemminger 						 const char *ifname)
1990e6fce5b9SRobert Olsson {
1991e6fce5b9SRobert Olsson 	char b[IFNAMSIZ+5];
1992d6182223SPaul Gortmaker 	int i;
1993e6fce5b9SRobert Olsson 
1994e6fce5b9SRobert Olsson 	for (i = 0; ifname[i] != '@'; i++) {
1995e6fce5b9SRobert Olsson 		if (i == IFNAMSIZ)
1996e6fce5b9SRobert Olsson 			break;
1997e6fce5b9SRobert Olsson 
1998e6fce5b9SRobert Olsson 		b[i] = ifname[i];
1999e6fce5b9SRobert Olsson 	}
2000e6fce5b9SRobert Olsson 	b[i] = 0;
2001e6fce5b9SRobert Olsson 
20024e58a027SCong Wang 	return dev_get_by_name(pn->net, b);
2003e6fce5b9SRobert Olsson }
2004e6fce5b9SRobert Olsson 
2005e6fce5b9SRobert Olsson 
20061da177e4SLinus Torvalds /* Associate pktgen_dev with a device. */
20071da177e4SLinus Torvalds 
20084e58a027SCong Wang static int pktgen_setup_dev(const struct pktgen_net *pn,
20094e58a027SCong Wang 			    struct pktgen_dev *pkt_dev, const char *ifname)
2010222f1806SLuiz Capitulino {
20111da177e4SLinus Torvalds 	struct net_device *odev;
201239df232fSStephen Hemminger 	int err;
20131da177e4SLinus Torvalds 
20141da177e4SLinus Torvalds 	/* Clean old setups */
20151da177e4SLinus Torvalds 	if (pkt_dev->odev) {
20161da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
20171da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
20181da177e4SLinus Torvalds 	}
20191da177e4SLinus Torvalds 
20204e58a027SCong Wang 	odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname);
20211da177e4SLinus Torvalds 	if (!odev) {
2022f9467eaeSJoe Perches 		pr_err("no such netdevice: \"%s\"\n", ifname);
202339df232fSStephen Hemminger 		return -ENODEV;
20241da177e4SLinus Torvalds 	}
202539df232fSStephen Hemminger 
20261da177e4SLinus Torvalds 	if (odev->type != ARPHRD_ETHER) {
2027f9467eaeSJoe Perches 		pr_err("not an ethernet device: \"%s\"\n", ifname);
202839df232fSStephen Hemminger 		err = -EINVAL;
202939df232fSStephen Hemminger 	} else if (!netif_running(odev)) {
2030f9467eaeSJoe Perches 		pr_err("device is down: \"%s\"\n", ifname);
203139df232fSStephen Hemminger 		err = -ENETDOWN;
203239df232fSStephen Hemminger 	} else {
20331da177e4SLinus Torvalds 		pkt_dev->odev = odev;
203439df232fSStephen Hemminger 		return 0;
203539df232fSStephen Hemminger 	}
20361da177e4SLinus Torvalds 
20371da177e4SLinus Torvalds 	dev_put(odev);
203839df232fSStephen Hemminger 	return err;
20391da177e4SLinus Torvalds }
20401da177e4SLinus Torvalds 
20411da177e4SLinus Torvalds /* Read pkt_dev from the interface and set up internal pktgen_dev
20421da177e4SLinus Torvalds  * structure to have the right information to create/send packets
20431da177e4SLinus Torvalds  */
20441da177e4SLinus Torvalds static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
20451da177e4SLinus Torvalds {
204664c00d81SAndrew Gallatin 	int ntxq;
204764c00d81SAndrew Gallatin 
20481da177e4SLinus Torvalds 	if (!pkt_dev->odev) {
2049f9467eaeSJoe Perches 		pr_err("ERROR: pkt_dev->odev == NULL in setup_inject\n");
2050222f1806SLuiz Capitulino 		sprintf(pkt_dev->result,
2051222f1806SLuiz Capitulino 			"ERROR: pkt_dev->odev == NULL in setup_inject.\n");
20521da177e4SLinus Torvalds 		return;
20531da177e4SLinus Torvalds 	}
20541da177e4SLinus Torvalds 
205564c00d81SAndrew Gallatin 	/* make sure that we don't pick a non-existing transmit queue */
205664c00d81SAndrew Gallatin 	ntxq = pkt_dev->odev->real_num_tx_queues;
2057bfdbc0acSRobert Olsson 
205864c00d81SAndrew Gallatin 	if (ntxq <= pkt_dev->queue_map_min) {
2059*294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
206088271660SJesse Brandeburg 			pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
2061593f63b0SEric Dumazet 			pkt_dev->odevname);
206226e29eedSDan Carpenter 		pkt_dev->queue_map_min = (ntxq ?: 1) - 1;
206364c00d81SAndrew Gallatin 	}
206488271660SJesse Brandeburg 	if (pkt_dev->queue_map_max >= ntxq) {
2065*294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
206688271660SJesse Brandeburg 			pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
2067593f63b0SEric Dumazet 			pkt_dev->odevname);
206826e29eedSDan Carpenter 		pkt_dev->queue_map_max = (ntxq ?: 1) - 1;
206964c00d81SAndrew Gallatin 	}
207064c00d81SAndrew Gallatin 
20711da177e4SLinus Torvalds 	/* Default to the interface's mac if not explicitly set. */
20721da177e4SLinus Torvalds 
2073f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
20749ea08b12SJoe Perches 		ether_addr_copy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr);
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds 	/* Set up Dest MAC */
20779ea08b12SJoe Perches 	ether_addr_copy(&(pkt_dev->hh[0]), pkt_dev->dst_mac);
20781da177e4SLinus Torvalds 
20791da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
20804c139b8cSAmerigo Wang 		int i, set = 0, err = 1;
20814c139b8cSAmerigo Wang 		struct inet6_dev *idev;
20824c139b8cSAmerigo Wang 
208368bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
208468bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct ipv6hdr)
208568bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
208668bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
208768bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
208868bf9f0bSAmerigo Wang 		}
208968bf9f0bSAmerigo Wang 
20901da177e4SLinus Torvalds 		for (i = 0; i < IN6_ADDR_HSIZE; i++)
20911da177e4SLinus Torvalds 			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
20921da177e4SLinus Torvalds 				set = 1;
20931da177e4SLinus Torvalds 				break;
20941da177e4SLinus Torvalds 			}
20951da177e4SLinus Torvalds 
20961da177e4SLinus Torvalds 		if (!set) {
20971da177e4SLinus Torvalds 
20981da177e4SLinus Torvalds 			/*
20991da177e4SLinus Torvalds 			 * Use linklevel address if unconfigured.
21001da177e4SLinus Torvalds 			 *
21011da177e4SLinus Torvalds 			 * use ipv6_get_lladdr if/when it's get exported
21021da177e4SLinus Torvalds 			 */
21031da177e4SLinus Torvalds 
21048814c4b5SYOSHIFUJI Hideaki 			rcu_read_lock();
210563adc6fbSStephen Hemminger 			idev = __in6_dev_get(pkt_dev->odev);
210663adc6fbSStephen Hemminger 			if (idev) {
21071da177e4SLinus Torvalds 				struct inet6_ifaddr *ifp;
21081da177e4SLinus Torvalds 
21091da177e4SLinus Torvalds 				read_lock_bh(&idev->lock);
21104c139b8cSAmerigo Wang 				list_for_each_entry(ifp, &idev->addr_list, if_list) {
21114c139b8cSAmerigo Wang 					if ((ifp->scope & IFA_LINK) &&
2112f64f9e71SJoe Perches 					    !(ifp->flags & IFA_F_TENTATIVE)) {
21134e3fd7a0SAlexey Dobriyan 						pkt_dev->cur_in6_saddr = ifp->addr;
21141da177e4SLinus Torvalds 						err = 0;
21151da177e4SLinus Torvalds 						break;
21161da177e4SLinus Torvalds 					}
21171da177e4SLinus Torvalds 				}
21181da177e4SLinus Torvalds 				read_unlock_bh(&idev->lock);
21191da177e4SLinus Torvalds 			}
21208814c4b5SYOSHIFUJI Hideaki 			rcu_read_unlock();
2121222f1806SLuiz Capitulino 			if (err)
2122f9467eaeSJoe Perches 				pr_err("ERROR: IPv6 link address not available\n");
21231da177e4SLinus Torvalds 		}
2124222f1806SLuiz Capitulino 	} else {
212568bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
212668bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct iphdr)
212768bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
212868bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
212968bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
213068bf9f0bSAmerigo Wang 		}
213168bf9f0bSAmerigo Wang 
21321da177e4SLinus Torvalds 		pkt_dev->saddr_min = 0;
21331da177e4SLinus Torvalds 		pkt_dev->saddr_max = 0;
21341da177e4SLinus Torvalds 		if (strlen(pkt_dev->src_min) == 0) {
21351da177e4SLinus Torvalds 
21361da177e4SLinus Torvalds 			struct in_device *in_dev;
21371da177e4SLinus Torvalds 
21381da177e4SLinus Torvalds 			rcu_read_lock();
2139e5ed6399SHerbert Xu 			in_dev = __in_dev_get_rcu(pkt_dev->odev);
21401da177e4SLinus Torvalds 			if (in_dev) {
21411da177e4SLinus Torvalds 				if (in_dev->ifa_list) {
2142222f1806SLuiz Capitulino 					pkt_dev->saddr_min =
2143222f1806SLuiz Capitulino 					    in_dev->ifa_list->ifa_address;
21441da177e4SLinus Torvalds 					pkt_dev->saddr_max = pkt_dev->saddr_min;
21451da177e4SLinus Torvalds 				}
21461da177e4SLinus Torvalds 			}
21471da177e4SLinus Torvalds 			rcu_read_unlock();
2148222f1806SLuiz Capitulino 		} else {
21491da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
21501da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
21511da177e4SLinus Torvalds 		}
21521da177e4SLinus Torvalds 
21531da177e4SLinus Torvalds 		pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
21541da177e4SLinus Torvalds 		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
21551da177e4SLinus Torvalds 	}
21561da177e4SLinus Torvalds 	/* Initialize current values. */
215768bf9f0bSAmerigo Wang 	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
215868bf9f0bSAmerigo Wang 	if (pkt_dev->min_pkt_size > pkt_dev->max_pkt_size)
215968bf9f0bSAmerigo Wang 		pkt_dev->max_pkt_size = pkt_dev->min_pkt_size;
216068bf9f0bSAmerigo Wang 
21611da177e4SLinus Torvalds 	pkt_dev->cur_dst_mac_offset = 0;
21621da177e4SLinus Torvalds 	pkt_dev->cur_src_mac_offset = 0;
21631da177e4SLinus Torvalds 	pkt_dev->cur_saddr = pkt_dev->saddr_min;
21641da177e4SLinus Torvalds 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
21651da177e4SLinus Torvalds 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
21661da177e4SLinus Torvalds 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
21671da177e4SLinus Torvalds 	pkt_dev->nflows = 0;
21681da177e4SLinus Torvalds }
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds 
2171fd29cf72SStephen Hemminger static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
2172fd29cf72SStephen Hemminger {
2173ef87979cSStephen Hemminger 	ktime_t start_time, end_time;
2174417bc4b8SEric Dumazet 	s64 remaining;
21752bc481cfSStephen Hemminger 	struct hrtimer_sleeper t;
2176fd29cf72SStephen Hemminger 
21772bc481cfSStephen Hemminger 	hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
21782bc481cfSStephen Hemminger 	hrtimer_set_expires(&t.timer, spin_until);
2179fd29cf72SStephen Hemminger 
218043d28b65SDaniel Turull 	remaining = ktime_to_ns(hrtimer_expires_remaining(&t.timer));
2181417bc4b8SEric Dumazet 	if (remaining <= 0) {
2182417bc4b8SEric Dumazet 		pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
21831da177e4SLinus Torvalds 		return;
2184417bc4b8SEric Dumazet 	}
21852bc481cfSStephen Hemminger 
2186398f382cSDaniel Borkmann 	start_time = ktime_get();
218733136d12SEric Dumazet 	if (remaining < 100000) {
218833136d12SEric Dumazet 		/* for small delays (<100us), just loop until limit is reached */
218933136d12SEric Dumazet 		do {
2190398f382cSDaniel Borkmann 			end_time = ktime_get();
2191398f382cSDaniel Borkmann 		} while (ktime_compare(end_time, spin_until) < 0);
219233136d12SEric Dumazet 	} else {
21932bc481cfSStephen Hemminger 		/* see do_nanosleep */
21942bc481cfSStephen Hemminger 		hrtimer_init_sleeper(&t, current);
21952bc481cfSStephen Hemminger 		do {
21962bc481cfSStephen Hemminger 			set_current_state(TASK_INTERRUPTIBLE);
21972bc481cfSStephen Hemminger 			hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
21982bc481cfSStephen Hemminger 			if (!hrtimer_active(&t.timer))
21992bc481cfSStephen Hemminger 				t.task = NULL;
22002bc481cfSStephen Hemminger 
22012bc481cfSStephen Hemminger 			if (likely(t.task))
22021da177e4SLinus Torvalds 				schedule();
22031da177e4SLinus Torvalds 
22042bc481cfSStephen Hemminger 			hrtimer_cancel(&t.timer);
22052bc481cfSStephen Hemminger 		} while (t.task && pkt_dev->running && !signal_pending(current));
22062bc481cfSStephen Hemminger 		__set_current_state(TASK_RUNNING);
2207398f382cSDaniel Borkmann 		end_time = ktime_get();
220833136d12SEric Dumazet 	}
2209ef87979cSStephen Hemminger 
2210ef87979cSStephen Hemminger 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
221107a0f0f0SDaniel Turull 	pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
22121da177e4SLinus Torvalds }
22131da177e4SLinus Torvalds 
221416dab72fSJamal Hadi Salim static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
221516dab72fSJamal Hadi Salim {
2216a553e4a6SJamal Hadi Salim 	pkt_dev->pkt_overhead = 0;
221716dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
221816dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
221916dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
222016dab72fSJamal Hadi Salim }
222116dab72fSJamal Hadi Salim 
2222648fda74SStephen Hemminger static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
2223007a531bSJamal Hadi Salim {
2224648fda74SStephen Hemminger 	return !!(pkt_dev->flows[flow].flags & F_INIT);
2225007a531bSJamal Hadi Salim }
2226007a531bSJamal Hadi Salim 
2227007a531bSJamal Hadi Salim static inline int f_pick(struct pktgen_dev *pkt_dev)
2228007a531bSJamal Hadi Salim {
2229007a531bSJamal Hadi Salim 	int flow = pkt_dev->curfl;
2230007a531bSJamal Hadi Salim 
2231007a531bSJamal Hadi Salim 	if (pkt_dev->flags & F_FLOW_SEQ) {
2232007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2233007a531bSJamal Hadi Salim 			/* reset time */
2234007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
22351211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
2236007a531bSJamal Hadi Salim 			pkt_dev->curfl += 1;
2237007a531bSJamal Hadi Salim 			if (pkt_dev->curfl >= pkt_dev->cflows)
2238007a531bSJamal Hadi Salim 				pkt_dev->curfl = 0; /*reset */
2239007a531bSJamal Hadi Salim 		}
2240007a531bSJamal Hadi Salim 	} else {
224133d7c5e5SAkinobu Mita 		flow = prandom_u32() % pkt_dev->cflows;
22421211a645SRobert Olsson 		pkt_dev->curfl = flow;
2243007a531bSJamal Hadi Salim 
22441211a645SRobert Olsson 		if (pkt_dev->flows[flow].count > pkt_dev->lflow) {
2245007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
22461211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
22471211a645SRobert Olsson 		}
2248007a531bSJamal Hadi Salim 	}
2249007a531bSJamal Hadi Salim 
2250007a531bSJamal Hadi Salim 	return pkt_dev->curfl;
2251007a531bSJamal Hadi Salim }
2252007a531bSJamal Hadi Salim 
2253a553e4a6SJamal Hadi Salim 
2254a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2255a553e4a6SJamal Hadi Salim /* If there was already an IPSEC SA, we keep it as is, else
2256a553e4a6SJamal Hadi Salim  * we go look for it ...
2257a553e4a6SJamal Hadi Salim */
2258bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
2259fea1ab0fSAdrian Bunk static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
2260a553e4a6SJamal Hadi Salim {
2261a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[flow].x;
22624e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id);
2263a553e4a6SJamal Hadi Salim 	if (!x) {
2264c454997eSFan Du 
2265c454997eSFan Du 		if (pkt_dev->spi) {
2266c454997eSFan Du 			/* We need as quick as possible to find the right SA
2267c454997eSFan Du 			 * Searching with minimum criteria to archieve this.
2268c454997eSFan Du 			 */
2269c454997eSFan Du 			x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
2270c454997eSFan Du 		} else {
2271a553e4a6SJamal Hadi Salim 			/* slow path: we dont already have xfrm_state */
22724e58a027SCong Wang 			x = xfrm_stateonly_find(pn->net, DUMMY_MARK,
22735447c5e4SAlexey Dobriyan 						(xfrm_address_t *)&pkt_dev->cur_daddr,
2274a553e4a6SJamal Hadi Salim 						(xfrm_address_t *)&pkt_dev->cur_saddr,
2275a553e4a6SJamal Hadi Salim 						AF_INET,
2276a553e4a6SJamal Hadi Salim 						pkt_dev->ipsmode,
2277a553e4a6SJamal Hadi Salim 						pkt_dev->ipsproto, 0);
2278c454997eSFan Du 		}
2279a553e4a6SJamal Hadi Salim 		if (x) {
2280a553e4a6SJamal Hadi Salim 			pkt_dev->flows[flow].x = x;
2281a553e4a6SJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
2282a553e4a6SJamal Hadi Salim 			pkt_dev->pkt_overhead += x->props.header_len;
2283a553e4a6SJamal Hadi Salim 		}
2284a553e4a6SJamal Hadi Salim 
2285a553e4a6SJamal Hadi Salim 	}
2286a553e4a6SJamal Hadi Salim }
2287a553e4a6SJamal Hadi Salim #endif
2288fd2ea0a7SDavid S. Miller static void set_cur_queue_map(struct pktgen_dev *pkt_dev)
2289fd2ea0a7SDavid S. Miller {
2290e6fce5b9SRobert Olsson 
2291e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
2292e6fce5b9SRobert Olsson 		pkt_dev->cur_queue_map = smp_processor_id();
2293e6fce5b9SRobert Olsson 
2294896a7cf8SEric Dumazet 	else if (pkt_dev->queue_map_min <= pkt_dev->queue_map_max) {
2295fd2ea0a7SDavid S. Miller 		__u16 t;
2296fd2ea0a7SDavid S. Miller 		if (pkt_dev->flags & F_QUEUE_MAP_RND) {
229733d7c5e5SAkinobu Mita 			t = prandom_u32() %
2298fd2ea0a7SDavid S. Miller 				(pkt_dev->queue_map_max -
2299fd2ea0a7SDavid S. Miller 				 pkt_dev->queue_map_min + 1)
2300fd2ea0a7SDavid S. Miller 				+ pkt_dev->queue_map_min;
2301fd2ea0a7SDavid S. Miller 		} else {
2302fd2ea0a7SDavid S. Miller 			t = pkt_dev->cur_queue_map + 1;
2303fd2ea0a7SDavid S. Miller 			if (t > pkt_dev->queue_map_max)
2304fd2ea0a7SDavid S. Miller 				t = pkt_dev->queue_map_min;
2305fd2ea0a7SDavid S. Miller 		}
2306fd2ea0a7SDavid S. Miller 		pkt_dev->cur_queue_map = t;
2307fd2ea0a7SDavid S. Miller 	}
2308bfdbc0acSRobert Olsson 	pkt_dev->cur_queue_map  = pkt_dev->cur_queue_map % pkt_dev->odev->real_num_tx_queues;
2309fd2ea0a7SDavid S. Miller }
2310fd2ea0a7SDavid S. Miller 
23111da177e4SLinus Torvalds /* Increment/randomize headers according to flags and current values
23121da177e4SLinus Torvalds  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
23131da177e4SLinus Torvalds  */
2314222f1806SLuiz Capitulino static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2315222f1806SLuiz Capitulino {
23161da177e4SLinus Torvalds 	__u32 imn;
23171da177e4SLinus Torvalds 	__u32 imx;
23181da177e4SLinus Torvalds 	int flow = 0;
23191da177e4SLinus Torvalds 
2320007a531bSJamal Hadi Salim 	if (pkt_dev->cflows)
2321007a531bSJamal Hadi Salim 		flow = f_pick(pkt_dev);
23221da177e4SLinus Torvalds 
23231da177e4SLinus Torvalds 	/*  Deal with source MAC */
23241da177e4SLinus Torvalds 	if (pkt_dev->src_mac_count > 1) {
23251da177e4SLinus Torvalds 		__u32 mc;
23261da177e4SLinus Torvalds 		__u32 tmp;
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACSRC_RND)
232933d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->src_mac_count;
23301da177e4SLinus Torvalds 		else {
23311da177e4SLinus Torvalds 			mc = pkt_dev->cur_src_mac_offset++;
2332ff2a79a5SRobert Olsson 			if (pkt_dev->cur_src_mac_offset >=
2333222f1806SLuiz Capitulino 			    pkt_dev->src_mac_count)
23341da177e4SLinus Torvalds 				pkt_dev->cur_src_mac_offset = 0;
23351da177e4SLinus Torvalds 		}
23361da177e4SLinus Torvalds 
23371da177e4SLinus Torvalds 		tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
23381da177e4SLinus Torvalds 		pkt_dev->hh[11] = tmp;
23391da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
23401da177e4SLinus Torvalds 		pkt_dev->hh[10] = tmp;
23411da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
23421da177e4SLinus Torvalds 		pkt_dev->hh[9] = tmp;
23431da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
23441da177e4SLinus Torvalds 		pkt_dev->hh[8] = tmp;
23451da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
23461da177e4SLinus Torvalds 		pkt_dev->hh[7] = tmp;
23471da177e4SLinus Torvalds 	}
23481da177e4SLinus Torvalds 
23491da177e4SLinus Torvalds 	/*  Deal with Destination MAC */
23501da177e4SLinus Torvalds 	if (pkt_dev->dst_mac_count > 1) {
23511da177e4SLinus Torvalds 		__u32 mc;
23521da177e4SLinus Torvalds 		__u32 tmp;
23531da177e4SLinus Torvalds 
23541da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACDST_RND)
235533d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->dst_mac_count;
23561da177e4SLinus Torvalds 
23571da177e4SLinus Torvalds 		else {
23581da177e4SLinus Torvalds 			mc = pkt_dev->cur_dst_mac_offset++;
2359ff2a79a5SRobert Olsson 			if (pkt_dev->cur_dst_mac_offset >=
2360222f1806SLuiz Capitulino 			    pkt_dev->dst_mac_count) {
23611da177e4SLinus Torvalds 				pkt_dev->cur_dst_mac_offset = 0;
23621da177e4SLinus Torvalds 			}
23631da177e4SLinus Torvalds 		}
23641da177e4SLinus Torvalds 
23651da177e4SLinus Torvalds 		tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
23661da177e4SLinus Torvalds 		pkt_dev->hh[5] = tmp;
23671da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
23681da177e4SLinus Torvalds 		pkt_dev->hh[4] = tmp;
23691da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
23701da177e4SLinus Torvalds 		pkt_dev->hh[3] = tmp;
23711da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
23721da177e4SLinus Torvalds 		pkt_dev->hh[2] = tmp;
23731da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
23741da177e4SLinus Torvalds 		pkt_dev->hh[1] = tmp;
23751da177e4SLinus Torvalds 	}
23761da177e4SLinus Torvalds 
2377ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND) {
237895c96174SEric Dumazet 		unsigned int i;
2379ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
2380ca6549afSSteven Whitehouse 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2381ca6549afSSteven Whitehouse 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
238233d7c5e5SAkinobu Mita 					     ((__force __be32)prandom_u32() &
2383ca6549afSSteven Whitehouse 						      htonl(0x000fffff));
2384ca6549afSSteven Whitehouse 	}
2385ca6549afSSteven Whitehouse 
238634954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
238733d7c5e5SAkinobu Mita 		pkt_dev->vlan_id = prandom_u32() & (4096 - 1);
238834954ddcSFrancesco Fondelli 	}
238934954ddcSFrancesco Fondelli 
239034954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
239133d7c5e5SAkinobu Mita 		pkt_dev->svlan_id = prandom_u32() & (4096 - 1);
239234954ddcSFrancesco Fondelli 	}
239334954ddcSFrancesco Fondelli 
23941da177e4SLinus Torvalds 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
23951da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPSRC_RND)
239633d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_src = prandom_u32() %
23975fa6fc76SStephen Hemminger 				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)
23985fa6fc76SStephen Hemminger 				+ pkt_dev->udp_src_min;
23991da177e4SLinus Torvalds 
24001da177e4SLinus Torvalds 		else {
24011da177e4SLinus Torvalds 			pkt_dev->cur_udp_src++;
24021da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
24031da177e4SLinus Torvalds 				pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
24041da177e4SLinus Torvalds 		}
24051da177e4SLinus Torvalds 	}
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
24081da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPDST_RND) {
240933d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_dst = prandom_u32() %
24105fa6fc76SStephen Hemminger 				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
24115fa6fc76SStephen Hemminger 				+ pkt_dev->udp_dst_min;
2412222f1806SLuiz Capitulino 		} else {
24131da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst++;
24141da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
24151da177e4SLinus Torvalds 				pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
24161da177e4SLinus Torvalds 		}
24171da177e4SLinus Torvalds 	}
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 	if (!(pkt_dev->flags & F_IPV6)) {
24201da177e4SLinus Torvalds 
242163adc6fbSStephen Hemminger 		imn = ntohl(pkt_dev->saddr_min);
242263adc6fbSStephen Hemminger 		imx = ntohl(pkt_dev->saddr_max);
242363adc6fbSStephen Hemminger 		if (imn < imx) {
24241da177e4SLinus Torvalds 			__u32 t;
24251da177e4SLinus Torvalds 			if (pkt_dev->flags & F_IPSRC_RND)
242633d7c5e5SAkinobu Mita 				t = prandom_u32() % (imx - imn) + imn;
24271da177e4SLinus Torvalds 			else {
24281da177e4SLinus Torvalds 				t = ntohl(pkt_dev->cur_saddr);
24291da177e4SLinus Torvalds 				t++;
243063adc6fbSStephen Hemminger 				if (t > imx)
24311da177e4SLinus Torvalds 					t = imn;
243263adc6fbSStephen Hemminger 
24331da177e4SLinus Torvalds 			}
24341da177e4SLinus Torvalds 			pkt_dev->cur_saddr = htonl(t);
24351da177e4SLinus Torvalds 		}
24361da177e4SLinus Torvalds 
2437007a531bSJamal Hadi Salim 		if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
24381da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
24391da177e4SLinus Torvalds 		} else {
2440252e3346SAl Viro 			imn = ntohl(pkt_dev->daddr_min);
2441252e3346SAl Viro 			imx = ntohl(pkt_dev->daddr_max);
2442252e3346SAl Viro 			if (imn < imx) {
24431da177e4SLinus Torvalds 				__u32 t;
2444252e3346SAl Viro 				__be32 s;
24451da177e4SLinus Torvalds 				if (pkt_dev->flags & F_IPDST_RND) {
24461da177e4SLinus Torvalds 
244770e3ba72SAkinobu Mita 					do {
244833d7c5e5SAkinobu Mita 						t = prandom_u32() %
244933d7c5e5SAkinobu Mita 							(imx - imn) + imn;
2450252e3346SAl Viro 						s = htonl(t);
245170e3ba72SAkinobu Mita 					} while (ipv4_is_loopback(s) ||
245270e3ba72SAkinobu Mita 						ipv4_is_multicast(s) ||
245370e3ba72SAkinobu Mita 						ipv4_is_lbcast(s) ||
245470e3ba72SAkinobu Mita 						ipv4_is_zeronet(s) ||
245570e3ba72SAkinobu Mita 						ipv4_is_local_multicast(s));
2456252e3346SAl Viro 					pkt_dev->cur_daddr = s;
2457252e3346SAl Viro 				} else {
24581da177e4SLinus Torvalds 					t = ntohl(pkt_dev->cur_daddr);
24591da177e4SLinus Torvalds 					t++;
24601da177e4SLinus Torvalds 					if (t > imx) {
24611da177e4SLinus Torvalds 						t = imn;
24621da177e4SLinus Torvalds 					}
24631da177e4SLinus Torvalds 					pkt_dev->cur_daddr = htonl(t);
24641da177e4SLinus Torvalds 				}
24651da177e4SLinus Torvalds 			}
24661da177e4SLinus Torvalds 			if (pkt_dev->cflows) {
2467007a531bSJamal Hadi Salim 				pkt_dev->flows[flow].flags |= F_INIT;
2468222f1806SLuiz Capitulino 				pkt_dev->flows[flow].cur_daddr =
2469222f1806SLuiz Capitulino 				    pkt_dev->cur_daddr;
2470a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2471a553e4a6SJamal Hadi Salim 				if (pkt_dev->flags & F_IPSEC_ON)
2472a553e4a6SJamal Hadi Salim 					get_ipsec_sa(pkt_dev, flow);
2473a553e4a6SJamal Hadi Salim #endif
24741da177e4SLinus Torvalds 				pkt_dev->nflows++;
24751da177e4SLinus Torvalds 			}
24761da177e4SLinus Torvalds 		}
2477222f1806SLuiz Capitulino 	} else {		/* IPV6 * */
2478222f1806SLuiz Capitulino 
247906e30411SJoe Perches 		if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {
24801da177e4SLinus Torvalds 			int i;
24811da177e4SLinus Torvalds 
24821da177e4SLinus Torvalds 			/* Only random destinations yet */
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds 			for (i = 0; i < 4; i++) {
24851da177e4SLinus Torvalds 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
248633d7c5e5SAkinobu Mita 				    (((__force __be32)prandom_u32() |
24871da177e4SLinus Torvalds 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
24881da177e4SLinus Torvalds 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
24891da177e4SLinus Torvalds 			}
24901da177e4SLinus Torvalds 		}
24911da177e4SLinus Torvalds 	}
24921da177e4SLinus Torvalds 
24931da177e4SLinus Torvalds 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
24941da177e4SLinus Torvalds 		__u32 t;
24951da177e4SLinus Torvalds 		if (pkt_dev->flags & F_TXSIZE_RND) {
249633d7c5e5SAkinobu Mita 			t = prandom_u32() %
24975fa6fc76SStephen Hemminger 				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
24985fa6fc76SStephen Hemminger 				+ pkt_dev->min_pkt_size;
2499222f1806SLuiz Capitulino 		} else {
25001da177e4SLinus Torvalds 			t = pkt_dev->cur_pkt_size + 1;
25011da177e4SLinus Torvalds 			if (t > pkt_dev->max_pkt_size)
25021da177e4SLinus Torvalds 				t = pkt_dev->min_pkt_size;
25031da177e4SLinus Torvalds 		}
25041da177e4SLinus Torvalds 		pkt_dev->cur_pkt_size = t;
25051da177e4SLinus Torvalds 	}
25061da177e4SLinus Torvalds 
2507fd2ea0a7SDavid S. Miller 	set_cur_queue_map(pkt_dev);
250845b270f8SRobert Olsson 
25091da177e4SLinus Torvalds 	pkt_dev->flows[flow].count++;
25101da177e4SLinus Torvalds }
25111da177e4SLinus Torvalds 
2512a553e4a6SJamal Hadi Salim 
2513a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
25145537a055SFengguang Wu static u32 pktgen_dst_metrics[RTAX_MAX + 1] = {
2515cf93d47eSFan Du 
2516cf93d47eSFan Du 	[RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */
2517cf93d47eSFan Du };
2518cf93d47eSFan Du 
2519a553e4a6SJamal Hadi Salim static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
2520a553e4a6SJamal Hadi Salim {
2521a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2522a553e4a6SJamal Hadi Salim 	int err = 0;
25236de9ace4SFan Du 	struct net *net = dev_net(pkt_dev->odev);
2524a553e4a6SJamal Hadi Salim 
2525a553e4a6SJamal Hadi Salim 	if (!x)
2526a553e4a6SJamal Hadi Salim 		return 0;
2527a553e4a6SJamal Hadi Salim 	/* XXX: we dont support tunnel mode for now until
2528a553e4a6SJamal Hadi Salim 	 * we resolve the dst issue */
2529cf93d47eSFan Du 	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))
2530a553e4a6SJamal Hadi Salim 		return 0;
2531a553e4a6SJamal Hadi Salim 
2532cf93d47eSFan Du 	/* But when user specify an valid SPI, transformation
2533cf93d47eSFan Du 	 * supports both transport/tunnel mode + ESP/AH type.
2534cf93d47eSFan Du 	 */
2535cf93d47eSFan Du 	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0))
2536cf93d47eSFan Du 		skb->_skb_refdst = (unsigned long)&pkt_dev->dst | SKB_DST_NOREF;
2537cf93d47eSFan Du 
2538cf93d47eSFan Du 	rcu_read_lock_bh();
253913996378SHerbert Xu 	err = x->outer_mode->output(x, skb);
2540cf93d47eSFan Du 	rcu_read_unlock_bh();
25416de9ace4SFan Du 	if (err) {
25426de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
2543a553e4a6SJamal Hadi Salim 		goto error;
25446de9ace4SFan Du 	}
2545a553e4a6SJamal Hadi Salim 	err = x->type->output(x, skb);
25466de9ace4SFan Du 	if (err) {
25476de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
2548a553e4a6SJamal Hadi Salim 		goto error;
25496de9ace4SFan Du 	}
25500af0a413SFan Du 	spin_lock_bh(&x->lock);
2551a553e4a6SJamal Hadi Salim 	x->curlft.bytes += skb->len;
2552a553e4a6SJamal Hadi Salim 	x->curlft.packets++;
25530af0a413SFan Du 	spin_unlock_bh(&x->lock);
2554a553e4a6SJamal Hadi Salim error:
2555a553e4a6SJamal Hadi Salim 	return err;
2556a553e4a6SJamal Hadi Salim }
2557a553e4a6SJamal Hadi Salim 
2558475ac1e4SStephen Hemminger static void free_SAs(struct pktgen_dev *pkt_dev)
2559a553e4a6SJamal Hadi Salim {
2560a553e4a6SJamal Hadi Salim 	if (pkt_dev->cflows) {
2561a553e4a6SJamal Hadi Salim 		/* let go of the SAs if we have them */
2562d6182223SPaul Gortmaker 		int i;
2563d6182223SPaul Gortmaker 		for (i = 0; i < pkt_dev->cflows; i++) {
2564a553e4a6SJamal Hadi Salim 			struct xfrm_state *x = pkt_dev->flows[i].x;
2565a553e4a6SJamal Hadi Salim 			if (x) {
2566a553e4a6SJamal Hadi Salim 				xfrm_state_put(x);
2567a553e4a6SJamal Hadi Salim 				pkt_dev->flows[i].x = NULL;
2568a553e4a6SJamal Hadi Salim 			}
2569a553e4a6SJamal Hadi Salim 		}
2570a553e4a6SJamal Hadi Salim 	}
2571a553e4a6SJamal Hadi Salim }
2572a553e4a6SJamal Hadi Salim 
2573475ac1e4SStephen Hemminger static int process_ipsec(struct pktgen_dev *pkt_dev,
2574a553e4a6SJamal Hadi Salim 			      struct sk_buff *skb, __be16 protocol)
2575a553e4a6SJamal Hadi Salim {
2576a553e4a6SJamal Hadi Salim 	if (pkt_dev->flags & F_IPSEC_ON) {
2577a553e4a6SJamal Hadi Salim 		struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2578a553e4a6SJamal Hadi Salim 		int nhead = 0;
2579a553e4a6SJamal Hadi Salim 		if (x) {
2580a553e4a6SJamal Hadi Salim 			int ret;
2581a553e4a6SJamal Hadi Salim 			__u8 *eth;
25823868204dSfan.du 			struct iphdr *iph;
25833868204dSfan.du 
2584a553e4a6SJamal Hadi Salim 			nhead = x->props.header_len - skb_headroom(skb);
2585a553e4a6SJamal Hadi Salim 			if (nhead > 0) {
2586a553e4a6SJamal Hadi Salim 				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
2587a553e4a6SJamal Hadi Salim 				if (ret < 0) {
2588f9467eaeSJoe Perches 					pr_err("Error expanding ipsec packet %d\n",
2589f9467eaeSJoe Perches 					       ret);
2590b4bb4ac8SIlpo Järvinen 					goto err;
2591a553e4a6SJamal Hadi Salim 				}
2592a553e4a6SJamal Hadi Salim 			}
2593a553e4a6SJamal Hadi Salim 
2594a553e4a6SJamal Hadi Salim 			/* ipsec is not expecting ll header */
2595a553e4a6SJamal Hadi Salim 			skb_pull(skb, ETH_HLEN);
2596a553e4a6SJamal Hadi Salim 			ret = pktgen_output_ipsec(skb, pkt_dev);
2597a553e4a6SJamal Hadi Salim 			if (ret) {
2598f9467eaeSJoe Perches 				pr_err("Error creating ipsec packet %d\n", ret);
2599b4bb4ac8SIlpo Järvinen 				goto err;
2600a553e4a6SJamal Hadi Salim 			}
2601a553e4a6SJamal Hadi Salim 			/* restore ll */
2602a553e4a6SJamal Hadi Salim 			eth = (__u8 *) skb_push(skb, ETH_HLEN);
2603a553e4a6SJamal Hadi Salim 			memcpy(eth, pkt_dev->hh, 12);
2604a553e4a6SJamal Hadi Salim 			*(u16 *) &eth[12] = protocol;
26053868204dSfan.du 
26063868204dSfan.du 			/* Update IPv4 header len as well as checksum value */
26073868204dSfan.du 			iph = ip_hdr(skb);
26083868204dSfan.du 			iph->tot_len = htons(skb->len - ETH_HLEN);
26093868204dSfan.du 			ip_send_check(iph);
2610a553e4a6SJamal Hadi Salim 		}
2611a553e4a6SJamal Hadi Salim 	}
2612a553e4a6SJamal Hadi Salim 	return 1;
2613b4bb4ac8SIlpo Järvinen err:
2614b4bb4ac8SIlpo Järvinen 	kfree_skb(skb);
2615b4bb4ac8SIlpo Järvinen 	return 0;
2616a553e4a6SJamal Hadi Salim }
2617a553e4a6SJamal Hadi Salim #endif
2618a553e4a6SJamal Hadi Salim 
2619ca6549afSSteven Whitehouse static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2620ca6549afSSteven Whitehouse {
262195c96174SEric Dumazet 	unsigned int i;
262263adc6fbSStephen Hemminger 	for (i = 0; i < pkt_dev->nr_labels; i++)
2623ca6549afSSteven Whitehouse 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
262463adc6fbSStephen Hemminger 
2625ca6549afSSteven Whitehouse 	mpls--;
2626ca6549afSSteven Whitehouse 	*mpls |= MPLS_STACK_BOTTOM;
2627ca6549afSSteven Whitehouse }
2628ca6549afSSteven Whitehouse 
26290f37c605SAl Viro static inline __be16 build_tci(unsigned int id, unsigned int cfi,
26300f37c605SAl Viro 			       unsigned int prio)
26310f37c605SAl Viro {
26320f37c605SAl Viro 	return htons(id | (cfi << 12) | (prio << 13));
26330f37c605SAl Viro }
26340f37c605SAl Viro 
263526ad7879SEric Dumazet static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
263626ad7879SEric Dumazet 				int datalen)
263726ad7879SEric Dumazet {
263826ad7879SEric Dumazet 	struct timeval timestamp;
263926ad7879SEric Dumazet 	struct pktgen_hdr *pgh;
264026ad7879SEric Dumazet 
264126ad7879SEric Dumazet 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
264226ad7879SEric Dumazet 	datalen -= sizeof(*pgh);
264326ad7879SEric Dumazet 
264426ad7879SEric Dumazet 	if (pkt_dev->nfrags <= 0) {
264505aebe2eSDaniel Turull 		memset(skb_put(skb, datalen), 0, datalen);
264626ad7879SEric Dumazet 	} else {
264726ad7879SEric Dumazet 		int frags = pkt_dev->nfrags;
264826ad7879SEric Dumazet 		int i, len;
26497d36a991Samit salecha 		int frag_len;
265026ad7879SEric Dumazet 
265126ad7879SEric Dumazet 
265226ad7879SEric Dumazet 		if (frags > MAX_SKB_FRAGS)
265326ad7879SEric Dumazet 			frags = MAX_SKB_FRAGS;
265426ad7879SEric Dumazet 		len = datalen - frags * PAGE_SIZE;
265526ad7879SEric Dumazet 		if (len > 0) {
265626ad7879SEric Dumazet 			memset(skb_put(skb, len), 0, len);
265726ad7879SEric Dumazet 			datalen = frags * PAGE_SIZE;
265826ad7879SEric Dumazet 		}
265926ad7879SEric Dumazet 
266026ad7879SEric Dumazet 		i = 0;
26617d36a991Samit salecha 		frag_len = (datalen/frags) < PAGE_SIZE ?
26627d36a991Samit salecha 			   (datalen/frags) : PAGE_SIZE;
266326ad7879SEric Dumazet 		while (datalen > 0) {
266426ad7879SEric Dumazet 			if (unlikely(!pkt_dev->page)) {
266526ad7879SEric Dumazet 				int node = numa_node_id();
266626ad7879SEric Dumazet 
266726ad7879SEric Dumazet 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
266826ad7879SEric Dumazet 					node = pkt_dev->node;
266926ad7879SEric Dumazet 				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
267026ad7879SEric Dumazet 				if (!pkt_dev->page)
267126ad7879SEric Dumazet 					break;
267226ad7879SEric Dumazet 			}
2673a0bec1cdSIan Campbell 			get_page(pkt_dev->page);
2674ea2ab693SIan Campbell 			skb_frag_set_page(skb, i, pkt_dev->page);
267526ad7879SEric Dumazet 			skb_shinfo(skb)->frags[i].page_offset = 0;
26767d36a991Samit salecha 			/*last fragment, fill rest of data*/
26777d36a991Samit salecha 			if (i == (frags - 1))
26789e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
26799e903e08SEric Dumazet 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
26807d36a991Samit salecha 			else
26819e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
26829e903e08SEric Dumazet 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
26839e903e08SEric Dumazet 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
26849e903e08SEric Dumazet 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
268526ad7879SEric Dumazet 			i++;
268626ad7879SEric Dumazet 			skb_shinfo(skb)->nr_frags = i;
268726ad7879SEric Dumazet 		}
268826ad7879SEric Dumazet 	}
268926ad7879SEric Dumazet 
269026ad7879SEric Dumazet 	/* Stamp the time, and sequence number,
269126ad7879SEric Dumazet 	 * convert them to network byte order
269226ad7879SEric Dumazet 	 */
269326ad7879SEric Dumazet 	pgh->pgh_magic = htonl(PKTGEN_MAGIC);
269426ad7879SEric Dumazet 	pgh->seq_num = htonl(pkt_dev->seq_num);
269526ad7879SEric Dumazet 
2696afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP) {
2697afb84b62SJesper Dangaard Brouer 		pgh->tv_sec = 0;
2698afb84b62SJesper Dangaard Brouer 		pgh->tv_usec = 0;
2699afb84b62SJesper Dangaard Brouer 	} else {
270026ad7879SEric Dumazet 		do_gettimeofday(&timestamp);
270126ad7879SEric Dumazet 		pgh->tv_sec = htonl(timestamp.tv_sec);
270226ad7879SEric Dumazet 		pgh->tv_usec = htonl(timestamp.tv_usec);
270326ad7879SEric Dumazet 	}
2704afb84b62SJesper Dangaard Brouer }
270526ad7879SEric Dumazet 
27067a6e288dSDaniel Borkmann static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
27077a6e288dSDaniel Borkmann 					struct pktgen_dev *pkt_dev,
27087a6e288dSDaniel Borkmann 					unsigned int extralen)
27097a6e288dSDaniel Borkmann {
27107a6e288dSDaniel Borkmann 	struct sk_buff *skb = NULL;
27117a6e288dSDaniel Borkmann 	unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
27127a6e288dSDaniel Borkmann 			    pkt_dev->pkt_overhead;
27137a6e288dSDaniel Borkmann 
27147a6e288dSDaniel Borkmann 	if (pkt_dev->flags & F_NODE) {
27157a6e288dSDaniel Borkmann 		int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
27167a6e288dSDaniel Borkmann 
27177a6e288dSDaniel Borkmann 		skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node);
27187a6e288dSDaniel Borkmann 		if (likely(skb)) {
27197a6e288dSDaniel Borkmann 			skb_reserve(skb, NET_SKB_PAD);
27207a6e288dSDaniel Borkmann 			skb->dev = dev;
27217a6e288dSDaniel Borkmann 		}
27227a6e288dSDaniel Borkmann 	} else {
27237a6e288dSDaniel Borkmann 		 skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
27247a6e288dSDaniel Borkmann 	}
27257a6e288dSDaniel Borkmann 
27267a6e288dSDaniel Borkmann 	return skb;
27277a6e288dSDaniel Borkmann }
27287a6e288dSDaniel Borkmann 
27291da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
27301da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
27311da177e4SLinus Torvalds {
27321da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
27331da177e4SLinus Torvalds 	__u8 *eth;
27341da177e4SLinus Torvalds 	struct udphdr *udph;
27351da177e4SLinus Torvalds 	int datalen, iplen;
27361da177e4SLinus Torvalds 	struct iphdr *iph;
2737d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IP);
2738ca6549afSSteven Whitehouse 	__be32 *mpls;
273934954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
274034954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
274134954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
274234954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2743fd2ea0a7SDavid S. Miller 	u16 queue_map;
2744ca6549afSSteven Whitehouse 
2745ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2746d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
27471da177e4SLinus Torvalds 
274834954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2749d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
275034954ddcSFrancesco Fondelli 
275164053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
275264053beeSRobert Olsson 	 * fields.
275364053beeSRobert Olsson 	 */
275464053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2755eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
275664053beeSRobert Olsson 
27577ac5459eSDavid S. Miller 	datalen = (odev->hard_header_len + 16) & ~0xf;
2758e99b99b4SRobert Olsson 
27597a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
27601da177e4SLinus Torvalds 	if (!skb) {
27611da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
27621da177e4SLinus Torvalds 		return NULL;
27631da177e4SLinus Torvalds 	}
27641da177e4SLinus Torvalds 
27657a6e288dSDaniel Borkmann 	prefetchw(skb->data);
27667ac5459eSDavid S. Miller 	skb_reserve(skb, datalen);
27671da177e4SLinus Torvalds 
27681da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
27691da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2770ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2771ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2772ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
277334954ddcSFrancesco Fondelli 
277434954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
277534954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
277634954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
27770f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
27780f37c605SAl Viro 					       pkt_dev->svlan_cfi,
27790f37c605SAl Viro 					       pkt_dev->svlan_p);
278034954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2781d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
278234954ddcSFrancesco Fondelli 		}
278334954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
27840f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
27850f37c605SAl Viro 				      pkt_dev->vlan_cfi,
27860f37c605SAl Viro 				      pkt_dev->vlan_p);
278734954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2788d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IP);
278934954ddcSFrancesco Fondelli 	}
279034954ddcSFrancesco Fondelli 
2791525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2792525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2793525cebedSThomas Graf 	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
2794525cebedSThomas Graf 
2795525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2796525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2797fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
27989e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
27999e50e3acSJohn Fastabend 
28001da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2801252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
28021da177e4SLinus Torvalds 
2803ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2804ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
280516dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
28066af773e7SNishank Trivedi 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
28071da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
28081da177e4SLinus Torvalds 
28091da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
28101da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
28111da177e4SLinus Torvalds 	udph->len = htons(datalen + 8);	/* DATA + udphdr */
2812c26bf4a5SThomas Graf 	udph->check = 0;
28131da177e4SLinus Torvalds 
28141da177e4SLinus Torvalds 	iph->ihl = 5;
28151da177e4SLinus Torvalds 	iph->version = 4;
28161da177e4SLinus Torvalds 	iph->ttl = 32;
28171ca7768cSFrancesco Fondelli 	iph->tos = pkt_dev->tos;
28181da177e4SLinus Torvalds 	iph->protocol = IPPROTO_UDP;	/* UDP */
28191da177e4SLinus Torvalds 	iph->saddr = pkt_dev->cur_saddr;
28201da177e4SLinus Torvalds 	iph->daddr = pkt_dev->cur_daddr;
282166ed1e5eSEric Dumazet 	iph->id = htons(pkt_dev->ip_id);
282266ed1e5eSEric Dumazet 	pkt_dev->ip_id++;
28231da177e4SLinus Torvalds 	iph->frag_off = 0;
28241da177e4SLinus Torvalds 	iplen = 20 + 8 + datalen;
28251da177e4SLinus Torvalds 	iph->tot_len = htons(iplen);
282603c633e7SThomas Graf 	ip_send_check(iph);
2827ca6549afSSteven Whitehouse 	skb->protocol = protocol;
28281da177e4SLinus Torvalds 	skb->dev = odev;
28291da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
2830c26bf4a5SThomas Graf 
2831c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
2832c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
2833c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V4_CSUM) {
2834c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
2835c26bf4a5SThomas Graf 		skb->csum = 0;
2836c26bf4a5SThomas Graf 		udp4_hwcsum(skb, udph->source, udph->dest);
2837c26bf4a5SThomas Graf 	} else {
2838c26bf4a5SThomas Graf 		__wsum csum = udp_csum(skb);
2839c26bf4a5SThomas Graf 
2840c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
2841c26bf4a5SThomas Graf 		udph->check = csum_tcpudp_magic(udph->source, udph->dest,
2842c26bf4a5SThomas Graf 						datalen + 8, IPPROTO_UDP, csum);
2843c26bf4a5SThomas Graf 
2844c26bf4a5SThomas Graf 		if (udph->check == 0)
2845c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
2846c26bf4a5SThomas Graf 	}
2847c26bf4a5SThomas Graf 
284826ad7879SEric Dumazet 	pktgen_finalize_skb(pkt_dev, skb, datalen);
28491da177e4SLinus Torvalds 
2850a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2851a553e4a6SJamal Hadi Salim 	if (!process_ipsec(pkt_dev, skb, protocol))
2852a553e4a6SJamal Hadi Salim 		return NULL;
2853a553e4a6SJamal Hadi Salim #endif
2854a553e4a6SJamal Hadi Salim 
28551da177e4SLinus Torvalds 	return skb;
28561da177e4SLinus Torvalds }
28571da177e4SLinus Torvalds 
28581da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
28591da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
28601da177e4SLinus Torvalds {
28611da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
28621da177e4SLinus Torvalds 	__u8 *eth;
28631da177e4SLinus Torvalds 	struct udphdr *udph;
2864c26bf4a5SThomas Graf 	int datalen, udplen;
28651da177e4SLinus Torvalds 	struct ipv6hdr *iph;
2866d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IPV6);
2867ca6549afSSteven Whitehouse 	__be32 *mpls;
286834954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
286934954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
287034954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
287134954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2872fd2ea0a7SDavid S. Miller 	u16 queue_map;
2873ca6549afSSteven Whitehouse 
2874ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2875d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
28761da177e4SLinus Torvalds 
287734954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2878d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
287934954ddcSFrancesco Fondelli 
288064053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
288164053beeSRobert Olsson 	 * fields.
288264053beeSRobert Olsson 	 */
288364053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2884eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
288564053beeSRobert Olsson 
28867a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, 16);
28871da177e4SLinus Torvalds 	if (!skb) {
28881da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
28891da177e4SLinus Torvalds 		return NULL;
28901da177e4SLinus Torvalds 	}
28911da177e4SLinus Torvalds 
28927a6e288dSDaniel Borkmann 	prefetchw(skb->data);
28931da177e4SLinus Torvalds 	skb_reserve(skb, 16);
28941da177e4SLinus Torvalds 
28951da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
28961da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2897ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2898ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2899ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
290034954ddcSFrancesco Fondelli 
290134954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
290234954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
290334954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29040f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
29050f37c605SAl Viro 					       pkt_dev->svlan_cfi,
29060f37c605SAl Viro 					       pkt_dev->svlan_p);
290734954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2908d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
290934954ddcSFrancesco Fondelli 		}
291034954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29110f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
29120f37c605SAl Viro 				      pkt_dev->vlan_cfi,
29130f37c605SAl Viro 				      pkt_dev->vlan_p);
291434954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2915d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
291634954ddcSFrancesco Fondelli 	}
291734954ddcSFrancesco Fondelli 
2918525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2919525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2920525cebedSThomas Graf 	iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
2921525cebedSThomas Graf 
2922525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2923525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2924fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
29259e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
29261da177e4SLinus Torvalds 
29271da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2928252e3346SAl Viro 	*(__be16 *) &eth[12] = protocol;
29291da177e4SLinus Torvalds 
2930ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2931ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 -
2932ca6549afSSteven Whitehouse 		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
293316dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
29341da177e4SLinus Torvalds 
29355aa8b572SAmerigo Wang 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr)) {
29361da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
2937e87cc472SJoe Perches 		net_info_ratelimited("increased datalen to %d\n", datalen);
29381da177e4SLinus Torvalds 	}
29391da177e4SLinus Torvalds 
2940c26bf4a5SThomas Graf 	udplen = datalen + sizeof(struct udphdr);
29411da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
29421da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
2943c26bf4a5SThomas Graf 	udph->len = htons(udplen);
2944c26bf4a5SThomas Graf 	udph->check = 0;
29451da177e4SLinus Torvalds 
2946d5f1ce9aSStephen Hemminger 	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
29471da177e4SLinus Torvalds 
29481ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
29491ca7768cSFrancesco Fondelli 		/* Version + traffic class + flow (0) */
2950252e3346SAl Viro 		*(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
29511ca7768cSFrancesco Fondelli 	}
29521ca7768cSFrancesco Fondelli 
29531da177e4SLinus Torvalds 	iph->hop_limit = 32;
29541da177e4SLinus Torvalds 
2955c26bf4a5SThomas Graf 	iph->payload_len = htons(udplen);
29561da177e4SLinus Torvalds 	iph->nexthdr = IPPROTO_UDP;
29571da177e4SLinus Torvalds 
29584e3fd7a0SAlexey Dobriyan 	iph->daddr = pkt_dev->cur_in6_daddr;
29594e3fd7a0SAlexey Dobriyan 	iph->saddr = pkt_dev->cur_in6_saddr;
29601da177e4SLinus Torvalds 
2961ca6549afSSteven Whitehouse 	skb->protocol = protocol;
29621da177e4SLinus Torvalds 	skb->dev = odev;
29631da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
29641da177e4SLinus Torvalds 
2965c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
2966c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
2967c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V6_CSUM) {
2968c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
2969c26bf4a5SThomas Graf 		skb->csum_start = skb_transport_header(skb) - skb->head;
2970c26bf4a5SThomas Graf 		skb->csum_offset = offsetof(struct udphdr, check);
2971c26bf4a5SThomas Graf 		udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0);
2972c26bf4a5SThomas Graf 	} else {
2973c26bf4a5SThomas Graf 		__wsum csum = udp_csum(skb);
2974c26bf4a5SThomas Graf 
2975c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
2976c26bf4a5SThomas Graf 		udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum);
2977c26bf4a5SThomas Graf 
2978c26bf4a5SThomas Graf 		if (udph->check == 0)
2979c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
2980c26bf4a5SThomas Graf 	}
2981c26bf4a5SThomas Graf 
298226ad7879SEric Dumazet 	pktgen_finalize_skb(pkt_dev, skb, datalen);
29831da177e4SLinus Torvalds 
29841da177e4SLinus Torvalds 	return skb;
29851da177e4SLinus Torvalds }
29861da177e4SLinus Torvalds 
2987475ac1e4SStephen Hemminger static struct sk_buff *fill_packet(struct net_device *odev,
29881da177e4SLinus Torvalds 				   struct pktgen_dev *pkt_dev)
29891da177e4SLinus Torvalds {
29901da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
29911da177e4SLinus Torvalds 		return fill_packet_ipv6(odev, pkt_dev);
29921da177e4SLinus Torvalds 	else
29931da177e4SLinus Torvalds 		return fill_packet_ipv4(odev, pkt_dev);
29941da177e4SLinus Torvalds }
29951da177e4SLinus Torvalds 
29961da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
29971da177e4SLinus Torvalds {
29981da177e4SLinus Torvalds 	pkt_dev->seq_num = 1;
29991da177e4SLinus Torvalds 	pkt_dev->idle_acc = 0;
30001da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
30011da177e4SLinus Torvalds 	pkt_dev->tx_bytes = 0;
30021da177e4SLinus Torvalds 	pkt_dev->errors = 0;
30031da177e4SLinus Torvalds }
30041da177e4SLinus Torvalds 
30051da177e4SLinus Torvalds /* Set up structure for sending pkts, clear counters */
30061da177e4SLinus Torvalds 
30071da177e4SLinus Torvalds static void pktgen_run(struct pktgen_thread *t)
30081da177e4SLinus Torvalds {
3009c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
30101da177e4SLinus Torvalds 	int started = 0;
30111da177e4SLinus Torvalds 
3012f9467eaeSJoe Perches 	func_enter();
30131da177e4SLinus Torvalds 
30148788370aSJesper Dangaard Brouer 	rcu_read_lock();
30158788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
30161da177e4SLinus Torvalds 
30171da177e4SLinus Torvalds 		/*
30181da177e4SLinus Torvalds 		 * setup odev and create initial packet.
30191da177e4SLinus Torvalds 		 */
30201da177e4SLinus Torvalds 		pktgen_setup_inject(pkt_dev);
30211da177e4SLinus Torvalds 
30221da177e4SLinus Torvalds 		if (pkt_dev->odev) {
30231da177e4SLinus Torvalds 			pktgen_clear_counters(pkt_dev);
30241da177e4SLinus Torvalds 			pkt_dev->skb = NULL;
3025398f382cSDaniel Borkmann 			pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
3026fd29cf72SStephen Hemminger 
302716dab72fSJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
30281da177e4SLinus Torvalds 
30291da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Starting");
30308788370aSJesper Dangaard Brouer 			pkt_dev->running = 1;	/* Cranke yeself! */
30311da177e4SLinus Torvalds 			started++;
3032222f1806SLuiz Capitulino 		} else
30331da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Error starting");
30341da177e4SLinus Torvalds 	}
30358788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3036222f1806SLuiz Capitulino 	if (started)
3037222f1806SLuiz Capitulino 		t->control &= ~(T_STOP);
30381da177e4SLinus Torvalds }
30391da177e4SLinus Torvalds 
30404e58a027SCong Wang static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn)
30411da177e4SLinus Torvalds {
3042cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
30431da177e4SLinus Torvalds 
3044f9467eaeSJoe Perches 	func_enter();
30451da177e4SLinus Torvalds 
30466146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3047cdcdbe0bSLuiz Capitulino 
30484e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
304995ed63f7SArthur Kepner 		t->control |= T_STOP;
3050cdcdbe0bSLuiz Capitulino 
30516146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
30521da177e4SLinus Torvalds }
30531da177e4SLinus Torvalds 
3054648fda74SStephen Hemminger static int thread_is_running(const struct pktgen_thread *t)
30551da177e4SLinus Torvalds {
3056648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
30571da177e4SLinus Torvalds 
30588788370aSJesper Dangaard Brouer 	rcu_read_lock();
30598788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
30608788370aSJesper Dangaard Brouer 		if (pkt_dev->running) {
30618788370aSJesper Dangaard Brouer 			rcu_read_unlock();
3062648fda74SStephen Hemminger 			return 1;
30638788370aSJesper Dangaard Brouer 		}
30648788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3065648fda74SStephen Hemminger 	return 0;
30661da177e4SLinus Torvalds }
30671da177e4SLinus Torvalds 
30681da177e4SLinus Torvalds static int pktgen_wait_thread_run(struct pktgen_thread *t)
30691da177e4SLinus Torvalds {
30701da177e4SLinus Torvalds 	while (thread_is_running(t)) {
30711da177e4SLinus Torvalds 
30721da177e4SLinus Torvalds 		msleep_interruptible(100);
30731da177e4SLinus Torvalds 
30741da177e4SLinus Torvalds 		if (signal_pending(current))
30751da177e4SLinus Torvalds 			goto signal;
30761da177e4SLinus Torvalds 	}
30771da177e4SLinus Torvalds 	return 1;
30781da177e4SLinus Torvalds signal:
30791da177e4SLinus Torvalds 	return 0;
30801da177e4SLinus Torvalds }
30811da177e4SLinus Torvalds 
30824e58a027SCong Wang static int pktgen_wait_all_threads_run(struct pktgen_net *pn)
30831da177e4SLinus Torvalds {
3084cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
30851da177e4SLinus Torvalds 	int sig = 1;
30861da177e4SLinus Torvalds 
30876146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3088cdcdbe0bSLuiz Capitulino 
30894e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
30901da177e4SLinus Torvalds 		sig = pktgen_wait_thread_run(t);
3091222f1806SLuiz Capitulino 		if (sig == 0)
3092222f1806SLuiz Capitulino 			break;
30931da177e4SLinus Torvalds 	}
3094cdcdbe0bSLuiz Capitulino 
3095cdcdbe0bSLuiz Capitulino 	if (sig == 0)
30964e58a027SCong Wang 		list_for_each_entry(t, &pn->pktgen_threads, th_list)
30971da177e4SLinus Torvalds 			t->control |= (T_STOP);
3098cdcdbe0bSLuiz Capitulino 
30996146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31001da177e4SLinus Torvalds 	return sig;
31011da177e4SLinus Torvalds }
31021da177e4SLinus Torvalds 
31034e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn)
31041da177e4SLinus Torvalds {
3105cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31061da177e4SLinus Torvalds 
3107f9467eaeSJoe Perches 	func_enter();
31081da177e4SLinus Torvalds 
31096146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
31101da177e4SLinus Torvalds 
31114e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
31121da177e4SLinus Torvalds 		t->control |= (T_RUN);
3113cdcdbe0bSLuiz Capitulino 
31146146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31151da177e4SLinus Torvalds 
311663adc6fbSStephen Hemminger 	/* Propagate thread->control  */
311763adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
31181da177e4SLinus Torvalds 
31194e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
31201da177e4SLinus Torvalds }
31211da177e4SLinus Torvalds 
31224e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn)
3123eb37b41cSJesse Brandeburg {
3124eb37b41cSJesse Brandeburg 	struct pktgen_thread *t;
3125eb37b41cSJesse Brandeburg 
3126f9467eaeSJoe Perches 	func_enter();
3127eb37b41cSJesse Brandeburg 
3128eb37b41cSJesse Brandeburg 	mutex_lock(&pktgen_thread_lock);
3129eb37b41cSJesse Brandeburg 
31304e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
3131eb37b41cSJesse Brandeburg 		t->control |= (T_REMDEVALL);
3132eb37b41cSJesse Brandeburg 
3133eb37b41cSJesse Brandeburg 	mutex_unlock(&pktgen_thread_lock);
3134eb37b41cSJesse Brandeburg 
313563adc6fbSStephen Hemminger 	/* Propagate thread->control  */
313663adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
3137eb37b41cSJesse Brandeburg 
31384e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
3139eb37b41cSJesse Brandeburg }
3140eb37b41cSJesse Brandeburg 
31411da177e4SLinus Torvalds static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
31421da177e4SLinus Torvalds {
3143fd29cf72SStephen Hemminger 	__u64 bps, mbps, pps;
31441da177e4SLinus Torvalds 	char *p = pkt_dev->result;
3145fd29cf72SStephen Hemminger 	ktime_t elapsed = ktime_sub(pkt_dev->stopped_at,
3146fd29cf72SStephen Hemminger 				    pkt_dev->started_at);
3147fd29cf72SStephen Hemminger 	ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
31481da177e4SLinus Torvalds 
314903a14ab1SDaniel Turull 	p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
3150fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(elapsed),
3151fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
3152fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(idle),
31531da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->sofar,
31541da177e4SLinus Torvalds 		     pkt_dev->cur_pkt_size, nr_frags);
31551da177e4SLinus Torvalds 
3156fd29cf72SStephen Hemminger 	pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC,
3157fd29cf72SStephen Hemminger 			ktime_to_ns(elapsed));
31581da177e4SLinus Torvalds 
31591da177e4SLinus Torvalds 	bps = pps * 8 * pkt_dev->cur_pkt_size;
31601da177e4SLinus Torvalds 
31611da177e4SLinus Torvalds 	mbps = bps;
31621da177e4SLinus Torvalds 	do_div(mbps, 1000000);
31631da177e4SLinus Torvalds 	p += sprintf(p, "  %llupps %lluMb/sec (%llubps) errors: %llu",
31641da177e4SLinus Torvalds 		     (unsigned long long)pps,
31651da177e4SLinus Torvalds 		     (unsigned long long)mbps,
31661da177e4SLinus Torvalds 		     (unsigned long long)bps,
31671da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->errors);
31681da177e4SLinus Torvalds }
31691da177e4SLinus Torvalds 
31701da177e4SLinus Torvalds /* Set stopped-at timer, remove from running list, do counters & statistics */
31711da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
31721da177e4SLinus Torvalds {
3173222f1806SLuiz Capitulino 	int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
31741da177e4SLinus Torvalds 
31751da177e4SLinus Torvalds 	if (!pkt_dev->running) {
3176*294a0b7fSJoe Perches 		pr_warn("interface: %s is already stopped\n",
3177f9467eaeSJoe Perches 			pkt_dev->odevname);
31781da177e4SLinus Torvalds 		return -EINVAL;
31791da177e4SLinus Torvalds 	}
31801da177e4SLinus Torvalds 
31818788370aSJesper Dangaard Brouer 	pkt_dev->running = 0;
31823bda06a3SStephen Hemminger 	kfree_skb(pkt_dev->skb);
31833bda06a3SStephen Hemminger 	pkt_dev->skb = NULL;
3184398f382cSDaniel Borkmann 	pkt_dev->stopped_at = ktime_get();
31851da177e4SLinus Torvalds 
318695ed63f7SArthur Kepner 	show_results(pkt_dev, nr_frags);
31871da177e4SLinus Torvalds 
31881da177e4SLinus Torvalds 	return 0;
31891da177e4SLinus Torvalds }
31901da177e4SLinus Torvalds 
31911da177e4SLinus Torvalds static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
31921da177e4SLinus Torvalds {
3193c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev, *best = NULL;
31941da177e4SLinus Torvalds 
31958788370aSJesper Dangaard Brouer 	rcu_read_lock();
31968788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3197c26a8016SLuiz Capitulino 		if (!pkt_dev->running)
3198222f1806SLuiz Capitulino 			continue;
3199222f1806SLuiz Capitulino 		if (best == NULL)
3200c26a8016SLuiz Capitulino 			best = pkt_dev;
3201398f382cSDaniel Borkmann 		else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
3202c26a8016SLuiz Capitulino 			best = pkt_dev;
32031da177e4SLinus Torvalds 	}
32048788370aSJesper Dangaard Brouer 	rcu_read_unlock();
32058788370aSJesper Dangaard Brouer 
32061da177e4SLinus Torvalds 	return best;
32071da177e4SLinus Torvalds }
32081da177e4SLinus Torvalds 
3209222f1806SLuiz Capitulino static void pktgen_stop(struct pktgen_thread *t)
3210222f1806SLuiz Capitulino {
3211c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
32121da177e4SLinus Torvalds 
3213f9467eaeSJoe Perches 	func_enter();
32141da177e4SLinus Torvalds 
32158788370aSJesper Dangaard Brouer 	rcu_read_lock();
32161da177e4SLinus Torvalds 
32178788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3218c26a8016SLuiz Capitulino 		pktgen_stop_device(pkt_dev);
321995ed63f7SArthur Kepner 	}
322095ed63f7SArthur Kepner 
32218788370aSJesper Dangaard Brouer 	rcu_read_unlock();
322295ed63f7SArthur Kepner }
322395ed63f7SArthur Kepner 
322495ed63f7SArthur Kepner /*
322595ed63f7SArthur Kepner  * one of our devices needs to be removed - find it
322695ed63f7SArthur Kepner  * and remove it
322795ed63f7SArthur Kepner  */
322895ed63f7SArthur Kepner static void pktgen_rem_one_if(struct pktgen_thread *t)
322995ed63f7SArthur Kepner {
3230c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3231c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
323295ed63f7SArthur Kepner 
3233f9467eaeSJoe Perches 	func_enter();
323495ed63f7SArthur Kepner 
3235c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3236c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
323795ed63f7SArthur Kepner 
3238222f1806SLuiz Capitulino 		if (!cur->removal_mark)
3239222f1806SLuiz Capitulino 			continue;
324095ed63f7SArthur Kepner 
324195ed63f7SArthur Kepner 		kfree_skb(cur->skb);
324295ed63f7SArthur Kepner 		cur->skb = NULL;
324395ed63f7SArthur Kepner 
324495ed63f7SArthur Kepner 		pktgen_remove_device(t, cur);
324595ed63f7SArthur Kepner 
324695ed63f7SArthur Kepner 		break;
324795ed63f7SArthur Kepner 	}
32481da177e4SLinus Torvalds }
32491da177e4SLinus Torvalds 
32501da177e4SLinus Torvalds static void pktgen_rem_all_ifs(struct pktgen_thread *t)
32511da177e4SLinus Torvalds {
3252c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3253c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
32541da177e4SLinus Torvalds 
3255f9467eaeSJoe Perches 	func_enter();
3256f9467eaeSJoe Perches 
32571da177e4SLinus Torvalds 	/* Remove all devices, free mem */
32581da177e4SLinus Torvalds 
3259c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3260c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
326195ed63f7SArthur Kepner 
326295ed63f7SArthur Kepner 		kfree_skb(cur->skb);
326395ed63f7SArthur Kepner 		cur->skb = NULL;
326495ed63f7SArthur Kepner 
32651da177e4SLinus Torvalds 		pktgen_remove_device(t, cur);
32661da177e4SLinus Torvalds 	}
32671da177e4SLinus Torvalds }
32681da177e4SLinus Torvalds 
32691da177e4SLinus Torvalds static void pktgen_rem_thread(struct pktgen_thread *t)
32701da177e4SLinus Torvalds {
32711da177e4SLinus Torvalds 	/* Remove from the thread list */
32724e58a027SCong Wang 	remove_proc_entry(t->tsk->comm, t->net->proc_dir);
32731da177e4SLinus Torvalds }
32741da177e4SLinus Torvalds 
3275ef87979cSStephen Hemminger static void pktgen_resched(struct pktgen_dev *pkt_dev)
32763791decbSStephen Hemminger {
3277398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
32783791decbSStephen Hemminger 	schedule();
3279398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
32803791decbSStephen Hemminger }
32813791decbSStephen Hemminger 
3282ef87979cSStephen Hemminger static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
3283ef87979cSStephen Hemminger {
3284398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
3285ef87979cSStephen Hemminger 
3286ef87979cSStephen Hemminger 	while (atomic_read(&(pkt_dev->skb->users)) != 1) {
3287ef87979cSStephen Hemminger 		if (signal_pending(current))
3288ef87979cSStephen Hemminger 			break;
3289ef87979cSStephen Hemminger 
3290ef87979cSStephen Hemminger 		if (need_resched())
3291ef87979cSStephen Hemminger 			pktgen_resched(pkt_dev);
3292ef87979cSStephen Hemminger 		else
3293ef87979cSStephen Hemminger 			cpu_relax();
3294ef87979cSStephen Hemminger 	}
3295398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
3296ef87979cSStephen Hemminger }
3297fd29cf72SStephen Hemminger 
3298475ac1e4SStephen Hemminger static void pktgen_xmit(struct pktgen_dev *pkt_dev)
32991da177e4SLinus Torvalds {
330000829823SStephen Hemminger 	struct net_device *odev = pkt_dev->odev;
3301fd2ea0a7SDavid S. Miller 	struct netdev_queue *txq;
33021da177e4SLinus Torvalds 	int ret;
33031da177e4SLinus Torvalds 
3304ef87979cSStephen Hemminger 	/* If device is offline, then don't send */
3305ef87979cSStephen Hemminger 	if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
33061da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
33073791decbSStephen Hemminger 		return;
33081da177e4SLinus Torvalds 	}
33091da177e4SLinus Torvalds 
3310ef87979cSStephen Hemminger 	/* This is max DELAY, this has special meaning of
3311ef87979cSStephen Hemminger 	 * "never transmit"
3312ef87979cSStephen Hemminger 	 */
3313ef87979cSStephen Hemminger 	if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
3314398f382cSDaniel Borkmann 		pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
3315ef87979cSStephen Hemminger 		return;
3316ef87979cSStephen Hemminger 	}
3317ef87979cSStephen Hemminger 
3318ef87979cSStephen Hemminger 	/* If no skb or clone count exhausted then get new one */
33197d7bb1cfSStephen Hemminger 	if (!pkt_dev->skb || (pkt_dev->last_ok &&
33207d7bb1cfSStephen Hemminger 			      ++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
33211da177e4SLinus Torvalds 		/* build a new pkt */
33221da177e4SLinus Torvalds 		kfree_skb(pkt_dev->skb);
33231da177e4SLinus Torvalds 
33241da177e4SLinus Torvalds 		pkt_dev->skb = fill_packet(odev, pkt_dev);
33251da177e4SLinus Torvalds 		if (pkt_dev->skb == NULL) {
3326f9467eaeSJoe Perches 			pr_err("ERROR: couldn't allocate skb in fill_packet\n");
33271da177e4SLinus Torvalds 			schedule();
33281da177e4SLinus Torvalds 			pkt_dev->clone_count--;	/* back out increment, OOM */
33293791decbSStephen Hemminger 			return;
33301da177e4SLinus Torvalds 		}
3331baac8564SEric Dumazet 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
33321da177e4SLinus Torvalds 		pkt_dev->allocated_skbs++;
33331da177e4SLinus Torvalds 		pkt_dev->clone_count = 0;	/* reset counter */
33341da177e4SLinus Torvalds 	}
33351da177e4SLinus Torvalds 
3336ef87979cSStephen Hemminger 	if (pkt_dev->delay && pkt_dev->last_ok)
3337ef87979cSStephen Hemminger 		spin(pkt_dev, pkt_dev->next_tx);
3338ef87979cSStephen Hemminger 
333910c51b56SDaniel Borkmann 	txq = skb_get_tx_queue(odev, pkt_dev->skb);
3340fd2ea0a7SDavid S. Miller 
33410f2eea4bSDaniel Borkmann 	local_bh_disable();
33420f2eea4bSDaniel Borkmann 
33430f2eea4bSDaniel Borkmann 	HARD_TX_LOCK(odev, txq, smp_processor_id());
33445b8db2f5SStephen Hemminger 
33456f25cd47SDaniel Borkmann 	if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) {
3346ef87979cSStephen Hemminger 		ret = NETDEV_TX_BUSY;
33470835acfeSEric Dumazet 		pkt_dev->last_ok = 0;
33480835acfeSEric Dumazet 		goto unlock;
33490835acfeSEric Dumazet 	}
33500835acfeSEric Dumazet 	atomic_inc(&(pkt_dev->skb->users));
3351fa2dbdc2SDavid S. Miller 	ret = netdev_start_xmit(pkt_dev->skb, odev, txq, false);
3352ef87979cSStephen Hemminger 
33535b8db2f5SStephen Hemminger 	switch (ret) {
33545b8db2f5SStephen Hemminger 	case NETDEV_TX_OK:
33551da177e4SLinus Torvalds 		pkt_dev->last_ok = 1;
33561da177e4SLinus Torvalds 		pkt_dev->sofar++;
33571da177e4SLinus Torvalds 		pkt_dev->seq_num++;
3358baac8564SEric Dumazet 		pkt_dev->tx_bytes += pkt_dev->last_pkt_size;
33595b8db2f5SStephen Hemminger 		break;
3360f466dba1SJohn Fastabend 	case NET_XMIT_DROP:
3361f466dba1SJohn Fastabend 	case NET_XMIT_CN:
3362f466dba1SJohn Fastabend 	case NET_XMIT_POLICED:
3363f466dba1SJohn Fastabend 		/* skb has been consumed */
3364f466dba1SJohn Fastabend 		pkt_dev->errors++;
3365f466dba1SJohn Fastabend 		break;
33665b8db2f5SStephen Hemminger 	default: /* Drivers are not supposed to return other values! */
3367e87cc472SJoe Perches 		net_info_ratelimited("%s xmit error: %d\n",
3368e87cc472SJoe Perches 				     pkt_dev->odevname, ret);
33691da177e4SLinus Torvalds 		pkt_dev->errors++;
33705b8db2f5SStephen Hemminger 		/* fallthru */
3371ef87979cSStephen Hemminger 	case NETDEV_TX_LOCKED:
33725b8db2f5SStephen Hemminger 	case NETDEV_TX_BUSY:
33735b8db2f5SStephen Hemminger 		/* Retry it next time */
33745b8db2f5SStephen Hemminger 		atomic_dec(&(pkt_dev->skb->users));
33751da177e4SLinus Torvalds 		pkt_dev->last_ok = 0;
33761da177e4SLinus Torvalds 	}
33770835acfeSEric Dumazet unlock:
33780f2eea4bSDaniel Borkmann 	HARD_TX_UNLOCK(odev, txq);
33790f2eea4bSDaniel Borkmann 
33800f2eea4bSDaniel Borkmann 	local_bh_enable();
33811da177e4SLinus Torvalds 
33821da177e4SLinus Torvalds 	/* If pkt_dev->count is zero, then run forever */
33831da177e4SLinus Torvalds 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
3384ef87979cSStephen Hemminger 		pktgen_wait_for_skb(pkt_dev);
33851da177e4SLinus Torvalds 
33861da177e4SLinus Torvalds 		/* Done with this */
33871da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
33881da177e4SLinus Torvalds 	}
33891da177e4SLinus Torvalds }
33901da177e4SLinus Torvalds 
33911da177e4SLinus Torvalds /*
33921da177e4SLinus Torvalds  * Main loop of the thread goes here
33931da177e4SLinus Torvalds  */
33941da177e4SLinus Torvalds 
3395ee74baa7SDavid S. Miller static int pktgen_thread_worker(void *arg)
33961da177e4SLinus Torvalds {
33971da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
3398ee74baa7SDavid S. Miller 	struct pktgen_thread *t = arg;
33991da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
34001da177e4SLinus Torvalds 	int cpu = t->cpu;
34011da177e4SLinus Torvalds 
3402ee74baa7SDavid S. Miller 	BUG_ON(smp_processor_id() != cpu);
34031da177e4SLinus Torvalds 
34041da177e4SLinus Torvalds 	init_waitqueue_head(&t->queue);
3405d3ede327SDenis V. Lunev 	complete(&t->start_done);
34061da177e4SLinus Torvalds 
3407f9467eaeSJoe Perches 	pr_debug("starting pktgen/%d:  pid=%d\n", cpu, task_pid_nr(current));
34081da177e4SLinus Torvalds 
340983144186SRafael J. Wysocki 	set_freezable();
341083144186SRafael J. Wysocki 
3411baac167bSJesper Dangaard Brouer 	__set_current_state(TASK_RUNNING);
3412baac167bSJesper Dangaard Brouer 
3413ee74baa7SDavid S. Miller 	while (!kthread_should_stop()) {
3414ee74baa7SDavid S. Miller 		pkt_dev = next_to_run(t);
3415ee74baa7SDavid S. Miller 
3416ef87979cSStephen Hemminger 		if (unlikely(!pkt_dev && t->control == 0)) {
34174e58a027SCong Wang 			if (t->net->pktgen_exiting)
3418551eaff1SEric Dumazet 				break;
3419ef87979cSStephen Hemminger 			wait_event_interruptible_timeout(t->queue,
3420ef87979cSStephen Hemminger 							 t->control != 0,
3421ef87979cSStephen Hemminger 							 HZ/10);
34221b3f720bSRafael J. Wysocki 			try_to_freeze();
3423ef87979cSStephen Hemminger 			continue;
3424ee74baa7SDavid S. Miller 		}
34251da177e4SLinus Torvalds 
3426ef87979cSStephen Hemminger 		if (likely(pkt_dev)) {
34271da177e4SLinus Torvalds 			pktgen_xmit(pkt_dev);
34281da177e4SLinus Torvalds 
3429ef87979cSStephen Hemminger 			if (need_resched())
3430ef87979cSStephen Hemminger 				pktgen_resched(pkt_dev);
3431ef87979cSStephen Hemminger 			else
3432ef87979cSStephen Hemminger 				cpu_relax();
3433ef87979cSStephen Hemminger 		}
3434ef87979cSStephen Hemminger 
34351da177e4SLinus Torvalds 		if (t->control & T_STOP) {
34361da177e4SLinus Torvalds 			pktgen_stop(t);
34371da177e4SLinus Torvalds 			t->control &= ~(T_STOP);
34381da177e4SLinus Torvalds 		}
34391da177e4SLinus Torvalds 
34401da177e4SLinus Torvalds 		if (t->control & T_RUN) {
34411da177e4SLinus Torvalds 			pktgen_run(t);
34421da177e4SLinus Torvalds 			t->control &= ~(T_RUN);
34431da177e4SLinus Torvalds 		}
34441da177e4SLinus Torvalds 
344595ed63f7SArthur Kepner 		if (t->control & T_REMDEVALL) {
34461da177e4SLinus Torvalds 			pktgen_rem_all_ifs(t);
344795ed63f7SArthur Kepner 			t->control &= ~(T_REMDEVALL);
344895ed63f7SArthur Kepner 		}
344995ed63f7SArthur Kepner 
345095ed63f7SArthur Kepner 		if (t->control & T_REMDEV) {
345195ed63f7SArthur Kepner 			pktgen_rem_one_if(t);
34521da177e4SLinus Torvalds 			t->control &= ~(T_REMDEV);
34531da177e4SLinus Torvalds 		}
34541da177e4SLinus Torvalds 
345509fe3ef4SAndrew Morton 		try_to_freeze();
34561da177e4SLinus Torvalds 	}
3457baac167bSJesper Dangaard Brouer 	set_current_state(TASK_INTERRUPTIBLE);
34581da177e4SLinus Torvalds 
3459f9467eaeSJoe Perches 	pr_debug("%s stopping all device\n", t->tsk->comm);
34601da177e4SLinus Torvalds 	pktgen_stop(t);
34611da177e4SLinus Torvalds 
3462f9467eaeSJoe Perches 	pr_debug("%s removing all device\n", t->tsk->comm);
34631da177e4SLinus Torvalds 	pktgen_rem_all_ifs(t);
34641da177e4SLinus Torvalds 
3465f9467eaeSJoe Perches 	pr_debug("%s removing thread\n", t->tsk->comm);
34661da177e4SLinus Torvalds 	pktgen_rem_thread(t);
3467cdcdbe0bSLuiz Capitulino 
3468551eaff1SEric Dumazet 	/* Wait for kthread_stop */
3469551eaff1SEric Dumazet 	while (!kthread_should_stop()) {
3470551eaff1SEric Dumazet 		set_current_state(TASK_INTERRUPTIBLE);
3471551eaff1SEric Dumazet 		schedule();
3472551eaff1SEric Dumazet 	}
3473551eaff1SEric Dumazet 	__set_current_state(TASK_RUNNING);
3474551eaff1SEric Dumazet 
3475ee74baa7SDavid S. Miller 	return 0;
34761da177e4SLinus Torvalds }
34771da177e4SLinus Torvalds 
3478222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
34793e984840SEric Dumazet 					  const char *ifname, bool exact)
34801da177e4SLinus Torvalds {
3481c26a8016SLuiz Capitulino 	struct pktgen_dev *p, *pkt_dev = NULL;
34823e984840SEric Dumazet 	size_t len = strlen(ifname);
34831da177e4SLinus Torvalds 
34848788370aSJesper Dangaard Brouer 	rcu_read_lock();
34858788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(p, &t->if_list, list)
34863e984840SEric Dumazet 		if (strncmp(p->odevname, ifname, len) == 0) {
34873e984840SEric Dumazet 			if (p->odevname[len]) {
34883e984840SEric Dumazet 				if (exact || p->odevname[len] != '@')
34893e984840SEric Dumazet 					continue;
34903e984840SEric Dumazet 			}
3491c26a8016SLuiz Capitulino 			pkt_dev = p;
34921da177e4SLinus Torvalds 			break;
34931da177e4SLinus Torvalds 		}
34941da177e4SLinus Torvalds 
34958788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3496f9467eaeSJoe Perches 	pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev);
34971da177e4SLinus Torvalds 	return pkt_dev;
34981da177e4SLinus Torvalds }
34991da177e4SLinus Torvalds 
35001da177e4SLinus Torvalds /*
35011da177e4SLinus Torvalds  * Adds a dev at front of if_list.
35021da177e4SLinus Torvalds  */
35031da177e4SLinus Torvalds 
3504222f1806SLuiz Capitulino static int add_dev_to_thread(struct pktgen_thread *t,
3505222f1806SLuiz Capitulino 			     struct pktgen_dev *pkt_dev)
35061da177e4SLinus Torvalds {
35071da177e4SLinus Torvalds 	int rv = 0;
35081da177e4SLinus Torvalds 
35098788370aSJesper Dangaard Brouer 	/* This function cannot be called concurrently, as its called
35108788370aSJesper Dangaard Brouer 	 * under pktgen_thread_lock mutex, but it can run from
35118788370aSJesper Dangaard Brouer 	 * userspace on another CPU than the kthread.  The if_lock()
35128788370aSJesper Dangaard Brouer 	 * is used here to sync with concurrent instances of
35138788370aSJesper Dangaard Brouer 	 * _rem_dev_from_if_list() invoked via kthread, which is also
35148788370aSJesper Dangaard Brouer 	 * updating the if_list */
35151da177e4SLinus Torvalds 	if_lock(t);
35161da177e4SLinus Torvalds 
35171da177e4SLinus Torvalds 	if (pkt_dev->pg_thread) {
3518f9467eaeSJoe Perches 		pr_err("ERROR: already assigned to a thread\n");
35191da177e4SLinus Torvalds 		rv = -EBUSY;
35201da177e4SLinus Torvalds 		goto out;
35211da177e4SLinus Torvalds 	}
3522c26a8016SLuiz Capitulino 
35231da177e4SLinus Torvalds 	pkt_dev->running = 0;
35248788370aSJesper Dangaard Brouer 	pkt_dev->pg_thread = t;
35258788370aSJesper Dangaard Brouer 	list_add_rcu(&pkt_dev->list, &t->if_list);
35261da177e4SLinus Torvalds 
35271da177e4SLinus Torvalds out:
35281da177e4SLinus Torvalds 	if_unlock(t);
35291da177e4SLinus Torvalds 	return rv;
35301da177e4SLinus Torvalds }
35311da177e4SLinus Torvalds 
35321da177e4SLinus Torvalds /* Called under thread lock */
35331da177e4SLinus Torvalds 
35341da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
35351da177e4SLinus Torvalds {
35361da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev;
353739df232fSStephen Hemminger 	int err;
35383291b9dbSEric Dumazet 	int node = cpu_to_node(t->cpu);
35391da177e4SLinus Torvalds 
35401da177e4SLinus Torvalds 	/* We don't allow a device to be on several threads */
35411da177e4SLinus Torvalds 
35424e58a027SCong Wang 	pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND);
3543d50a6b56SStephen Hemminger 	if (pkt_dev) {
3544f9467eaeSJoe Perches 		pr_err("ERROR: interface already used\n");
3545d50a6b56SStephen Hemminger 		return -EBUSY;
3546d50a6b56SStephen Hemminger 	}
35471da177e4SLinus Torvalds 
35483291b9dbSEric Dumazet 	pkt_dev = kzalloc_node(sizeof(struct pktgen_dev), GFP_KERNEL, node);
35491da177e4SLinus Torvalds 	if (!pkt_dev)
35501da177e4SLinus Torvalds 		return -ENOMEM;
35511da177e4SLinus Torvalds 
3552593f63b0SEric Dumazet 	strcpy(pkt_dev->odevname, ifname);
355368d5ac2eSWANG Cong 	pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
35543291b9dbSEric Dumazet 				      node);
35551da177e4SLinus Torvalds 	if (pkt_dev->flows == NULL) {
35561da177e4SLinus Torvalds 		kfree(pkt_dev);
35571da177e4SLinus Torvalds 		return -ENOMEM;
35581da177e4SLinus Torvalds 	}
35591da177e4SLinus Torvalds 
356095ed63f7SArthur Kepner 	pkt_dev->removal_mark = 0;
35611da177e4SLinus Torvalds 	pkt_dev->nfrags = 0;
3562fd29cf72SStephen Hemminger 	pkt_dev->delay = pg_delay_d;
35631da177e4SLinus Torvalds 	pkt_dev->count = pg_count_d;
35641da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
35651da177e4SLinus Torvalds 	pkt_dev->udp_src_min = 9;	/* sink port */
35661da177e4SLinus Torvalds 	pkt_dev->udp_src_max = 9;
35671da177e4SLinus Torvalds 	pkt_dev->udp_dst_min = 9;
35681da177e4SLinus Torvalds 	pkt_dev->udp_dst_max = 9;
356934954ddcSFrancesco Fondelli 	pkt_dev->vlan_p = 0;
357034954ddcSFrancesco Fondelli 	pkt_dev->vlan_cfi = 0;
357134954ddcSFrancesco Fondelli 	pkt_dev->vlan_id = 0xffff;
357234954ddcSFrancesco Fondelli 	pkt_dev->svlan_p = 0;
357334954ddcSFrancesco Fondelli 	pkt_dev->svlan_cfi = 0;
357434954ddcSFrancesco Fondelli 	pkt_dev->svlan_id = 0xffff;
3575e99b99b4SRobert Olsson 	pkt_dev->node = -1;
357634954ddcSFrancesco Fondelli 
35774e58a027SCong Wang 	err = pktgen_setup_dev(t->net, pkt_dev, ifname);
357839df232fSStephen Hemminger 	if (err)
357939df232fSStephen Hemminger 		goto out1;
3580d8873315SNeil Horman 	if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
3581d8873315SNeil Horman 		pkt_dev->clone_skb = pg_clone_skb_d;
35821da177e4SLinus Torvalds 
35834e58a027SCong Wang 	pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir,
35845efdccbcSDenis V. Lunev 					  &pktgen_if_fops, pkt_dev);
358539df232fSStephen Hemminger 	if (!pkt_dev->entry) {
3586f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3587d50a6b56SStephen Hemminger 		       PG_PROC_DIR, ifname);
358839df232fSStephen Hemminger 		err = -EINVAL;
358939df232fSStephen Hemminger 		goto out2;
359039df232fSStephen Hemminger 	}
3591a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3592a553e4a6SJamal Hadi Salim 	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;
3593a553e4a6SJamal Hadi Salim 	pkt_dev->ipsproto = IPPROTO_ESP;
3594cf93d47eSFan Du 
3595cf93d47eSFan Du 	/* xfrm tunnel mode needs additional dst to extract outter
3596cf93d47eSFan Du 	 * ip header protocol/ttl/id field, here creat a phony one.
3597cf93d47eSFan Du 	 * instead of looking for a valid rt, which definitely hurting
3598cf93d47eSFan Du 	 * performance under such circumstance.
3599cf93d47eSFan Du 	 */
3600cf93d47eSFan Du 	pkt_dev->dstops.family = AF_INET;
3601cf93d47eSFan Du 	pkt_dev->dst.dev = pkt_dev->odev;
3602cf93d47eSFan Du 	dst_init_metrics(&pkt_dev->dst, pktgen_dst_metrics, false);
3603cf93d47eSFan Du 	pkt_dev->dst.child = &pkt_dev->dst;
3604cf93d47eSFan Du 	pkt_dev->dst.ops = &pkt_dev->dstops;
3605a553e4a6SJamal Hadi Salim #endif
360639df232fSStephen Hemminger 
360739df232fSStephen Hemminger 	return add_dev_to_thread(t, pkt_dev);
360839df232fSStephen Hemminger out2:
360939df232fSStephen Hemminger 	dev_put(pkt_dev->odev);
361039df232fSStephen Hemminger out1:
3611a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3612a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3613a553e4a6SJamal Hadi Salim #endif
36141da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
36151da177e4SLinus Torvalds 	kfree(pkt_dev);
361639df232fSStephen Hemminger 	return err;
36171da177e4SLinus Torvalds }
36181da177e4SLinus Torvalds 
36194e58a027SCong Wang static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
36201da177e4SLinus Torvalds {
3621cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3622d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3623ee74baa7SDavid S. Miller 	struct task_struct *p;
36241da177e4SLinus Torvalds 
36253291b9dbSEric Dumazet 	t = kzalloc_node(sizeof(struct pktgen_thread), GFP_KERNEL,
36263291b9dbSEric Dumazet 			 cpu_to_node(cpu));
36271da177e4SLinus Torvalds 	if (!t) {
3628f9467eaeSJoe Perches 		pr_err("ERROR: out of memory, can't create new thread\n");
36291da177e4SLinus Torvalds 		return -ENOMEM;
36301da177e4SLinus Torvalds 	}
36311da177e4SLinus Torvalds 
36321da177e4SLinus Torvalds 	spin_lock_init(&t->if_lock);
36331da177e4SLinus Torvalds 	t->cpu = cpu;
36341da177e4SLinus Torvalds 
3635ee74baa7SDavid S. Miller 	INIT_LIST_HEAD(&t->if_list);
3636ee74baa7SDavid S. Miller 
36374e58a027SCong Wang 	list_add_tail(&t->th_list, &pn->pktgen_threads);
3638d3ede327SDenis V. Lunev 	init_completion(&t->start_done);
3639ee74baa7SDavid S. Miller 
364094dcf29aSEric Dumazet 	p = kthread_create_on_node(pktgen_thread_worker,
364194dcf29aSEric Dumazet 				   t,
364294dcf29aSEric Dumazet 				   cpu_to_node(cpu),
364394dcf29aSEric Dumazet 				   "kpktgend_%d", cpu);
3644ee74baa7SDavid S. Miller 	if (IS_ERR(p)) {
3645f9467eaeSJoe Perches 		pr_err("kernel_thread() failed for cpu %d\n", t->cpu);
3646ee74baa7SDavid S. Miller 		list_del(&t->th_list);
3647ee74baa7SDavid S. Miller 		kfree(t);
3648ee74baa7SDavid S. Miller 		return PTR_ERR(p);
3649ee74baa7SDavid S. Miller 	}
3650ee74baa7SDavid S. Miller 	kthread_bind(p, cpu);
3651ee74baa7SDavid S. Miller 	t->tsk = p;
3652ee74baa7SDavid S. Miller 
36534e58a027SCong Wang 	pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir,
36545efdccbcSDenis V. Lunev 			      &pktgen_thread_fops, t);
3655d50a6b56SStephen Hemminger 	if (!pe) {
3656f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3657ee74baa7SDavid S. Miller 		       PG_PROC_DIR, t->tsk->comm);
3658ee74baa7SDavid S. Miller 		kthread_stop(p);
3659ee74baa7SDavid S. Miller 		list_del(&t->th_list);
36601da177e4SLinus Torvalds 		kfree(t);
36611da177e4SLinus Torvalds 		return -EINVAL;
36621da177e4SLinus Torvalds 	}
3663d50a6b56SStephen Hemminger 
36644e58a027SCong Wang 	t->net = pn;
3665ee74baa7SDavid S. Miller 	wake_up_process(p);
3666d3ede327SDenis V. Lunev 	wait_for_completion(&t->start_done);
36671da177e4SLinus Torvalds 
36681da177e4SLinus Torvalds 	return 0;
36691da177e4SLinus Torvalds }
36701da177e4SLinus Torvalds 
36711da177e4SLinus Torvalds /*
36721da177e4SLinus Torvalds  * Removes a device from the thread if_list.
36731da177e4SLinus Torvalds  */
3674222f1806SLuiz Capitulino static void _rem_dev_from_if_list(struct pktgen_thread *t,
3675222f1806SLuiz Capitulino 				  struct pktgen_dev *pkt_dev)
36761da177e4SLinus Torvalds {
3677c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3678c26a8016SLuiz Capitulino 	struct pktgen_dev *p;
36791da177e4SLinus Torvalds 
36808788370aSJesper Dangaard Brouer 	if_lock(t);
3681c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3682c26a8016SLuiz Capitulino 		p = list_entry(q, struct pktgen_dev, list);
3683c26a8016SLuiz Capitulino 		if (p == pkt_dev)
36848788370aSJesper Dangaard Brouer 			list_del_rcu(&p->list);
36851da177e4SLinus Torvalds 	}
36868788370aSJesper Dangaard Brouer 	if_unlock(t);
36871da177e4SLinus Torvalds }
36881da177e4SLinus Torvalds 
3689222f1806SLuiz Capitulino static int pktgen_remove_device(struct pktgen_thread *t,
3690222f1806SLuiz Capitulino 				struct pktgen_dev *pkt_dev)
36911da177e4SLinus Torvalds {
3692f9467eaeSJoe Perches 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
36931da177e4SLinus Torvalds 
36941da177e4SLinus Torvalds 	if (pkt_dev->running) {
3695*294a0b7fSJoe Perches 		pr_warn("WARNING: trying to remove a running interface, stopping it now\n");
36961da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
36971da177e4SLinus Torvalds 	}
36981da177e4SLinus Torvalds 
36991da177e4SLinus Torvalds 	/* Dis-associate from the interface */
37001da177e4SLinus Torvalds 
37011da177e4SLinus Torvalds 	if (pkt_dev->odev) {
37021da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
37031da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
37041da177e4SLinus Torvalds 	}
37051da177e4SLinus Torvalds 
37068788370aSJesper Dangaard Brouer 	/* Remove proc before if_list entry, because add_device uses
37078788370aSJesper Dangaard Brouer 	 * list to determine if interface already exist, avoid race
37088788370aSJesper Dangaard Brouer 	 * with proc_create_data() */
370939df232fSStephen Hemminger 	if (pkt_dev->entry)
3710a8ca16eaSDavid Howells 		proc_remove(pkt_dev->entry);
37111da177e4SLinus Torvalds 
37128788370aSJesper Dangaard Brouer 	/* And update the thread if_list */
37138788370aSJesper Dangaard Brouer 	_rem_dev_from_if_list(t, pkt_dev);
37148788370aSJesper Dangaard Brouer 
3715a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3716a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3717a553e4a6SJamal Hadi Salim #endif
37181da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
371926ad7879SEric Dumazet 	if (pkt_dev->page)
372026ad7879SEric Dumazet 		put_page(pkt_dev->page);
37218788370aSJesper Dangaard Brouer 	kfree_rcu(pkt_dev, rcu);
37221da177e4SLinus Torvalds 	return 0;
37231da177e4SLinus Torvalds }
37241da177e4SLinus Torvalds 
37254e58a027SCong Wang static int __net_init pg_net_init(struct net *net)
37261da177e4SLinus Torvalds {
37274e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3728d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
37294e58a027SCong Wang 	int cpu, ret = 0;
3730d50a6b56SStephen Hemminger 
37314e58a027SCong Wang 	pn->net = net;
37324e58a027SCong Wang 	INIT_LIST_HEAD(&pn->pktgen_threads);
37334e58a027SCong Wang 	pn->pktgen_exiting = false;
37344e58a027SCong Wang 	pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net);
37354e58a027SCong Wang 	if (!pn->proc_dir) {
37364e58a027SCong Wang 		pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR);
3737d50a6b56SStephen Hemminger 		return -ENODEV;
37381da177e4SLinus Torvalds 	}
37394e58a027SCong Wang 	pe = proc_create(PGCTRL, 0600, pn->proc_dir, &pktgen_fops);
37404e58a027SCong Wang 	if (pe == NULL) {
37414e58a027SCong Wang 		pr_err("cannot create %s procfs entry\n", PGCTRL);
37424e58a027SCong Wang 		ret = -EINVAL;
37434e58a027SCong Wang 		goto remove;
37444e58a027SCong Wang 	}
37451da177e4SLinus Torvalds 
3746670c02c2SJohn Hawkes 	for_each_online_cpu(cpu) {
37478024bb24SLuiz Capitulino 		int err;
37481da177e4SLinus Torvalds 
37494e58a027SCong Wang 		err = pktgen_create_thread(cpu, pn);
37508024bb24SLuiz Capitulino 		if (err)
37514e58a027SCong Wang 			pr_warn("Cannot create thread for cpu %d (%d)\n",
3752f9467eaeSJoe Perches 				   cpu, err);
37531da177e4SLinus Torvalds 	}
37548024bb24SLuiz Capitulino 
37554e58a027SCong Wang 	if (list_empty(&pn->pktgen_threads)) {
37564e58a027SCong Wang 		pr_err("Initialization failed for all threads\n");
3757ce14f894SWANG Cong 		ret = -ENODEV;
37584e58a027SCong Wang 		goto remove_entry;
37598024bb24SLuiz Capitulino 	}
37608024bb24SLuiz Capitulino 
37611da177e4SLinus Torvalds 	return 0;
3762ce14f894SWANG Cong 
37634e58a027SCong Wang remove_entry:
37644e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
37654e58a027SCong Wang remove:
3766ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
3767ce14f894SWANG Cong 	return ret;
37681da177e4SLinus Torvalds }
37691da177e4SLinus Torvalds 
37704e58a027SCong Wang static void __net_exit pg_net_exit(struct net *net)
37711da177e4SLinus Torvalds {
37724e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3773cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3774cdcdbe0bSLuiz Capitulino 	struct list_head *q, *n;
3775d4b11335SEric Dumazet 	LIST_HEAD(list);
37761da177e4SLinus Torvalds 
37771da177e4SLinus Torvalds 	/* Stop all interfaces & threads */
37784e58a027SCong Wang 	pn->pktgen_exiting = true;
37791da177e4SLinus Torvalds 
3780c57b5468SEric Dumazet 	mutex_lock(&pktgen_thread_lock);
37814e58a027SCong Wang 	list_splice_init(&pn->pktgen_threads, &list);
3782c57b5468SEric Dumazet 	mutex_unlock(&pktgen_thread_lock);
3783c57b5468SEric Dumazet 
3784c57b5468SEric Dumazet 	list_for_each_safe(q, n, &list) {
3785cdcdbe0bSLuiz Capitulino 		t = list_entry(q, struct pktgen_thread, th_list);
3786c57b5468SEric Dumazet 		list_del(&t->th_list);
3787ee74baa7SDavid S. Miller 		kthread_stop(t->tsk);
3788ee74baa7SDavid S. Miller 		kfree(t);
37891da177e4SLinus Torvalds 	}
37901da177e4SLinus Torvalds 
37914e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
3792ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
37934e58a027SCong Wang }
37941da177e4SLinus Torvalds 
37954e58a027SCong Wang static struct pernet_operations pg_net_ops = {
37964e58a027SCong Wang 	.init = pg_net_init,
37974e58a027SCong Wang 	.exit = pg_net_exit,
37984e58a027SCong Wang 	.id   = &pg_net_id,
37994e58a027SCong Wang 	.size = sizeof(struct pktgen_net),
38004e58a027SCong Wang };
38014e58a027SCong Wang 
38024e58a027SCong Wang static int __init pg_init(void)
38034e58a027SCong Wang {
38044e58a027SCong Wang 	int ret = 0;
38054e58a027SCong Wang 
38064e58a027SCong Wang 	pr_info("%s", version);
38074e58a027SCong Wang 	ret = register_pernet_subsys(&pg_net_ops);
38084e58a027SCong Wang 	if (ret)
38094e58a027SCong Wang 		return ret;
38104e58a027SCong Wang 	ret = register_netdevice_notifier(&pktgen_notifier_block);
38114e58a027SCong Wang 	if (ret)
38124e58a027SCong Wang 		unregister_pernet_subsys(&pg_net_ops);
38134e58a027SCong Wang 
38144e58a027SCong Wang 	return ret;
38154e58a027SCong Wang }
38164e58a027SCong Wang 
38174e58a027SCong Wang static void __exit pg_cleanup(void)
38184e58a027SCong Wang {
38194e58a027SCong Wang 	unregister_netdevice_notifier(&pktgen_notifier_block);
38204e58a027SCong Wang 	unregister_pernet_subsys(&pg_net_ops);
38218788370aSJesper Dangaard Brouer 	/* Don't need rcu_barrier() due to use of kfree_rcu() */
38221da177e4SLinus Torvalds }
38231da177e4SLinus Torvalds 
38241da177e4SLinus Torvalds module_init(pg_init);
38251da177e4SLinus Torvalds module_exit(pg_cleanup);
38261da177e4SLinus Torvalds 
3827c3d2f52dSStephen Hemminger MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se>");
38281da177e4SLinus Torvalds MODULE_DESCRIPTION("Packet Generator tool");
38291da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3830c3d2f52dSStephen Hemminger MODULE_VERSION(VERSION);
38311da177e4SLinus Torvalds module_param(pg_count_d, int, 0);
3832c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject");
38331da177e4SLinus Torvalds module_param(pg_delay_d, int, 0);
3834c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)");
38351da177e4SLinus Torvalds module_param(pg_clone_skb_d, int, 0);
3836c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet");
38371da177e4SLinus Torvalds module_param(debug, int, 0);
3838c3d2f52dSStephen Hemminger MODULE_PARM_DESC(debug, "Enable debugging of pktgen module");
3839