xref: /openbmc/linux/net/core/pktgen.c (revision 62f64aed622b6055b5fc447e3e421c9351563fc8)
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  *
100ca9f1fd2SStephen Hemminger  * Randy Dunlap fixed u64 printk compiler warning
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 
213*62f64aedSAlexei Starovoitov /* Xmit modes */
214*62f64aedSAlexei Starovoitov #define M_START_XMIT		0	/* Default normal TX */
215*62f64aedSAlexei Starovoitov #define M_NETIF_RECEIVE 	1	/* Inject packets into stack */
216*62f64aedSAlexei Starovoitov 
2178788370aSJesper Dangaard Brouer /* If lock -- protects updating of if_list */
2181da177e4SLinus Torvalds #define   if_lock(t)           spin_lock(&(t->if_lock));
2191da177e4SLinus Torvalds #define   if_unlock(t)           spin_unlock(&(t->if_lock));
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds /* Used to help with determining the pkts on receive */
2221da177e4SLinus Torvalds #define PKTGEN_MAGIC 0xbe9be955
223d50a6b56SStephen Hemminger #define PG_PROC_DIR "pktgen"
224d50a6b56SStephen Hemminger #define PGCTRL	    "pgctrl"
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds #define MAX_CFLOWS  65536
2271da177e4SLinus Torvalds 
22834954ddcSFrancesco Fondelli #define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
22934954ddcSFrancesco Fondelli #define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
23034954ddcSFrancesco Fondelli 
231222f1806SLuiz Capitulino struct flow_state {
232252e3346SAl Viro 	__be32 cur_daddr;
2331da177e4SLinus Torvalds 	int count;
234a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
235a553e4a6SJamal Hadi Salim 	struct xfrm_state *x;
236a553e4a6SJamal Hadi Salim #endif
237007a531bSJamal Hadi Salim 	__u32 flags;
2381da177e4SLinus Torvalds };
2391da177e4SLinus Torvalds 
240007a531bSJamal Hadi Salim /* flow flag bits */
241007a531bSJamal Hadi Salim #define F_INIT   (1<<0)		/* flow has been initialized */
242007a531bSJamal Hadi Salim 
2431da177e4SLinus Torvalds struct pktgen_dev {
2441da177e4SLinus Torvalds 	/*
2451da177e4SLinus Torvalds 	 * Try to keep frequent/infrequent used vars. separated.
2461da177e4SLinus Torvalds 	 */
24739df232fSStephen Hemminger 	struct proc_dir_entry *entry;	/* proc file */
2481da177e4SLinus Torvalds 	struct pktgen_thread *pg_thread;/* the owner */
24963adc6fbSStephen Hemminger 	struct list_head list;		/* chaining in the thread's run-queue */
2508788370aSJesper Dangaard Brouer 	struct rcu_head	 rcu;		/* freed by RCU */
2511da177e4SLinus Torvalds 
25263adc6fbSStephen Hemminger 	int running;		/* if false, the test will stop */
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	/* If min != max, then we will either do a linear iteration, or
2551da177e4SLinus Torvalds 	 * we will do a random selection from within the range.
2561da177e4SLinus Torvalds 	 */
2571da177e4SLinus Torvalds 	__u32 flags;
258*62f64aedSAlexei Starovoitov 	int xmit_mode;
25968bf9f0bSAmerigo Wang 	int min_pkt_size;
26068bf9f0bSAmerigo Wang 	int max_pkt_size;
26116dab72fSJamal Hadi Salim 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
2621da177e4SLinus Torvalds 	int nfrags;
263*62f64aedSAlexei Starovoitov 	int removal_mark;	/* non-zero => the device is marked for
264*62f64aedSAlexei Starovoitov 				 * removal by worker thread */
265*62f64aedSAlexei Starovoitov 
26626ad7879SEric Dumazet 	struct page *page;
267fd29cf72SStephen Hemminger 	u64 delay;		/* nano-seconds */
268fd29cf72SStephen Hemminger 
2691da177e4SLinus Torvalds 	__u64 count;		/* Default No packets to send */
2701da177e4SLinus Torvalds 	__u64 sofar;		/* How many pkts we've sent so far */
2711da177e4SLinus Torvalds 	__u64 tx_bytes;		/* How many bytes we've transmitted */
272f466dba1SJohn Fastabend 	__u64 errors;		/* Errors when trying to transmit, */
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	/* runtime counters relating to clone_skb */
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	__u64 allocated_skbs;
2771da177e4SLinus Torvalds 	__u32 clone_count;
2781da177e4SLinus Torvalds 	int last_ok;		/* Was last skb sent?
27963adc6fbSStephen Hemminger 				 * Or a failed transmit of some sort?
28063adc6fbSStephen Hemminger 				 * This will keep sequence numbers in order
2811da177e4SLinus Torvalds 				 */
282fd29cf72SStephen Hemminger 	ktime_t next_tx;
283fd29cf72SStephen Hemminger 	ktime_t started_at;
284fd29cf72SStephen Hemminger 	ktime_t stopped_at;
285fd29cf72SStephen Hemminger 	u64	idle_acc;	/* nano-seconds */
286fd29cf72SStephen Hemminger 
2871da177e4SLinus Torvalds 	__u32 seq_num;
2881da177e4SLinus Torvalds 
28963adc6fbSStephen Hemminger 	int clone_skb;		/*
29063adc6fbSStephen Hemminger 				 * Use multiple SKBs during packet gen.
29163adc6fbSStephen Hemminger 				 * If this number is greater than 1, then
29263adc6fbSStephen Hemminger 				 * that many copies of the same packet will be
29363adc6fbSStephen Hemminger 				 * sent before a new packet is allocated.
29463adc6fbSStephen Hemminger 				 * If you want to send 1024 identical packets
29563adc6fbSStephen Hemminger 				 * before creating a new packet,
29663adc6fbSStephen Hemminger 				 * set clone_skb to 1024.
2971da177e4SLinus Torvalds 				 */
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	char dst_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3001da177e4SLinus Torvalds 	char dst_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3011da177e4SLinus Torvalds 	char src_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3021da177e4SLinus Torvalds 	char src_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	struct in6_addr in6_saddr;
3051da177e4SLinus Torvalds 	struct in6_addr in6_daddr;
3061da177e4SLinus Torvalds 	struct in6_addr cur_in6_daddr;
3071da177e4SLinus Torvalds 	struct in6_addr cur_in6_saddr;
3081da177e4SLinus Torvalds 	/* For ranges */
3091da177e4SLinus Torvalds 	struct in6_addr min_in6_daddr;
3101da177e4SLinus Torvalds 	struct in6_addr max_in6_daddr;
3111da177e4SLinus Torvalds 	struct in6_addr min_in6_saddr;
3121da177e4SLinus Torvalds 	struct in6_addr max_in6_saddr;
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	/* If we're doing ranges, random or incremental, then this
3151da177e4SLinus Torvalds 	 * defines the min/max for those ranges.
3161da177e4SLinus Torvalds 	 */
317252e3346SAl Viro 	__be32 saddr_min;	/* inclusive, source IP address */
318252e3346SAl Viro 	__be32 saddr_max;	/* exclusive, source IP address */
319252e3346SAl Viro 	__be32 daddr_min;	/* inclusive, dest IP address */
320252e3346SAl Viro 	__be32 daddr_max;	/* exclusive, dest IP address */
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	__u16 udp_src_min;	/* inclusive, source UDP port */
3231da177e4SLinus Torvalds 	__u16 udp_src_max;	/* exclusive, source UDP port */
3241da177e4SLinus Torvalds 	__u16 udp_dst_min;	/* inclusive, dest UDP port */
3251da177e4SLinus Torvalds 	__u16 udp_dst_max;	/* exclusive, dest UDP port */
3261da177e4SLinus Torvalds 
3271ca7768cSFrancesco Fondelli 	/* DSCP + ECN */
32863adc6fbSStephen Hemminger 	__u8 tos;            /* six MSB of (former) IPv4 TOS
32963adc6fbSStephen Hemminger 				are for dscp codepoint */
33063adc6fbSStephen Hemminger 	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6
33163adc6fbSStephen Hemminger 				(see RFC 3260, sec. 4) */
3321ca7768cSFrancesco Fondelli 
333ca6549afSSteven Whitehouse 	/* MPLS */
33495c96174SEric Dumazet 	unsigned int nr_labels;	/* Depth of stack, 0 = no MPLS */
335ca6549afSSteven Whitehouse 	__be32 labels[MAX_MPLS_LABELS];
336ca6549afSSteven Whitehouse 
33734954ddcSFrancesco Fondelli 	/* VLAN/SVLAN (802.1Q/Q-in-Q) */
33834954ddcSFrancesco Fondelli 	__u8  vlan_p;
33934954ddcSFrancesco Fondelli 	__u8  vlan_cfi;
34034954ddcSFrancesco Fondelli 	__u16 vlan_id;  /* 0xffff means no vlan tag */
34134954ddcSFrancesco Fondelli 
34234954ddcSFrancesco Fondelli 	__u8  svlan_p;
34334954ddcSFrancesco Fondelli 	__u8  svlan_cfi;
34434954ddcSFrancesco Fondelli 	__u16 svlan_id; /* 0xffff means no svlan tag */
34534954ddcSFrancesco Fondelli 
3461da177e4SLinus Torvalds 	__u32 src_mac_count;	/* How many MACs to iterate through */
3471da177e4SLinus Torvalds 	__u32 dst_mac_count;	/* How many MACs to iterate through */
3481da177e4SLinus Torvalds 
349f404e9a6SKris Katterjohn 	unsigned char dst_mac[ETH_ALEN];
350f404e9a6SKris Katterjohn 	unsigned char src_mac[ETH_ALEN];
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	__u32 cur_dst_mac_offset;
3531da177e4SLinus Torvalds 	__u32 cur_src_mac_offset;
354252e3346SAl Viro 	__be32 cur_saddr;
355252e3346SAl Viro 	__be32 cur_daddr;
35666ed1e5eSEric Dumazet 	__u16 ip_id;
3571da177e4SLinus Torvalds 	__u16 cur_udp_dst;
3581da177e4SLinus Torvalds 	__u16 cur_udp_src;
35945b270f8SRobert Olsson 	__u16 cur_queue_map;
3601da177e4SLinus Torvalds 	__u32 cur_pkt_size;
361baac8564SEric Dumazet 	__u32 last_pkt_size;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 	__u8 hh[14];
3641da177e4SLinus Torvalds 	/* = {
3651da177e4SLinus Torvalds 	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	   We fill in SRC address later
3681da177e4SLinus Torvalds 	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3691da177e4SLinus Torvalds 	   0x08, 0x00
3701da177e4SLinus Torvalds 	   };
3711da177e4SLinus Torvalds 	 */
3721da177e4SLinus Torvalds 	__u16 pad;		/* pad out the hh struct to an even 16 bytes */
3731da177e4SLinus Torvalds 
37463adc6fbSStephen Hemminger 	struct sk_buff *skb;	/* skb we are to transmit next, used for when we
3751da177e4SLinus Torvalds 				 * are transmitting the same one multiple times
3761da177e4SLinus Torvalds 				 */
37763adc6fbSStephen Hemminger 	struct net_device *odev; /* The out-going device.
37863adc6fbSStephen Hemminger 				  * Note that the device should have it's
37963adc6fbSStephen Hemminger 				  * pg_info pointer pointing back to this
38063adc6fbSStephen Hemminger 				  * device.
38163adc6fbSStephen Hemminger 				  * Set when the user specifies the out-going
38263adc6fbSStephen Hemminger 				  * device name (not when the inject is
3831da177e4SLinus Torvalds 				  * started as it used to do.)
3841da177e4SLinus Torvalds 				  */
385593f63b0SEric Dumazet 	char odevname[32];
3861da177e4SLinus Torvalds 	struct flow_state *flows;
38795c96174SEric Dumazet 	unsigned int cflows;	/* Concurrent flows (config) */
38895c96174SEric Dumazet 	unsigned int lflow;		/* Flow length  (config) */
38995c96174SEric Dumazet 	unsigned int nflows;	/* accumulated flows (stats) */
39095c96174SEric Dumazet 	unsigned int curfl;		/* current sequenced flow (state)*/
39145b270f8SRobert Olsson 
39245b270f8SRobert Olsson 	u16 queue_map_min;
39345b270f8SRobert Olsson 	u16 queue_map_max;
3949e50e3acSJohn Fastabend 	__u32 skb_priority;	/* skb priority field */
39538b2cf29SAlexei Starovoitov 	unsigned int burst;	/* number of duplicated packets to burst */
396e99b99b4SRobert Olsson 	int node;               /* Memory node */
39745b270f8SRobert Olsson 
398a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
399a553e4a6SJamal Hadi Salim 	__u8	ipsmode;		/* IPSEC mode (config) */
400a553e4a6SJamal Hadi Salim 	__u8	ipsproto;		/* IPSEC type (config) */
401de4aee7dSFan Du 	__u32	spi;
402cf93d47eSFan Du 	struct dst_entry dst;
403cf93d47eSFan Du 	struct dst_ops dstops;
404a553e4a6SJamal Hadi Salim #endif
40539df232fSStephen Hemminger 	char result[512];
4061da177e4SLinus Torvalds };
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds struct pktgen_hdr {
409252e3346SAl Viro 	__be32 pgh_magic;
410252e3346SAl Viro 	__be32 seq_num;
411252e3346SAl Viro 	__be32 tv_sec;
412252e3346SAl Viro 	__be32 tv_usec;
4131da177e4SLinus Torvalds };
4141da177e4SLinus Torvalds 
4154e58a027SCong Wang 
4164e58a027SCong Wang static int pg_net_id __read_mostly;
4174e58a027SCong Wang 
4184e58a027SCong Wang struct pktgen_net {
4194e58a027SCong Wang 	struct net		*net;
4204e58a027SCong Wang 	struct proc_dir_entry	*proc_dir;
4214e58a027SCong Wang 	struct list_head	pktgen_threads;
4224e58a027SCong Wang 	bool			pktgen_exiting;
4234e58a027SCong Wang };
424551eaff1SEric Dumazet 
4251da177e4SLinus Torvalds struct pktgen_thread {
42663adc6fbSStephen Hemminger 	spinlock_t if_lock;		/* for list of devices */
427c26a8016SLuiz Capitulino 	struct list_head if_list;	/* All device here */
428cdcdbe0bSLuiz Capitulino 	struct list_head th_list;
429ee74baa7SDavid S. Miller 	struct task_struct *tsk;
4301da177e4SLinus Torvalds 	char result[512];
4311da177e4SLinus Torvalds 
43263adc6fbSStephen Hemminger 	/* Field for thread to receive "posted" events terminate,
43363adc6fbSStephen Hemminger 	   stop ifs etc. */
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	u32 control;
4361da177e4SLinus Torvalds 	int cpu;
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	wait_queue_head_t queue;
439d3ede327SDenis V. Lunev 	struct completion start_done;
4404e58a027SCong Wang 	struct pktgen_net *net;
4411da177e4SLinus Torvalds };
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds #define REMOVE 1
4441da177e4SLinus Torvalds #define FIND   0
4451da177e4SLinus Torvalds 
446c3d2f52dSStephen Hemminger static const char version[] =
447f9467eaeSJoe Perches 	"Packet Generator for packet performance testing. "
448f9467eaeSJoe Perches 	"Version: " VERSION "\n";
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
4511da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
452222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
4533e984840SEric Dumazet 					  const char *ifname, bool exact);
4541da177e4SLinus Torvalds static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
4554e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn);
4564e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn);
4574e58a027SCong Wang static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn);
4583bda06a3SStephen Hemminger 
4591da177e4SLinus Torvalds static void pktgen_stop(struct pktgen_thread *t);
4601da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
46139df232fSStephen Hemminger 
4621da177e4SLinus Torvalds /* Module parameters, defaults. */
46365c5b786SStephen Hemminger static int pg_count_d __read_mostly = 1000;
46465c5b786SStephen Hemminger static int pg_delay_d __read_mostly;
46565c5b786SStephen Hemminger static int pg_clone_skb_d  __read_mostly;
46665c5b786SStephen Hemminger static int debug  __read_mostly;
4671da177e4SLinus Torvalds 
468222fa076SLuiz Capitulino static DEFINE_MUTEX(pktgen_thread_lock);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds static struct notifier_block pktgen_notifier_block = {
4711da177e4SLinus Torvalds 	.notifier_call = pktgen_device_event,
4721da177e4SLinus Torvalds };
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds /*
4751da177e4SLinus Torvalds  * /proc handling functions
4761da177e4SLinus Torvalds  *
4771da177e4SLinus Torvalds  */
4781da177e4SLinus Torvalds 
479d50a6b56SStephen Hemminger static int pgctrl_show(struct seq_file *seq, void *v)
480d50a6b56SStephen Hemminger {
481c3d2f52dSStephen Hemminger 	seq_puts(seq, version);
482d50a6b56SStephen Hemminger 	return 0;
483d50a6b56SStephen Hemminger }
4841da177e4SLinus Torvalds 
485d50a6b56SStephen Hemminger static ssize_t pgctrl_write(struct file *file, const char __user *buf,
4861da177e4SLinus Torvalds 			    size_t count, loff_t *ppos)
4871da177e4SLinus Torvalds {
488d50a6b56SStephen Hemminger 	char data[128];
4894e58a027SCong Wang 	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
4901da177e4SLinus Torvalds 
49109455747SMathias Krause 	if (!capable(CAP_NET_ADMIN))
49209455747SMathias Krause 		return -EPERM;
4931da177e4SLinus Torvalds 
49420b0c718SMathias Krause 	if (count == 0)
49520b0c718SMathias Krause 		return -EINVAL;
49620b0c718SMathias Krause 
497d50a6b56SStephen Hemminger 	if (count > sizeof(data))
498d50a6b56SStephen Hemminger 		count = sizeof(data);
4991da177e4SLinus Torvalds 
50009455747SMathias Krause 	if (copy_from_user(data, buf, count))
50109455747SMathias Krause 		return -EFAULT;
50209455747SMathias Krause 
50320b0c718SMathias Krause 	data[count - 1] = 0;	/* Strip trailing '\n' and terminate string */
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	if (!strcmp(data, "stop"))
5064e58a027SCong Wang 		pktgen_stop_all_threads_ifs(pn);
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	else if (!strcmp(data, "start"))
5094e58a027SCong Wang 		pktgen_run_all_threads(pn);
5101da177e4SLinus Torvalds 
511eb37b41cSJesse Brandeburg 	else if (!strcmp(data, "reset"))
5124e58a027SCong Wang 		pktgen_reset_all_threads(pn);
513eb37b41cSJesse Brandeburg 
5141da177e4SLinus Torvalds 	else
515294a0b7fSJoe Perches 		pr_warn("Unknown command: %s\n", data);
5161da177e4SLinus Torvalds 
51709455747SMathias Krause 	return count;
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds 
520d50a6b56SStephen Hemminger static int pgctrl_open(struct inode *inode, struct file *file)
5211da177e4SLinus Torvalds {
522d9dda78bSAl Viro 	return single_open(file, pgctrl_show, PDE_DATA(inode));
523d50a6b56SStephen Hemminger }
524d50a6b56SStephen Hemminger 
5259a32144eSArjan van de Ven static const struct file_operations pktgen_fops = {
526d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
527d50a6b56SStephen Hemminger 	.open    = pgctrl_open,
528d50a6b56SStephen Hemminger 	.read    = seq_read,
529d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
530d50a6b56SStephen Hemminger 	.write   = pgctrl_write,
531d50a6b56SStephen Hemminger 	.release = single_release,
532d50a6b56SStephen Hemminger };
533d50a6b56SStephen Hemminger 
534d50a6b56SStephen Hemminger static int pktgen_if_show(struct seq_file *seq, void *v)
535d50a6b56SStephen Hemminger {
536648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev = seq->private;
537fd29cf72SStephen Hemminger 	ktime_t stopped;
538fd29cf72SStephen Hemminger 	u64 idle;
5391da177e4SLinus Torvalds 
540222f1806SLuiz Capitulino 	seq_printf(seq,
541222f1806SLuiz Capitulino 		   "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n",
542222f1806SLuiz Capitulino 		   (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
543222f1806SLuiz Capitulino 		   pkt_dev->max_pkt_size);
5441da177e4SLinus Torvalds 
545222f1806SLuiz Capitulino 	seq_printf(seq,
546fd29cf72SStephen Hemminger 		   "     frags: %d  delay: %llu  clone_skb: %d  ifname: %s\n",
547fd29cf72SStephen Hemminger 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
548593f63b0SEric Dumazet 		   pkt_dev->clone_skb, pkt_dev->odevname);
5491da177e4SLinus Torvalds 
550222f1806SLuiz Capitulino 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
551222f1806SLuiz Capitulino 		   pkt_dev->lflow);
5521da177e4SLinus Torvalds 
55345b270f8SRobert Olsson 	seq_printf(seq,
55445b270f8SRobert Olsson 		   "     queue_map_min: %u  queue_map_max: %u\n",
55545b270f8SRobert Olsson 		   pkt_dev->queue_map_min,
55645b270f8SRobert Olsson 		   pkt_dev->queue_map_max);
55745b270f8SRobert Olsson 
5589e50e3acSJohn Fastabend 	if (pkt_dev->skb_priority)
5599e50e3acSJohn Fastabend 		seq_printf(seq, "     skb_priority: %u\n",
5609e50e3acSJohn Fastabend 			   pkt_dev->skb_priority);
5619e50e3acSJohn Fastabend 
5621da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
563222f1806SLuiz Capitulino 		seq_printf(seq,
56447a0200dSAlexey Dobriyan 			   "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n"
56547a0200dSAlexey Dobriyan 			   "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n",
56647a0200dSAlexey Dobriyan 			   &pkt_dev->in6_saddr,
56747a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr,
56847a0200dSAlexey Dobriyan 			   &pkt_dev->in6_daddr,
56947a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);
57063adc6fbSStephen Hemminger 	} else {
571222f1806SLuiz Capitulino 		seq_printf(seq,
57263adc6fbSStephen Hemminger 			   "     dst_min: %s  dst_max: %s\n",
57363adc6fbSStephen Hemminger 			   pkt_dev->dst_min, pkt_dev->dst_max);
57463adc6fbSStephen Hemminger 		seq_printf(seq,
57563adc6fbSStephen Hemminger 			   "        src_min: %s  src_max: %s\n",
57663adc6fbSStephen Hemminger 			   pkt_dev->src_min, pkt_dev->src_max);
57763adc6fbSStephen Hemminger 	}
5781da177e4SLinus Torvalds 
579d50a6b56SStephen Hemminger 	seq_puts(seq, "     src_mac: ");
5801da177e4SLinus Torvalds 
581e174961cSJohannes Berg 	seq_printf(seq, "%pM ",
582e174961cSJohannes Berg 		   is_zero_ether_addr(pkt_dev->src_mac) ?
583e174961cSJohannes Berg 			     pkt_dev->odev->dev_addr : pkt_dev->src_mac);
5841da177e4SLinus Torvalds 
58597dc48e2SThomas Graf 	seq_puts(seq, "dst_mac: ");
586e174961cSJohannes Berg 	seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
5871da177e4SLinus Torvalds 
588222f1806SLuiz Capitulino 	seq_printf(seq,
58963adc6fbSStephen Hemminger 		   "     udp_src_min: %d  udp_src_max: %d"
59063adc6fbSStephen Hemminger 		   "  udp_dst_min: %d  udp_dst_max: %d\n",
591222f1806SLuiz Capitulino 		   pkt_dev->udp_src_min, pkt_dev->udp_src_max,
592222f1806SLuiz Capitulino 		   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
5931da177e4SLinus Torvalds 
594222f1806SLuiz Capitulino 	seq_printf(seq,
595ca6549afSSteven Whitehouse 		   "     src_mac_count: %d  dst_mac_count: %d\n",
5961da177e4SLinus Torvalds 		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
5971da177e4SLinus Torvalds 
598ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels) {
59995c96174SEric Dumazet 		unsigned int i;
60097dc48e2SThomas Graf 		seq_puts(seq, "     mpls: ");
601ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
602ca6549afSSteven Whitehouse 			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
603ca6549afSSteven Whitehouse 				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
604ca6549afSSteven Whitehouse 	}
605ca6549afSSteven Whitehouse 
60663adc6fbSStephen Hemminger 	if (pkt_dev->vlan_id != 0xffff)
60734954ddcSFrancesco Fondelli 		seq_printf(seq, "     vlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
60863adc6fbSStephen Hemminger 			   pkt_dev->vlan_id, pkt_dev->vlan_p,
60963adc6fbSStephen Hemminger 			   pkt_dev->vlan_cfi);
61034954ddcSFrancesco Fondelli 
61163adc6fbSStephen Hemminger 	if (pkt_dev->svlan_id != 0xffff)
61234954ddcSFrancesco Fondelli 		seq_printf(seq, "     svlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
61363adc6fbSStephen Hemminger 			   pkt_dev->svlan_id, pkt_dev->svlan_p,
61463adc6fbSStephen Hemminger 			   pkt_dev->svlan_cfi);
61534954ddcSFrancesco Fondelli 
61663adc6fbSStephen Hemminger 	if (pkt_dev->tos)
6171ca7768cSFrancesco Fondelli 		seq_printf(seq, "     tos: 0x%02x\n", pkt_dev->tos);
6181ca7768cSFrancesco Fondelli 
61963adc6fbSStephen Hemminger 	if (pkt_dev->traffic_class)
6201ca7768cSFrancesco Fondelli 		seq_printf(seq, "     traffic_class: 0x%02x\n", pkt_dev->traffic_class);
6211ca7768cSFrancesco Fondelli 
62238b2cf29SAlexei Starovoitov 	if (pkt_dev->burst > 1)
62338b2cf29SAlexei Starovoitov 		seq_printf(seq, "     burst: %d\n", pkt_dev->burst);
62438b2cf29SAlexei Starovoitov 
625e99b99b4SRobert Olsson 	if (pkt_dev->node >= 0)
626e99b99b4SRobert Olsson 		seq_printf(seq, "     node: %d\n", pkt_dev->node);
627e99b99b4SRobert Olsson 
628*62f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE)
629*62f64aedSAlexei Starovoitov 		seq_puts(seq, "     xmit_mode: netif_receive\n");
630*62f64aedSAlexei Starovoitov 
63197dc48e2SThomas Graf 	seq_puts(seq, "     Flags: ");
632ca6549afSSteven Whitehouse 
6331da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
63497dc48e2SThomas Graf 		seq_puts(seq, "IPV6  ");
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPSRC_RND)
63797dc48e2SThomas Graf 		seq_puts(seq, "IPSRC_RND  ");
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPDST_RND)
64097dc48e2SThomas Graf 		seq_puts(seq, "IPDST_RND  ");
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 	if (pkt_dev->flags & F_TXSIZE_RND)
64397dc48e2SThomas Graf 		seq_puts(seq, "TXSIZE_RND  ");
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPSRC_RND)
64697dc48e2SThomas Graf 		seq_puts(seq, "UDPSRC_RND  ");
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	if (pkt_dev->flags & F_UDPDST_RND)
64997dc48e2SThomas Graf 		seq_puts(seq, "UDPDST_RND  ");
6501da177e4SLinus Torvalds 
651c26bf4a5SThomas Graf 	if (pkt_dev->flags & F_UDPCSUM)
65297dc48e2SThomas Graf 		seq_puts(seq, "UDPCSUM  ");
653c26bf4a5SThomas Graf 
654afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP)
655afb84b62SJesper Dangaard Brouer 		seq_puts(seq, "NO_TIMESTAMP  ");
656afb84b62SJesper Dangaard Brouer 
657ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND)
65897dc48e2SThomas Graf 		seq_puts(seq,  "MPLS_RND  ");
659ca6549afSSteven Whitehouse 
66045b270f8SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_RND)
66197dc48e2SThomas Graf 		seq_puts(seq,  "QUEUE_MAP_RND  ");
66245b270f8SRobert Olsson 
663e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
66497dc48e2SThomas Graf 		seq_puts(seq,  "QUEUE_MAP_CPU  ");
665e6fce5b9SRobert Olsson 
666007a531bSJamal Hadi Salim 	if (pkt_dev->cflows) {
667007a531bSJamal Hadi Salim 		if (pkt_dev->flags & F_FLOW_SEQ)
66897dc48e2SThomas Graf 			seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/
669007a531bSJamal Hadi Salim 		else
67097dc48e2SThomas Graf 			seq_puts(seq,  "FLOW_RND  ");
671007a531bSJamal Hadi Salim 	}
672007a531bSJamal Hadi Salim 
673a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
6748101328bSFan Du 	if (pkt_dev->flags & F_IPSEC_ON) {
67597dc48e2SThomas Graf 		seq_puts(seq,  "IPSEC  ");
6768101328bSFan Du 		if (pkt_dev->spi)
6778101328bSFan Du 			seq_printf(seq, "spi:%u", pkt_dev->spi);
6788101328bSFan Du 	}
679a553e4a6SJamal Hadi Salim #endif
680a553e4a6SJamal Hadi Salim 
6811da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACSRC_RND)
68297dc48e2SThomas Graf 		seq_puts(seq, "MACSRC_RND  ");
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	if (pkt_dev->flags & F_MACDST_RND)
68597dc48e2SThomas Graf 		seq_puts(seq, "MACDST_RND  ");
6861da177e4SLinus Torvalds 
68734954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_VID_RND)
68897dc48e2SThomas Graf 		seq_puts(seq, "VID_RND  ");
68934954ddcSFrancesco Fondelli 
69034954ddcSFrancesco Fondelli 	if (pkt_dev->flags & F_SVID_RND)
69197dc48e2SThomas Graf 		seq_puts(seq, "SVID_RND  ");
69234954ddcSFrancesco Fondelli 
693e99b99b4SRobert Olsson 	if (pkt_dev->flags & F_NODE)
69497dc48e2SThomas Graf 		seq_puts(seq, "NODE_ALLOC  ");
695e99b99b4SRobert Olsson 
696d50a6b56SStephen Hemminger 	seq_puts(seq, "\n");
6971da177e4SLinus Torvalds 
698fd29cf72SStephen Hemminger 	/* not really stopped, more like last-running-at */
699398f382cSDaniel Borkmann 	stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
700fd29cf72SStephen Hemminger 	idle = pkt_dev->idle_acc;
701fd29cf72SStephen Hemminger 	do_div(idle, NSEC_PER_USEC);
7021da177e4SLinus Torvalds 
703222f1806SLuiz Capitulino 	seq_printf(seq,
704fd29cf72SStephen Hemminger 		   "Current:\n     pkts-sofar: %llu  errors: %llu\n",
7051da177e4SLinus Torvalds 		   (unsigned long long)pkt_dev->sofar,
706fd29cf72SStephen Hemminger 		   (unsigned long long)pkt_dev->errors);
707fd29cf72SStephen Hemminger 
708fd29cf72SStephen Hemminger 	seq_printf(seq,
709fd29cf72SStephen Hemminger 		   "     started: %lluus  stopped: %lluus idle: %lluus\n",
710fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(pkt_dev->started_at),
711fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(stopped),
712fd29cf72SStephen Hemminger 		   (unsigned long long) idle);
7131da177e4SLinus Torvalds 
714222f1806SLuiz Capitulino 	seq_printf(seq,
715222f1806SLuiz Capitulino 		   "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
716d50a6b56SStephen Hemminger 		   pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
717d50a6b56SStephen Hemminger 		   pkt_dev->cur_src_mac_offset);
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
72047a0200dSAlexey Dobriyan 		seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n",
72147a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_saddr,
72247a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_daddr);
723222f1806SLuiz Capitulino 	} else
7240373a946SAmerigo Wang 		seq_printf(seq, "     cur_saddr: %pI4  cur_daddr: %pI4\n",
7250373a946SAmerigo Wang 			   &pkt_dev->cur_saddr, &pkt_dev->cur_daddr);
7261da177e4SLinus Torvalds 
727d50a6b56SStephen Hemminger 	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",
7281da177e4SLinus Torvalds 		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
7291da177e4SLinus Torvalds 
73045b270f8SRobert Olsson 	seq_printf(seq, "     cur_queue_map: %u\n", pkt_dev->cur_queue_map);
73145b270f8SRobert Olsson 
732d50a6b56SStephen Hemminger 	seq_printf(seq, "     flows: %u\n", pkt_dev->nflows);
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	if (pkt_dev->result[0])
735d50a6b56SStephen Hemminger 		seq_printf(seq, "Result: %s\n", pkt_dev->result);
7361da177e4SLinus Torvalds 	else
73797dc48e2SThomas Graf 		seq_puts(seq, "Result: Idle\n");
7381da177e4SLinus Torvalds 
739d50a6b56SStephen Hemminger 	return 0;
7401da177e4SLinus Torvalds }
7411da177e4SLinus Torvalds 
742ca6549afSSteven Whitehouse 
74363adc6fbSStephen Hemminger static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
74463adc6fbSStephen Hemminger 		     __u32 *num)
745ca6549afSSteven Whitehouse {
746ca6549afSSteven Whitehouse 	int i = 0;
747ca6549afSSteven Whitehouse 	*num = 0;
748ca6549afSSteven Whitehouse 
7491ca7768cSFrancesco Fondelli 	for (; i < maxlen; i++) {
75082fd5b5dSAndy Shevchenko 		int value;
751ca6549afSSteven Whitehouse 		char c;
752ca6549afSSteven Whitehouse 		*num <<= 4;
753ca6549afSSteven Whitehouse 		if (get_user(c, &user_buffer[i]))
754ca6549afSSteven Whitehouse 			return -EFAULT;
75582fd5b5dSAndy Shevchenko 		value = hex_to_bin(c);
75682fd5b5dSAndy Shevchenko 		if (value >= 0)
75782fd5b5dSAndy Shevchenko 			*num |= value;
758ca6549afSSteven Whitehouse 		else
759ca6549afSSteven Whitehouse 			break;
760ca6549afSSteven Whitehouse 	}
761ca6549afSSteven Whitehouse 	return i;
762ca6549afSSteven Whitehouse }
763ca6549afSSteven Whitehouse 
764222f1806SLuiz Capitulino static int count_trail_chars(const char __user * user_buffer,
765222f1806SLuiz Capitulino 			     unsigned int maxlen)
7661da177e4SLinus Torvalds {
7671da177e4SLinus Torvalds 	int i;
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	for (i = 0; i < maxlen; i++) {
7701da177e4SLinus Torvalds 		char c;
7711da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
7721da177e4SLinus Torvalds 			return -EFAULT;
7731da177e4SLinus Torvalds 		switch (c) {
7741da177e4SLinus Torvalds 		case '\"':
7751da177e4SLinus Torvalds 		case '\n':
7761da177e4SLinus Torvalds 		case '\r':
7771da177e4SLinus Torvalds 		case '\t':
7781da177e4SLinus Torvalds 		case ' ':
7791da177e4SLinus Torvalds 		case '=':
7801da177e4SLinus Torvalds 			break;
7811da177e4SLinus Torvalds 		default:
7821da177e4SLinus Torvalds 			goto done;
7833ff50b79SStephen Hemminger 		}
7841da177e4SLinus Torvalds 	}
7851da177e4SLinus Torvalds done:
7861da177e4SLinus Torvalds 	return i;
7871da177e4SLinus Torvalds }
7881da177e4SLinus Torvalds 
789bf0813bdSPaul Gortmaker static long num_arg(const char __user *user_buffer, unsigned long maxlen,
790bf0813bdSPaul Gortmaker 				unsigned long *num)
7911da177e4SLinus Torvalds {
792d6182223SPaul Gortmaker 	int i;
7931da177e4SLinus Torvalds 	*num = 0;
7941da177e4SLinus Torvalds 
795d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
7961da177e4SLinus Torvalds 		char c;
7971da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
7981da177e4SLinus Torvalds 			return -EFAULT;
7991da177e4SLinus Torvalds 		if ((c >= '0') && (c <= '9')) {
8001da177e4SLinus Torvalds 			*num *= 10;
8011da177e4SLinus Torvalds 			*num += c - '0';
8021da177e4SLinus Torvalds 		} else
8031da177e4SLinus Torvalds 			break;
8041da177e4SLinus Torvalds 	}
8051da177e4SLinus Torvalds 	return i;
8061da177e4SLinus Torvalds }
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds static int strn_len(const char __user * user_buffer, unsigned int maxlen)
8091da177e4SLinus Torvalds {
810d6182223SPaul Gortmaker 	int i;
8111da177e4SLinus Torvalds 
812d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
8131da177e4SLinus Torvalds 		char c;
8141da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8151da177e4SLinus Torvalds 			return -EFAULT;
8161da177e4SLinus Torvalds 		switch (c) {
8171da177e4SLinus Torvalds 		case '\"':
8181da177e4SLinus Torvalds 		case '\n':
8191da177e4SLinus Torvalds 		case '\r':
8201da177e4SLinus Torvalds 		case '\t':
8211da177e4SLinus Torvalds 		case ' ':
8221da177e4SLinus Torvalds 			goto done_str;
8231da177e4SLinus Torvalds 		default:
8241da177e4SLinus Torvalds 			break;
8253ff50b79SStephen Hemminger 		}
8261da177e4SLinus Torvalds 	}
8271da177e4SLinus Torvalds done_str:
8281da177e4SLinus Torvalds 	return i;
8291da177e4SLinus Torvalds }
8301da177e4SLinus Torvalds 
831ca6549afSSteven Whitehouse static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
832ca6549afSSteven Whitehouse {
83395c96174SEric Dumazet 	unsigned int n = 0;
834ca6549afSSteven Whitehouse 	char c;
835ca6549afSSteven Whitehouse 	ssize_t i = 0;
836ca6549afSSteven Whitehouse 	int len;
837ca6549afSSteven Whitehouse 
838ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = 0;
839ca6549afSSteven Whitehouse 	do {
840ca6549afSSteven Whitehouse 		__u32 tmp;
8411ca7768cSFrancesco Fondelli 		len = hex32_arg(&buffer[i], 8, &tmp);
842ca6549afSSteven Whitehouse 		if (len <= 0)
843ca6549afSSteven Whitehouse 			return len;
844ca6549afSSteven Whitehouse 		pkt_dev->labels[n] = htonl(tmp);
845ca6549afSSteven Whitehouse 		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
846ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
847ca6549afSSteven Whitehouse 		i += len;
848ca6549afSSteven Whitehouse 		if (get_user(c, &buffer[i]))
849ca6549afSSteven Whitehouse 			return -EFAULT;
850ca6549afSSteven Whitehouse 		i++;
851ca6549afSSteven Whitehouse 		n++;
852ca6549afSSteven Whitehouse 		if (n >= MAX_MPLS_LABELS)
853ca6549afSSteven Whitehouse 			return -E2BIG;
854ca6549afSSteven Whitehouse 	} while (c == ',');
855ca6549afSSteven Whitehouse 
856ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = n;
857ca6549afSSteven Whitehouse 	return i;
858ca6549afSSteven Whitehouse }
859ca6549afSSteven Whitehouse 
860222f1806SLuiz Capitulino static ssize_t pktgen_if_write(struct file *file,
861222f1806SLuiz Capitulino 			       const char __user * user_buffer, size_t count,
862222f1806SLuiz Capitulino 			       loff_t * offset)
8631da177e4SLinus Torvalds {
8648a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
865d50a6b56SStephen Hemminger 	struct pktgen_dev *pkt_dev = seq->private;
866d6182223SPaul Gortmaker 	int i, max, len;
8671da177e4SLinus Torvalds 	char name[16], valstr[32];
8681da177e4SLinus Torvalds 	unsigned long value = 0;
8691da177e4SLinus Torvalds 	char *pg_result = NULL;
8701da177e4SLinus Torvalds 	int tmp = 0;
8711da177e4SLinus Torvalds 	char buf[128];
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	pg_result = &(pkt_dev->result[0]);
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	if (count < 1) {
876294a0b7fSJoe Perches 		pr_warn("wrong command format\n");
8771da177e4SLinus Torvalds 		return -EINVAL;
8781da177e4SLinus Torvalds 	}
8791da177e4SLinus Torvalds 
880d6182223SPaul Gortmaker 	max = count;
881d6182223SPaul Gortmaker 	tmp = count_trail_chars(user_buffer, max);
8821da177e4SLinus Torvalds 	if (tmp < 0) {
883294a0b7fSJoe Perches 		pr_warn("illegal format\n");
8841da177e4SLinus Torvalds 		return tmp;
8851da177e4SLinus Torvalds 	}
886d6182223SPaul Gortmaker 	i = tmp;
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	/* Read variable name */
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
89163adc6fbSStephen Hemminger 	if (len < 0)
892222f1806SLuiz Capitulino 		return len;
89363adc6fbSStephen Hemminger 
8941da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
8951da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
8961da177e4SLinus Torvalds 		return -EFAULT;
8971da177e4SLinus Torvalds 	i += len;
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	max = count - i;
9001da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
9011da177e4SLinus Torvalds 	if (len < 0)
9021da177e4SLinus Torvalds 		return len;
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	i += len;
9051da177e4SLinus Torvalds 
9061da177e4SLinus Torvalds 	if (debug) {
90786c2c0a8SDmitry Torokhov 		size_t copy = min_t(size_t, count, 1023);
908448d7b5dSNelson Elhage 		char tb[copy + 1];
909448d7b5dSNelson Elhage 		if (copy_from_user(tb, user_buffer, copy))
9101da177e4SLinus Torvalds 			return -EFAULT;
911448d7b5dSNelson Elhage 		tb[copy] = 0;
912f342cda7SJoe Perches 		pr_debug("%s,%lu  buffer -:%s:-\n",
913f342cda7SJoe Perches 			 name, (unsigned long)count, tb);
9141da177e4SLinus Torvalds 	}
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	if (!strcmp(name, "min_pkt_size")) {
9171da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
91863adc6fbSStephen Hemminger 		if (len < 0)
919222f1806SLuiz Capitulino 			return len;
92063adc6fbSStephen Hemminger 
9211da177e4SLinus Torvalds 		i += len;
9221da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9231da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9241da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9251da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9261da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9271da177e4SLinus Torvalds 		}
928222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: min_pkt_size=%u",
929222f1806SLuiz Capitulino 			pkt_dev->min_pkt_size);
9301da177e4SLinus Torvalds 		return count;
9311da177e4SLinus Torvalds 	}
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 	if (!strcmp(name, "max_pkt_size")) {
9341da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
93563adc6fbSStephen Hemminger 		if (len < 0)
936222f1806SLuiz Capitulino 			return len;
93763adc6fbSStephen Hemminger 
9381da177e4SLinus Torvalds 		i += len;
9391da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9401da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9411da177e4SLinus Torvalds 		if (value != pkt_dev->max_pkt_size) {
9421da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9431da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9441da177e4SLinus Torvalds 		}
945222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: max_pkt_size=%u",
946222f1806SLuiz Capitulino 			pkt_dev->max_pkt_size);
9471da177e4SLinus Torvalds 		return count;
9481da177e4SLinus Torvalds 	}
9491da177e4SLinus Torvalds 
9501da177e4SLinus Torvalds 	/* Shortcut for min = max */
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	if (!strcmp(name, "pkt_size")) {
9531da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
95463adc6fbSStephen Hemminger 		if (len < 0)
955222f1806SLuiz Capitulino 			return len;
95663adc6fbSStephen Hemminger 
9571da177e4SLinus Torvalds 		i += len;
9581da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
9591da177e4SLinus Torvalds 			value = 14 + 20 + 8;
9601da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
9611da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
9621da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
9631da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
9641da177e4SLinus Torvalds 		}
9651da177e4SLinus Torvalds 		sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
9661da177e4SLinus Torvalds 		return count;
9671da177e4SLinus Torvalds 	}
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 	if (!strcmp(name, "debug")) {
9701da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
97163adc6fbSStephen Hemminger 		if (len < 0)
972222f1806SLuiz Capitulino 			return len;
97363adc6fbSStephen Hemminger 
9741da177e4SLinus Torvalds 		i += len;
9751da177e4SLinus Torvalds 		debug = value;
9761da177e4SLinus Torvalds 		sprintf(pg_result, "OK: debug=%u", debug);
9771da177e4SLinus Torvalds 		return count;
9781da177e4SLinus Torvalds 	}
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	if (!strcmp(name, "frags")) {
9811da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
98263adc6fbSStephen Hemminger 		if (len < 0)
983222f1806SLuiz Capitulino 			return len;
98463adc6fbSStephen Hemminger 
9851da177e4SLinus Torvalds 		i += len;
9861da177e4SLinus Torvalds 		pkt_dev->nfrags = value;
9871da177e4SLinus Torvalds 		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
9881da177e4SLinus Torvalds 		return count;
9891da177e4SLinus Torvalds 	}
9901da177e4SLinus Torvalds 	if (!strcmp(name, "delay")) {
9911da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
99263adc6fbSStephen Hemminger 		if (len < 0)
993222f1806SLuiz Capitulino 			return len;
99463adc6fbSStephen Hemminger 
9951da177e4SLinus Torvalds 		i += len;
996fd29cf72SStephen Hemminger 		if (value == 0x7FFFFFFF)
997fd29cf72SStephen Hemminger 			pkt_dev->delay = ULLONG_MAX;
998fd29cf72SStephen Hemminger 		else
9999240d715SEric Dumazet 			pkt_dev->delay = (u64)value;
1000fd29cf72SStephen Hemminger 
1001fd29cf72SStephen Hemminger 		sprintf(pg_result, "OK: delay=%llu",
1002fd29cf72SStephen Hemminger 			(unsigned long long) pkt_dev->delay);
10031da177e4SLinus Torvalds 		return count;
10041da177e4SLinus Torvalds 	}
100543d28b65SDaniel Turull 	if (!strcmp(name, "rate")) {
100643d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
100743d28b65SDaniel Turull 		if (len < 0)
100843d28b65SDaniel Turull 			return len;
100943d28b65SDaniel Turull 
101043d28b65SDaniel Turull 		i += len;
101143d28b65SDaniel Turull 		if (!value)
101243d28b65SDaniel Turull 			return len;
101343d28b65SDaniel Turull 		pkt_dev->delay = pkt_dev->min_pkt_size*8*NSEC_PER_USEC/value;
101443d28b65SDaniel Turull 		if (debug)
1015f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
101643d28b65SDaniel Turull 
101743d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
101843d28b65SDaniel Turull 		return count;
101943d28b65SDaniel Turull 	}
102043d28b65SDaniel Turull 	if (!strcmp(name, "ratep")) {
102143d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
102243d28b65SDaniel Turull 		if (len < 0)
102343d28b65SDaniel Turull 			return len;
102443d28b65SDaniel Turull 
102543d28b65SDaniel Turull 		i += len;
102643d28b65SDaniel Turull 		if (!value)
102743d28b65SDaniel Turull 			return len;
102843d28b65SDaniel Turull 		pkt_dev->delay = NSEC_PER_SEC/value;
102943d28b65SDaniel Turull 		if (debug)
1030f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
103143d28b65SDaniel Turull 
103243d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
103343d28b65SDaniel Turull 		return count;
103443d28b65SDaniel Turull 	}
10351da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_min")) {
10361da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
103763adc6fbSStephen Hemminger 		if (len < 0)
1038222f1806SLuiz Capitulino 			return len;
103963adc6fbSStephen Hemminger 
10401da177e4SLinus Torvalds 		i += len;
10411da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_min) {
10421da177e4SLinus Torvalds 			pkt_dev->udp_src_min = value;
10431da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10441da177e4SLinus Torvalds 		}
10451da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
10461da177e4SLinus Torvalds 		return count;
10471da177e4SLinus Torvalds 	}
10481da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_min")) {
10491da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
105063adc6fbSStephen Hemminger 		if (len < 0)
1051222f1806SLuiz Capitulino 			return len;
105263adc6fbSStephen Hemminger 
10531da177e4SLinus Torvalds 		i += len;
10541da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_min) {
10551da177e4SLinus Torvalds 			pkt_dev->udp_dst_min = value;
10561da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10571da177e4SLinus Torvalds 		}
10581da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
10591da177e4SLinus Torvalds 		return count;
10601da177e4SLinus Torvalds 	}
10611da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_max")) {
10621da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
106363adc6fbSStephen Hemminger 		if (len < 0)
1064222f1806SLuiz Capitulino 			return len;
106563adc6fbSStephen Hemminger 
10661da177e4SLinus Torvalds 		i += len;
10671da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_max) {
10681da177e4SLinus Torvalds 			pkt_dev->udp_src_max = value;
10691da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
10701da177e4SLinus Torvalds 		}
10711da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
10721da177e4SLinus Torvalds 		return count;
10731da177e4SLinus Torvalds 	}
10741da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_max")) {
10751da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
107663adc6fbSStephen Hemminger 		if (len < 0)
1077222f1806SLuiz Capitulino 			return len;
107863adc6fbSStephen Hemminger 
10791da177e4SLinus Torvalds 		i += len;
10801da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_max) {
10811da177e4SLinus Torvalds 			pkt_dev->udp_dst_max = value;
10821da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
10831da177e4SLinus Torvalds 		}
10841da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
10851da177e4SLinus Torvalds 		return count;
10861da177e4SLinus Torvalds 	}
10871da177e4SLinus Torvalds 	if (!strcmp(name, "clone_skb")) {
10881da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
108963adc6fbSStephen Hemminger 		if (len < 0)
1090222f1806SLuiz Capitulino 			return len;
1091d8873315SNeil Horman 		if ((value > 0) &&
1092*62f64aedSAlexei Starovoitov 		    ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
1093*62f64aedSAlexei Starovoitov 		     !(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
1094d8873315SNeil Horman 			return -ENOTSUPP;
10951da177e4SLinus Torvalds 		i += len;
10961da177e4SLinus Torvalds 		pkt_dev->clone_skb = value;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 		sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
10991da177e4SLinus Torvalds 		return count;
11001da177e4SLinus Torvalds 	}
11011da177e4SLinus Torvalds 	if (!strcmp(name, "count")) {
11021da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
110363adc6fbSStephen Hemminger 		if (len < 0)
1104222f1806SLuiz Capitulino 			return len;
110563adc6fbSStephen Hemminger 
11061da177e4SLinus Torvalds 		i += len;
11071da177e4SLinus Torvalds 		pkt_dev->count = value;
11081da177e4SLinus Torvalds 		sprintf(pg_result, "OK: count=%llu",
11091da177e4SLinus Torvalds 			(unsigned long long)pkt_dev->count);
11101da177e4SLinus Torvalds 		return count;
11111da177e4SLinus Torvalds 	}
11121da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac_count")) {
11131da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
111463adc6fbSStephen Hemminger 		if (len < 0)
1115222f1806SLuiz Capitulino 			return len;
111663adc6fbSStephen Hemminger 
11171da177e4SLinus Torvalds 		i += len;
11181da177e4SLinus Torvalds 		if (pkt_dev->src_mac_count != value) {
11191da177e4SLinus Torvalds 			pkt_dev->src_mac_count = value;
11201da177e4SLinus Torvalds 			pkt_dev->cur_src_mac_offset = 0;
11211da177e4SLinus Torvalds 		}
1122222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: src_mac_count=%d",
1123222f1806SLuiz Capitulino 			pkt_dev->src_mac_count);
11241da177e4SLinus Torvalds 		return count;
11251da177e4SLinus Torvalds 	}
11261da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac_count")) {
11271da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
112863adc6fbSStephen Hemminger 		if (len < 0)
1129222f1806SLuiz Capitulino 			return len;
113063adc6fbSStephen Hemminger 
11311da177e4SLinus Torvalds 		i += len;
11321da177e4SLinus Torvalds 		if (pkt_dev->dst_mac_count != value) {
11331da177e4SLinus Torvalds 			pkt_dev->dst_mac_count = value;
11341da177e4SLinus Torvalds 			pkt_dev->cur_dst_mac_offset = 0;
11351da177e4SLinus Torvalds 		}
1136222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: dst_mac_count=%d",
1137222f1806SLuiz Capitulino 			pkt_dev->dst_mac_count);
11381da177e4SLinus Torvalds 		return count;
11391da177e4SLinus Torvalds 	}
114038b2cf29SAlexei Starovoitov 	if (!strcmp(name, "burst")) {
114138b2cf29SAlexei Starovoitov 		len = num_arg(&user_buffer[i], 10, &value);
114238b2cf29SAlexei Starovoitov 		if (len < 0)
114338b2cf29SAlexei Starovoitov 			return len;
114438b2cf29SAlexei Starovoitov 
114538b2cf29SAlexei Starovoitov 		i += len;
1146*62f64aedSAlexei Starovoitov 		if ((value > 1) && (pkt_dev->xmit_mode == M_START_XMIT) &&
114752d6c8c6SEric Dumazet 		    (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
114852d6c8c6SEric Dumazet 			return -ENOTSUPP;
114938b2cf29SAlexei Starovoitov 		pkt_dev->burst = value < 1 ? 1 : value;
115038b2cf29SAlexei Starovoitov 		sprintf(pg_result, "OK: burst=%d", pkt_dev->burst);
115138b2cf29SAlexei Starovoitov 		return count;
115238b2cf29SAlexei Starovoitov 	}
1153e99b99b4SRobert Olsson 	if (!strcmp(name, "node")) {
1154e99b99b4SRobert Olsson 		len = num_arg(&user_buffer[i], 10, &value);
1155e99b99b4SRobert Olsson 		if (len < 0)
1156e99b99b4SRobert Olsson 			return len;
1157e99b99b4SRobert Olsson 
1158e99b99b4SRobert Olsson 		i += len;
1159e99b99b4SRobert Olsson 
1160e99b99b4SRobert Olsson 		if (node_possible(value)) {
1161e99b99b4SRobert Olsson 			pkt_dev->node = value;
1162e99b99b4SRobert Olsson 			sprintf(pg_result, "OK: node=%d", pkt_dev->node);
116326ad7879SEric Dumazet 			if (pkt_dev->page) {
116426ad7879SEric Dumazet 				put_page(pkt_dev->page);
116526ad7879SEric Dumazet 				pkt_dev->page = NULL;
116626ad7879SEric Dumazet 			}
1167e99b99b4SRobert Olsson 		}
1168e99b99b4SRobert Olsson 		else
1169e99b99b4SRobert Olsson 			sprintf(pg_result, "ERROR: node not possible");
1170e99b99b4SRobert Olsson 		return count;
1171e99b99b4SRobert Olsson 	}
1172*62f64aedSAlexei Starovoitov 	if (!strcmp(name, "xmit_mode")) {
1173*62f64aedSAlexei Starovoitov 		char f[32];
1174*62f64aedSAlexei Starovoitov 
1175*62f64aedSAlexei Starovoitov 		memset(f, 0, 32);
1176*62f64aedSAlexei Starovoitov 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
1177*62f64aedSAlexei Starovoitov 		if (len < 0)
1178*62f64aedSAlexei Starovoitov 			return len;
1179*62f64aedSAlexei Starovoitov 
1180*62f64aedSAlexei Starovoitov 		if (copy_from_user(f, &user_buffer[i], len))
1181*62f64aedSAlexei Starovoitov 			return -EFAULT;
1182*62f64aedSAlexei Starovoitov 		i += len;
1183*62f64aedSAlexei Starovoitov 
1184*62f64aedSAlexei Starovoitov 		if (strcmp(f, "start_xmit") == 0) {
1185*62f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_START_XMIT;
1186*62f64aedSAlexei Starovoitov 		} else if (strcmp(f, "netif_receive") == 0) {
1187*62f64aedSAlexei Starovoitov 			/* clone_skb set earlier, not supported in this mode */
1188*62f64aedSAlexei Starovoitov 			if (pkt_dev->clone_skb > 0)
1189*62f64aedSAlexei Starovoitov 				return -ENOTSUPP;
1190*62f64aedSAlexei Starovoitov 
1191*62f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_NETIF_RECEIVE;
1192*62f64aedSAlexei Starovoitov 		} else {
1193*62f64aedSAlexei Starovoitov 			sprintf(pg_result,
1194*62f64aedSAlexei Starovoitov 				"xmit_mode -:%s:- unknown\nAvailable modes: %s",
1195*62f64aedSAlexei Starovoitov 				f, "start_xmit, netif_receive\n");
1196*62f64aedSAlexei Starovoitov 			return count;
1197*62f64aedSAlexei Starovoitov 		}
1198*62f64aedSAlexei Starovoitov 		sprintf(pg_result, "OK: xmit_mode=%s", f);
1199*62f64aedSAlexei Starovoitov 		return count;
1200*62f64aedSAlexei Starovoitov 	}
12011da177e4SLinus Torvalds 	if (!strcmp(name, "flag")) {
12021da177e4SLinus Torvalds 		char f[32];
12031da177e4SLinus Torvalds 		memset(f, 0, 32);
12041da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
120563adc6fbSStephen Hemminger 		if (len < 0)
1206222f1806SLuiz Capitulino 			return len;
120763adc6fbSStephen Hemminger 
12081da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
12091da177e4SLinus Torvalds 			return -EFAULT;
12101da177e4SLinus Torvalds 		i += len;
12111da177e4SLinus Torvalds 		if (strcmp(f, "IPSRC_RND") == 0)
12121da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPSRC_RND;
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 		else if (strcmp(f, "!IPSRC_RND") == 0)
12151da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPSRC_RND;
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds 		else if (strcmp(f, "TXSIZE_RND") == 0)
12181da177e4SLinus Torvalds 			pkt_dev->flags |= F_TXSIZE_RND;
12191da177e4SLinus Torvalds 
12201da177e4SLinus Torvalds 		else if (strcmp(f, "!TXSIZE_RND") == 0)
12211da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_TXSIZE_RND;
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 		else if (strcmp(f, "IPDST_RND") == 0)
12241da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPDST_RND;
12251da177e4SLinus Torvalds 
12261da177e4SLinus Torvalds 		else if (strcmp(f, "!IPDST_RND") == 0)
12271da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPDST_RND;
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds 		else if (strcmp(f, "UDPSRC_RND") == 0)
12301da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPSRC_RND;
12311da177e4SLinus Torvalds 
12321da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPSRC_RND") == 0)
12331da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPSRC_RND;
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds 		else if (strcmp(f, "UDPDST_RND") == 0)
12361da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPDST_RND;
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPDST_RND") == 0)
12391da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPDST_RND;
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds 		else if (strcmp(f, "MACSRC_RND") == 0)
12421da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACSRC_RND;
12431da177e4SLinus Torvalds 
12441da177e4SLinus Torvalds 		else if (strcmp(f, "!MACSRC_RND") == 0)
12451da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACSRC_RND;
12461da177e4SLinus Torvalds 
12471da177e4SLinus Torvalds 		else if (strcmp(f, "MACDST_RND") == 0)
12481da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACDST_RND;
12491da177e4SLinus Torvalds 
12501da177e4SLinus Torvalds 		else if (strcmp(f, "!MACDST_RND") == 0)
12511da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACDST_RND;
12521da177e4SLinus Torvalds 
1253ca6549afSSteven Whitehouse 		else if (strcmp(f, "MPLS_RND") == 0)
1254ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
1255ca6549afSSteven Whitehouse 
1256ca6549afSSteven Whitehouse 		else if (strcmp(f, "!MPLS_RND") == 0)
1257ca6549afSSteven Whitehouse 			pkt_dev->flags &= ~F_MPLS_RND;
1258ca6549afSSteven Whitehouse 
125934954ddcSFrancesco Fondelli 		else if (strcmp(f, "VID_RND") == 0)
126034954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_VID_RND;
126134954ddcSFrancesco Fondelli 
126234954ddcSFrancesco Fondelli 		else if (strcmp(f, "!VID_RND") == 0)
126334954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_VID_RND;
126434954ddcSFrancesco Fondelli 
126534954ddcSFrancesco Fondelli 		else if (strcmp(f, "SVID_RND") == 0)
126634954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_SVID_RND;
126734954ddcSFrancesco Fondelli 
126834954ddcSFrancesco Fondelli 		else if (strcmp(f, "!SVID_RND") == 0)
126934954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_SVID_RND;
127034954ddcSFrancesco Fondelli 
1271007a531bSJamal Hadi Salim 		else if (strcmp(f, "FLOW_SEQ") == 0)
1272007a531bSJamal Hadi Salim 			pkt_dev->flags |= F_FLOW_SEQ;
1273007a531bSJamal Hadi Salim 
127445b270f8SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_RND") == 0)
127545b270f8SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_RND;
127645b270f8SRobert Olsson 
127745b270f8SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_RND") == 0)
127845b270f8SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_RND;
1279e6fce5b9SRobert Olsson 
1280e6fce5b9SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_CPU") == 0)
1281e6fce5b9SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_CPU;
1282e6fce5b9SRobert Olsson 
1283e6fce5b9SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_CPU") == 0)
1284e6fce5b9SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_CPU;
1285a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
1286a553e4a6SJamal Hadi Salim 		else if (strcmp(f, "IPSEC") == 0)
1287a553e4a6SJamal Hadi Salim 			pkt_dev->flags |= F_IPSEC_ON;
1288a553e4a6SJamal Hadi Salim #endif
1289a553e4a6SJamal Hadi Salim 
12901ca7768cSFrancesco Fondelli 		else if (strcmp(f, "!IPV6") == 0)
12911ca7768cSFrancesco Fondelli 			pkt_dev->flags &= ~F_IPV6;
12921ca7768cSFrancesco Fondelli 
1293e99b99b4SRobert Olsson 		else if (strcmp(f, "NODE_ALLOC") == 0)
1294e99b99b4SRobert Olsson 			pkt_dev->flags |= F_NODE;
1295e99b99b4SRobert Olsson 
1296e99b99b4SRobert Olsson 		else if (strcmp(f, "!NODE_ALLOC") == 0)
1297e99b99b4SRobert Olsson 			pkt_dev->flags &= ~F_NODE;
1298e99b99b4SRobert Olsson 
1299c26bf4a5SThomas Graf 		else if (strcmp(f, "UDPCSUM") == 0)
1300c26bf4a5SThomas Graf 			pkt_dev->flags |= F_UDPCSUM;
1301c26bf4a5SThomas Graf 
1302c26bf4a5SThomas Graf 		else if (strcmp(f, "!UDPCSUM") == 0)
1303c26bf4a5SThomas Graf 			pkt_dev->flags &= ~F_UDPCSUM;
1304c26bf4a5SThomas Graf 
1305afb84b62SJesper Dangaard Brouer 		else if (strcmp(f, "NO_TIMESTAMP") == 0)
1306afb84b62SJesper Dangaard Brouer 			pkt_dev->flags |= F_NO_TIMESTAMP;
1307afb84b62SJesper Dangaard Brouer 
1308f1f00d8fSJesper Dangaard Brouer 		else if (strcmp(f, "!NO_TIMESTAMP") == 0)
1309f1f00d8fSJesper Dangaard Brouer 			pkt_dev->flags &= ~F_NO_TIMESTAMP;
1310f1f00d8fSJesper Dangaard Brouer 
13111da177e4SLinus Torvalds 		else {
1312222f1806SLuiz Capitulino 			sprintf(pg_result,
1313222f1806SLuiz Capitulino 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
13141da177e4SLinus Torvalds 				f,
13151ca7768cSFrancesco Fondelli 				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
131672f8e06fSMathias Krause 				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, "
131772f8e06fSMathias Krause 				"MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, "
131872f8e06fSMathias Krause 				"QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, "
1319afb84b62SJesper Dangaard Brouer 				"NO_TIMESTAMP, "
132072f8e06fSMathias Krause #ifdef CONFIG_XFRM
132172f8e06fSMathias Krause 				"IPSEC, "
132272f8e06fSMathias Krause #endif
132372f8e06fSMathias Krause 				"NODE_ALLOC\n");
13241da177e4SLinus Torvalds 			return count;
13251da177e4SLinus Torvalds 		}
13261da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
13271da177e4SLinus Torvalds 		return count;
13281da177e4SLinus Torvalds 	}
13291da177e4SLinus Torvalds 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
13301da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
133163adc6fbSStephen Hemminger 		if (len < 0)
1332222f1806SLuiz Capitulino 			return len;
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13351da177e4SLinus Torvalds 			return -EFAULT;
13361da177e4SLinus Torvalds 		buf[len] = 0;
13371da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
13381da177e4SLinus Torvalds 			memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
13391da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_min, buf, len);
13401da177e4SLinus Torvalds 			pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
13411da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_min;
13421da177e4SLinus Torvalds 		}
13431da177e4SLinus Torvalds 		if (debug)
1344f342cda7SJoe Perches 			pr_debug("dst_min set to: %s\n", pkt_dev->dst_min);
13451da177e4SLinus Torvalds 		i += len;
13461da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
13471da177e4SLinus Torvalds 		return count;
13481da177e4SLinus Torvalds 	}
13491da177e4SLinus Torvalds 	if (!strcmp(name, "dst_max")) {
13501da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
135163adc6fbSStephen Hemminger 		if (len < 0)
1352222f1806SLuiz Capitulino 			return len;
135363adc6fbSStephen Hemminger 
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13561da177e4SLinus Torvalds 			return -EFAULT;
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 		buf[len] = 0;
13591da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
13601da177e4SLinus Torvalds 			memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
13611da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_max, buf, len);
13621da177e4SLinus Torvalds 			pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
13631da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_max;
13641da177e4SLinus Torvalds 		}
13651da177e4SLinus Torvalds 		if (debug)
1366f342cda7SJoe Perches 			pr_debug("dst_max set to: %s\n", pkt_dev->dst_max);
13671da177e4SLinus Torvalds 		i += len;
13681da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
13691da177e4SLinus Torvalds 		return count;
13701da177e4SLinus Torvalds 	}
13711da177e4SLinus Torvalds 	if (!strcmp(name, "dst6")) {
13721da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1373222f1806SLuiz Capitulino 		if (len < 0)
1374222f1806SLuiz Capitulino 			return len;
13751da177e4SLinus Torvalds 
13761da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13791da177e4SLinus Torvalds 			return -EFAULT;
13801da177e4SLinus Torvalds 		buf[len] = 0;
13811da177e4SLinus Torvalds 
1382c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL);
138347a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);
13841da177e4SLinus Torvalds 
13854e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->in6_daddr;
13861da177e4SLinus Torvalds 
13871da177e4SLinus Torvalds 		if (debug)
1388f342cda7SJoe Perches 			pr_debug("dst6 set to: %s\n", buf);
13891da177e4SLinus Torvalds 
13901da177e4SLinus Torvalds 		i += len;
13911da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6=%s", buf);
13921da177e4SLinus Torvalds 		return count;
13931da177e4SLinus Torvalds 	}
13941da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_min")) {
13951da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1396222f1806SLuiz Capitulino 		if (len < 0)
1397222f1806SLuiz Capitulino 			return len;
13981da177e4SLinus Torvalds 
13991da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14001da177e4SLinus Torvalds 
14011da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14021da177e4SLinus Torvalds 			return -EFAULT;
14031da177e4SLinus Torvalds 		buf[len] = 0;
14041da177e4SLinus Torvalds 
1405c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL);
140647a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);
14071da177e4SLinus Torvalds 
14084e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->min_in6_daddr;
14091da177e4SLinus Torvalds 		if (debug)
1410f342cda7SJoe Perches 			pr_debug("dst6_min set to: %s\n", buf);
14111da177e4SLinus Torvalds 
14121da177e4SLinus Torvalds 		i += len;
14131da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_min=%s", buf);
14141da177e4SLinus Torvalds 		return count;
14151da177e4SLinus Torvalds 	}
14161da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_max")) {
14171da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1418222f1806SLuiz Capitulino 		if (len < 0)
1419222f1806SLuiz Capitulino 			return len;
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14241da177e4SLinus Torvalds 			return -EFAULT;
14251da177e4SLinus Torvalds 		buf[len] = 0;
14261da177e4SLinus Torvalds 
1427c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL);
142847a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);
14291da177e4SLinus Torvalds 
14301da177e4SLinus Torvalds 		if (debug)
1431f342cda7SJoe Perches 			pr_debug("dst6_max set to: %s\n", buf);
14321da177e4SLinus Torvalds 
14331da177e4SLinus Torvalds 		i += len;
14341da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_max=%s", buf);
14351da177e4SLinus Torvalds 		return count;
14361da177e4SLinus Torvalds 	}
14371da177e4SLinus Torvalds 	if (!strcmp(name, "src6")) {
14381da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1439222f1806SLuiz Capitulino 		if (len < 0)
1440222f1806SLuiz Capitulino 			return len;
14411da177e4SLinus Torvalds 
14421da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14431da177e4SLinus Torvalds 
14441da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14451da177e4SLinus Torvalds 			return -EFAULT;
14461da177e4SLinus Torvalds 		buf[len] = 0;
14471da177e4SLinus Torvalds 
1448c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL);
144947a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);
14501da177e4SLinus Torvalds 
14514e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_saddr = pkt_dev->in6_saddr;
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds 		if (debug)
1454f342cda7SJoe Perches 			pr_debug("src6 set to: %s\n", buf);
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds 		i += len;
14571da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src6=%s", buf);
14581da177e4SLinus Torvalds 		return count;
14591da177e4SLinus Torvalds 	}
14601da177e4SLinus Torvalds 	if (!strcmp(name, "src_min")) {
14611da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
146263adc6fbSStephen Hemminger 		if (len < 0)
1463222f1806SLuiz Capitulino 			return len;
146463adc6fbSStephen Hemminger 
14651da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14661da177e4SLinus Torvalds 			return -EFAULT;
14671da177e4SLinus Torvalds 		buf[len] = 0;
14681da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_min) != 0) {
14691da177e4SLinus Torvalds 			memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
14701da177e4SLinus Torvalds 			strncpy(pkt_dev->src_min, buf, len);
14711da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
14721da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_min;
14731da177e4SLinus Torvalds 		}
14741da177e4SLinus Torvalds 		if (debug)
1475f342cda7SJoe Perches 			pr_debug("src_min set to: %s\n", pkt_dev->src_min);
14761da177e4SLinus Torvalds 		i += len;
14771da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
14781da177e4SLinus Torvalds 		return count;
14791da177e4SLinus Torvalds 	}
14801da177e4SLinus Torvalds 	if (!strcmp(name, "src_max")) {
14811da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
148263adc6fbSStephen Hemminger 		if (len < 0)
1483222f1806SLuiz Capitulino 			return len;
148463adc6fbSStephen Hemminger 
14851da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14861da177e4SLinus Torvalds 			return -EFAULT;
14871da177e4SLinus Torvalds 		buf[len] = 0;
14881da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_max) != 0) {
14891da177e4SLinus Torvalds 			memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
14901da177e4SLinus Torvalds 			strncpy(pkt_dev->src_max, buf, len);
14911da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
14921da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_max;
14931da177e4SLinus Torvalds 		}
14941da177e4SLinus Torvalds 		if (debug)
1495f342cda7SJoe Perches 			pr_debug("src_max set to: %s\n", pkt_dev->src_max);
14961da177e4SLinus Torvalds 		i += len;
14971da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
14981da177e4SLinus Torvalds 		return count;
14991da177e4SLinus Torvalds 	}
15001da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac")) {
15011da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
150263adc6fbSStephen Hemminger 		if (len < 0)
1503222f1806SLuiz Capitulino 			return len;
150463adc6fbSStephen Hemminger 
15051da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15061da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15071da177e4SLinus Torvalds 			return -EFAULT;
15081da177e4SLinus Torvalds 
15094940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->dst_mac))
15104940fc88SAlexey Dobriyan 			return -EINVAL;
15111da177e4SLinus Torvalds 		/* Set up Dest MAC */
15129ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[0], pkt_dev->dst_mac);
15131da177e4SLinus Torvalds 
15144940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);
15151da177e4SLinus Torvalds 		return count;
15161da177e4SLinus Torvalds 	}
15171da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac")) {
15181da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
151963adc6fbSStephen Hemminger 		if (len < 0)
1520222f1806SLuiz Capitulino 			return len;
152163adc6fbSStephen Hemminger 
15221da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15231da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15241da177e4SLinus Torvalds 			return -EFAULT;
15251da177e4SLinus Torvalds 
15264940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->src_mac))
15274940fc88SAlexey Dobriyan 			return -EINVAL;
1528ce5d0b47SAdit Ranadive 		/* Set up Src MAC */
15299ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[6], pkt_dev->src_mac);
1530ce5d0b47SAdit Ranadive 
15314940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);
15321da177e4SLinus Torvalds 		return count;
15331da177e4SLinus Torvalds 	}
15341da177e4SLinus Torvalds 
15351da177e4SLinus Torvalds 	if (!strcmp(name, "clear_counters")) {
15361da177e4SLinus Torvalds 		pktgen_clear_counters(pkt_dev);
15371da177e4SLinus Torvalds 		sprintf(pg_result, "OK: Clearing counters.\n");
15381da177e4SLinus Torvalds 		return count;
15391da177e4SLinus Torvalds 	}
15401da177e4SLinus Torvalds 
15411da177e4SLinus Torvalds 	if (!strcmp(name, "flows")) {
15421da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
154363adc6fbSStephen Hemminger 		if (len < 0)
1544222f1806SLuiz Capitulino 			return len;
154563adc6fbSStephen Hemminger 
15461da177e4SLinus Torvalds 		i += len;
15471da177e4SLinus Torvalds 		if (value > MAX_CFLOWS)
15481da177e4SLinus Torvalds 			value = MAX_CFLOWS;
15491da177e4SLinus Torvalds 
15501da177e4SLinus Torvalds 		pkt_dev->cflows = value;
15511da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
15521da177e4SLinus Torvalds 		return count;
15531da177e4SLinus Torvalds 	}
15546bae9190SFan Du #ifdef CONFIG_XFRM
1555de4aee7dSFan Du 	if (!strcmp(name, "spi")) {
1556de4aee7dSFan Du 		len = num_arg(&user_buffer[i], 10, &value);
1557de4aee7dSFan Du 		if (len < 0)
1558de4aee7dSFan Du 			return len;
1559de4aee7dSFan Du 
1560de4aee7dSFan Du 		i += len;
1561de4aee7dSFan Du 		pkt_dev->spi = value;
1562de4aee7dSFan Du 		sprintf(pg_result, "OK: spi=%u", pkt_dev->spi);
1563de4aee7dSFan Du 		return count;
1564de4aee7dSFan Du 	}
15656bae9190SFan Du #endif
15661da177e4SLinus Torvalds 	if (!strcmp(name, "flowlen")) {
15671da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
156863adc6fbSStephen Hemminger 		if (len < 0)
1569222f1806SLuiz Capitulino 			return len;
157063adc6fbSStephen Hemminger 
15711da177e4SLinus Torvalds 		i += len;
15721da177e4SLinus Torvalds 		pkt_dev->lflow = value;
15731da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
15741da177e4SLinus Torvalds 		return count;
15751da177e4SLinus Torvalds 	}
15761da177e4SLinus Torvalds 
157745b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_min")) {
157845b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
157963adc6fbSStephen Hemminger 		if (len < 0)
158045b270f8SRobert Olsson 			return len;
158163adc6fbSStephen Hemminger 
158245b270f8SRobert Olsson 		i += len;
158345b270f8SRobert Olsson 		pkt_dev->queue_map_min = value;
158445b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min);
158545b270f8SRobert Olsson 		return count;
158645b270f8SRobert Olsson 	}
158745b270f8SRobert Olsson 
158845b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_max")) {
158945b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
159063adc6fbSStephen Hemminger 		if (len < 0)
159145b270f8SRobert Olsson 			return len;
159263adc6fbSStephen Hemminger 
159345b270f8SRobert Olsson 		i += len;
159445b270f8SRobert Olsson 		pkt_dev->queue_map_max = value;
159545b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max);
159645b270f8SRobert Olsson 		return count;
159745b270f8SRobert Olsson 	}
159845b270f8SRobert Olsson 
1599ca6549afSSteven Whitehouse 	if (!strcmp(name, "mpls")) {
160095c96174SEric Dumazet 		unsigned int n, cnt;
1601cfcabdccSStephen Hemminger 
1602ca6549afSSteven Whitehouse 		len = get_labels(&user_buffer[i], pkt_dev);
1603cfcabdccSStephen Hemminger 		if (len < 0)
1604cfcabdccSStephen Hemminger 			return len;
1605ca6549afSSteven Whitehouse 		i += len;
1606cfcabdccSStephen Hemminger 		cnt = sprintf(pg_result, "OK: mpls=");
1607ca6549afSSteven Whitehouse 		for (n = 0; n < pkt_dev->nr_labels; n++)
1608cfcabdccSStephen Hemminger 			cnt += sprintf(pg_result + cnt,
1609ca6549afSSteven Whitehouse 				       "%08x%s", ntohl(pkt_dev->labels[n]),
1610ca6549afSSteven Whitehouse 				       n == pkt_dev->nr_labels-1 ? "" : ",");
161134954ddcSFrancesco Fondelli 
161234954ddcSFrancesco Fondelli 		if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
161334954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
161434954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
161534954ddcSFrancesco Fondelli 
161634954ddcSFrancesco Fondelli 			if (debug)
1617f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN auto turned off\n");
161834954ddcSFrancesco Fondelli 		}
161934954ddcSFrancesco Fondelli 		return count;
162034954ddcSFrancesco Fondelli 	}
162134954ddcSFrancesco Fondelli 
162234954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_id")) {
162334954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
162463adc6fbSStephen Hemminger 		if (len < 0)
162534954ddcSFrancesco Fondelli 			return len;
162663adc6fbSStephen Hemminger 
162734954ddcSFrancesco Fondelli 		i += len;
162834954ddcSFrancesco Fondelli 		if (value <= 4095) {
162934954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = value;  /* turn on VLAN */
163034954ddcSFrancesco Fondelli 
163134954ddcSFrancesco Fondelli 			if (debug)
1632f342cda7SJoe Perches 				pr_debug("VLAN turned on\n");
163334954ddcSFrancesco Fondelli 
163434954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1635f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
163634954ddcSFrancesco Fondelli 
163734954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
163834954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
163934954ddcSFrancesco Fondelli 		} else {
164034954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
164134954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
164234954ddcSFrancesco Fondelli 
164334954ddcSFrancesco Fondelli 			if (debug)
1644f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
164534954ddcSFrancesco Fondelli 		}
164634954ddcSFrancesco Fondelli 		return count;
164734954ddcSFrancesco Fondelli 	}
164834954ddcSFrancesco Fondelli 
164934954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_p")) {
165034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
165163adc6fbSStephen Hemminger 		if (len < 0)
165234954ddcSFrancesco Fondelli 			return len;
165363adc6fbSStephen Hemminger 
165434954ddcSFrancesco Fondelli 		i += len;
165534954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
165634954ddcSFrancesco Fondelli 			pkt_dev->vlan_p = value;
165734954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
165834954ddcSFrancesco Fondelli 		} else {
165934954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_p must be 0-7");
166034954ddcSFrancesco Fondelli 		}
166134954ddcSFrancesco Fondelli 		return count;
166234954ddcSFrancesco Fondelli 	}
166334954ddcSFrancesco Fondelli 
166434954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_cfi")) {
166534954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
166663adc6fbSStephen Hemminger 		if (len < 0)
166734954ddcSFrancesco Fondelli 			return len;
166863adc6fbSStephen Hemminger 
166934954ddcSFrancesco Fondelli 		i += len;
167034954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
167134954ddcSFrancesco Fondelli 			pkt_dev->vlan_cfi = value;
167234954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
167334954ddcSFrancesco Fondelli 		} else {
167434954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
167534954ddcSFrancesco Fondelli 		}
167634954ddcSFrancesco Fondelli 		return count;
167734954ddcSFrancesco Fondelli 	}
167834954ddcSFrancesco Fondelli 
167934954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_id")) {
168034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
168163adc6fbSStephen Hemminger 		if (len < 0)
168234954ddcSFrancesco Fondelli 			return len;
168363adc6fbSStephen Hemminger 
168434954ddcSFrancesco Fondelli 		i += len;
168534954ddcSFrancesco Fondelli 		if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
168634954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = value;  /* turn on SVLAN */
168734954ddcSFrancesco Fondelli 
168834954ddcSFrancesco Fondelli 			if (debug)
1689f342cda7SJoe Perches 				pr_debug("SVLAN turned on\n");
169034954ddcSFrancesco Fondelli 
169134954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1692f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
169334954ddcSFrancesco Fondelli 
169434954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
169534954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
169634954ddcSFrancesco Fondelli 		} else {
169734954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
169834954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
169934954ddcSFrancesco Fondelli 
170034954ddcSFrancesco Fondelli 			if (debug)
1701f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
170234954ddcSFrancesco Fondelli 		}
170334954ddcSFrancesco Fondelli 		return count;
170434954ddcSFrancesco Fondelli 	}
170534954ddcSFrancesco Fondelli 
170634954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_p")) {
170734954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
170863adc6fbSStephen Hemminger 		if (len < 0)
170934954ddcSFrancesco Fondelli 			return len;
171063adc6fbSStephen Hemminger 
171134954ddcSFrancesco Fondelli 		i += len;
171234954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
171334954ddcSFrancesco Fondelli 			pkt_dev->svlan_p = value;
171434954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
171534954ddcSFrancesco Fondelli 		} else {
171634954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_p must be 0-7");
171734954ddcSFrancesco Fondelli 		}
171834954ddcSFrancesco Fondelli 		return count;
171934954ddcSFrancesco Fondelli 	}
172034954ddcSFrancesco Fondelli 
172134954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_cfi")) {
172234954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
172363adc6fbSStephen Hemminger 		if (len < 0)
172434954ddcSFrancesco Fondelli 			return len;
172563adc6fbSStephen Hemminger 
172634954ddcSFrancesco Fondelli 		i += len;
172734954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
172834954ddcSFrancesco Fondelli 			pkt_dev->svlan_cfi = value;
172934954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
173034954ddcSFrancesco Fondelli 		} else {
173134954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
173234954ddcSFrancesco Fondelli 		}
1733ca6549afSSteven Whitehouse 		return count;
1734ca6549afSSteven Whitehouse 	}
1735ca6549afSSteven Whitehouse 
17361ca7768cSFrancesco Fondelli 	if (!strcmp(name, "tos")) {
17371ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17381ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
173963adc6fbSStephen Hemminger 		if (len < 0)
17401ca7768cSFrancesco Fondelli 			return len;
174163adc6fbSStephen Hemminger 
17421ca7768cSFrancesco Fondelli 		i += len;
17431ca7768cSFrancesco Fondelli 		if (len == 2) {
17441ca7768cSFrancesco Fondelli 			pkt_dev->tos = tmp_value;
17451ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
17461ca7768cSFrancesco Fondelli 		} else {
17471ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: tos must be 00-ff");
17481ca7768cSFrancesco Fondelli 		}
17491ca7768cSFrancesco Fondelli 		return count;
17501ca7768cSFrancesco Fondelli 	}
17511ca7768cSFrancesco Fondelli 
17521ca7768cSFrancesco Fondelli 	if (!strcmp(name, "traffic_class")) {
17531ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17541ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
175563adc6fbSStephen Hemminger 		if (len < 0)
17561ca7768cSFrancesco Fondelli 			return len;
175763adc6fbSStephen Hemminger 
17581ca7768cSFrancesco Fondelli 		i += len;
17591ca7768cSFrancesco Fondelli 		if (len == 2) {
17601ca7768cSFrancesco Fondelli 			pkt_dev->traffic_class = tmp_value;
17611ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
17621ca7768cSFrancesco Fondelli 		} else {
17631ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
17641ca7768cSFrancesco Fondelli 		}
17651ca7768cSFrancesco Fondelli 		return count;
17661ca7768cSFrancesco Fondelli 	}
17671ca7768cSFrancesco Fondelli 
17689e50e3acSJohn Fastabend 	if (!strcmp(name, "skb_priority")) {
17699e50e3acSJohn Fastabend 		len = num_arg(&user_buffer[i], 9, &value);
17709e50e3acSJohn Fastabend 		if (len < 0)
17719e50e3acSJohn Fastabend 			return len;
17729e50e3acSJohn Fastabend 
17739e50e3acSJohn Fastabend 		i += len;
17749e50e3acSJohn Fastabend 		pkt_dev->skb_priority = value;
17759e50e3acSJohn Fastabend 		sprintf(pg_result, "OK: skb_priority=%i",
17769e50e3acSJohn Fastabend 			pkt_dev->skb_priority);
17779e50e3acSJohn Fastabend 		return count;
17789e50e3acSJohn Fastabend 	}
17799e50e3acSJohn Fastabend 
17801da177e4SLinus Torvalds 	sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
17811da177e4SLinus Torvalds 	return -EINVAL;
17821da177e4SLinus Torvalds }
17831da177e4SLinus Torvalds 
1784d50a6b56SStephen Hemminger static int pktgen_if_open(struct inode *inode, struct file *file)
17851da177e4SLinus Torvalds {
1786d9dda78bSAl Viro 	return single_open(file, pktgen_if_show, PDE_DATA(inode));
17871da177e4SLinus Torvalds }
17881da177e4SLinus Torvalds 
17899a32144eSArjan van de Ven static const struct file_operations pktgen_if_fops = {
1790d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1791d50a6b56SStephen Hemminger 	.open    = pktgen_if_open,
1792d50a6b56SStephen Hemminger 	.read    = seq_read,
1793d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1794d50a6b56SStephen Hemminger 	.write   = pktgen_if_write,
1795d50a6b56SStephen Hemminger 	.release = single_release,
1796d50a6b56SStephen Hemminger };
1797d50a6b56SStephen Hemminger 
1798d50a6b56SStephen Hemminger static int pktgen_thread_show(struct seq_file *seq, void *v)
1799d50a6b56SStephen Hemminger {
1800d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1801648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
1802d50a6b56SStephen Hemminger 
1803d50a6b56SStephen Hemminger 	BUG_ON(!t);
1804d50a6b56SStephen Hemminger 
180597dc48e2SThomas Graf 	seq_puts(seq, "Running: ");
18061da177e4SLinus Torvalds 
18078788370aSJesper Dangaard Brouer 	rcu_read_lock();
18088788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18091da177e4SLinus Torvalds 		if (pkt_dev->running)
1810593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18111da177e4SLinus Torvalds 
181297dc48e2SThomas Graf 	seq_puts(seq, "\nStopped: ");
18131da177e4SLinus Torvalds 
18148788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18151da177e4SLinus Torvalds 		if (!pkt_dev->running)
1816593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18171da177e4SLinus Torvalds 
18181da177e4SLinus Torvalds 	if (t->result[0])
1819d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: %s\n", t->result);
18201da177e4SLinus Torvalds 	else
182197dc48e2SThomas Graf 		seq_puts(seq, "\nResult: NA\n");
18221da177e4SLinus Torvalds 
18238788370aSJesper Dangaard Brouer 	rcu_read_unlock();
18241da177e4SLinus Torvalds 
1825d50a6b56SStephen Hemminger 	return 0;
18261da177e4SLinus Torvalds }
18271da177e4SLinus Torvalds 
1828d50a6b56SStephen Hemminger static ssize_t pktgen_thread_write(struct file *file,
1829d50a6b56SStephen Hemminger 				   const char __user * user_buffer,
1830d50a6b56SStephen Hemminger 				   size_t count, loff_t * offset)
18311da177e4SLinus Torvalds {
18328a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
1833d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1834d6182223SPaul Gortmaker 	int i, max, len, ret;
18351da177e4SLinus Torvalds 	char name[40];
18361da177e4SLinus Torvalds 	char *pg_result;
18371da177e4SLinus Torvalds 
18381da177e4SLinus Torvalds 	if (count < 1) {
18391da177e4SLinus Torvalds 		//      sprintf(pg_result, "Wrong command format");
18401da177e4SLinus Torvalds 		return -EINVAL;
18411da177e4SLinus Torvalds 	}
18421da177e4SLinus Torvalds 
1843d6182223SPaul Gortmaker 	max = count;
1844d6182223SPaul Gortmaker 	len = count_trail_chars(user_buffer, max);
18451da177e4SLinus Torvalds 	if (len < 0)
18461da177e4SLinus Torvalds 		return len;
18471da177e4SLinus Torvalds 
1848d6182223SPaul Gortmaker 	i = len;
18491da177e4SLinus Torvalds 
18501da177e4SLinus Torvalds 	/* Read variable name */
18511da177e4SLinus Torvalds 
18521da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
18531da177e4SLinus Torvalds 	if (len < 0)
18541da177e4SLinus Torvalds 		return len;
18551da177e4SLinus Torvalds 
18561da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
18571da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
18581da177e4SLinus Torvalds 		return -EFAULT;
18591da177e4SLinus Torvalds 	i += len;
18601da177e4SLinus Torvalds 
18611da177e4SLinus Torvalds 	max = count - i;
18621da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
18631da177e4SLinus Torvalds 	if (len < 0)
18641da177e4SLinus Torvalds 		return len;
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	i += len;
18671da177e4SLinus Torvalds 
18681da177e4SLinus Torvalds 	if (debug)
1869f342cda7SJoe Perches 		pr_debug("t=%s, count=%lu\n", name, (unsigned long)count);
18701da177e4SLinus Torvalds 
18711da177e4SLinus Torvalds 	if (!t) {
1872f9467eaeSJoe Perches 		pr_err("ERROR: No thread\n");
18731da177e4SLinus Torvalds 		ret = -EINVAL;
18741da177e4SLinus Torvalds 		goto out;
18751da177e4SLinus Torvalds 	}
18761da177e4SLinus Torvalds 
18771da177e4SLinus Torvalds 	pg_result = &(t->result[0]);
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds 	if (!strcmp(name, "add_device")) {
18801da177e4SLinus Torvalds 		char f[32];
18811da177e4SLinus Torvalds 		memset(f, 0, 32);
18821da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
18831da177e4SLinus Torvalds 		if (len < 0) {
18841da177e4SLinus Torvalds 			ret = len;
18851da177e4SLinus Torvalds 			goto out;
18861da177e4SLinus Torvalds 		}
18871da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
18881da177e4SLinus Torvalds 			return -EFAULT;
18891da177e4SLinus Torvalds 		i += len;
18906146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
1891604dfd6eSCong Wang 		ret = pktgen_add_device(t, f);
18926146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1893604dfd6eSCong Wang 		if (!ret) {
18941da177e4SLinus Torvalds 			ret = count;
18951da177e4SLinus Torvalds 			sprintf(pg_result, "OK: add_device=%s", f);
1896604dfd6eSCong Wang 		} else
1897604dfd6eSCong Wang 			sprintf(pg_result, "ERROR: can not add device %s", f);
18981da177e4SLinus Torvalds 		goto out;
18991da177e4SLinus Torvalds 	}
19001da177e4SLinus Torvalds 
19011da177e4SLinus Torvalds 	if (!strcmp(name, "rem_device_all")) {
19026146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
190395ed63f7SArthur Kepner 		t->control |= T_REMDEVALL;
19046146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1905121caf57SNishanth Aravamudan 		schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
19061da177e4SLinus Torvalds 		ret = count;
19071da177e4SLinus Torvalds 		sprintf(pg_result, "OK: rem_device_all");
19081da177e4SLinus Torvalds 		goto out;
19091da177e4SLinus Torvalds 	}
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds 	if (!strcmp(name, "max_before_softirq")) {
1912b163911fSRobert Olsson 		sprintf(pg_result, "OK: Note! max_before_softirq is obsoleted -- Do not use");
19131da177e4SLinus Torvalds 		ret = count;
19141da177e4SLinus Torvalds 		goto out;
19151da177e4SLinus Torvalds 	}
19161da177e4SLinus Torvalds 
19171da177e4SLinus Torvalds 	ret = -EINVAL;
19181da177e4SLinus Torvalds out:
19191da177e4SLinus Torvalds 	return ret;
19201da177e4SLinus Torvalds }
19211da177e4SLinus Torvalds 
1922d50a6b56SStephen Hemminger static int pktgen_thread_open(struct inode *inode, struct file *file)
19231da177e4SLinus Torvalds {
1924d9dda78bSAl Viro 	return single_open(file, pktgen_thread_show, PDE_DATA(inode));
19251da177e4SLinus Torvalds }
19261da177e4SLinus Torvalds 
19279a32144eSArjan van de Ven static const struct file_operations pktgen_thread_fops = {
1928d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1929d50a6b56SStephen Hemminger 	.open    = pktgen_thread_open,
1930d50a6b56SStephen Hemminger 	.read    = seq_read,
1931d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1932d50a6b56SStephen Hemminger 	.write   = pktgen_thread_write,
1933d50a6b56SStephen Hemminger 	.release = single_release,
1934d50a6b56SStephen Hemminger };
19351da177e4SLinus Torvalds 
19361da177e4SLinus Torvalds /* Think find or remove for NN */
19374e58a027SCong Wang static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
19384e58a027SCong Wang 					      const char *ifname, int remove)
19391da177e4SLinus Torvalds {
19401da177e4SLinus Torvalds 	struct pktgen_thread *t;
19411da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
19423e984840SEric Dumazet 	bool exact = (remove == FIND);
19431da177e4SLinus Torvalds 
19444e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
19453e984840SEric Dumazet 		pkt_dev = pktgen_find_dev(t, ifname, exact);
19461da177e4SLinus Torvalds 		if (pkt_dev) {
19471da177e4SLinus Torvalds 			if (remove) {
194895ed63f7SArthur Kepner 				pkt_dev->removal_mark = 1;
194995ed63f7SArthur Kepner 				t->control |= T_REMDEV;
19501da177e4SLinus Torvalds 			}
19511da177e4SLinus Torvalds 			break;
19521da177e4SLinus Torvalds 		}
19531da177e4SLinus Torvalds 	}
19541da177e4SLinus Torvalds 	return pkt_dev;
19551da177e4SLinus Torvalds }
19561da177e4SLinus Torvalds 
195795ed63f7SArthur Kepner /*
195895ed63f7SArthur Kepner  * mark a device for removal
195995ed63f7SArthur Kepner  */
19604e58a027SCong Wang static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)
19611da177e4SLinus Torvalds {
19621da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
196395ed63f7SArthur Kepner 	const int max_tries = 10, msec_per_try = 125;
196495ed63f7SArthur Kepner 	int i = 0;
196595ed63f7SArthur Kepner 
19666146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
1967f9467eaeSJoe Perches 	pr_debug("%s: marking %s for removal\n", __func__, ifname);
196895ed63f7SArthur Kepner 
196995ed63f7SArthur Kepner 	while (1) {
197095ed63f7SArthur Kepner 
19714e58a027SCong Wang 		pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE);
1972222f1806SLuiz Capitulino 		if (pkt_dev == NULL)
1973222f1806SLuiz Capitulino 			break;	/* success */
197495ed63f7SArthur Kepner 
19756146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1976f9467eaeSJoe Perches 		pr_debug("%s: waiting for %s to disappear....\n",
1977f9467eaeSJoe Perches 			 __func__, ifname);
197895ed63f7SArthur Kepner 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
19796146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
198095ed63f7SArthur Kepner 
198195ed63f7SArthur Kepner 		if (++i >= max_tries) {
1982f9467eaeSJoe Perches 			pr_err("%s: timed out after waiting %d msec for device %s to be removed\n",
1983f9467eaeSJoe Perches 			       __func__, msec_per_try * i, ifname);
198495ed63f7SArthur Kepner 			break;
198595ed63f7SArthur Kepner 		}
198695ed63f7SArthur Kepner 
198795ed63f7SArthur Kepner 	}
198895ed63f7SArthur Kepner 
19896146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
199039df232fSStephen Hemminger }
199195ed63f7SArthur Kepner 
19924e58a027SCong Wang static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)
199339df232fSStephen Hemminger {
199439df232fSStephen Hemminger 	struct pktgen_thread *t;
199539df232fSStephen Hemminger 
19964e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
199739df232fSStephen Hemminger 		struct pktgen_dev *pkt_dev;
199839df232fSStephen Hemminger 
19998788370aSJesper Dangaard Brouer 		rcu_read_lock();
20008788370aSJesper Dangaard Brouer 		list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
200139df232fSStephen Hemminger 			if (pkt_dev->odev != dev)
200239df232fSStephen Hemminger 				continue;
200339df232fSStephen Hemminger 
2004a8ca16eaSDavid Howells 			proc_remove(pkt_dev->entry);
200539df232fSStephen Hemminger 
20062975315bSAlexey Dobriyan 			pkt_dev->entry = proc_create_data(dev->name, 0600,
20074e58a027SCong Wang 							  pn->proc_dir,
20082975315bSAlexey Dobriyan 							  &pktgen_if_fops,
20092975315bSAlexey Dobriyan 							  pkt_dev);
201039df232fSStephen Hemminger 			if (!pkt_dev->entry)
2011f9467eaeSJoe Perches 				pr_err("can't move proc entry for '%s'\n",
2012f9467eaeSJoe Perches 				       dev->name);
201339df232fSStephen Hemminger 			break;
201439df232fSStephen Hemminger 		}
20158788370aSJesper Dangaard Brouer 		rcu_read_unlock();
201639df232fSStephen Hemminger 	}
20171da177e4SLinus Torvalds }
20181da177e4SLinus Torvalds 
2019222f1806SLuiz Capitulino static int pktgen_device_event(struct notifier_block *unused,
2020222f1806SLuiz Capitulino 			       unsigned long event, void *ptr)
20211da177e4SLinus Torvalds {
2022351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
20234e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id);
20241da177e4SLinus Torvalds 
20254e58a027SCong Wang 	if (pn->pktgen_exiting)
2026e9dc8653SEric W. Biederman 		return NOTIFY_DONE;
2027e9dc8653SEric W. Biederman 
20281da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
20291da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
20301da177e4SLinus Torvalds 	 */
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds 	switch (event) {
203339df232fSStephen Hemminger 	case NETDEV_CHANGENAME:
20344e58a027SCong Wang 		pktgen_change_name(pn, dev);
20351da177e4SLinus Torvalds 		break;
20361da177e4SLinus Torvalds 
20371da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
20384e58a027SCong Wang 		pktgen_mark_device(pn, dev->name);
20391da177e4SLinus Torvalds 		break;
20403ff50b79SStephen Hemminger 	}
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds 	return NOTIFY_DONE;
20431da177e4SLinus Torvalds }
20441da177e4SLinus Torvalds 
20454e58a027SCong Wang static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn,
20464e58a027SCong Wang 						 struct pktgen_dev *pkt_dev,
204763adc6fbSStephen Hemminger 						 const char *ifname)
2048e6fce5b9SRobert Olsson {
2049e6fce5b9SRobert Olsson 	char b[IFNAMSIZ+5];
2050d6182223SPaul Gortmaker 	int i;
2051e6fce5b9SRobert Olsson 
2052e6fce5b9SRobert Olsson 	for (i = 0; ifname[i] != '@'; i++) {
2053e6fce5b9SRobert Olsson 		if (i == IFNAMSIZ)
2054e6fce5b9SRobert Olsson 			break;
2055e6fce5b9SRobert Olsson 
2056e6fce5b9SRobert Olsson 		b[i] = ifname[i];
2057e6fce5b9SRobert Olsson 	}
2058e6fce5b9SRobert Olsson 	b[i] = 0;
2059e6fce5b9SRobert Olsson 
20604e58a027SCong Wang 	return dev_get_by_name(pn->net, b);
2061e6fce5b9SRobert Olsson }
2062e6fce5b9SRobert Olsson 
2063e6fce5b9SRobert Olsson 
20641da177e4SLinus Torvalds /* Associate pktgen_dev with a device. */
20651da177e4SLinus Torvalds 
20664e58a027SCong Wang static int pktgen_setup_dev(const struct pktgen_net *pn,
20674e58a027SCong Wang 			    struct pktgen_dev *pkt_dev, const char *ifname)
2068222f1806SLuiz Capitulino {
20691da177e4SLinus Torvalds 	struct net_device *odev;
207039df232fSStephen Hemminger 	int err;
20711da177e4SLinus Torvalds 
20721da177e4SLinus Torvalds 	/* Clean old setups */
20731da177e4SLinus Torvalds 	if (pkt_dev->odev) {
20741da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
20751da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
20761da177e4SLinus Torvalds 	}
20771da177e4SLinus Torvalds 
20784e58a027SCong Wang 	odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname);
20791da177e4SLinus Torvalds 	if (!odev) {
2080f9467eaeSJoe Perches 		pr_err("no such netdevice: \"%s\"\n", ifname);
208139df232fSStephen Hemminger 		return -ENODEV;
20821da177e4SLinus Torvalds 	}
208339df232fSStephen Hemminger 
20841da177e4SLinus Torvalds 	if (odev->type != ARPHRD_ETHER) {
2085f9467eaeSJoe Perches 		pr_err("not an ethernet device: \"%s\"\n", ifname);
208639df232fSStephen Hemminger 		err = -EINVAL;
208739df232fSStephen Hemminger 	} else if (!netif_running(odev)) {
2088f9467eaeSJoe Perches 		pr_err("device is down: \"%s\"\n", ifname);
208939df232fSStephen Hemminger 		err = -ENETDOWN;
209039df232fSStephen Hemminger 	} else {
20911da177e4SLinus Torvalds 		pkt_dev->odev = odev;
209239df232fSStephen Hemminger 		return 0;
209339df232fSStephen Hemminger 	}
20941da177e4SLinus Torvalds 
20951da177e4SLinus Torvalds 	dev_put(odev);
209639df232fSStephen Hemminger 	return err;
20971da177e4SLinus Torvalds }
20981da177e4SLinus Torvalds 
20991da177e4SLinus Torvalds /* Read pkt_dev from the interface and set up internal pktgen_dev
21001da177e4SLinus Torvalds  * structure to have the right information to create/send packets
21011da177e4SLinus Torvalds  */
21021da177e4SLinus Torvalds static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
21031da177e4SLinus Torvalds {
210464c00d81SAndrew Gallatin 	int ntxq;
210564c00d81SAndrew Gallatin 
21061da177e4SLinus Torvalds 	if (!pkt_dev->odev) {
2107f9467eaeSJoe Perches 		pr_err("ERROR: pkt_dev->odev == NULL in setup_inject\n");
2108222f1806SLuiz Capitulino 		sprintf(pkt_dev->result,
2109222f1806SLuiz Capitulino 			"ERROR: pkt_dev->odev == NULL in setup_inject.\n");
21101da177e4SLinus Torvalds 		return;
21111da177e4SLinus Torvalds 	}
21121da177e4SLinus Torvalds 
211364c00d81SAndrew Gallatin 	/* make sure that we don't pick a non-existing transmit queue */
211464c00d81SAndrew Gallatin 	ntxq = pkt_dev->odev->real_num_tx_queues;
2115bfdbc0acSRobert Olsson 
211664c00d81SAndrew Gallatin 	if (ntxq <= pkt_dev->queue_map_min) {
2117294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
211888271660SJesse Brandeburg 			pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
2119593f63b0SEric Dumazet 			pkt_dev->odevname);
212026e29eedSDan Carpenter 		pkt_dev->queue_map_min = (ntxq ?: 1) - 1;
212164c00d81SAndrew Gallatin 	}
212288271660SJesse Brandeburg 	if (pkt_dev->queue_map_max >= ntxq) {
2123294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
212488271660SJesse Brandeburg 			pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
2125593f63b0SEric Dumazet 			pkt_dev->odevname);
212626e29eedSDan Carpenter 		pkt_dev->queue_map_max = (ntxq ?: 1) - 1;
212764c00d81SAndrew Gallatin 	}
212864c00d81SAndrew Gallatin 
21291da177e4SLinus Torvalds 	/* Default to the interface's mac if not explicitly set. */
21301da177e4SLinus Torvalds 
2131f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
21329ea08b12SJoe Perches 		ether_addr_copy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr);
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds 	/* Set up Dest MAC */
21359ea08b12SJoe Perches 	ether_addr_copy(&(pkt_dev->hh[0]), pkt_dev->dst_mac);
21361da177e4SLinus Torvalds 
21371da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
21384c139b8cSAmerigo Wang 		int i, set = 0, err = 1;
21394c139b8cSAmerigo Wang 		struct inet6_dev *idev;
21404c139b8cSAmerigo Wang 
214168bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
214268bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct ipv6hdr)
214368bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
214468bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
214568bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
214668bf9f0bSAmerigo Wang 		}
214768bf9f0bSAmerigo Wang 
21481da177e4SLinus Torvalds 		for (i = 0; i < IN6_ADDR_HSIZE; i++)
21491da177e4SLinus Torvalds 			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
21501da177e4SLinus Torvalds 				set = 1;
21511da177e4SLinus Torvalds 				break;
21521da177e4SLinus Torvalds 			}
21531da177e4SLinus Torvalds 
21541da177e4SLinus Torvalds 		if (!set) {
21551da177e4SLinus Torvalds 
21561da177e4SLinus Torvalds 			/*
21571da177e4SLinus Torvalds 			 * Use linklevel address if unconfigured.
21581da177e4SLinus Torvalds 			 *
21591da177e4SLinus Torvalds 			 * use ipv6_get_lladdr if/when it's get exported
21601da177e4SLinus Torvalds 			 */
21611da177e4SLinus Torvalds 
21628814c4b5SYOSHIFUJI Hideaki 			rcu_read_lock();
216363adc6fbSStephen Hemminger 			idev = __in6_dev_get(pkt_dev->odev);
216463adc6fbSStephen Hemminger 			if (idev) {
21651da177e4SLinus Torvalds 				struct inet6_ifaddr *ifp;
21661da177e4SLinus Torvalds 
21671da177e4SLinus Torvalds 				read_lock_bh(&idev->lock);
21684c139b8cSAmerigo Wang 				list_for_each_entry(ifp, &idev->addr_list, if_list) {
21694c139b8cSAmerigo Wang 					if ((ifp->scope & IFA_LINK) &&
2170f64f9e71SJoe Perches 					    !(ifp->flags & IFA_F_TENTATIVE)) {
21714e3fd7a0SAlexey Dobriyan 						pkt_dev->cur_in6_saddr = ifp->addr;
21721da177e4SLinus Torvalds 						err = 0;
21731da177e4SLinus Torvalds 						break;
21741da177e4SLinus Torvalds 					}
21751da177e4SLinus Torvalds 				}
21761da177e4SLinus Torvalds 				read_unlock_bh(&idev->lock);
21771da177e4SLinus Torvalds 			}
21788814c4b5SYOSHIFUJI Hideaki 			rcu_read_unlock();
2179222f1806SLuiz Capitulino 			if (err)
2180f9467eaeSJoe Perches 				pr_err("ERROR: IPv6 link address not available\n");
21811da177e4SLinus Torvalds 		}
2182222f1806SLuiz Capitulino 	} else {
218368bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
218468bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct iphdr)
218568bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
218668bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
218768bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
218868bf9f0bSAmerigo Wang 		}
218968bf9f0bSAmerigo Wang 
21901da177e4SLinus Torvalds 		pkt_dev->saddr_min = 0;
21911da177e4SLinus Torvalds 		pkt_dev->saddr_max = 0;
21921da177e4SLinus Torvalds 		if (strlen(pkt_dev->src_min) == 0) {
21931da177e4SLinus Torvalds 
21941da177e4SLinus Torvalds 			struct in_device *in_dev;
21951da177e4SLinus Torvalds 
21961da177e4SLinus Torvalds 			rcu_read_lock();
2197e5ed6399SHerbert Xu 			in_dev = __in_dev_get_rcu(pkt_dev->odev);
21981da177e4SLinus Torvalds 			if (in_dev) {
21991da177e4SLinus Torvalds 				if (in_dev->ifa_list) {
2200222f1806SLuiz Capitulino 					pkt_dev->saddr_min =
2201222f1806SLuiz Capitulino 					    in_dev->ifa_list->ifa_address;
22021da177e4SLinus Torvalds 					pkt_dev->saddr_max = pkt_dev->saddr_min;
22031da177e4SLinus Torvalds 				}
22041da177e4SLinus Torvalds 			}
22051da177e4SLinus Torvalds 			rcu_read_unlock();
2206222f1806SLuiz Capitulino 		} else {
22071da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
22081da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
22091da177e4SLinus Torvalds 		}
22101da177e4SLinus Torvalds 
22111da177e4SLinus Torvalds 		pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
22121da177e4SLinus Torvalds 		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
22131da177e4SLinus Torvalds 	}
22141da177e4SLinus Torvalds 	/* Initialize current values. */
221568bf9f0bSAmerigo Wang 	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
221668bf9f0bSAmerigo Wang 	if (pkt_dev->min_pkt_size > pkt_dev->max_pkt_size)
221768bf9f0bSAmerigo Wang 		pkt_dev->max_pkt_size = pkt_dev->min_pkt_size;
221868bf9f0bSAmerigo Wang 
22191da177e4SLinus Torvalds 	pkt_dev->cur_dst_mac_offset = 0;
22201da177e4SLinus Torvalds 	pkt_dev->cur_src_mac_offset = 0;
22211da177e4SLinus Torvalds 	pkt_dev->cur_saddr = pkt_dev->saddr_min;
22221da177e4SLinus Torvalds 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
22231da177e4SLinus Torvalds 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
22241da177e4SLinus Torvalds 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
22251da177e4SLinus Torvalds 	pkt_dev->nflows = 0;
22261da177e4SLinus Torvalds }
22271da177e4SLinus Torvalds 
22281da177e4SLinus Torvalds 
2229fd29cf72SStephen Hemminger static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
2230fd29cf72SStephen Hemminger {
2231ef87979cSStephen Hemminger 	ktime_t start_time, end_time;
2232417bc4b8SEric Dumazet 	s64 remaining;
22332bc481cfSStephen Hemminger 	struct hrtimer_sleeper t;
2234fd29cf72SStephen Hemminger 
22352bc481cfSStephen Hemminger 	hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
22362bc481cfSStephen Hemminger 	hrtimer_set_expires(&t.timer, spin_until);
2237fd29cf72SStephen Hemminger 
223843d28b65SDaniel Turull 	remaining = ktime_to_ns(hrtimer_expires_remaining(&t.timer));
2239417bc4b8SEric Dumazet 	if (remaining <= 0) {
2240417bc4b8SEric Dumazet 		pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
22411da177e4SLinus Torvalds 		return;
2242417bc4b8SEric Dumazet 	}
22432bc481cfSStephen Hemminger 
2244398f382cSDaniel Borkmann 	start_time = ktime_get();
224533136d12SEric Dumazet 	if (remaining < 100000) {
224633136d12SEric Dumazet 		/* for small delays (<100us), just loop until limit is reached */
224733136d12SEric Dumazet 		do {
2248398f382cSDaniel Borkmann 			end_time = ktime_get();
2249398f382cSDaniel Borkmann 		} while (ktime_compare(end_time, spin_until) < 0);
225033136d12SEric Dumazet 	} else {
22512bc481cfSStephen Hemminger 		/* see do_nanosleep */
22522bc481cfSStephen Hemminger 		hrtimer_init_sleeper(&t, current);
22532bc481cfSStephen Hemminger 		do {
22542bc481cfSStephen Hemminger 			set_current_state(TASK_INTERRUPTIBLE);
22552bc481cfSStephen Hemminger 			hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
22562bc481cfSStephen Hemminger 			if (!hrtimer_active(&t.timer))
22572bc481cfSStephen Hemminger 				t.task = NULL;
22582bc481cfSStephen Hemminger 
22592bc481cfSStephen Hemminger 			if (likely(t.task))
22601da177e4SLinus Torvalds 				schedule();
22611da177e4SLinus Torvalds 
22622bc481cfSStephen Hemminger 			hrtimer_cancel(&t.timer);
22632bc481cfSStephen Hemminger 		} while (t.task && pkt_dev->running && !signal_pending(current));
22642bc481cfSStephen Hemminger 		__set_current_state(TASK_RUNNING);
2265398f382cSDaniel Borkmann 		end_time = ktime_get();
226633136d12SEric Dumazet 	}
2267ef87979cSStephen Hemminger 
2268ef87979cSStephen Hemminger 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
226907a0f0f0SDaniel Turull 	pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
22701da177e4SLinus Torvalds }
22711da177e4SLinus Torvalds 
227216dab72fSJamal Hadi Salim static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
227316dab72fSJamal Hadi Salim {
2274a553e4a6SJamal Hadi Salim 	pkt_dev->pkt_overhead = 0;
227516dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
227616dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
227716dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
227816dab72fSJamal Hadi Salim }
227916dab72fSJamal Hadi Salim 
2280648fda74SStephen Hemminger static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
2281007a531bSJamal Hadi Salim {
2282648fda74SStephen Hemminger 	return !!(pkt_dev->flows[flow].flags & F_INIT);
2283007a531bSJamal Hadi Salim }
2284007a531bSJamal Hadi Salim 
2285007a531bSJamal Hadi Salim static inline int f_pick(struct pktgen_dev *pkt_dev)
2286007a531bSJamal Hadi Salim {
2287007a531bSJamal Hadi Salim 	int flow = pkt_dev->curfl;
2288007a531bSJamal Hadi Salim 
2289007a531bSJamal Hadi Salim 	if (pkt_dev->flags & F_FLOW_SEQ) {
2290007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2291007a531bSJamal Hadi Salim 			/* reset time */
2292007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
22931211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
2294007a531bSJamal Hadi Salim 			pkt_dev->curfl += 1;
2295007a531bSJamal Hadi Salim 			if (pkt_dev->curfl >= pkt_dev->cflows)
2296007a531bSJamal Hadi Salim 				pkt_dev->curfl = 0; /*reset */
2297007a531bSJamal Hadi Salim 		}
2298007a531bSJamal Hadi Salim 	} else {
229933d7c5e5SAkinobu Mita 		flow = prandom_u32() % pkt_dev->cflows;
23001211a645SRobert Olsson 		pkt_dev->curfl = flow;
2301007a531bSJamal Hadi Salim 
23021211a645SRobert Olsson 		if (pkt_dev->flows[flow].count > pkt_dev->lflow) {
2303007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
23041211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
23051211a645SRobert Olsson 		}
2306007a531bSJamal Hadi Salim 	}
2307007a531bSJamal Hadi Salim 
2308007a531bSJamal Hadi Salim 	return pkt_dev->curfl;
2309007a531bSJamal Hadi Salim }
2310007a531bSJamal Hadi Salim 
2311a553e4a6SJamal Hadi Salim 
2312a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2313a553e4a6SJamal Hadi Salim /* If there was already an IPSEC SA, we keep it as is, else
2314a553e4a6SJamal Hadi Salim  * we go look for it ...
2315a553e4a6SJamal Hadi Salim */
2316bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
2317fea1ab0fSAdrian Bunk static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
2318a553e4a6SJamal Hadi Salim {
2319a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[flow].x;
23204e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id);
2321a553e4a6SJamal Hadi Salim 	if (!x) {
2322c454997eSFan Du 
2323c454997eSFan Du 		if (pkt_dev->spi) {
2324c454997eSFan Du 			/* We need as quick as possible to find the right SA
2325c454997eSFan Du 			 * Searching with minimum criteria to archieve this.
2326c454997eSFan Du 			 */
2327c454997eSFan Du 			x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
2328c454997eSFan Du 		} else {
2329a553e4a6SJamal Hadi Salim 			/* slow path: we dont already have xfrm_state */
23304e58a027SCong Wang 			x = xfrm_stateonly_find(pn->net, DUMMY_MARK,
23315447c5e4SAlexey Dobriyan 						(xfrm_address_t *)&pkt_dev->cur_daddr,
2332a553e4a6SJamal Hadi Salim 						(xfrm_address_t *)&pkt_dev->cur_saddr,
2333a553e4a6SJamal Hadi Salim 						AF_INET,
2334a553e4a6SJamal Hadi Salim 						pkt_dev->ipsmode,
2335a553e4a6SJamal Hadi Salim 						pkt_dev->ipsproto, 0);
2336c454997eSFan Du 		}
2337a553e4a6SJamal Hadi Salim 		if (x) {
2338a553e4a6SJamal Hadi Salim 			pkt_dev->flows[flow].x = x;
2339a553e4a6SJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
2340a553e4a6SJamal Hadi Salim 			pkt_dev->pkt_overhead += x->props.header_len;
2341a553e4a6SJamal Hadi Salim 		}
2342a553e4a6SJamal Hadi Salim 
2343a553e4a6SJamal Hadi Salim 	}
2344a553e4a6SJamal Hadi Salim }
2345a553e4a6SJamal Hadi Salim #endif
2346fd2ea0a7SDavid S. Miller static void set_cur_queue_map(struct pktgen_dev *pkt_dev)
2347fd2ea0a7SDavid S. Miller {
2348e6fce5b9SRobert Olsson 
2349e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
2350e6fce5b9SRobert Olsson 		pkt_dev->cur_queue_map = smp_processor_id();
2351e6fce5b9SRobert Olsson 
2352896a7cf8SEric Dumazet 	else if (pkt_dev->queue_map_min <= pkt_dev->queue_map_max) {
2353fd2ea0a7SDavid S. Miller 		__u16 t;
2354fd2ea0a7SDavid S. Miller 		if (pkt_dev->flags & F_QUEUE_MAP_RND) {
235533d7c5e5SAkinobu Mita 			t = prandom_u32() %
2356fd2ea0a7SDavid S. Miller 				(pkt_dev->queue_map_max -
2357fd2ea0a7SDavid S. Miller 				 pkt_dev->queue_map_min + 1)
2358fd2ea0a7SDavid S. Miller 				+ pkt_dev->queue_map_min;
2359fd2ea0a7SDavid S. Miller 		} else {
2360fd2ea0a7SDavid S. Miller 			t = pkt_dev->cur_queue_map + 1;
2361fd2ea0a7SDavid S. Miller 			if (t > pkt_dev->queue_map_max)
2362fd2ea0a7SDavid S. Miller 				t = pkt_dev->queue_map_min;
2363fd2ea0a7SDavid S. Miller 		}
2364fd2ea0a7SDavid S. Miller 		pkt_dev->cur_queue_map = t;
2365fd2ea0a7SDavid S. Miller 	}
2366bfdbc0acSRobert Olsson 	pkt_dev->cur_queue_map  = pkt_dev->cur_queue_map % pkt_dev->odev->real_num_tx_queues;
2367fd2ea0a7SDavid S. Miller }
2368fd2ea0a7SDavid S. Miller 
23691da177e4SLinus Torvalds /* Increment/randomize headers according to flags and current values
23701da177e4SLinus Torvalds  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
23711da177e4SLinus Torvalds  */
2372222f1806SLuiz Capitulino static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2373222f1806SLuiz Capitulino {
23741da177e4SLinus Torvalds 	__u32 imn;
23751da177e4SLinus Torvalds 	__u32 imx;
23761da177e4SLinus Torvalds 	int flow = 0;
23771da177e4SLinus Torvalds 
2378007a531bSJamal Hadi Salim 	if (pkt_dev->cflows)
2379007a531bSJamal Hadi Salim 		flow = f_pick(pkt_dev);
23801da177e4SLinus Torvalds 
23811da177e4SLinus Torvalds 	/*  Deal with source MAC */
23821da177e4SLinus Torvalds 	if (pkt_dev->src_mac_count > 1) {
23831da177e4SLinus Torvalds 		__u32 mc;
23841da177e4SLinus Torvalds 		__u32 tmp;
23851da177e4SLinus Torvalds 
23861da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACSRC_RND)
238733d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->src_mac_count;
23881da177e4SLinus Torvalds 		else {
23891da177e4SLinus Torvalds 			mc = pkt_dev->cur_src_mac_offset++;
2390ff2a79a5SRobert Olsson 			if (pkt_dev->cur_src_mac_offset >=
2391222f1806SLuiz Capitulino 			    pkt_dev->src_mac_count)
23921da177e4SLinus Torvalds 				pkt_dev->cur_src_mac_offset = 0;
23931da177e4SLinus Torvalds 		}
23941da177e4SLinus Torvalds 
23951da177e4SLinus Torvalds 		tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
23961da177e4SLinus Torvalds 		pkt_dev->hh[11] = tmp;
23971da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
23981da177e4SLinus Torvalds 		pkt_dev->hh[10] = tmp;
23991da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24001da177e4SLinus Torvalds 		pkt_dev->hh[9] = tmp;
24011da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24021da177e4SLinus Torvalds 		pkt_dev->hh[8] = tmp;
24031da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
24041da177e4SLinus Torvalds 		pkt_dev->hh[7] = tmp;
24051da177e4SLinus Torvalds 	}
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	/*  Deal with Destination MAC */
24081da177e4SLinus Torvalds 	if (pkt_dev->dst_mac_count > 1) {
24091da177e4SLinus Torvalds 		__u32 mc;
24101da177e4SLinus Torvalds 		__u32 tmp;
24111da177e4SLinus Torvalds 
24121da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACDST_RND)
241333d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->dst_mac_count;
24141da177e4SLinus Torvalds 
24151da177e4SLinus Torvalds 		else {
24161da177e4SLinus Torvalds 			mc = pkt_dev->cur_dst_mac_offset++;
2417ff2a79a5SRobert Olsson 			if (pkt_dev->cur_dst_mac_offset >=
2418222f1806SLuiz Capitulino 			    pkt_dev->dst_mac_count) {
24191da177e4SLinus Torvalds 				pkt_dev->cur_dst_mac_offset = 0;
24201da177e4SLinus Torvalds 			}
24211da177e4SLinus Torvalds 		}
24221da177e4SLinus Torvalds 
24231da177e4SLinus Torvalds 		tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
24241da177e4SLinus Torvalds 		pkt_dev->hh[5] = tmp;
24251da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
24261da177e4SLinus Torvalds 		pkt_dev->hh[4] = tmp;
24271da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24281da177e4SLinus Torvalds 		pkt_dev->hh[3] = tmp;
24291da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24301da177e4SLinus Torvalds 		pkt_dev->hh[2] = tmp;
24311da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
24321da177e4SLinus Torvalds 		pkt_dev->hh[1] = tmp;
24331da177e4SLinus Torvalds 	}
24341da177e4SLinus Torvalds 
2435ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND) {
243695c96174SEric Dumazet 		unsigned int i;
2437ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
2438ca6549afSSteven Whitehouse 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2439ca6549afSSteven Whitehouse 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
244033d7c5e5SAkinobu Mita 					     ((__force __be32)prandom_u32() &
2441ca6549afSSteven Whitehouse 						      htonl(0x000fffff));
2442ca6549afSSteven Whitehouse 	}
2443ca6549afSSteven Whitehouse 
244434954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
244533d7c5e5SAkinobu Mita 		pkt_dev->vlan_id = prandom_u32() & (4096 - 1);
244634954ddcSFrancesco Fondelli 	}
244734954ddcSFrancesco Fondelli 
244834954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
244933d7c5e5SAkinobu Mita 		pkt_dev->svlan_id = prandom_u32() & (4096 - 1);
245034954ddcSFrancesco Fondelli 	}
245134954ddcSFrancesco Fondelli 
24521da177e4SLinus Torvalds 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
24531da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPSRC_RND)
245433d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_src = prandom_u32() %
24555fa6fc76SStephen Hemminger 				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)
24565fa6fc76SStephen Hemminger 				+ pkt_dev->udp_src_min;
24571da177e4SLinus Torvalds 
24581da177e4SLinus Torvalds 		else {
24591da177e4SLinus Torvalds 			pkt_dev->cur_udp_src++;
24601da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
24611da177e4SLinus Torvalds 				pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
24621da177e4SLinus Torvalds 		}
24631da177e4SLinus Torvalds 	}
24641da177e4SLinus Torvalds 
24651da177e4SLinus Torvalds 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
24661da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPDST_RND) {
246733d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_dst = prandom_u32() %
24685fa6fc76SStephen Hemminger 				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
24695fa6fc76SStephen Hemminger 				+ pkt_dev->udp_dst_min;
2470222f1806SLuiz Capitulino 		} else {
24711da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst++;
24721da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
24731da177e4SLinus Torvalds 				pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
24741da177e4SLinus Torvalds 		}
24751da177e4SLinus Torvalds 	}
24761da177e4SLinus Torvalds 
24771da177e4SLinus Torvalds 	if (!(pkt_dev->flags & F_IPV6)) {
24781da177e4SLinus Torvalds 
247963adc6fbSStephen Hemminger 		imn = ntohl(pkt_dev->saddr_min);
248063adc6fbSStephen Hemminger 		imx = ntohl(pkt_dev->saddr_max);
248163adc6fbSStephen Hemminger 		if (imn < imx) {
24821da177e4SLinus Torvalds 			__u32 t;
24831da177e4SLinus Torvalds 			if (pkt_dev->flags & F_IPSRC_RND)
248433d7c5e5SAkinobu Mita 				t = prandom_u32() % (imx - imn) + imn;
24851da177e4SLinus Torvalds 			else {
24861da177e4SLinus Torvalds 				t = ntohl(pkt_dev->cur_saddr);
24871da177e4SLinus Torvalds 				t++;
248863adc6fbSStephen Hemminger 				if (t > imx)
24891da177e4SLinus Torvalds 					t = imn;
249063adc6fbSStephen Hemminger 
24911da177e4SLinus Torvalds 			}
24921da177e4SLinus Torvalds 			pkt_dev->cur_saddr = htonl(t);
24931da177e4SLinus Torvalds 		}
24941da177e4SLinus Torvalds 
2495007a531bSJamal Hadi Salim 		if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
24961da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
24971da177e4SLinus Torvalds 		} else {
2498252e3346SAl Viro 			imn = ntohl(pkt_dev->daddr_min);
2499252e3346SAl Viro 			imx = ntohl(pkt_dev->daddr_max);
2500252e3346SAl Viro 			if (imn < imx) {
25011da177e4SLinus Torvalds 				__u32 t;
2502252e3346SAl Viro 				__be32 s;
25031da177e4SLinus Torvalds 				if (pkt_dev->flags & F_IPDST_RND) {
25041da177e4SLinus Torvalds 
250570e3ba72SAkinobu Mita 					do {
250633d7c5e5SAkinobu Mita 						t = prandom_u32() %
250733d7c5e5SAkinobu Mita 							(imx - imn) + imn;
2508252e3346SAl Viro 						s = htonl(t);
250970e3ba72SAkinobu Mita 					} while (ipv4_is_loopback(s) ||
251070e3ba72SAkinobu Mita 						ipv4_is_multicast(s) ||
251170e3ba72SAkinobu Mita 						ipv4_is_lbcast(s) ||
251270e3ba72SAkinobu Mita 						ipv4_is_zeronet(s) ||
251370e3ba72SAkinobu Mita 						ipv4_is_local_multicast(s));
2514252e3346SAl Viro 					pkt_dev->cur_daddr = s;
2515252e3346SAl Viro 				} else {
25161da177e4SLinus Torvalds 					t = ntohl(pkt_dev->cur_daddr);
25171da177e4SLinus Torvalds 					t++;
25181da177e4SLinus Torvalds 					if (t > imx) {
25191da177e4SLinus Torvalds 						t = imn;
25201da177e4SLinus Torvalds 					}
25211da177e4SLinus Torvalds 					pkt_dev->cur_daddr = htonl(t);
25221da177e4SLinus Torvalds 				}
25231da177e4SLinus Torvalds 			}
25241da177e4SLinus Torvalds 			if (pkt_dev->cflows) {
2525007a531bSJamal Hadi Salim 				pkt_dev->flows[flow].flags |= F_INIT;
2526222f1806SLuiz Capitulino 				pkt_dev->flows[flow].cur_daddr =
2527222f1806SLuiz Capitulino 				    pkt_dev->cur_daddr;
2528a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2529a553e4a6SJamal Hadi Salim 				if (pkt_dev->flags & F_IPSEC_ON)
2530a553e4a6SJamal Hadi Salim 					get_ipsec_sa(pkt_dev, flow);
2531a553e4a6SJamal Hadi Salim #endif
25321da177e4SLinus Torvalds 				pkt_dev->nflows++;
25331da177e4SLinus Torvalds 			}
25341da177e4SLinus Torvalds 		}
2535222f1806SLuiz Capitulino 	} else {		/* IPV6 * */
2536222f1806SLuiz Capitulino 
253706e30411SJoe Perches 		if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {
25381da177e4SLinus Torvalds 			int i;
25391da177e4SLinus Torvalds 
25401da177e4SLinus Torvalds 			/* Only random destinations yet */
25411da177e4SLinus Torvalds 
25421da177e4SLinus Torvalds 			for (i = 0; i < 4; i++) {
25431da177e4SLinus Torvalds 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
254433d7c5e5SAkinobu Mita 				    (((__force __be32)prandom_u32() |
25451da177e4SLinus Torvalds 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
25461da177e4SLinus Torvalds 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
25471da177e4SLinus Torvalds 			}
25481da177e4SLinus Torvalds 		}
25491da177e4SLinus Torvalds 	}
25501da177e4SLinus Torvalds 
25511da177e4SLinus Torvalds 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
25521da177e4SLinus Torvalds 		__u32 t;
25531da177e4SLinus Torvalds 		if (pkt_dev->flags & F_TXSIZE_RND) {
255433d7c5e5SAkinobu Mita 			t = prandom_u32() %
25555fa6fc76SStephen Hemminger 				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
25565fa6fc76SStephen Hemminger 				+ pkt_dev->min_pkt_size;
2557222f1806SLuiz Capitulino 		} else {
25581da177e4SLinus Torvalds 			t = pkt_dev->cur_pkt_size + 1;
25591da177e4SLinus Torvalds 			if (t > pkt_dev->max_pkt_size)
25601da177e4SLinus Torvalds 				t = pkt_dev->min_pkt_size;
25611da177e4SLinus Torvalds 		}
25621da177e4SLinus Torvalds 		pkt_dev->cur_pkt_size = t;
25631da177e4SLinus Torvalds 	}
25641da177e4SLinus Torvalds 
2565fd2ea0a7SDavid S. Miller 	set_cur_queue_map(pkt_dev);
256645b270f8SRobert Olsson 
25671da177e4SLinus Torvalds 	pkt_dev->flows[flow].count++;
25681da177e4SLinus Torvalds }
25691da177e4SLinus Torvalds 
2570a553e4a6SJamal Hadi Salim 
2571a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
25725537a055SFengguang Wu static u32 pktgen_dst_metrics[RTAX_MAX + 1] = {
2573cf93d47eSFan Du 
2574cf93d47eSFan Du 	[RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */
2575cf93d47eSFan Du };
2576cf93d47eSFan Du 
2577a553e4a6SJamal Hadi Salim static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
2578a553e4a6SJamal Hadi Salim {
2579a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2580a553e4a6SJamal Hadi Salim 	int err = 0;
25816de9ace4SFan Du 	struct net *net = dev_net(pkt_dev->odev);
2582a553e4a6SJamal Hadi Salim 
2583a553e4a6SJamal Hadi Salim 	if (!x)
2584a553e4a6SJamal Hadi Salim 		return 0;
2585a553e4a6SJamal Hadi Salim 	/* XXX: we dont support tunnel mode for now until
2586a553e4a6SJamal Hadi Salim 	 * we resolve the dst issue */
2587cf93d47eSFan Du 	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))
2588a553e4a6SJamal Hadi Salim 		return 0;
2589a553e4a6SJamal Hadi Salim 
2590cf93d47eSFan Du 	/* But when user specify an valid SPI, transformation
2591cf93d47eSFan Du 	 * supports both transport/tunnel mode + ESP/AH type.
2592cf93d47eSFan Du 	 */
2593cf93d47eSFan Du 	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0))
2594cf93d47eSFan Du 		skb->_skb_refdst = (unsigned long)&pkt_dev->dst | SKB_DST_NOREF;
2595cf93d47eSFan Du 
2596cf93d47eSFan Du 	rcu_read_lock_bh();
259713996378SHerbert Xu 	err = x->outer_mode->output(x, skb);
2598cf93d47eSFan Du 	rcu_read_unlock_bh();
25996de9ace4SFan Du 	if (err) {
26006de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
2601a553e4a6SJamal Hadi Salim 		goto error;
26026de9ace4SFan Du 	}
2603a553e4a6SJamal Hadi Salim 	err = x->type->output(x, skb);
26046de9ace4SFan Du 	if (err) {
26056de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
2606a553e4a6SJamal Hadi Salim 		goto error;
26076de9ace4SFan Du 	}
26080af0a413SFan Du 	spin_lock_bh(&x->lock);
2609a553e4a6SJamal Hadi Salim 	x->curlft.bytes += skb->len;
2610a553e4a6SJamal Hadi Salim 	x->curlft.packets++;
26110af0a413SFan Du 	spin_unlock_bh(&x->lock);
2612a553e4a6SJamal Hadi Salim error:
2613a553e4a6SJamal Hadi Salim 	return err;
2614a553e4a6SJamal Hadi Salim }
2615a553e4a6SJamal Hadi Salim 
2616475ac1e4SStephen Hemminger static void free_SAs(struct pktgen_dev *pkt_dev)
2617a553e4a6SJamal Hadi Salim {
2618a553e4a6SJamal Hadi Salim 	if (pkt_dev->cflows) {
2619a553e4a6SJamal Hadi Salim 		/* let go of the SAs if we have them */
2620d6182223SPaul Gortmaker 		int i;
2621d6182223SPaul Gortmaker 		for (i = 0; i < pkt_dev->cflows; i++) {
2622a553e4a6SJamal Hadi Salim 			struct xfrm_state *x = pkt_dev->flows[i].x;
2623a553e4a6SJamal Hadi Salim 			if (x) {
2624a553e4a6SJamal Hadi Salim 				xfrm_state_put(x);
2625a553e4a6SJamal Hadi Salim 				pkt_dev->flows[i].x = NULL;
2626a553e4a6SJamal Hadi Salim 			}
2627a553e4a6SJamal Hadi Salim 		}
2628a553e4a6SJamal Hadi Salim 	}
2629a553e4a6SJamal Hadi Salim }
2630a553e4a6SJamal Hadi Salim 
2631475ac1e4SStephen Hemminger static int process_ipsec(struct pktgen_dev *pkt_dev,
2632a553e4a6SJamal Hadi Salim 			      struct sk_buff *skb, __be16 protocol)
2633a553e4a6SJamal Hadi Salim {
2634a553e4a6SJamal Hadi Salim 	if (pkt_dev->flags & F_IPSEC_ON) {
2635a553e4a6SJamal Hadi Salim 		struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2636a553e4a6SJamal Hadi Salim 		int nhead = 0;
2637a553e4a6SJamal Hadi Salim 		if (x) {
2638a553e4a6SJamal Hadi Salim 			int ret;
2639a553e4a6SJamal Hadi Salim 			__u8 *eth;
26403868204dSfan.du 			struct iphdr *iph;
26413868204dSfan.du 
2642a553e4a6SJamal Hadi Salim 			nhead = x->props.header_len - skb_headroom(skb);
2643a553e4a6SJamal Hadi Salim 			if (nhead > 0) {
2644a553e4a6SJamal Hadi Salim 				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
2645a553e4a6SJamal Hadi Salim 				if (ret < 0) {
2646f9467eaeSJoe Perches 					pr_err("Error expanding ipsec packet %d\n",
2647f9467eaeSJoe Perches 					       ret);
2648b4bb4ac8SIlpo Järvinen 					goto err;
2649a553e4a6SJamal Hadi Salim 				}
2650a553e4a6SJamal Hadi Salim 			}
2651a553e4a6SJamal Hadi Salim 
2652a553e4a6SJamal Hadi Salim 			/* ipsec is not expecting ll header */
2653a553e4a6SJamal Hadi Salim 			skb_pull(skb, ETH_HLEN);
2654a553e4a6SJamal Hadi Salim 			ret = pktgen_output_ipsec(skb, pkt_dev);
2655a553e4a6SJamal Hadi Salim 			if (ret) {
2656f9467eaeSJoe Perches 				pr_err("Error creating ipsec packet %d\n", ret);
2657b4bb4ac8SIlpo Järvinen 				goto err;
2658a553e4a6SJamal Hadi Salim 			}
2659a553e4a6SJamal Hadi Salim 			/* restore ll */
2660a553e4a6SJamal Hadi Salim 			eth = (__u8 *) skb_push(skb, ETH_HLEN);
2661a553e4a6SJamal Hadi Salim 			memcpy(eth, pkt_dev->hh, 12);
2662a553e4a6SJamal Hadi Salim 			*(u16 *) &eth[12] = protocol;
26633868204dSfan.du 
26643868204dSfan.du 			/* Update IPv4 header len as well as checksum value */
26653868204dSfan.du 			iph = ip_hdr(skb);
26663868204dSfan.du 			iph->tot_len = htons(skb->len - ETH_HLEN);
26673868204dSfan.du 			ip_send_check(iph);
2668a553e4a6SJamal Hadi Salim 		}
2669a553e4a6SJamal Hadi Salim 	}
2670a553e4a6SJamal Hadi Salim 	return 1;
2671b4bb4ac8SIlpo Järvinen err:
2672b4bb4ac8SIlpo Järvinen 	kfree_skb(skb);
2673b4bb4ac8SIlpo Järvinen 	return 0;
2674a553e4a6SJamal Hadi Salim }
2675a553e4a6SJamal Hadi Salim #endif
2676a553e4a6SJamal Hadi Salim 
2677ca6549afSSteven Whitehouse static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2678ca6549afSSteven Whitehouse {
267995c96174SEric Dumazet 	unsigned int i;
268063adc6fbSStephen Hemminger 	for (i = 0; i < pkt_dev->nr_labels; i++)
2681ca6549afSSteven Whitehouse 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
268263adc6fbSStephen Hemminger 
2683ca6549afSSteven Whitehouse 	mpls--;
2684ca6549afSSteven Whitehouse 	*mpls |= MPLS_STACK_BOTTOM;
2685ca6549afSSteven Whitehouse }
2686ca6549afSSteven Whitehouse 
26870f37c605SAl Viro static inline __be16 build_tci(unsigned int id, unsigned int cfi,
26880f37c605SAl Viro 			       unsigned int prio)
26890f37c605SAl Viro {
26900f37c605SAl Viro 	return htons(id | (cfi << 12) | (prio << 13));
26910f37c605SAl Viro }
26920f37c605SAl Viro 
269326ad7879SEric Dumazet static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
269426ad7879SEric Dumazet 				int datalen)
269526ad7879SEric Dumazet {
269626ad7879SEric Dumazet 	struct timeval timestamp;
269726ad7879SEric Dumazet 	struct pktgen_hdr *pgh;
269826ad7879SEric Dumazet 
269926ad7879SEric Dumazet 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
270026ad7879SEric Dumazet 	datalen -= sizeof(*pgh);
270126ad7879SEric Dumazet 
270226ad7879SEric Dumazet 	if (pkt_dev->nfrags <= 0) {
270305aebe2eSDaniel Turull 		memset(skb_put(skb, datalen), 0, datalen);
270426ad7879SEric Dumazet 	} else {
270526ad7879SEric Dumazet 		int frags = pkt_dev->nfrags;
270626ad7879SEric Dumazet 		int i, len;
27077d36a991Samit salecha 		int frag_len;
270826ad7879SEric Dumazet 
270926ad7879SEric Dumazet 
271026ad7879SEric Dumazet 		if (frags > MAX_SKB_FRAGS)
271126ad7879SEric Dumazet 			frags = MAX_SKB_FRAGS;
271226ad7879SEric Dumazet 		len = datalen - frags * PAGE_SIZE;
271326ad7879SEric Dumazet 		if (len > 0) {
271426ad7879SEric Dumazet 			memset(skb_put(skb, len), 0, len);
271526ad7879SEric Dumazet 			datalen = frags * PAGE_SIZE;
271626ad7879SEric Dumazet 		}
271726ad7879SEric Dumazet 
271826ad7879SEric Dumazet 		i = 0;
27197d36a991Samit salecha 		frag_len = (datalen/frags) < PAGE_SIZE ?
27207d36a991Samit salecha 			   (datalen/frags) : PAGE_SIZE;
272126ad7879SEric Dumazet 		while (datalen > 0) {
272226ad7879SEric Dumazet 			if (unlikely(!pkt_dev->page)) {
272326ad7879SEric Dumazet 				int node = numa_node_id();
272426ad7879SEric Dumazet 
272526ad7879SEric Dumazet 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
272626ad7879SEric Dumazet 					node = pkt_dev->node;
272726ad7879SEric Dumazet 				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
272826ad7879SEric Dumazet 				if (!pkt_dev->page)
272926ad7879SEric Dumazet 					break;
273026ad7879SEric Dumazet 			}
2731a0bec1cdSIan Campbell 			get_page(pkt_dev->page);
2732ea2ab693SIan Campbell 			skb_frag_set_page(skb, i, pkt_dev->page);
273326ad7879SEric Dumazet 			skb_shinfo(skb)->frags[i].page_offset = 0;
27347d36a991Samit salecha 			/*last fragment, fill rest of data*/
27357d36a991Samit salecha 			if (i == (frags - 1))
27369e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
27379e903e08SEric Dumazet 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
27387d36a991Samit salecha 			else
27399e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
27409e903e08SEric Dumazet 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
27419e903e08SEric Dumazet 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
27429e903e08SEric Dumazet 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
274326ad7879SEric Dumazet 			i++;
274426ad7879SEric Dumazet 			skb_shinfo(skb)->nr_frags = i;
274526ad7879SEric Dumazet 		}
274626ad7879SEric Dumazet 	}
274726ad7879SEric Dumazet 
274826ad7879SEric Dumazet 	/* Stamp the time, and sequence number,
274926ad7879SEric Dumazet 	 * convert them to network byte order
275026ad7879SEric Dumazet 	 */
275126ad7879SEric Dumazet 	pgh->pgh_magic = htonl(PKTGEN_MAGIC);
275226ad7879SEric Dumazet 	pgh->seq_num = htonl(pkt_dev->seq_num);
275326ad7879SEric Dumazet 
2754afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP) {
2755afb84b62SJesper Dangaard Brouer 		pgh->tv_sec = 0;
2756afb84b62SJesper Dangaard Brouer 		pgh->tv_usec = 0;
2757afb84b62SJesper Dangaard Brouer 	} else {
275826ad7879SEric Dumazet 		do_gettimeofday(&timestamp);
275926ad7879SEric Dumazet 		pgh->tv_sec = htonl(timestamp.tv_sec);
276026ad7879SEric Dumazet 		pgh->tv_usec = htonl(timestamp.tv_usec);
276126ad7879SEric Dumazet 	}
2762afb84b62SJesper Dangaard Brouer }
276326ad7879SEric Dumazet 
27647a6e288dSDaniel Borkmann static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
27657a6e288dSDaniel Borkmann 					struct pktgen_dev *pkt_dev,
27667a6e288dSDaniel Borkmann 					unsigned int extralen)
27677a6e288dSDaniel Borkmann {
27687a6e288dSDaniel Borkmann 	struct sk_buff *skb = NULL;
27697a6e288dSDaniel Borkmann 	unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
27707a6e288dSDaniel Borkmann 			    pkt_dev->pkt_overhead;
27717a6e288dSDaniel Borkmann 
27727a6e288dSDaniel Borkmann 	if (pkt_dev->flags & F_NODE) {
27737a6e288dSDaniel Borkmann 		int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
27747a6e288dSDaniel Borkmann 
27757a6e288dSDaniel Borkmann 		skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node);
27767a6e288dSDaniel Borkmann 		if (likely(skb)) {
27777a6e288dSDaniel Borkmann 			skb_reserve(skb, NET_SKB_PAD);
27787a6e288dSDaniel Borkmann 			skb->dev = dev;
27797a6e288dSDaniel Borkmann 		}
27807a6e288dSDaniel Borkmann 	} else {
27817a6e288dSDaniel Borkmann 		 skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
27827a6e288dSDaniel Borkmann 	}
27837a6e288dSDaniel Borkmann 
27847a6e288dSDaniel Borkmann 	return skb;
27857a6e288dSDaniel Borkmann }
27867a6e288dSDaniel Borkmann 
27871da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
27881da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
27891da177e4SLinus Torvalds {
27901da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
27911da177e4SLinus Torvalds 	__u8 *eth;
27921da177e4SLinus Torvalds 	struct udphdr *udph;
27931da177e4SLinus Torvalds 	int datalen, iplen;
27941da177e4SLinus Torvalds 	struct iphdr *iph;
2795d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IP);
2796ca6549afSSteven Whitehouse 	__be32 *mpls;
279734954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
279834954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
279934954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
280034954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2801fd2ea0a7SDavid S. Miller 	u16 queue_map;
2802ca6549afSSteven Whitehouse 
2803ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2804d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
28051da177e4SLinus Torvalds 
280634954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2807d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
280834954ddcSFrancesco Fondelli 
280964053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
281064053beeSRobert Olsson 	 * fields.
281164053beeSRobert Olsson 	 */
281264053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2813eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
281464053beeSRobert Olsson 
28157ac5459eSDavid S. Miller 	datalen = (odev->hard_header_len + 16) & ~0xf;
2816e99b99b4SRobert Olsson 
28177a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
28181da177e4SLinus Torvalds 	if (!skb) {
28191da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
28201da177e4SLinus Torvalds 		return NULL;
28211da177e4SLinus Torvalds 	}
28221da177e4SLinus Torvalds 
28237a6e288dSDaniel Borkmann 	prefetchw(skb->data);
28247ac5459eSDavid S. Miller 	skb_reserve(skb, datalen);
28251da177e4SLinus Torvalds 
28261da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
28271da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2828ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2829ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2830ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
283134954ddcSFrancesco Fondelli 
283234954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
283334954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
283434954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28350f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
28360f37c605SAl Viro 					       pkt_dev->svlan_cfi,
28370f37c605SAl Viro 					       pkt_dev->svlan_p);
283834954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2839d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
284034954ddcSFrancesco Fondelli 		}
284134954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28420f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
28430f37c605SAl Viro 				      pkt_dev->vlan_cfi,
28440f37c605SAl Viro 				      pkt_dev->vlan_p);
284534954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2846d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IP);
284734954ddcSFrancesco Fondelli 	}
284834954ddcSFrancesco Fondelli 
2849525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2850525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2851525cebedSThomas Graf 	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
2852525cebedSThomas Graf 
2853525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2854525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2855fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
28569e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
28579e50e3acSJohn Fastabend 
28581da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2859252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
28601da177e4SLinus Torvalds 
2861ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2862ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
286316dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
28646af773e7SNishank Trivedi 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
28651da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
28661da177e4SLinus Torvalds 
28671da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
28681da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
28691da177e4SLinus Torvalds 	udph->len = htons(datalen + 8);	/* DATA + udphdr */
2870c26bf4a5SThomas Graf 	udph->check = 0;
28711da177e4SLinus Torvalds 
28721da177e4SLinus Torvalds 	iph->ihl = 5;
28731da177e4SLinus Torvalds 	iph->version = 4;
28741da177e4SLinus Torvalds 	iph->ttl = 32;
28751ca7768cSFrancesco Fondelli 	iph->tos = pkt_dev->tos;
28761da177e4SLinus Torvalds 	iph->protocol = IPPROTO_UDP;	/* UDP */
28771da177e4SLinus Torvalds 	iph->saddr = pkt_dev->cur_saddr;
28781da177e4SLinus Torvalds 	iph->daddr = pkt_dev->cur_daddr;
287966ed1e5eSEric Dumazet 	iph->id = htons(pkt_dev->ip_id);
288066ed1e5eSEric Dumazet 	pkt_dev->ip_id++;
28811da177e4SLinus Torvalds 	iph->frag_off = 0;
28821da177e4SLinus Torvalds 	iplen = 20 + 8 + datalen;
28831da177e4SLinus Torvalds 	iph->tot_len = htons(iplen);
288403c633e7SThomas Graf 	ip_send_check(iph);
2885ca6549afSSteven Whitehouse 	skb->protocol = protocol;
28861da177e4SLinus Torvalds 	skb->dev = odev;
28871da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
2888c26bf4a5SThomas Graf 
28897744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
28907744b5f3SSabrina Dubroca 
2891c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
2892c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
2893c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V4_CSUM) {
2894c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
2895c26bf4a5SThomas Graf 		skb->csum = 0;
28967744b5f3SSabrina Dubroca 		udp4_hwcsum(skb, iph->saddr, iph->daddr);
2897c26bf4a5SThomas Graf 	} else {
28987744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), datalen + 8, 0);
2899c26bf4a5SThomas Graf 
2900c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
29017744b5f3SSabrina Dubroca 		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
2902c26bf4a5SThomas Graf 						datalen + 8, IPPROTO_UDP, csum);
2903c26bf4a5SThomas Graf 
2904c26bf4a5SThomas Graf 		if (udph->check == 0)
2905c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
2906c26bf4a5SThomas Graf 	}
2907c26bf4a5SThomas Graf 
2908a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2909a553e4a6SJamal Hadi Salim 	if (!process_ipsec(pkt_dev, skb, protocol))
2910a553e4a6SJamal Hadi Salim 		return NULL;
2911a553e4a6SJamal Hadi Salim #endif
2912a553e4a6SJamal Hadi Salim 
29131da177e4SLinus Torvalds 	return skb;
29141da177e4SLinus Torvalds }
29151da177e4SLinus Torvalds 
29161da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
29171da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
29181da177e4SLinus Torvalds {
29191da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
29201da177e4SLinus Torvalds 	__u8 *eth;
29211da177e4SLinus Torvalds 	struct udphdr *udph;
2922c26bf4a5SThomas Graf 	int datalen, udplen;
29231da177e4SLinus Torvalds 	struct ipv6hdr *iph;
2924d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IPV6);
2925ca6549afSSteven Whitehouse 	__be32 *mpls;
292634954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
292734954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
292834954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
292934954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2930fd2ea0a7SDavid S. Miller 	u16 queue_map;
2931ca6549afSSteven Whitehouse 
2932ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2933d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
29341da177e4SLinus Torvalds 
293534954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2936d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
293734954ddcSFrancesco Fondelli 
293864053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
293964053beeSRobert Olsson 	 * fields.
294064053beeSRobert Olsson 	 */
294164053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2942eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
294364053beeSRobert Olsson 
29447a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, 16);
29451da177e4SLinus Torvalds 	if (!skb) {
29461da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
29471da177e4SLinus Torvalds 		return NULL;
29481da177e4SLinus Torvalds 	}
29491da177e4SLinus Torvalds 
29507a6e288dSDaniel Borkmann 	prefetchw(skb->data);
29511da177e4SLinus Torvalds 	skb_reserve(skb, 16);
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
29541da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2955ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2956ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2957ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
295834954ddcSFrancesco Fondelli 
295934954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
296034954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
296134954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29620f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
29630f37c605SAl Viro 					       pkt_dev->svlan_cfi,
29640f37c605SAl Viro 					       pkt_dev->svlan_p);
296534954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2966d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
296734954ddcSFrancesco Fondelli 		}
296834954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29690f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
29700f37c605SAl Viro 				      pkt_dev->vlan_cfi,
29710f37c605SAl Viro 				      pkt_dev->vlan_p);
297234954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2973d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
297434954ddcSFrancesco Fondelli 	}
297534954ddcSFrancesco Fondelli 
2976525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2977525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2978525cebedSThomas Graf 	iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
2979525cebedSThomas Graf 
2980525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2981525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2982fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
29839e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
29841da177e4SLinus Torvalds 
29851da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2986252e3346SAl Viro 	*(__be16 *) &eth[12] = protocol;
29871da177e4SLinus Torvalds 
2988ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2989ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 -
2990ca6549afSSteven Whitehouse 		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
299116dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
29921da177e4SLinus Torvalds 
29935aa8b572SAmerigo Wang 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr)) {
29941da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
2995e87cc472SJoe Perches 		net_info_ratelimited("increased datalen to %d\n", datalen);
29961da177e4SLinus Torvalds 	}
29971da177e4SLinus Torvalds 
2998c26bf4a5SThomas Graf 	udplen = datalen + sizeof(struct udphdr);
29991da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
30001da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
3001c26bf4a5SThomas Graf 	udph->len = htons(udplen);
3002c26bf4a5SThomas Graf 	udph->check = 0;
30031da177e4SLinus Torvalds 
3004d5f1ce9aSStephen Hemminger 	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
30051da177e4SLinus Torvalds 
30061ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
30071ca7768cSFrancesco Fondelli 		/* Version + traffic class + flow (0) */
3008252e3346SAl Viro 		*(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
30091ca7768cSFrancesco Fondelli 	}
30101ca7768cSFrancesco Fondelli 
30111da177e4SLinus Torvalds 	iph->hop_limit = 32;
30121da177e4SLinus Torvalds 
3013c26bf4a5SThomas Graf 	iph->payload_len = htons(udplen);
30141da177e4SLinus Torvalds 	iph->nexthdr = IPPROTO_UDP;
30151da177e4SLinus Torvalds 
30164e3fd7a0SAlexey Dobriyan 	iph->daddr = pkt_dev->cur_in6_daddr;
30174e3fd7a0SAlexey Dobriyan 	iph->saddr = pkt_dev->cur_in6_saddr;
30181da177e4SLinus Torvalds 
3019ca6549afSSteven Whitehouse 	skb->protocol = protocol;
30201da177e4SLinus Torvalds 	skb->dev = odev;
30211da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
30221da177e4SLinus Torvalds 
30237744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
30247744b5f3SSabrina Dubroca 
3025c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
3026c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
3027c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V6_CSUM) {
3028c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
3029c26bf4a5SThomas Graf 		skb->csum_start = skb_transport_header(skb) - skb->head;
3030c26bf4a5SThomas Graf 		skb->csum_offset = offsetof(struct udphdr, check);
3031c26bf4a5SThomas Graf 		udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0);
3032c26bf4a5SThomas Graf 	} else {
30337744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), udplen, 0);
3034c26bf4a5SThomas Graf 
3035c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
3036c26bf4a5SThomas Graf 		udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum);
3037c26bf4a5SThomas Graf 
3038c26bf4a5SThomas Graf 		if (udph->check == 0)
3039c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
3040c26bf4a5SThomas Graf 	}
3041c26bf4a5SThomas Graf 
30421da177e4SLinus Torvalds 	return skb;
30431da177e4SLinus Torvalds }
30441da177e4SLinus Torvalds 
3045475ac1e4SStephen Hemminger static struct sk_buff *fill_packet(struct net_device *odev,
30461da177e4SLinus Torvalds 				   struct pktgen_dev *pkt_dev)
30471da177e4SLinus Torvalds {
30481da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
30491da177e4SLinus Torvalds 		return fill_packet_ipv6(odev, pkt_dev);
30501da177e4SLinus Torvalds 	else
30511da177e4SLinus Torvalds 		return fill_packet_ipv4(odev, pkt_dev);
30521da177e4SLinus Torvalds }
30531da177e4SLinus Torvalds 
30541da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
30551da177e4SLinus Torvalds {
30561da177e4SLinus Torvalds 	pkt_dev->seq_num = 1;
30571da177e4SLinus Torvalds 	pkt_dev->idle_acc = 0;
30581da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
30591da177e4SLinus Torvalds 	pkt_dev->tx_bytes = 0;
30601da177e4SLinus Torvalds 	pkt_dev->errors = 0;
30611da177e4SLinus Torvalds }
30621da177e4SLinus Torvalds 
30631da177e4SLinus Torvalds /* Set up structure for sending pkts, clear counters */
30641da177e4SLinus Torvalds 
30651da177e4SLinus Torvalds static void pktgen_run(struct pktgen_thread *t)
30661da177e4SLinus Torvalds {
3067c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
30681da177e4SLinus Torvalds 	int started = 0;
30691da177e4SLinus Torvalds 
3070f9467eaeSJoe Perches 	func_enter();
30711da177e4SLinus Torvalds 
30728788370aSJesper Dangaard Brouer 	rcu_read_lock();
30738788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds 		/*
30761da177e4SLinus Torvalds 		 * setup odev and create initial packet.
30771da177e4SLinus Torvalds 		 */
30781da177e4SLinus Torvalds 		pktgen_setup_inject(pkt_dev);
30791da177e4SLinus Torvalds 
30801da177e4SLinus Torvalds 		if (pkt_dev->odev) {
30811da177e4SLinus Torvalds 			pktgen_clear_counters(pkt_dev);
30821da177e4SLinus Torvalds 			pkt_dev->skb = NULL;
3083398f382cSDaniel Borkmann 			pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
3084fd29cf72SStephen Hemminger 
308516dab72fSJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
30861da177e4SLinus Torvalds 
30871da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Starting");
30888788370aSJesper Dangaard Brouer 			pkt_dev->running = 1;	/* Cranke yeself! */
30891da177e4SLinus Torvalds 			started++;
3090222f1806SLuiz Capitulino 		} else
30911da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Error starting");
30921da177e4SLinus Torvalds 	}
30938788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3094222f1806SLuiz Capitulino 	if (started)
3095222f1806SLuiz Capitulino 		t->control &= ~(T_STOP);
30961da177e4SLinus Torvalds }
30971da177e4SLinus Torvalds 
30984e58a027SCong Wang static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn)
30991da177e4SLinus Torvalds {
3100cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31011da177e4SLinus Torvalds 
3102f9467eaeSJoe Perches 	func_enter();
31031da177e4SLinus Torvalds 
31046146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3105cdcdbe0bSLuiz Capitulino 
31064e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
310795ed63f7SArthur Kepner 		t->control |= T_STOP;
3108cdcdbe0bSLuiz Capitulino 
31096146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31101da177e4SLinus Torvalds }
31111da177e4SLinus Torvalds 
3112648fda74SStephen Hemminger static int thread_is_running(const struct pktgen_thread *t)
31131da177e4SLinus Torvalds {
3114648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
31151da177e4SLinus Torvalds 
31168788370aSJesper Dangaard Brouer 	rcu_read_lock();
31178788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
31188788370aSJesper Dangaard Brouer 		if (pkt_dev->running) {
31198788370aSJesper Dangaard Brouer 			rcu_read_unlock();
3120648fda74SStephen Hemminger 			return 1;
31218788370aSJesper Dangaard Brouer 		}
31228788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3123648fda74SStephen Hemminger 	return 0;
31241da177e4SLinus Torvalds }
31251da177e4SLinus Torvalds 
31261da177e4SLinus Torvalds static int pktgen_wait_thread_run(struct pktgen_thread *t)
31271da177e4SLinus Torvalds {
31281da177e4SLinus Torvalds 	while (thread_is_running(t)) {
31291da177e4SLinus Torvalds 
31301da177e4SLinus Torvalds 		msleep_interruptible(100);
31311da177e4SLinus Torvalds 
31321da177e4SLinus Torvalds 		if (signal_pending(current))
31331da177e4SLinus Torvalds 			goto signal;
31341da177e4SLinus Torvalds 	}
31351da177e4SLinus Torvalds 	return 1;
31361da177e4SLinus Torvalds signal:
31371da177e4SLinus Torvalds 	return 0;
31381da177e4SLinus Torvalds }
31391da177e4SLinus Torvalds 
31404e58a027SCong Wang static int pktgen_wait_all_threads_run(struct pktgen_net *pn)
31411da177e4SLinus Torvalds {
3142cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31431da177e4SLinus Torvalds 	int sig = 1;
31441da177e4SLinus Torvalds 
31456146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3146cdcdbe0bSLuiz Capitulino 
31474e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
31481da177e4SLinus Torvalds 		sig = pktgen_wait_thread_run(t);
3149222f1806SLuiz Capitulino 		if (sig == 0)
3150222f1806SLuiz Capitulino 			break;
31511da177e4SLinus Torvalds 	}
3152cdcdbe0bSLuiz Capitulino 
3153cdcdbe0bSLuiz Capitulino 	if (sig == 0)
31544e58a027SCong Wang 		list_for_each_entry(t, &pn->pktgen_threads, th_list)
31551da177e4SLinus Torvalds 			t->control |= (T_STOP);
3156cdcdbe0bSLuiz Capitulino 
31576146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31581da177e4SLinus Torvalds 	return sig;
31591da177e4SLinus Torvalds }
31601da177e4SLinus Torvalds 
31614e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn)
31621da177e4SLinus Torvalds {
3163cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31641da177e4SLinus Torvalds 
3165f9467eaeSJoe Perches 	func_enter();
31661da177e4SLinus Torvalds 
31676146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
31681da177e4SLinus Torvalds 
31694e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
31701da177e4SLinus Torvalds 		t->control |= (T_RUN);
3171cdcdbe0bSLuiz Capitulino 
31726146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31731da177e4SLinus Torvalds 
317463adc6fbSStephen Hemminger 	/* Propagate thread->control  */
317563adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
31761da177e4SLinus Torvalds 
31774e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
31781da177e4SLinus Torvalds }
31791da177e4SLinus Torvalds 
31804e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn)
3181eb37b41cSJesse Brandeburg {
3182eb37b41cSJesse Brandeburg 	struct pktgen_thread *t;
3183eb37b41cSJesse Brandeburg 
3184f9467eaeSJoe Perches 	func_enter();
3185eb37b41cSJesse Brandeburg 
3186eb37b41cSJesse Brandeburg 	mutex_lock(&pktgen_thread_lock);
3187eb37b41cSJesse Brandeburg 
31884e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
3189eb37b41cSJesse Brandeburg 		t->control |= (T_REMDEVALL);
3190eb37b41cSJesse Brandeburg 
3191eb37b41cSJesse Brandeburg 	mutex_unlock(&pktgen_thread_lock);
3192eb37b41cSJesse Brandeburg 
319363adc6fbSStephen Hemminger 	/* Propagate thread->control  */
319463adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
3195eb37b41cSJesse Brandeburg 
31964e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
3197eb37b41cSJesse Brandeburg }
3198eb37b41cSJesse Brandeburg 
31991da177e4SLinus Torvalds static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
32001da177e4SLinus Torvalds {
3201fd29cf72SStephen Hemminger 	__u64 bps, mbps, pps;
32021da177e4SLinus Torvalds 	char *p = pkt_dev->result;
3203fd29cf72SStephen Hemminger 	ktime_t elapsed = ktime_sub(pkt_dev->stopped_at,
3204fd29cf72SStephen Hemminger 				    pkt_dev->started_at);
3205fd29cf72SStephen Hemminger 	ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
32061da177e4SLinus Torvalds 
320703a14ab1SDaniel Turull 	p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
3208fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(elapsed),
3209fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
3210fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(idle),
32111da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->sofar,
32121da177e4SLinus Torvalds 		     pkt_dev->cur_pkt_size, nr_frags);
32131da177e4SLinus Torvalds 
3214fd29cf72SStephen Hemminger 	pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC,
3215fd29cf72SStephen Hemminger 			ktime_to_ns(elapsed));
32161da177e4SLinus Torvalds 
32171da177e4SLinus Torvalds 	bps = pps * 8 * pkt_dev->cur_pkt_size;
32181da177e4SLinus Torvalds 
32191da177e4SLinus Torvalds 	mbps = bps;
32201da177e4SLinus Torvalds 	do_div(mbps, 1000000);
32211da177e4SLinus Torvalds 	p += sprintf(p, "  %llupps %lluMb/sec (%llubps) errors: %llu",
32221da177e4SLinus Torvalds 		     (unsigned long long)pps,
32231da177e4SLinus Torvalds 		     (unsigned long long)mbps,
32241da177e4SLinus Torvalds 		     (unsigned long long)bps,
32251da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->errors);
32261da177e4SLinus Torvalds }
32271da177e4SLinus Torvalds 
32281da177e4SLinus Torvalds /* Set stopped-at timer, remove from running list, do counters & statistics */
32291da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
32301da177e4SLinus Torvalds {
3231222f1806SLuiz Capitulino 	int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
32321da177e4SLinus Torvalds 
32331da177e4SLinus Torvalds 	if (!pkt_dev->running) {
3234294a0b7fSJoe Perches 		pr_warn("interface: %s is already stopped\n",
3235f9467eaeSJoe Perches 			pkt_dev->odevname);
32361da177e4SLinus Torvalds 		return -EINVAL;
32371da177e4SLinus Torvalds 	}
32381da177e4SLinus Torvalds 
32398788370aSJesper Dangaard Brouer 	pkt_dev->running = 0;
32403bda06a3SStephen Hemminger 	kfree_skb(pkt_dev->skb);
32413bda06a3SStephen Hemminger 	pkt_dev->skb = NULL;
3242398f382cSDaniel Borkmann 	pkt_dev->stopped_at = ktime_get();
32431da177e4SLinus Torvalds 
324495ed63f7SArthur Kepner 	show_results(pkt_dev, nr_frags);
32451da177e4SLinus Torvalds 
32461da177e4SLinus Torvalds 	return 0;
32471da177e4SLinus Torvalds }
32481da177e4SLinus Torvalds 
32491da177e4SLinus Torvalds static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
32501da177e4SLinus Torvalds {
3251c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev, *best = NULL;
32521da177e4SLinus Torvalds 
32538788370aSJesper Dangaard Brouer 	rcu_read_lock();
32548788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3255c26a8016SLuiz Capitulino 		if (!pkt_dev->running)
3256222f1806SLuiz Capitulino 			continue;
3257222f1806SLuiz Capitulino 		if (best == NULL)
3258c26a8016SLuiz Capitulino 			best = pkt_dev;
3259398f382cSDaniel Borkmann 		else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
3260c26a8016SLuiz Capitulino 			best = pkt_dev;
32611da177e4SLinus Torvalds 	}
32628788370aSJesper Dangaard Brouer 	rcu_read_unlock();
32638788370aSJesper Dangaard Brouer 
32641da177e4SLinus Torvalds 	return best;
32651da177e4SLinus Torvalds }
32661da177e4SLinus Torvalds 
3267222f1806SLuiz Capitulino static void pktgen_stop(struct pktgen_thread *t)
3268222f1806SLuiz Capitulino {
3269c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
32701da177e4SLinus Torvalds 
3271f9467eaeSJoe Perches 	func_enter();
32721da177e4SLinus Torvalds 
32738788370aSJesper Dangaard Brouer 	rcu_read_lock();
32741da177e4SLinus Torvalds 
32758788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3276c26a8016SLuiz Capitulino 		pktgen_stop_device(pkt_dev);
327795ed63f7SArthur Kepner 	}
327895ed63f7SArthur Kepner 
32798788370aSJesper Dangaard Brouer 	rcu_read_unlock();
328095ed63f7SArthur Kepner }
328195ed63f7SArthur Kepner 
328295ed63f7SArthur Kepner /*
328395ed63f7SArthur Kepner  * one of our devices needs to be removed - find it
328495ed63f7SArthur Kepner  * and remove it
328595ed63f7SArthur Kepner  */
328695ed63f7SArthur Kepner static void pktgen_rem_one_if(struct pktgen_thread *t)
328795ed63f7SArthur Kepner {
3288c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3289c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
329095ed63f7SArthur Kepner 
3291f9467eaeSJoe Perches 	func_enter();
329295ed63f7SArthur Kepner 
3293c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3294c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
329595ed63f7SArthur Kepner 
3296222f1806SLuiz Capitulino 		if (!cur->removal_mark)
3297222f1806SLuiz Capitulino 			continue;
329895ed63f7SArthur Kepner 
329995ed63f7SArthur Kepner 		kfree_skb(cur->skb);
330095ed63f7SArthur Kepner 		cur->skb = NULL;
330195ed63f7SArthur Kepner 
330295ed63f7SArthur Kepner 		pktgen_remove_device(t, cur);
330395ed63f7SArthur Kepner 
330495ed63f7SArthur Kepner 		break;
330595ed63f7SArthur Kepner 	}
33061da177e4SLinus Torvalds }
33071da177e4SLinus Torvalds 
33081da177e4SLinus Torvalds static void pktgen_rem_all_ifs(struct pktgen_thread *t)
33091da177e4SLinus Torvalds {
3310c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3311c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
33121da177e4SLinus Torvalds 
3313f9467eaeSJoe Perches 	func_enter();
3314f9467eaeSJoe Perches 
33151da177e4SLinus Torvalds 	/* Remove all devices, free mem */
33161da177e4SLinus Torvalds 
3317c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3318c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
331995ed63f7SArthur Kepner 
332095ed63f7SArthur Kepner 		kfree_skb(cur->skb);
332195ed63f7SArthur Kepner 		cur->skb = NULL;
332295ed63f7SArthur Kepner 
33231da177e4SLinus Torvalds 		pktgen_remove_device(t, cur);
33241da177e4SLinus Torvalds 	}
33251da177e4SLinus Torvalds }
33261da177e4SLinus Torvalds 
33271da177e4SLinus Torvalds static void pktgen_rem_thread(struct pktgen_thread *t)
33281da177e4SLinus Torvalds {
33291da177e4SLinus Torvalds 	/* Remove from the thread list */
33304e58a027SCong Wang 	remove_proc_entry(t->tsk->comm, t->net->proc_dir);
33311da177e4SLinus Torvalds }
33321da177e4SLinus Torvalds 
3333ef87979cSStephen Hemminger static void pktgen_resched(struct pktgen_dev *pkt_dev)
33343791decbSStephen Hemminger {
3335398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
33363791decbSStephen Hemminger 	schedule();
3337398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
33383791decbSStephen Hemminger }
33393791decbSStephen Hemminger 
3340ef87979cSStephen Hemminger static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
3341ef87979cSStephen Hemminger {
3342398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
3343ef87979cSStephen Hemminger 
3344ef87979cSStephen Hemminger 	while (atomic_read(&(pkt_dev->skb->users)) != 1) {
3345ef87979cSStephen Hemminger 		if (signal_pending(current))
3346ef87979cSStephen Hemminger 			break;
3347ef87979cSStephen Hemminger 
3348ef87979cSStephen Hemminger 		if (need_resched())
3349ef87979cSStephen Hemminger 			pktgen_resched(pkt_dev);
3350ef87979cSStephen Hemminger 		else
3351ef87979cSStephen Hemminger 			cpu_relax();
3352ef87979cSStephen Hemminger 	}
3353398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
3354ef87979cSStephen Hemminger }
3355fd29cf72SStephen Hemminger 
3356475ac1e4SStephen Hemminger static void pktgen_xmit(struct pktgen_dev *pkt_dev)
33571da177e4SLinus Torvalds {
335838b2cf29SAlexei Starovoitov 	unsigned int burst = ACCESS_ONCE(pkt_dev->burst);
335900829823SStephen Hemminger 	struct net_device *odev = pkt_dev->odev;
3360fd2ea0a7SDavid S. Miller 	struct netdev_queue *txq;
3361*62f64aedSAlexei Starovoitov 	struct sk_buff *skb;
33621da177e4SLinus Torvalds 	int ret;
33631da177e4SLinus Torvalds 
3364ef87979cSStephen Hemminger 	/* If device is offline, then don't send */
3365ef87979cSStephen Hemminger 	if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
33661da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
33673791decbSStephen Hemminger 		return;
33681da177e4SLinus Torvalds 	}
33691da177e4SLinus Torvalds 
3370ef87979cSStephen Hemminger 	/* This is max DELAY, this has special meaning of
3371ef87979cSStephen Hemminger 	 * "never transmit"
3372ef87979cSStephen Hemminger 	 */
3373ef87979cSStephen Hemminger 	if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
3374398f382cSDaniel Borkmann 		pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
3375ef87979cSStephen Hemminger 		return;
3376ef87979cSStephen Hemminger 	}
3377ef87979cSStephen Hemminger 
3378ef87979cSStephen Hemminger 	/* If no skb or clone count exhausted then get new one */
33797d7bb1cfSStephen Hemminger 	if (!pkt_dev->skb || (pkt_dev->last_ok &&
33807d7bb1cfSStephen Hemminger 			      ++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
33811da177e4SLinus Torvalds 		/* build a new pkt */
33821da177e4SLinus Torvalds 		kfree_skb(pkt_dev->skb);
33831da177e4SLinus Torvalds 
33841da177e4SLinus Torvalds 		pkt_dev->skb = fill_packet(odev, pkt_dev);
33851da177e4SLinus Torvalds 		if (pkt_dev->skb == NULL) {
3386f9467eaeSJoe Perches 			pr_err("ERROR: couldn't allocate skb in fill_packet\n");
33871da177e4SLinus Torvalds 			schedule();
33881da177e4SLinus Torvalds 			pkt_dev->clone_count--;	/* back out increment, OOM */
33893791decbSStephen Hemminger 			return;
33901da177e4SLinus Torvalds 		}
3391baac8564SEric Dumazet 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
33921da177e4SLinus Torvalds 		pkt_dev->allocated_skbs++;
33931da177e4SLinus Torvalds 		pkt_dev->clone_count = 0;	/* reset counter */
33941da177e4SLinus Torvalds 	}
33951da177e4SLinus Torvalds 
3396ef87979cSStephen Hemminger 	if (pkt_dev->delay && pkt_dev->last_ok)
3397ef87979cSStephen Hemminger 		spin(pkt_dev, pkt_dev->next_tx);
3398ef87979cSStephen Hemminger 
3399*62f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
3400*62f64aedSAlexei Starovoitov 		skb = pkt_dev->skb;
3401*62f64aedSAlexei Starovoitov 		skb->protocol = eth_type_trans(skb, skb->dev);
3402*62f64aedSAlexei Starovoitov 		atomic_add(burst, &skb->users);
3403*62f64aedSAlexei Starovoitov 		local_bh_disable();
3404*62f64aedSAlexei Starovoitov 		do {
3405*62f64aedSAlexei Starovoitov 			ret = netif_receive_skb(skb);
3406*62f64aedSAlexei Starovoitov 			if (ret == NET_RX_DROP)
3407*62f64aedSAlexei Starovoitov 				pkt_dev->errors++;
3408*62f64aedSAlexei Starovoitov 			pkt_dev->sofar++;
3409*62f64aedSAlexei Starovoitov 			pkt_dev->seq_num++;
3410*62f64aedSAlexei Starovoitov 			if (atomic_read(&skb->users) != burst) {
3411*62f64aedSAlexei Starovoitov 				/* skb was queued by rps/rfs or taps,
3412*62f64aedSAlexei Starovoitov 				 * so cannot reuse this skb
3413*62f64aedSAlexei Starovoitov 				 */
3414*62f64aedSAlexei Starovoitov 				atomic_sub(burst - 1, &skb->users);
3415*62f64aedSAlexei Starovoitov 				/* get out of the loop and wait
3416*62f64aedSAlexei Starovoitov 				 * until skb is consumed
3417*62f64aedSAlexei Starovoitov 				 */
3418*62f64aedSAlexei Starovoitov 				pkt_dev->last_ok = 1;
3419*62f64aedSAlexei Starovoitov 				break;
3420*62f64aedSAlexei Starovoitov 			}
3421*62f64aedSAlexei Starovoitov 			/* skb was 'freed' by stack, so clean few
3422*62f64aedSAlexei Starovoitov 			 * bits and reuse it
3423*62f64aedSAlexei Starovoitov 			 */
3424*62f64aedSAlexei Starovoitov #ifdef CONFIG_NET_CLS_ACT
3425*62f64aedSAlexei Starovoitov 			skb->tc_verd = 0; /* reset reclass/redir ttl */
3426*62f64aedSAlexei Starovoitov #endif
3427*62f64aedSAlexei Starovoitov 		} while (--burst > 0);
3428*62f64aedSAlexei Starovoitov 		goto out; /* Skips xmit_mode M_START_XMIT */
3429*62f64aedSAlexei Starovoitov 	}
3430*62f64aedSAlexei Starovoitov 
343110c51b56SDaniel Borkmann 	txq = skb_get_tx_queue(odev, pkt_dev->skb);
3432fd2ea0a7SDavid S. Miller 
34330f2eea4bSDaniel Borkmann 	local_bh_disable();
34340f2eea4bSDaniel Borkmann 
34350f2eea4bSDaniel Borkmann 	HARD_TX_LOCK(odev, txq, smp_processor_id());
34365b8db2f5SStephen Hemminger 
34376f25cd47SDaniel Borkmann 	if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) {
3438ef87979cSStephen Hemminger 		ret = NETDEV_TX_BUSY;
34390835acfeSEric Dumazet 		pkt_dev->last_ok = 0;
34400835acfeSEric Dumazet 		goto unlock;
34410835acfeSEric Dumazet 	}
344238b2cf29SAlexei Starovoitov 	atomic_add(burst, &pkt_dev->skb->users);
344338b2cf29SAlexei Starovoitov 
344438b2cf29SAlexei Starovoitov xmit_more:
344538b2cf29SAlexei Starovoitov 	ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0);
3446ef87979cSStephen Hemminger 
34475b8db2f5SStephen Hemminger 	switch (ret) {
34485b8db2f5SStephen Hemminger 	case NETDEV_TX_OK:
34491da177e4SLinus Torvalds 		pkt_dev->last_ok = 1;
34501da177e4SLinus Torvalds 		pkt_dev->sofar++;
34511da177e4SLinus Torvalds 		pkt_dev->seq_num++;
3452baac8564SEric Dumazet 		pkt_dev->tx_bytes += pkt_dev->last_pkt_size;
345338b2cf29SAlexei Starovoitov 		if (burst > 0 && !netif_xmit_frozen_or_drv_stopped(txq))
345438b2cf29SAlexei Starovoitov 			goto xmit_more;
34555b8db2f5SStephen Hemminger 		break;
3456f466dba1SJohn Fastabend 	case NET_XMIT_DROP:
3457f466dba1SJohn Fastabend 	case NET_XMIT_CN:
3458f466dba1SJohn Fastabend 	case NET_XMIT_POLICED:
3459f466dba1SJohn Fastabend 		/* skb has been consumed */
3460f466dba1SJohn Fastabend 		pkt_dev->errors++;
3461f466dba1SJohn Fastabend 		break;
34625b8db2f5SStephen Hemminger 	default: /* Drivers are not supposed to return other values! */
3463e87cc472SJoe Perches 		net_info_ratelimited("%s xmit error: %d\n",
3464e87cc472SJoe Perches 				     pkt_dev->odevname, ret);
34651da177e4SLinus Torvalds 		pkt_dev->errors++;
34665b8db2f5SStephen Hemminger 		/* fallthru */
3467ef87979cSStephen Hemminger 	case NETDEV_TX_LOCKED:
34685b8db2f5SStephen Hemminger 	case NETDEV_TX_BUSY:
34695b8db2f5SStephen Hemminger 		/* Retry it next time */
34705b8db2f5SStephen Hemminger 		atomic_dec(&(pkt_dev->skb->users));
34711da177e4SLinus Torvalds 		pkt_dev->last_ok = 0;
34721da177e4SLinus Torvalds 	}
347338b2cf29SAlexei Starovoitov 	if (unlikely(burst))
347438b2cf29SAlexei Starovoitov 		atomic_sub(burst, &pkt_dev->skb->users);
34750835acfeSEric Dumazet unlock:
34760f2eea4bSDaniel Borkmann 	HARD_TX_UNLOCK(odev, txq);
34770f2eea4bSDaniel Borkmann 
3478*62f64aedSAlexei Starovoitov out:
34790f2eea4bSDaniel Borkmann 	local_bh_enable();
34801da177e4SLinus Torvalds 
34811da177e4SLinus Torvalds 	/* If pkt_dev->count is zero, then run forever */
34821da177e4SLinus Torvalds 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
3483ef87979cSStephen Hemminger 		pktgen_wait_for_skb(pkt_dev);
34841da177e4SLinus Torvalds 
34851da177e4SLinus Torvalds 		/* Done with this */
34861da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
34871da177e4SLinus Torvalds 	}
34881da177e4SLinus Torvalds }
34891da177e4SLinus Torvalds 
34901da177e4SLinus Torvalds /*
34911da177e4SLinus Torvalds  * Main loop of the thread goes here
34921da177e4SLinus Torvalds  */
34931da177e4SLinus Torvalds 
3494ee74baa7SDavid S. Miller static int pktgen_thread_worker(void *arg)
34951da177e4SLinus Torvalds {
34961da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
3497ee74baa7SDavid S. Miller 	struct pktgen_thread *t = arg;
34981da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
34991da177e4SLinus Torvalds 	int cpu = t->cpu;
35001da177e4SLinus Torvalds 
3501ee74baa7SDavid S. Miller 	BUG_ON(smp_processor_id() != cpu);
35021da177e4SLinus Torvalds 
35031da177e4SLinus Torvalds 	init_waitqueue_head(&t->queue);
3504d3ede327SDenis V. Lunev 	complete(&t->start_done);
35051da177e4SLinus Torvalds 
3506f9467eaeSJoe Perches 	pr_debug("starting pktgen/%d:  pid=%d\n", cpu, task_pid_nr(current));
35071da177e4SLinus Torvalds 
350883144186SRafael J. Wysocki 	set_freezable();
350983144186SRafael J. Wysocki 
3510baac167bSJesper Dangaard Brouer 	__set_current_state(TASK_RUNNING);
3511baac167bSJesper Dangaard Brouer 
3512ee74baa7SDavid S. Miller 	while (!kthread_should_stop()) {
3513ee74baa7SDavid S. Miller 		pkt_dev = next_to_run(t);
3514ee74baa7SDavid S. Miller 
3515ef87979cSStephen Hemminger 		if (unlikely(!pkt_dev && t->control == 0)) {
35164e58a027SCong Wang 			if (t->net->pktgen_exiting)
3517551eaff1SEric Dumazet 				break;
3518ef87979cSStephen Hemminger 			wait_event_interruptible_timeout(t->queue,
3519ef87979cSStephen Hemminger 							 t->control != 0,
3520ef87979cSStephen Hemminger 							 HZ/10);
35211b3f720bSRafael J. Wysocki 			try_to_freeze();
3522ef87979cSStephen Hemminger 			continue;
3523ee74baa7SDavid S. Miller 		}
35241da177e4SLinus Torvalds 
3525ef87979cSStephen Hemminger 		if (likely(pkt_dev)) {
35261da177e4SLinus Torvalds 			pktgen_xmit(pkt_dev);
35271da177e4SLinus Torvalds 
3528ef87979cSStephen Hemminger 			if (need_resched())
3529ef87979cSStephen Hemminger 				pktgen_resched(pkt_dev);
3530ef87979cSStephen Hemminger 			else
3531ef87979cSStephen Hemminger 				cpu_relax();
3532ef87979cSStephen Hemminger 		}
3533ef87979cSStephen Hemminger 
35341da177e4SLinus Torvalds 		if (t->control & T_STOP) {
35351da177e4SLinus Torvalds 			pktgen_stop(t);
35361da177e4SLinus Torvalds 			t->control &= ~(T_STOP);
35371da177e4SLinus Torvalds 		}
35381da177e4SLinus Torvalds 
35391da177e4SLinus Torvalds 		if (t->control & T_RUN) {
35401da177e4SLinus Torvalds 			pktgen_run(t);
35411da177e4SLinus Torvalds 			t->control &= ~(T_RUN);
35421da177e4SLinus Torvalds 		}
35431da177e4SLinus Torvalds 
354495ed63f7SArthur Kepner 		if (t->control & T_REMDEVALL) {
35451da177e4SLinus Torvalds 			pktgen_rem_all_ifs(t);
354695ed63f7SArthur Kepner 			t->control &= ~(T_REMDEVALL);
354795ed63f7SArthur Kepner 		}
354895ed63f7SArthur Kepner 
354995ed63f7SArthur Kepner 		if (t->control & T_REMDEV) {
355095ed63f7SArthur Kepner 			pktgen_rem_one_if(t);
35511da177e4SLinus Torvalds 			t->control &= ~(T_REMDEV);
35521da177e4SLinus Torvalds 		}
35531da177e4SLinus Torvalds 
355409fe3ef4SAndrew Morton 		try_to_freeze();
35551da177e4SLinus Torvalds 	}
3556baac167bSJesper Dangaard Brouer 	set_current_state(TASK_INTERRUPTIBLE);
35571da177e4SLinus Torvalds 
3558f9467eaeSJoe Perches 	pr_debug("%s stopping all device\n", t->tsk->comm);
35591da177e4SLinus Torvalds 	pktgen_stop(t);
35601da177e4SLinus Torvalds 
3561f9467eaeSJoe Perches 	pr_debug("%s removing all device\n", t->tsk->comm);
35621da177e4SLinus Torvalds 	pktgen_rem_all_ifs(t);
35631da177e4SLinus Torvalds 
3564f9467eaeSJoe Perches 	pr_debug("%s removing thread\n", t->tsk->comm);
35651da177e4SLinus Torvalds 	pktgen_rem_thread(t);
3566cdcdbe0bSLuiz Capitulino 
3567551eaff1SEric Dumazet 	/* Wait for kthread_stop */
3568551eaff1SEric Dumazet 	while (!kthread_should_stop()) {
3569551eaff1SEric Dumazet 		set_current_state(TASK_INTERRUPTIBLE);
3570551eaff1SEric Dumazet 		schedule();
3571551eaff1SEric Dumazet 	}
3572551eaff1SEric Dumazet 	__set_current_state(TASK_RUNNING);
3573551eaff1SEric Dumazet 
3574ee74baa7SDavid S. Miller 	return 0;
35751da177e4SLinus Torvalds }
35761da177e4SLinus Torvalds 
3577222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
35783e984840SEric Dumazet 					  const char *ifname, bool exact)
35791da177e4SLinus Torvalds {
3580c26a8016SLuiz Capitulino 	struct pktgen_dev *p, *pkt_dev = NULL;
35813e984840SEric Dumazet 	size_t len = strlen(ifname);
35821da177e4SLinus Torvalds 
35838788370aSJesper Dangaard Brouer 	rcu_read_lock();
35848788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(p, &t->if_list, list)
35853e984840SEric Dumazet 		if (strncmp(p->odevname, ifname, len) == 0) {
35863e984840SEric Dumazet 			if (p->odevname[len]) {
35873e984840SEric Dumazet 				if (exact || p->odevname[len] != '@')
35883e984840SEric Dumazet 					continue;
35893e984840SEric Dumazet 			}
3590c26a8016SLuiz Capitulino 			pkt_dev = p;
35911da177e4SLinus Torvalds 			break;
35921da177e4SLinus Torvalds 		}
35931da177e4SLinus Torvalds 
35948788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3595f9467eaeSJoe Perches 	pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev);
35961da177e4SLinus Torvalds 	return pkt_dev;
35971da177e4SLinus Torvalds }
35981da177e4SLinus Torvalds 
35991da177e4SLinus Torvalds /*
36001da177e4SLinus Torvalds  * Adds a dev at front of if_list.
36011da177e4SLinus Torvalds  */
36021da177e4SLinus Torvalds 
3603222f1806SLuiz Capitulino static int add_dev_to_thread(struct pktgen_thread *t,
3604222f1806SLuiz Capitulino 			     struct pktgen_dev *pkt_dev)
36051da177e4SLinus Torvalds {
36061da177e4SLinus Torvalds 	int rv = 0;
36071da177e4SLinus Torvalds 
36088788370aSJesper Dangaard Brouer 	/* This function cannot be called concurrently, as its called
36098788370aSJesper Dangaard Brouer 	 * under pktgen_thread_lock mutex, but it can run from
36108788370aSJesper Dangaard Brouer 	 * userspace on another CPU than the kthread.  The if_lock()
36118788370aSJesper Dangaard Brouer 	 * is used here to sync with concurrent instances of
36128788370aSJesper Dangaard Brouer 	 * _rem_dev_from_if_list() invoked via kthread, which is also
36138788370aSJesper Dangaard Brouer 	 * updating the if_list */
36141da177e4SLinus Torvalds 	if_lock(t);
36151da177e4SLinus Torvalds 
36161da177e4SLinus Torvalds 	if (pkt_dev->pg_thread) {
3617f9467eaeSJoe Perches 		pr_err("ERROR: already assigned to a thread\n");
36181da177e4SLinus Torvalds 		rv = -EBUSY;
36191da177e4SLinus Torvalds 		goto out;
36201da177e4SLinus Torvalds 	}
3621c26a8016SLuiz Capitulino 
36221da177e4SLinus Torvalds 	pkt_dev->running = 0;
36238788370aSJesper Dangaard Brouer 	pkt_dev->pg_thread = t;
36248788370aSJesper Dangaard Brouer 	list_add_rcu(&pkt_dev->list, &t->if_list);
36251da177e4SLinus Torvalds 
36261da177e4SLinus Torvalds out:
36271da177e4SLinus Torvalds 	if_unlock(t);
36281da177e4SLinus Torvalds 	return rv;
36291da177e4SLinus Torvalds }
36301da177e4SLinus Torvalds 
36311da177e4SLinus Torvalds /* Called under thread lock */
36321da177e4SLinus Torvalds 
36331da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
36341da177e4SLinus Torvalds {
36351da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev;
363639df232fSStephen Hemminger 	int err;
36373291b9dbSEric Dumazet 	int node = cpu_to_node(t->cpu);
36381da177e4SLinus Torvalds 
36391da177e4SLinus Torvalds 	/* We don't allow a device to be on several threads */
36401da177e4SLinus Torvalds 
36414e58a027SCong Wang 	pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND);
3642d50a6b56SStephen Hemminger 	if (pkt_dev) {
3643f9467eaeSJoe Perches 		pr_err("ERROR: interface already used\n");
3644d50a6b56SStephen Hemminger 		return -EBUSY;
3645d50a6b56SStephen Hemminger 	}
36461da177e4SLinus Torvalds 
36473291b9dbSEric Dumazet 	pkt_dev = kzalloc_node(sizeof(struct pktgen_dev), GFP_KERNEL, node);
36481da177e4SLinus Torvalds 	if (!pkt_dev)
36491da177e4SLinus Torvalds 		return -ENOMEM;
36501da177e4SLinus Torvalds 
3651593f63b0SEric Dumazet 	strcpy(pkt_dev->odevname, ifname);
365268d5ac2eSWANG Cong 	pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
36533291b9dbSEric Dumazet 				      node);
36541da177e4SLinus Torvalds 	if (pkt_dev->flows == NULL) {
36551da177e4SLinus Torvalds 		kfree(pkt_dev);
36561da177e4SLinus Torvalds 		return -ENOMEM;
36571da177e4SLinus Torvalds 	}
36581da177e4SLinus Torvalds 
365995ed63f7SArthur Kepner 	pkt_dev->removal_mark = 0;
36601da177e4SLinus Torvalds 	pkt_dev->nfrags = 0;
3661fd29cf72SStephen Hemminger 	pkt_dev->delay = pg_delay_d;
36621da177e4SLinus Torvalds 	pkt_dev->count = pg_count_d;
36631da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
36641da177e4SLinus Torvalds 	pkt_dev->udp_src_min = 9;	/* sink port */
36651da177e4SLinus Torvalds 	pkt_dev->udp_src_max = 9;
36661da177e4SLinus Torvalds 	pkt_dev->udp_dst_min = 9;
36671da177e4SLinus Torvalds 	pkt_dev->udp_dst_max = 9;
366834954ddcSFrancesco Fondelli 	pkt_dev->vlan_p = 0;
366934954ddcSFrancesco Fondelli 	pkt_dev->vlan_cfi = 0;
367034954ddcSFrancesco Fondelli 	pkt_dev->vlan_id = 0xffff;
367134954ddcSFrancesco Fondelli 	pkt_dev->svlan_p = 0;
367234954ddcSFrancesco Fondelli 	pkt_dev->svlan_cfi = 0;
367334954ddcSFrancesco Fondelli 	pkt_dev->svlan_id = 0xffff;
367438b2cf29SAlexei Starovoitov 	pkt_dev->burst = 1;
3675e99b99b4SRobert Olsson 	pkt_dev->node = -1;
367634954ddcSFrancesco Fondelli 
36774e58a027SCong Wang 	err = pktgen_setup_dev(t->net, pkt_dev, ifname);
367839df232fSStephen Hemminger 	if (err)
367939df232fSStephen Hemminger 		goto out1;
3680d8873315SNeil Horman 	if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
3681d8873315SNeil Horman 		pkt_dev->clone_skb = pg_clone_skb_d;
36821da177e4SLinus Torvalds 
36834e58a027SCong Wang 	pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir,
36845efdccbcSDenis V. Lunev 					  &pktgen_if_fops, pkt_dev);
368539df232fSStephen Hemminger 	if (!pkt_dev->entry) {
3686f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3687d50a6b56SStephen Hemminger 		       PG_PROC_DIR, ifname);
368839df232fSStephen Hemminger 		err = -EINVAL;
368939df232fSStephen Hemminger 		goto out2;
369039df232fSStephen Hemminger 	}
3691a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3692a553e4a6SJamal Hadi Salim 	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;
3693a553e4a6SJamal Hadi Salim 	pkt_dev->ipsproto = IPPROTO_ESP;
3694cf93d47eSFan Du 
3695cf93d47eSFan Du 	/* xfrm tunnel mode needs additional dst to extract outter
3696cf93d47eSFan Du 	 * ip header protocol/ttl/id field, here creat a phony one.
3697cf93d47eSFan Du 	 * instead of looking for a valid rt, which definitely hurting
3698cf93d47eSFan Du 	 * performance under such circumstance.
3699cf93d47eSFan Du 	 */
3700cf93d47eSFan Du 	pkt_dev->dstops.family = AF_INET;
3701cf93d47eSFan Du 	pkt_dev->dst.dev = pkt_dev->odev;
3702cf93d47eSFan Du 	dst_init_metrics(&pkt_dev->dst, pktgen_dst_metrics, false);
3703cf93d47eSFan Du 	pkt_dev->dst.child = &pkt_dev->dst;
3704cf93d47eSFan Du 	pkt_dev->dst.ops = &pkt_dev->dstops;
3705a553e4a6SJamal Hadi Salim #endif
370639df232fSStephen Hemminger 
370739df232fSStephen Hemminger 	return add_dev_to_thread(t, pkt_dev);
370839df232fSStephen Hemminger out2:
370939df232fSStephen Hemminger 	dev_put(pkt_dev->odev);
371039df232fSStephen Hemminger out1:
3711a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3712a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3713a553e4a6SJamal Hadi Salim #endif
37141da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
37151da177e4SLinus Torvalds 	kfree(pkt_dev);
371639df232fSStephen Hemminger 	return err;
37171da177e4SLinus Torvalds }
37181da177e4SLinus Torvalds 
37194e58a027SCong Wang static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
37201da177e4SLinus Torvalds {
3721cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3722d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3723ee74baa7SDavid S. Miller 	struct task_struct *p;
37241da177e4SLinus Torvalds 
37253291b9dbSEric Dumazet 	t = kzalloc_node(sizeof(struct pktgen_thread), GFP_KERNEL,
37263291b9dbSEric Dumazet 			 cpu_to_node(cpu));
37271da177e4SLinus Torvalds 	if (!t) {
3728f9467eaeSJoe Perches 		pr_err("ERROR: out of memory, can't create new thread\n");
37291da177e4SLinus Torvalds 		return -ENOMEM;
37301da177e4SLinus Torvalds 	}
37311da177e4SLinus Torvalds 
37321da177e4SLinus Torvalds 	spin_lock_init(&t->if_lock);
37331da177e4SLinus Torvalds 	t->cpu = cpu;
37341da177e4SLinus Torvalds 
3735ee74baa7SDavid S. Miller 	INIT_LIST_HEAD(&t->if_list);
3736ee74baa7SDavid S. Miller 
37374e58a027SCong Wang 	list_add_tail(&t->th_list, &pn->pktgen_threads);
3738d3ede327SDenis V. Lunev 	init_completion(&t->start_done);
3739ee74baa7SDavid S. Miller 
374094dcf29aSEric Dumazet 	p = kthread_create_on_node(pktgen_thread_worker,
374194dcf29aSEric Dumazet 				   t,
374294dcf29aSEric Dumazet 				   cpu_to_node(cpu),
374394dcf29aSEric Dumazet 				   "kpktgend_%d", cpu);
3744ee74baa7SDavid S. Miller 	if (IS_ERR(p)) {
3745f9467eaeSJoe Perches 		pr_err("kernel_thread() failed for cpu %d\n", t->cpu);
3746ee74baa7SDavid S. Miller 		list_del(&t->th_list);
3747ee74baa7SDavid S. Miller 		kfree(t);
3748ee74baa7SDavid S. Miller 		return PTR_ERR(p);
3749ee74baa7SDavid S. Miller 	}
3750ee74baa7SDavid S. Miller 	kthread_bind(p, cpu);
3751ee74baa7SDavid S. Miller 	t->tsk = p;
3752ee74baa7SDavid S. Miller 
37534e58a027SCong Wang 	pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir,
37545efdccbcSDenis V. Lunev 			      &pktgen_thread_fops, t);
3755d50a6b56SStephen Hemminger 	if (!pe) {
3756f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3757ee74baa7SDavid S. Miller 		       PG_PROC_DIR, t->tsk->comm);
3758ee74baa7SDavid S. Miller 		kthread_stop(p);
3759ee74baa7SDavid S. Miller 		list_del(&t->th_list);
37601da177e4SLinus Torvalds 		kfree(t);
37611da177e4SLinus Torvalds 		return -EINVAL;
37621da177e4SLinus Torvalds 	}
3763d50a6b56SStephen Hemminger 
37644e58a027SCong Wang 	t->net = pn;
3765ee74baa7SDavid S. Miller 	wake_up_process(p);
3766d3ede327SDenis V. Lunev 	wait_for_completion(&t->start_done);
37671da177e4SLinus Torvalds 
37681da177e4SLinus Torvalds 	return 0;
37691da177e4SLinus Torvalds }
37701da177e4SLinus Torvalds 
37711da177e4SLinus Torvalds /*
37721da177e4SLinus Torvalds  * Removes a device from the thread if_list.
37731da177e4SLinus Torvalds  */
3774222f1806SLuiz Capitulino static void _rem_dev_from_if_list(struct pktgen_thread *t,
3775222f1806SLuiz Capitulino 				  struct pktgen_dev *pkt_dev)
37761da177e4SLinus Torvalds {
3777c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3778c26a8016SLuiz Capitulino 	struct pktgen_dev *p;
37791da177e4SLinus Torvalds 
37808788370aSJesper Dangaard Brouer 	if_lock(t);
3781c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3782c26a8016SLuiz Capitulino 		p = list_entry(q, struct pktgen_dev, list);
3783c26a8016SLuiz Capitulino 		if (p == pkt_dev)
37848788370aSJesper Dangaard Brouer 			list_del_rcu(&p->list);
37851da177e4SLinus Torvalds 	}
37868788370aSJesper Dangaard Brouer 	if_unlock(t);
37871da177e4SLinus Torvalds }
37881da177e4SLinus Torvalds 
3789222f1806SLuiz Capitulino static int pktgen_remove_device(struct pktgen_thread *t,
3790222f1806SLuiz Capitulino 				struct pktgen_dev *pkt_dev)
37911da177e4SLinus Torvalds {
3792f9467eaeSJoe Perches 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
37931da177e4SLinus Torvalds 
37941da177e4SLinus Torvalds 	if (pkt_dev->running) {
3795294a0b7fSJoe Perches 		pr_warn("WARNING: trying to remove a running interface, stopping it now\n");
37961da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
37971da177e4SLinus Torvalds 	}
37981da177e4SLinus Torvalds 
37991da177e4SLinus Torvalds 	/* Dis-associate from the interface */
38001da177e4SLinus Torvalds 
38011da177e4SLinus Torvalds 	if (pkt_dev->odev) {
38021da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
38031da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
38041da177e4SLinus Torvalds 	}
38051da177e4SLinus Torvalds 
38068788370aSJesper Dangaard Brouer 	/* Remove proc before if_list entry, because add_device uses
38078788370aSJesper Dangaard Brouer 	 * list to determine if interface already exist, avoid race
38088788370aSJesper Dangaard Brouer 	 * with proc_create_data() */
3809a8ca16eaSDavid Howells 	proc_remove(pkt_dev->entry);
38101da177e4SLinus Torvalds 
38118788370aSJesper Dangaard Brouer 	/* And update the thread if_list */
38128788370aSJesper Dangaard Brouer 	_rem_dev_from_if_list(t, pkt_dev);
38138788370aSJesper Dangaard Brouer 
3814a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3815a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3816a553e4a6SJamal Hadi Salim #endif
38171da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
381826ad7879SEric Dumazet 	if (pkt_dev->page)
381926ad7879SEric Dumazet 		put_page(pkt_dev->page);
38208788370aSJesper Dangaard Brouer 	kfree_rcu(pkt_dev, rcu);
38211da177e4SLinus Torvalds 	return 0;
38221da177e4SLinus Torvalds }
38231da177e4SLinus Torvalds 
38244e58a027SCong Wang static int __net_init pg_net_init(struct net *net)
38251da177e4SLinus Torvalds {
38264e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3827d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
38284e58a027SCong Wang 	int cpu, ret = 0;
3829d50a6b56SStephen Hemminger 
38304e58a027SCong Wang 	pn->net = net;
38314e58a027SCong Wang 	INIT_LIST_HEAD(&pn->pktgen_threads);
38324e58a027SCong Wang 	pn->pktgen_exiting = false;
38334e58a027SCong Wang 	pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net);
38344e58a027SCong Wang 	if (!pn->proc_dir) {
38354e58a027SCong Wang 		pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR);
3836d50a6b56SStephen Hemminger 		return -ENODEV;
38371da177e4SLinus Torvalds 	}
38384e58a027SCong Wang 	pe = proc_create(PGCTRL, 0600, pn->proc_dir, &pktgen_fops);
38394e58a027SCong Wang 	if (pe == NULL) {
38404e58a027SCong Wang 		pr_err("cannot create %s procfs entry\n", PGCTRL);
38414e58a027SCong Wang 		ret = -EINVAL;
38424e58a027SCong Wang 		goto remove;
38434e58a027SCong Wang 	}
38441da177e4SLinus Torvalds 
3845670c02c2SJohn Hawkes 	for_each_online_cpu(cpu) {
38468024bb24SLuiz Capitulino 		int err;
38471da177e4SLinus Torvalds 
38484e58a027SCong Wang 		err = pktgen_create_thread(cpu, pn);
38498024bb24SLuiz Capitulino 		if (err)
38504e58a027SCong Wang 			pr_warn("Cannot create thread for cpu %d (%d)\n",
3851f9467eaeSJoe Perches 				   cpu, err);
38521da177e4SLinus Torvalds 	}
38538024bb24SLuiz Capitulino 
38544e58a027SCong Wang 	if (list_empty(&pn->pktgen_threads)) {
38554e58a027SCong Wang 		pr_err("Initialization failed for all threads\n");
3856ce14f894SWANG Cong 		ret = -ENODEV;
38574e58a027SCong Wang 		goto remove_entry;
38588024bb24SLuiz Capitulino 	}
38598024bb24SLuiz Capitulino 
38601da177e4SLinus Torvalds 	return 0;
3861ce14f894SWANG Cong 
38624e58a027SCong Wang remove_entry:
38634e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
38644e58a027SCong Wang remove:
3865ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
3866ce14f894SWANG Cong 	return ret;
38671da177e4SLinus Torvalds }
38681da177e4SLinus Torvalds 
38694e58a027SCong Wang static void __net_exit pg_net_exit(struct net *net)
38701da177e4SLinus Torvalds {
38714e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3872cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3873cdcdbe0bSLuiz Capitulino 	struct list_head *q, *n;
3874d4b11335SEric Dumazet 	LIST_HEAD(list);
38751da177e4SLinus Torvalds 
38761da177e4SLinus Torvalds 	/* Stop all interfaces & threads */
38774e58a027SCong Wang 	pn->pktgen_exiting = true;
38781da177e4SLinus Torvalds 
3879c57b5468SEric Dumazet 	mutex_lock(&pktgen_thread_lock);
38804e58a027SCong Wang 	list_splice_init(&pn->pktgen_threads, &list);
3881c57b5468SEric Dumazet 	mutex_unlock(&pktgen_thread_lock);
3882c57b5468SEric Dumazet 
3883c57b5468SEric Dumazet 	list_for_each_safe(q, n, &list) {
3884cdcdbe0bSLuiz Capitulino 		t = list_entry(q, struct pktgen_thread, th_list);
3885c57b5468SEric Dumazet 		list_del(&t->th_list);
3886ee74baa7SDavid S. Miller 		kthread_stop(t->tsk);
3887ee74baa7SDavid S. Miller 		kfree(t);
38881da177e4SLinus Torvalds 	}
38891da177e4SLinus Torvalds 
38904e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
3891ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
38924e58a027SCong Wang }
38931da177e4SLinus Torvalds 
38944e58a027SCong Wang static struct pernet_operations pg_net_ops = {
38954e58a027SCong Wang 	.init = pg_net_init,
38964e58a027SCong Wang 	.exit = pg_net_exit,
38974e58a027SCong Wang 	.id   = &pg_net_id,
38984e58a027SCong Wang 	.size = sizeof(struct pktgen_net),
38994e58a027SCong Wang };
39004e58a027SCong Wang 
39014e58a027SCong Wang static int __init pg_init(void)
39024e58a027SCong Wang {
39034e58a027SCong Wang 	int ret = 0;
39044e58a027SCong Wang 
39054e58a027SCong Wang 	pr_info("%s", version);
39064e58a027SCong Wang 	ret = register_pernet_subsys(&pg_net_ops);
39074e58a027SCong Wang 	if (ret)
39084e58a027SCong Wang 		return ret;
39094e58a027SCong Wang 	ret = register_netdevice_notifier(&pktgen_notifier_block);
39104e58a027SCong Wang 	if (ret)
39114e58a027SCong Wang 		unregister_pernet_subsys(&pg_net_ops);
39124e58a027SCong Wang 
39134e58a027SCong Wang 	return ret;
39144e58a027SCong Wang }
39154e58a027SCong Wang 
39164e58a027SCong Wang static void __exit pg_cleanup(void)
39174e58a027SCong Wang {
39184e58a027SCong Wang 	unregister_netdevice_notifier(&pktgen_notifier_block);
39194e58a027SCong Wang 	unregister_pernet_subsys(&pg_net_ops);
39208788370aSJesper Dangaard Brouer 	/* Don't need rcu_barrier() due to use of kfree_rcu() */
39211da177e4SLinus Torvalds }
39221da177e4SLinus Torvalds 
39231da177e4SLinus Torvalds module_init(pg_init);
39241da177e4SLinus Torvalds module_exit(pg_cleanup);
39251da177e4SLinus Torvalds 
3926c3d2f52dSStephen Hemminger MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se>");
39271da177e4SLinus Torvalds MODULE_DESCRIPTION("Packet Generator tool");
39281da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3929c3d2f52dSStephen Hemminger MODULE_VERSION(VERSION);
39301da177e4SLinus Torvalds module_param(pg_count_d, int, 0);
3931c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject");
39321da177e4SLinus Torvalds module_param(pg_delay_d, int, 0);
3933c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)");
39341da177e4SLinus Torvalds module_param(pg_clone_skb_d, int, 0);
3935c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet");
39361da177e4SLinus Torvalds module_param(debug, int, 0);
3937c3d2f52dSStephen Hemminger MODULE_PARM_DESC(debug, "Enable debugging of pktgen module");
3938