xref: /openbmc/linux/net/core/pktgen.c (revision d4969581452be430135ba5040c5b6c9023c1d461)
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 
18040207264SJesper Dangaard Brouer #define VERSION	"2.75"
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 
21362f64aedSAlexei Starovoitov /* Xmit modes */
21462f64aedSAlexei Starovoitov #define M_START_XMIT		0	/* Default normal TX */
21562f64aedSAlexei Starovoitov #define M_NETIF_RECEIVE 	1	/* Inject packets into stack */
21662f64aedSAlexei 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;
25862f64aedSAlexei 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;
26362f64aedSAlexei Starovoitov 	int removal_mark;	/* non-zero => the device is marked for
26462f64aedSAlexei Starovoitov 				 * removal by worker thread */
26562f64aedSAlexei 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
51540207264SJesper Dangaard Brouer 		return -EINVAL;
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 
62862f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE)
62962f64aedSAlexei Starovoitov 		seq_puts(seq, "     xmit_mode: netif_receive\n");
63062f64aedSAlexei 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) &&
109262f64aedSAlexei Starovoitov 		    ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
109362f64aedSAlexei 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;
114662f64aedSAlexei 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 	}
117262f64aedSAlexei Starovoitov 	if (!strcmp(name, "xmit_mode")) {
117362f64aedSAlexei Starovoitov 		char f[32];
117462f64aedSAlexei Starovoitov 
117562f64aedSAlexei Starovoitov 		memset(f, 0, 32);
117662f64aedSAlexei Starovoitov 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
117762f64aedSAlexei Starovoitov 		if (len < 0)
117862f64aedSAlexei Starovoitov 			return len;
117962f64aedSAlexei Starovoitov 
118062f64aedSAlexei Starovoitov 		if (copy_from_user(f, &user_buffer[i], len))
118162f64aedSAlexei Starovoitov 			return -EFAULT;
118262f64aedSAlexei Starovoitov 		i += len;
118362f64aedSAlexei Starovoitov 
118462f64aedSAlexei Starovoitov 		if (strcmp(f, "start_xmit") == 0) {
118562f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_START_XMIT;
118662f64aedSAlexei Starovoitov 		} else if (strcmp(f, "netif_receive") == 0) {
118762f64aedSAlexei Starovoitov 			/* clone_skb set earlier, not supported in this mode */
118862f64aedSAlexei Starovoitov 			if (pkt_dev->clone_skb > 0)
118962f64aedSAlexei Starovoitov 				return -ENOTSUPP;
119062f64aedSAlexei Starovoitov 
119162f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_NETIF_RECEIVE;
11929eea9222SAlexei Starovoitov 
11939eea9222SAlexei Starovoitov 			/* make sure new packet is allocated every time
11949eea9222SAlexei Starovoitov 			 * pktgen_xmit() is called
11959eea9222SAlexei Starovoitov 			 */
11969eea9222SAlexei Starovoitov 			pkt_dev->last_ok = 1;
11979eea9222SAlexei Starovoitov 
11989eea9222SAlexei Starovoitov 			/* override clone_skb if user passed default value
11999eea9222SAlexei Starovoitov 			 * at module loading time
12009eea9222SAlexei Starovoitov 			 */
12019eea9222SAlexei Starovoitov 			pkt_dev->clone_skb = 0;
120262f64aedSAlexei Starovoitov 		} else {
120362f64aedSAlexei Starovoitov 			sprintf(pg_result,
120462f64aedSAlexei Starovoitov 				"xmit_mode -:%s:- unknown\nAvailable modes: %s",
120562f64aedSAlexei Starovoitov 				f, "start_xmit, netif_receive\n");
120662f64aedSAlexei Starovoitov 			return count;
120762f64aedSAlexei Starovoitov 		}
120862f64aedSAlexei Starovoitov 		sprintf(pg_result, "OK: xmit_mode=%s", f);
120962f64aedSAlexei Starovoitov 		return count;
121062f64aedSAlexei Starovoitov 	}
12111da177e4SLinus Torvalds 	if (!strcmp(name, "flag")) {
12121da177e4SLinus Torvalds 		char f[32];
12131da177e4SLinus Torvalds 		memset(f, 0, 32);
12141da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
121563adc6fbSStephen Hemminger 		if (len < 0)
1216222f1806SLuiz Capitulino 			return len;
121763adc6fbSStephen Hemminger 
12181da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
12191da177e4SLinus Torvalds 			return -EFAULT;
12201da177e4SLinus Torvalds 		i += len;
12211da177e4SLinus Torvalds 		if (strcmp(f, "IPSRC_RND") == 0)
12221da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPSRC_RND;
12231da177e4SLinus Torvalds 
12241da177e4SLinus Torvalds 		else if (strcmp(f, "!IPSRC_RND") == 0)
12251da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPSRC_RND;
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 		else if (strcmp(f, "TXSIZE_RND") == 0)
12281da177e4SLinus Torvalds 			pkt_dev->flags |= F_TXSIZE_RND;
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 		else if (strcmp(f, "!TXSIZE_RND") == 0)
12311da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_TXSIZE_RND;
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 		else if (strcmp(f, "IPDST_RND") == 0)
12341da177e4SLinus Torvalds 			pkt_dev->flags |= F_IPDST_RND;
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds 		else if (strcmp(f, "!IPDST_RND") == 0)
12371da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_IPDST_RND;
12381da177e4SLinus Torvalds 
12391da177e4SLinus Torvalds 		else if (strcmp(f, "UDPSRC_RND") == 0)
12401da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPSRC_RND;
12411da177e4SLinus Torvalds 
12421da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPSRC_RND") == 0)
12431da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPSRC_RND;
12441da177e4SLinus Torvalds 
12451da177e4SLinus Torvalds 		else if (strcmp(f, "UDPDST_RND") == 0)
12461da177e4SLinus Torvalds 			pkt_dev->flags |= F_UDPDST_RND;
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 		else if (strcmp(f, "!UDPDST_RND") == 0)
12491da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_UDPDST_RND;
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds 		else if (strcmp(f, "MACSRC_RND") == 0)
12521da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACSRC_RND;
12531da177e4SLinus Torvalds 
12541da177e4SLinus Torvalds 		else if (strcmp(f, "!MACSRC_RND") == 0)
12551da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACSRC_RND;
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds 		else if (strcmp(f, "MACDST_RND") == 0)
12581da177e4SLinus Torvalds 			pkt_dev->flags |= F_MACDST_RND;
12591da177e4SLinus Torvalds 
12601da177e4SLinus Torvalds 		else if (strcmp(f, "!MACDST_RND") == 0)
12611da177e4SLinus Torvalds 			pkt_dev->flags &= ~F_MACDST_RND;
12621da177e4SLinus Torvalds 
1263ca6549afSSteven Whitehouse 		else if (strcmp(f, "MPLS_RND") == 0)
1264ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
1265ca6549afSSteven Whitehouse 
1266ca6549afSSteven Whitehouse 		else if (strcmp(f, "!MPLS_RND") == 0)
1267ca6549afSSteven Whitehouse 			pkt_dev->flags &= ~F_MPLS_RND;
1268ca6549afSSteven Whitehouse 
126934954ddcSFrancesco Fondelli 		else if (strcmp(f, "VID_RND") == 0)
127034954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_VID_RND;
127134954ddcSFrancesco Fondelli 
127234954ddcSFrancesco Fondelli 		else if (strcmp(f, "!VID_RND") == 0)
127334954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_VID_RND;
127434954ddcSFrancesco Fondelli 
127534954ddcSFrancesco Fondelli 		else if (strcmp(f, "SVID_RND") == 0)
127634954ddcSFrancesco Fondelli 			pkt_dev->flags |= F_SVID_RND;
127734954ddcSFrancesco Fondelli 
127834954ddcSFrancesco Fondelli 		else if (strcmp(f, "!SVID_RND") == 0)
127934954ddcSFrancesco Fondelli 			pkt_dev->flags &= ~F_SVID_RND;
128034954ddcSFrancesco Fondelli 
1281007a531bSJamal Hadi Salim 		else if (strcmp(f, "FLOW_SEQ") == 0)
1282007a531bSJamal Hadi Salim 			pkt_dev->flags |= F_FLOW_SEQ;
1283007a531bSJamal Hadi Salim 
128445b270f8SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_RND") == 0)
128545b270f8SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_RND;
128645b270f8SRobert Olsson 
128745b270f8SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_RND") == 0)
128845b270f8SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_RND;
1289e6fce5b9SRobert Olsson 
1290e6fce5b9SRobert Olsson 		else if (strcmp(f, "QUEUE_MAP_CPU") == 0)
1291e6fce5b9SRobert Olsson 			pkt_dev->flags |= F_QUEUE_MAP_CPU;
1292e6fce5b9SRobert Olsson 
1293e6fce5b9SRobert Olsson 		else if (strcmp(f, "!QUEUE_MAP_CPU") == 0)
1294e6fce5b9SRobert Olsson 			pkt_dev->flags &= ~F_QUEUE_MAP_CPU;
1295a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
1296a553e4a6SJamal Hadi Salim 		else if (strcmp(f, "IPSEC") == 0)
1297a553e4a6SJamal Hadi Salim 			pkt_dev->flags |= F_IPSEC_ON;
1298a553e4a6SJamal Hadi Salim #endif
1299a553e4a6SJamal Hadi Salim 
13001ca7768cSFrancesco Fondelli 		else if (strcmp(f, "!IPV6") == 0)
13011ca7768cSFrancesco Fondelli 			pkt_dev->flags &= ~F_IPV6;
13021ca7768cSFrancesco Fondelli 
1303e99b99b4SRobert Olsson 		else if (strcmp(f, "NODE_ALLOC") == 0)
1304e99b99b4SRobert Olsson 			pkt_dev->flags |= F_NODE;
1305e99b99b4SRobert Olsson 
1306e99b99b4SRobert Olsson 		else if (strcmp(f, "!NODE_ALLOC") == 0)
1307e99b99b4SRobert Olsson 			pkt_dev->flags &= ~F_NODE;
1308e99b99b4SRobert Olsson 
1309c26bf4a5SThomas Graf 		else if (strcmp(f, "UDPCSUM") == 0)
1310c26bf4a5SThomas Graf 			pkt_dev->flags |= F_UDPCSUM;
1311c26bf4a5SThomas Graf 
1312c26bf4a5SThomas Graf 		else if (strcmp(f, "!UDPCSUM") == 0)
1313c26bf4a5SThomas Graf 			pkt_dev->flags &= ~F_UDPCSUM;
1314c26bf4a5SThomas Graf 
1315afb84b62SJesper Dangaard Brouer 		else if (strcmp(f, "NO_TIMESTAMP") == 0)
1316afb84b62SJesper Dangaard Brouer 			pkt_dev->flags |= F_NO_TIMESTAMP;
1317afb84b62SJesper Dangaard Brouer 
1318f1f00d8fSJesper Dangaard Brouer 		else if (strcmp(f, "!NO_TIMESTAMP") == 0)
1319f1f00d8fSJesper Dangaard Brouer 			pkt_dev->flags &= ~F_NO_TIMESTAMP;
1320f1f00d8fSJesper Dangaard Brouer 
13211da177e4SLinus Torvalds 		else {
1322222f1806SLuiz Capitulino 			sprintf(pg_result,
1323222f1806SLuiz Capitulino 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
13241da177e4SLinus Torvalds 				f,
13251ca7768cSFrancesco Fondelli 				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
132672f8e06fSMathias Krause 				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, "
132772f8e06fSMathias Krause 				"MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, "
132872f8e06fSMathias Krause 				"QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, "
1329afb84b62SJesper Dangaard Brouer 				"NO_TIMESTAMP, "
133072f8e06fSMathias Krause #ifdef CONFIG_XFRM
133172f8e06fSMathias Krause 				"IPSEC, "
133272f8e06fSMathias Krause #endif
133372f8e06fSMathias Krause 				"NODE_ALLOC\n");
13341da177e4SLinus Torvalds 			return count;
13351da177e4SLinus Torvalds 		}
13361da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
13371da177e4SLinus Torvalds 		return count;
13381da177e4SLinus Torvalds 	}
13391da177e4SLinus Torvalds 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
13401da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
134163adc6fbSStephen Hemminger 		if (len < 0)
1342222f1806SLuiz Capitulino 			return len;
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13451da177e4SLinus Torvalds 			return -EFAULT;
13461da177e4SLinus Torvalds 		buf[len] = 0;
13471da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
13481da177e4SLinus Torvalds 			memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
13491da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_min, buf, len);
13501da177e4SLinus Torvalds 			pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
13511da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_min;
13521da177e4SLinus Torvalds 		}
13531da177e4SLinus Torvalds 		if (debug)
1354f342cda7SJoe Perches 			pr_debug("dst_min set to: %s\n", pkt_dev->dst_min);
13551da177e4SLinus Torvalds 		i += len;
13561da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
13571da177e4SLinus Torvalds 		return count;
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 	if (!strcmp(name, "dst_max")) {
13601da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
136163adc6fbSStephen Hemminger 		if (len < 0)
1362222f1806SLuiz Capitulino 			return len;
136363adc6fbSStephen Hemminger 
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13661da177e4SLinus Torvalds 			return -EFAULT;
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 		buf[len] = 0;
13691da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
13701da177e4SLinus Torvalds 			memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
13711da177e4SLinus Torvalds 			strncpy(pkt_dev->dst_max, buf, len);
13721da177e4SLinus Torvalds 			pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
13731da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_max;
13741da177e4SLinus Torvalds 		}
13751da177e4SLinus Torvalds 		if (debug)
1376f342cda7SJoe Perches 			pr_debug("dst_max set to: %s\n", pkt_dev->dst_max);
13771da177e4SLinus Torvalds 		i += len;
13781da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
13791da177e4SLinus Torvalds 		return count;
13801da177e4SLinus Torvalds 	}
13811da177e4SLinus Torvalds 	if (!strcmp(name, "dst6")) {
13821da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1383222f1806SLuiz Capitulino 		if (len < 0)
1384222f1806SLuiz Capitulino 			return len;
13851da177e4SLinus Torvalds 
13861da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13891da177e4SLinus Torvalds 			return -EFAULT;
13901da177e4SLinus Torvalds 		buf[len] = 0;
13911da177e4SLinus Torvalds 
1392c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL);
139347a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);
13941da177e4SLinus Torvalds 
13954e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->in6_daddr;
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds 		if (debug)
1398f342cda7SJoe Perches 			pr_debug("dst6 set to: %s\n", buf);
13991da177e4SLinus Torvalds 
14001da177e4SLinus Torvalds 		i += len;
14011da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6=%s", buf);
14021da177e4SLinus Torvalds 		return count;
14031da177e4SLinus Torvalds 	}
14041da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_min")) {
14051da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1406222f1806SLuiz Capitulino 		if (len < 0)
1407222f1806SLuiz Capitulino 			return len;
14081da177e4SLinus Torvalds 
14091da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14101da177e4SLinus Torvalds 
14111da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14121da177e4SLinus Torvalds 			return -EFAULT;
14131da177e4SLinus Torvalds 		buf[len] = 0;
14141da177e4SLinus Torvalds 
1415c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL);
141647a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);
14171da177e4SLinus Torvalds 
14184e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->min_in6_daddr;
14191da177e4SLinus Torvalds 		if (debug)
1420f342cda7SJoe Perches 			pr_debug("dst6_min set to: %s\n", buf);
14211da177e4SLinus Torvalds 
14221da177e4SLinus Torvalds 		i += len;
14231da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_min=%s", buf);
14241da177e4SLinus Torvalds 		return count;
14251da177e4SLinus Torvalds 	}
14261da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_max")) {
14271da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1428222f1806SLuiz Capitulino 		if (len < 0)
1429222f1806SLuiz Capitulino 			return len;
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14321da177e4SLinus Torvalds 
14331da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14341da177e4SLinus Torvalds 			return -EFAULT;
14351da177e4SLinus Torvalds 		buf[len] = 0;
14361da177e4SLinus Torvalds 
1437c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL);
143847a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds 		if (debug)
1441f342cda7SJoe Perches 			pr_debug("dst6_max set to: %s\n", buf);
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds 		i += len;
14441da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_max=%s", buf);
14451da177e4SLinus Torvalds 		return count;
14461da177e4SLinus Torvalds 	}
14471da177e4SLinus Torvalds 	if (!strcmp(name, "src6")) {
14481da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1449222f1806SLuiz Capitulino 		if (len < 0)
1450222f1806SLuiz Capitulino 			return len;
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14531da177e4SLinus Torvalds 
14541da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14551da177e4SLinus Torvalds 			return -EFAULT;
14561da177e4SLinus Torvalds 		buf[len] = 0;
14571da177e4SLinus Torvalds 
1458c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL);
145947a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);
14601da177e4SLinus Torvalds 
14614e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_saddr = pkt_dev->in6_saddr;
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds 		if (debug)
1464f342cda7SJoe Perches 			pr_debug("src6 set to: %s\n", buf);
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 		i += len;
14671da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src6=%s", buf);
14681da177e4SLinus Torvalds 		return count;
14691da177e4SLinus Torvalds 	}
14701da177e4SLinus Torvalds 	if (!strcmp(name, "src_min")) {
14711da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
147263adc6fbSStephen Hemminger 		if (len < 0)
1473222f1806SLuiz Capitulino 			return len;
147463adc6fbSStephen Hemminger 
14751da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14761da177e4SLinus Torvalds 			return -EFAULT;
14771da177e4SLinus Torvalds 		buf[len] = 0;
14781da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_min) != 0) {
14791da177e4SLinus Torvalds 			memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
14801da177e4SLinus Torvalds 			strncpy(pkt_dev->src_min, buf, len);
14811da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
14821da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_min;
14831da177e4SLinus Torvalds 		}
14841da177e4SLinus Torvalds 		if (debug)
1485f342cda7SJoe Perches 			pr_debug("src_min set to: %s\n", pkt_dev->src_min);
14861da177e4SLinus Torvalds 		i += len;
14871da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
14881da177e4SLinus Torvalds 		return count;
14891da177e4SLinus Torvalds 	}
14901da177e4SLinus Torvalds 	if (!strcmp(name, "src_max")) {
14911da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
149263adc6fbSStephen Hemminger 		if (len < 0)
1493222f1806SLuiz Capitulino 			return len;
149463adc6fbSStephen Hemminger 
14951da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14961da177e4SLinus Torvalds 			return -EFAULT;
14971da177e4SLinus Torvalds 		buf[len] = 0;
14981da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_max) != 0) {
14991da177e4SLinus Torvalds 			memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
15001da177e4SLinus Torvalds 			strncpy(pkt_dev->src_max, buf, len);
15011da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
15021da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_max;
15031da177e4SLinus Torvalds 		}
15041da177e4SLinus Torvalds 		if (debug)
1505f342cda7SJoe Perches 			pr_debug("src_max set to: %s\n", pkt_dev->src_max);
15061da177e4SLinus Torvalds 		i += len;
15071da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
15081da177e4SLinus Torvalds 		return count;
15091da177e4SLinus Torvalds 	}
15101da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac")) {
15111da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
151263adc6fbSStephen Hemminger 		if (len < 0)
1513222f1806SLuiz Capitulino 			return len;
151463adc6fbSStephen Hemminger 
15151da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15161da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15171da177e4SLinus Torvalds 			return -EFAULT;
15181da177e4SLinus Torvalds 
15194940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->dst_mac))
15204940fc88SAlexey Dobriyan 			return -EINVAL;
15211da177e4SLinus Torvalds 		/* Set up Dest MAC */
15229ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[0], pkt_dev->dst_mac);
15231da177e4SLinus Torvalds 
15244940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);
15251da177e4SLinus Torvalds 		return count;
15261da177e4SLinus Torvalds 	}
15271da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac")) {
15281da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
152963adc6fbSStephen Hemminger 		if (len < 0)
1530222f1806SLuiz Capitulino 			return len;
153163adc6fbSStephen Hemminger 
15321da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15331da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15341da177e4SLinus Torvalds 			return -EFAULT;
15351da177e4SLinus Torvalds 
15364940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->src_mac))
15374940fc88SAlexey Dobriyan 			return -EINVAL;
1538ce5d0b47SAdit Ranadive 		/* Set up Src MAC */
15399ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[6], pkt_dev->src_mac);
1540ce5d0b47SAdit Ranadive 
15414940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);
15421da177e4SLinus Torvalds 		return count;
15431da177e4SLinus Torvalds 	}
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds 	if (!strcmp(name, "clear_counters")) {
15461da177e4SLinus Torvalds 		pktgen_clear_counters(pkt_dev);
15471da177e4SLinus Torvalds 		sprintf(pg_result, "OK: Clearing counters.\n");
15481da177e4SLinus Torvalds 		return count;
15491da177e4SLinus Torvalds 	}
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 	if (!strcmp(name, "flows")) {
15521da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
155363adc6fbSStephen Hemminger 		if (len < 0)
1554222f1806SLuiz Capitulino 			return len;
155563adc6fbSStephen Hemminger 
15561da177e4SLinus Torvalds 		i += len;
15571da177e4SLinus Torvalds 		if (value > MAX_CFLOWS)
15581da177e4SLinus Torvalds 			value = MAX_CFLOWS;
15591da177e4SLinus Torvalds 
15601da177e4SLinus Torvalds 		pkt_dev->cflows = value;
15611da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
15621da177e4SLinus Torvalds 		return count;
15631da177e4SLinus Torvalds 	}
15646bae9190SFan Du #ifdef CONFIG_XFRM
1565de4aee7dSFan Du 	if (!strcmp(name, "spi")) {
1566de4aee7dSFan Du 		len = num_arg(&user_buffer[i], 10, &value);
1567de4aee7dSFan Du 		if (len < 0)
1568de4aee7dSFan Du 			return len;
1569de4aee7dSFan Du 
1570de4aee7dSFan Du 		i += len;
1571de4aee7dSFan Du 		pkt_dev->spi = value;
1572de4aee7dSFan Du 		sprintf(pg_result, "OK: spi=%u", pkt_dev->spi);
1573de4aee7dSFan Du 		return count;
1574de4aee7dSFan Du 	}
15756bae9190SFan Du #endif
15761da177e4SLinus Torvalds 	if (!strcmp(name, "flowlen")) {
15771da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
157863adc6fbSStephen Hemminger 		if (len < 0)
1579222f1806SLuiz Capitulino 			return len;
158063adc6fbSStephen Hemminger 
15811da177e4SLinus Torvalds 		i += len;
15821da177e4SLinus Torvalds 		pkt_dev->lflow = value;
15831da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
15841da177e4SLinus Torvalds 		return count;
15851da177e4SLinus Torvalds 	}
15861da177e4SLinus Torvalds 
158745b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_min")) {
158845b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
158963adc6fbSStephen Hemminger 		if (len < 0)
159045b270f8SRobert Olsson 			return len;
159163adc6fbSStephen Hemminger 
159245b270f8SRobert Olsson 		i += len;
159345b270f8SRobert Olsson 		pkt_dev->queue_map_min = value;
159445b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min);
159545b270f8SRobert Olsson 		return count;
159645b270f8SRobert Olsson 	}
159745b270f8SRobert Olsson 
159845b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_max")) {
159945b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
160063adc6fbSStephen Hemminger 		if (len < 0)
160145b270f8SRobert Olsson 			return len;
160263adc6fbSStephen Hemminger 
160345b270f8SRobert Olsson 		i += len;
160445b270f8SRobert Olsson 		pkt_dev->queue_map_max = value;
160545b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max);
160645b270f8SRobert Olsson 		return count;
160745b270f8SRobert Olsson 	}
160845b270f8SRobert Olsson 
1609ca6549afSSteven Whitehouse 	if (!strcmp(name, "mpls")) {
161095c96174SEric Dumazet 		unsigned int n, cnt;
1611cfcabdccSStephen Hemminger 
1612ca6549afSSteven Whitehouse 		len = get_labels(&user_buffer[i], pkt_dev);
1613cfcabdccSStephen Hemminger 		if (len < 0)
1614cfcabdccSStephen Hemminger 			return len;
1615ca6549afSSteven Whitehouse 		i += len;
1616cfcabdccSStephen Hemminger 		cnt = sprintf(pg_result, "OK: mpls=");
1617ca6549afSSteven Whitehouse 		for (n = 0; n < pkt_dev->nr_labels; n++)
1618cfcabdccSStephen Hemminger 			cnt += sprintf(pg_result + cnt,
1619ca6549afSSteven Whitehouse 				       "%08x%s", ntohl(pkt_dev->labels[n]),
1620ca6549afSSteven Whitehouse 				       n == pkt_dev->nr_labels-1 ? "" : ",");
162134954ddcSFrancesco Fondelli 
162234954ddcSFrancesco Fondelli 		if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
162334954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
162434954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
162534954ddcSFrancesco Fondelli 
162634954ddcSFrancesco Fondelli 			if (debug)
1627f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN auto turned off\n");
162834954ddcSFrancesco Fondelli 		}
162934954ddcSFrancesco Fondelli 		return count;
163034954ddcSFrancesco Fondelli 	}
163134954ddcSFrancesco Fondelli 
163234954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_id")) {
163334954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
163463adc6fbSStephen Hemminger 		if (len < 0)
163534954ddcSFrancesco Fondelli 			return len;
163663adc6fbSStephen Hemminger 
163734954ddcSFrancesco Fondelli 		i += len;
163834954ddcSFrancesco Fondelli 		if (value <= 4095) {
163934954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = value;  /* turn on VLAN */
164034954ddcSFrancesco Fondelli 
164134954ddcSFrancesco Fondelli 			if (debug)
1642f342cda7SJoe Perches 				pr_debug("VLAN turned on\n");
164334954ddcSFrancesco Fondelli 
164434954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1645f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
164634954ddcSFrancesco Fondelli 
164734954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
164834954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
164934954ddcSFrancesco Fondelli 		} else {
165034954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
165134954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
165234954ddcSFrancesco Fondelli 
165334954ddcSFrancesco Fondelli 			if (debug)
1654f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
165534954ddcSFrancesco Fondelli 		}
165634954ddcSFrancesco Fondelli 		return count;
165734954ddcSFrancesco Fondelli 	}
165834954ddcSFrancesco Fondelli 
165934954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_p")) {
166034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
166163adc6fbSStephen Hemminger 		if (len < 0)
166234954ddcSFrancesco Fondelli 			return len;
166363adc6fbSStephen Hemminger 
166434954ddcSFrancesco Fondelli 		i += len;
166534954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
166634954ddcSFrancesco Fondelli 			pkt_dev->vlan_p = value;
166734954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
166834954ddcSFrancesco Fondelli 		} else {
166934954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_p must be 0-7");
167034954ddcSFrancesco Fondelli 		}
167134954ddcSFrancesco Fondelli 		return count;
167234954ddcSFrancesco Fondelli 	}
167334954ddcSFrancesco Fondelli 
167434954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_cfi")) {
167534954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
167663adc6fbSStephen Hemminger 		if (len < 0)
167734954ddcSFrancesco Fondelli 			return len;
167863adc6fbSStephen Hemminger 
167934954ddcSFrancesco Fondelli 		i += len;
168034954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
168134954ddcSFrancesco Fondelli 			pkt_dev->vlan_cfi = value;
168234954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
168334954ddcSFrancesco Fondelli 		} else {
168434954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
168534954ddcSFrancesco Fondelli 		}
168634954ddcSFrancesco Fondelli 		return count;
168734954ddcSFrancesco Fondelli 	}
168834954ddcSFrancesco Fondelli 
168934954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_id")) {
169034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
169163adc6fbSStephen Hemminger 		if (len < 0)
169234954ddcSFrancesco Fondelli 			return len;
169363adc6fbSStephen Hemminger 
169434954ddcSFrancesco Fondelli 		i += len;
169534954ddcSFrancesco Fondelli 		if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
169634954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = value;  /* turn on SVLAN */
169734954ddcSFrancesco Fondelli 
169834954ddcSFrancesco Fondelli 			if (debug)
1699f342cda7SJoe Perches 				pr_debug("SVLAN turned on\n");
170034954ddcSFrancesco Fondelli 
170134954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1702f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
170334954ddcSFrancesco Fondelli 
170434954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
170534954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
170634954ddcSFrancesco Fondelli 		} else {
170734954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
170834954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
170934954ddcSFrancesco Fondelli 
171034954ddcSFrancesco Fondelli 			if (debug)
1711f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
171234954ddcSFrancesco Fondelli 		}
171334954ddcSFrancesco Fondelli 		return count;
171434954ddcSFrancesco Fondelli 	}
171534954ddcSFrancesco Fondelli 
171634954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_p")) {
171734954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
171863adc6fbSStephen Hemminger 		if (len < 0)
171934954ddcSFrancesco Fondelli 			return len;
172063adc6fbSStephen Hemminger 
172134954ddcSFrancesco Fondelli 		i += len;
172234954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
172334954ddcSFrancesco Fondelli 			pkt_dev->svlan_p = value;
172434954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
172534954ddcSFrancesco Fondelli 		} else {
172634954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_p must be 0-7");
172734954ddcSFrancesco Fondelli 		}
172834954ddcSFrancesco Fondelli 		return count;
172934954ddcSFrancesco Fondelli 	}
173034954ddcSFrancesco Fondelli 
173134954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_cfi")) {
173234954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
173363adc6fbSStephen Hemminger 		if (len < 0)
173434954ddcSFrancesco Fondelli 			return len;
173563adc6fbSStephen Hemminger 
173634954ddcSFrancesco Fondelli 		i += len;
173734954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
173834954ddcSFrancesco Fondelli 			pkt_dev->svlan_cfi = value;
173934954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
174034954ddcSFrancesco Fondelli 		} else {
174134954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
174234954ddcSFrancesco Fondelli 		}
1743ca6549afSSteven Whitehouse 		return count;
1744ca6549afSSteven Whitehouse 	}
1745ca6549afSSteven Whitehouse 
17461ca7768cSFrancesco Fondelli 	if (!strcmp(name, "tos")) {
17471ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17481ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
174963adc6fbSStephen Hemminger 		if (len < 0)
17501ca7768cSFrancesco Fondelli 			return len;
175163adc6fbSStephen Hemminger 
17521ca7768cSFrancesco Fondelli 		i += len;
17531ca7768cSFrancesco Fondelli 		if (len == 2) {
17541ca7768cSFrancesco Fondelli 			pkt_dev->tos = tmp_value;
17551ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
17561ca7768cSFrancesco Fondelli 		} else {
17571ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: tos must be 00-ff");
17581ca7768cSFrancesco Fondelli 		}
17591ca7768cSFrancesco Fondelli 		return count;
17601ca7768cSFrancesco Fondelli 	}
17611ca7768cSFrancesco Fondelli 
17621ca7768cSFrancesco Fondelli 	if (!strcmp(name, "traffic_class")) {
17631ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17641ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
176563adc6fbSStephen Hemminger 		if (len < 0)
17661ca7768cSFrancesco Fondelli 			return len;
176763adc6fbSStephen Hemminger 
17681ca7768cSFrancesco Fondelli 		i += len;
17691ca7768cSFrancesco Fondelli 		if (len == 2) {
17701ca7768cSFrancesco Fondelli 			pkt_dev->traffic_class = tmp_value;
17711ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
17721ca7768cSFrancesco Fondelli 		} else {
17731ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
17741ca7768cSFrancesco Fondelli 		}
17751ca7768cSFrancesco Fondelli 		return count;
17761ca7768cSFrancesco Fondelli 	}
17771ca7768cSFrancesco Fondelli 
17789e50e3acSJohn Fastabend 	if (!strcmp(name, "skb_priority")) {
17799e50e3acSJohn Fastabend 		len = num_arg(&user_buffer[i], 9, &value);
17809e50e3acSJohn Fastabend 		if (len < 0)
17819e50e3acSJohn Fastabend 			return len;
17829e50e3acSJohn Fastabend 
17839e50e3acSJohn Fastabend 		i += len;
17849e50e3acSJohn Fastabend 		pkt_dev->skb_priority = value;
17859e50e3acSJohn Fastabend 		sprintf(pg_result, "OK: skb_priority=%i",
17869e50e3acSJohn Fastabend 			pkt_dev->skb_priority);
17879e50e3acSJohn Fastabend 		return count;
17889e50e3acSJohn Fastabend 	}
17899e50e3acSJohn Fastabend 
17901da177e4SLinus Torvalds 	sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
17911da177e4SLinus Torvalds 	return -EINVAL;
17921da177e4SLinus Torvalds }
17931da177e4SLinus Torvalds 
1794d50a6b56SStephen Hemminger static int pktgen_if_open(struct inode *inode, struct file *file)
17951da177e4SLinus Torvalds {
1796d9dda78bSAl Viro 	return single_open(file, pktgen_if_show, PDE_DATA(inode));
17971da177e4SLinus Torvalds }
17981da177e4SLinus Torvalds 
17999a32144eSArjan van de Ven static const struct file_operations pktgen_if_fops = {
1800d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1801d50a6b56SStephen Hemminger 	.open    = pktgen_if_open,
1802d50a6b56SStephen Hemminger 	.read    = seq_read,
1803d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1804d50a6b56SStephen Hemminger 	.write   = pktgen_if_write,
1805d50a6b56SStephen Hemminger 	.release = single_release,
1806d50a6b56SStephen Hemminger };
1807d50a6b56SStephen Hemminger 
1808d50a6b56SStephen Hemminger static int pktgen_thread_show(struct seq_file *seq, void *v)
1809d50a6b56SStephen Hemminger {
1810d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1811648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
1812d50a6b56SStephen Hemminger 
1813d50a6b56SStephen Hemminger 	BUG_ON(!t);
1814d50a6b56SStephen Hemminger 
181597dc48e2SThomas Graf 	seq_puts(seq, "Running: ");
18161da177e4SLinus Torvalds 
18178788370aSJesper Dangaard Brouer 	rcu_read_lock();
18188788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18191da177e4SLinus Torvalds 		if (pkt_dev->running)
1820593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18211da177e4SLinus Torvalds 
182297dc48e2SThomas Graf 	seq_puts(seq, "\nStopped: ");
18231da177e4SLinus Torvalds 
18248788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18251da177e4SLinus Torvalds 		if (!pkt_dev->running)
1826593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18271da177e4SLinus Torvalds 
18281da177e4SLinus Torvalds 	if (t->result[0])
1829d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: %s\n", t->result);
18301da177e4SLinus Torvalds 	else
183197dc48e2SThomas Graf 		seq_puts(seq, "\nResult: NA\n");
18321da177e4SLinus Torvalds 
18338788370aSJesper Dangaard Brouer 	rcu_read_unlock();
18341da177e4SLinus Torvalds 
1835d50a6b56SStephen Hemminger 	return 0;
18361da177e4SLinus Torvalds }
18371da177e4SLinus Torvalds 
1838d50a6b56SStephen Hemminger static ssize_t pktgen_thread_write(struct file *file,
1839d50a6b56SStephen Hemminger 				   const char __user * user_buffer,
1840d50a6b56SStephen Hemminger 				   size_t count, loff_t * offset)
18411da177e4SLinus Torvalds {
18428a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
1843d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1844d6182223SPaul Gortmaker 	int i, max, len, ret;
18451da177e4SLinus Torvalds 	char name[40];
18461da177e4SLinus Torvalds 	char *pg_result;
18471da177e4SLinus Torvalds 
18481da177e4SLinus Torvalds 	if (count < 1) {
18491da177e4SLinus Torvalds 		//      sprintf(pg_result, "Wrong command format");
18501da177e4SLinus Torvalds 		return -EINVAL;
18511da177e4SLinus Torvalds 	}
18521da177e4SLinus Torvalds 
1853d6182223SPaul Gortmaker 	max = count;
1854d6182223SPaul Gortmaker 	len = count_trail_chars(user_buffer, max);
18551da177e4SLinus Torvalds 	if (len < 0)
18561da177e4SLinus Torvalds 		return len;
18571da177e4SLinus Torvalds 
1858d6182223SPaul Gortmaker 	i = len;
18591da177e4SLinus Torvalds 
18601da177e4SLinus Torvalds 	/* Read variable name */
18611da177e4SLinus Torvalds 
18621da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
18631da177e4SLinus Torvalds 	if (len < 0)
18641da177e4SLinus Torvalds 		return len;
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
18671da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
18681da177e4SLinus Torvalds 		return -EFAULT;
18691da177e4SLinus Torvalds 	i += len;
18701da177e4SLinus Torvalds 
18711da177e4SLinus Torvalds 	max = count - i;
18721da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
18731da177e4SLinus Torvalds 	if (len < 0)
18741da177e4SLinus Torvalds 		return len;
18751da177e4SLinus Torvalds 
18761da177e4SLinus Torvalds 	i += len;
18771da177e4SLinus Torvalds 
18781da177e4SLinus Torvalds 	if (debug)
1879f342cda7SJoe Perches 		pr_debug("t=%s, count=%lu\n", name, (unsigned long)count);
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds 	if (!t) {
1882f9467eaeSJoe Perches 		pr_err("ERROR: No thread\n");
18831da177e4SLinus Torvalds 		ret = -EINVAL;
18841da177e4SLinus Torvalds 		goto out;
18851da177e4SLinus Torvalds 	}
18861da177e4SLinus Torvalds 
18871da177e4SLinus Torvalds 	pg_result = &(t->result[0]);
18881da177e4SLinus Torvalds 
18891da177e4SLinus Torvalds 	if (!strcmp(name, "add_device")) {
18901da177e4SLinus Torvalds 		char f[32];
18911da177e4SLinus Torvalds 		memset(f, 0, 32);
18921da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
18931da177e4SLinus Torvalds 		if (len < 0) {
18941da177e4SLinus Torvalds 			ret = len;
18951da177e4SLinus Torvalds 			goto out;
18961da177e4SLinus Torvalds 		}
18971da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
18981da177e4SLinus Torvalds 			return -EFAULT;
18991da177e4SLinus Torvalds 		i += len;
19006146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
1901604dfd6eSCong Wang 		ret = pktgen_add_device(t, f);
19026146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1903604dfd6eSCong Wang 		if (!ret) {
19041da177e4SLinus Torvalds 			ret = count;
19051da177e4SLinus Torvalds 			sprintf(pg_result, "OK: add_device=%s", f);
1906604dfd6eSCong Wang 		} else
1907604dfd6eSCong Wang 			sprintf(pg_result, "ERROR: can not add device %s", f);
19081da177e4SLinus Torvalds 		goto out;
19091da177e4SLinus Torvalds 	}
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds 	if (!strcmp(name, "rem_device_all")) {
19126146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
191395ed63f7SArthur Kepner 		t->control |= T_REMDEVALL;
19146146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1915121caf57SNishanth Aravamudan 		schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
19161da177e4SLinus Torvalds 		ret = count;
19171da177e4SLinus Torvalds 		sprintf(pg_result, "OK: rem_device_all");
19181da177e4SLinus Torvalds 		goto out;
19191da177e4SLinus Torvalds 	}
19201da177e4SLinus Torvalds 
19211da177e4SLinus Torvalds 	if (!strcmp(name, "max_before_softirq")) {
1922b163911fSRobert Olsson 		sprintf(pg_result, "OK: Note! max_before_softirq is obsoleted -- Do not use");
19231da177e4SLinus Torvalds 		ret = count;
19241da177e4SLinus Torvalds 		goto out;
19251da177e4SLinus Torvalds 	}
19261da177e4SLinus Torvalds 
19271da177e4SLinus Torvalds 	ret = -EINVAL;
19281da177e4SLinus Torvalds out:
19291da177e4SLinus Torvalds 	return ret;
19301da177e4SLinus Torvalds }
19311da177e4SLinus Torvalds 
1932d50a6b56SStephen Hemminger static int pktgen_thread_open(struct inode *inode, struct file *file)
19331da177e4SLinus Torvalds {
1934d9dda78bSAl Viro 	return single_open(file, pktgen_thread_show, PDE_DATA(inode));
19351da177e4SLinus Torvalds }
19361da177e4SLinus Torvalds 
19379a32144eSArjan van de Ven static const struct file_operations pktgen_thread_fops = {
1938d50a6b56SStephen Hemminger 	.owner   = THIS_MODULE,
1939d50a6b56SStephen Hemminger 	.open    = pktgen_thread_open,
1940d50a6b56SStephen Hemminger 	.read    = seq_read,
1941d50a6b56SStephen Hemminger 	.llseek  = seq_lseek,
1942d50a6b56SStephen Hemminger 	.write   = pktgen_thread_write,
1943d50a6b56SStephen Hemminger 	.release = single_release,
1944d50a6b56SStephen Hemminger };
19451da177e4SLinus Torvalds 
19461da177e4SLinus Torvalds /* Think find or remove for NN */
19474e58a027SCong Wang static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
19484e58a027SCong Wang 					      const char *ifname, int remove)
19491da177e4SLinus Torvalds {
19501da177e4SLinus Torvalds 	struct pktgen_thread *t;
19511da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
19523e984840SEric Dumazet 	bool exact = (remove == FIND);
19531da177e4SLinus Torvalds 
19544e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
19553e984840SEric Dumazet 		pkt_dev = pktgen_find_dev(t, ifname, exact);
19561da177e4SLinus Torvalds 		if (pkt_dev) {
19571da177e4SLinus Torvalds 			if (remove) {
195895ed63f7SArthur Kepner 				pkt_dev->removal_mark = 1;
195995ed63f7SArthur Kepner 				t->control |= T_REMDEV;
19601da177e4SLinus Torvalds 			}
19611da177e4SLinus Torvalds 			break;
19621da177e4SLinus Torvalds 		}
19631da177e4SLinus Torvalds 	}
19641da177e4SLinus Torvalds 	return pkt_dev;
19651da177e4SLinus Torvalds }
19661da177e4SLinus Torvalds 
196795ed63f7SArthur Kepner /*
196895ed63f7SArthur Kepner  * mark a device for removal
196995ed63f7SArthur Kepner  */
19704e58a027SCong Wang static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)
19711da177e4SLinus Torvalds {
19721da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
197395ed63f7SArthur Kepner 	const int max_tries = 10, msec_per_try = 125;
197495ed63f7SArthur Kepner 	int i = 0;
197595ed63f7SArthur Kepner 
19766146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
1977f9467eaeSJoe Perches 	pr_debug("%s: marking %s for removal\n", __func__, ifname);
197895ed63f7SArthur Kepner 
197995ed63f7SArthur Kepner 	while (1) {
198095ed63f7SArthur Kepner 
19814e58a027SCong Wang 		pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE);
1982222f1806SLuiz Capitulino 		if (pkt_dev == NULL)
1983222f1806SLuiz Capitulino 			break;	/* success */
198495ed63f7SArthur Kepner 
19856146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1986f9467eaeSJoe Perches 		pr_debug("%s: waiting for %s to disappear....\n",
1987f9467eaeSJoe Perches 			 __func__, ifname);
198895ed63f7SArthur Kepner 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
19896146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
199095ed63f7SArthur Kepner 
199195ed63f7SArthur Kepner 		if (++i >= max_tries) {
1992f9467eaeSJoe Perches 			pr_err("%s: timed out after waiting %d msec for device %s to be removed\n",
1993f9467eaeSJoe Perches 			       __func__, msec_per_try * i, ifname);
199495ed63f7SArthur Kepner 			break;
199595ed63f7SArthur Kepner 		}
199695ed63f7SArthur Kepner 
199795ed63f7SArthur Kepner 	}
199895ed63f7SArthur Kepner 
19996146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
200039df232fSStephen Hemminger }
200195ed63f7SArthur Kepner 
20024e58a027SCong Wang static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)
200339df232fSStephen Hemminger {
200439df232fSStephen Hemminger 	struct pktgen_thread *t;
200539df232fSStephen Hemminger 
20064e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
200739df232fSStephen Hemminger 		struct pktgen_dev *pkt_dev;
200839df232fSStephen Hemminger 
20098788370aSJesper Dangaard Brouer 		rcu_read_lock();
20108788370aSJesper Dangaard Brouer 		list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
201139df232fSStephen Hemminger 			if (pkt_dev->odev != dev)
201239df232fSStephen Hemminger 				continue;
201339df232fSStephen Hemminger 
2014a8ca16eaSDavid Howells 			proc_remove(pkt_dev->entry);
201539df232fSStephen Hemminger 
20162975315bSAlexey Dobriyan 			pkt_dev->entry = proc_create_data(dev->name, 0600,
20174e58a027SCong Wang 							  pn->proc_dir,
20182975315bSAlexey Dobriyan 							  &pktgen_if_fops,
20192975315bSAlexey Dobriyan 							  pkt_dev);
202039df232fSStephen Hemminger 			if (!pkt_dev->entry)
2021f9467eaeSJoe Perches 				pr_err("can't move proc entry for '%s'\n",
2022f9467eaeSJoe Perches 				       dev->name);
202339df232fSStephen Hemminger 			break;
202439df232fSStephen Hemminger 		}
20258788370aSJesper Dangaard Brouer 		rcu_read_unlock();
202639df232fSStephen Hemminger 	}
20271da177e4SLinus Torvalds }
20281da177e4SLinus Torvalds 
2029222f1806SLuiz Capitulino static int pktgen_device_event(struct notifier_block *unused,
2030222f1806SLuiz Capitulino 			       unsigned long event, void *ptr)
20311da177e4SLinus Torvalds {
2032351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
20334e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id);
20341da177e4SLinus Torvalds 
20354e58a027SCong Wang 	if (pn->pktgen_exiting)
2036e9dc8653SEric W. Biederman 		return NOTIFY_DONE;
2037e9dc8653SEric W. Biederman 
20381da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
20391da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
20401da177e4SLinus Torvalds 	 */
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds 	switch (event) {
204339df232fSStephen Hemminger 	case NETDEV_CHANGENAME:
20444e58a027SCong Wang 		pktgen_change_name(pn, dev);
20451da177e4SLinus Torvalds 		break;
20461da177e4SLinus Torvalds 
20471da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
20484e58a027SCong Wang 		pktgen_mark_device(pn, dev->name);
20491da177e4SLinus Torvalds 		break;
20503ff50b79SStephen Hemminger 	}
20511da177e4SLinus Torvalds 
20521da177e4SLinus Torvalds 	return NOTIFY_DONE;
20531da177e4SLinus Torvalds }
20541da177e4SLinus Torvalds 
20554e58a027SCong Wang static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn,
20564e58a027SCong Wang 						 struct pktgen_dev *pkt_dev,
205763adc6fbSStephen Hemminger 						 const char *ifname)
2058e6fce5b9SRobert Olsson {
2059e6fce5b9SRobert Olsson 	char b[IFNAMSIZ+5];
2060d6182223SPaul Gortmaker 	int i;
2061e6fce5b9SRobert Olsson 
2062e6fce5b9SRobert Olsson 	for (i = 0; ifname[i] != '@'; i++) {
2063e6fce5b9SRobert Olsson 		if (i == IFNAMSIZ)
2064e6fce5b9SRobert Olsson 			break;
2065e6fce5b9SRobert Olsson 
2066e6fce5b9SRobert Olsson 		b[i] = ifname[i];
2067e6fce5b9SRobert Olsson 	}
2068e6fce5b9SRobert Olsson 	b[i] = 0;
2069e6fce5b9SRobert Olsson 
20704e58a027SCong Wang 	return dev_get_by_name(pn->net, b);
2071e6fce5b9SRobert Olsson }
2072e6fce5b9SRobert Olsson 
2073e6fce5b9SRobert Olsson 
20741da177e4SLinus Torvalds /* Associate pktgen_dev with a device. */
20751da177e4SLinus Torvalds 
20764e58a027SCong Wang static int pktgen_setup_dev(const struct pktgen_net *pn,
20774e58a027SCong Wang 			    struct pktgen_dev *pkt_dev, const char *ifname)
2078222f1806SLuiz Capitulino {
20791da177e4SLinus Torvalds 	struct net_device *odev;
208039df232fSStephen Hemminger 	int err;
20811da177e4SLinus Torvalds 
20821da177e4SLinus Torvalds 	/* Clean old setups */
20831da177e4SLinus Torvalds 	if (pkt_dev->odev) {
20841da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
20851da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
20861da177e4SLinus Torvalds 	}
20871da177e4SLinus Torvalds 
20884e58a027SCong Wang 	odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname);
20891da177e4SLinus Torvalds 	if (!odev) {
2090f9467eaeSJoe Perches 		pr_err("no such netdevice: \"%s\"\n", ifname);
209139df232fSStephen Hemminger 		return -ENODEV;
20921da177e4SLinus Torvalds 	}
209339df232fSStephen Hemminger 
20941da177e4SLinus Torvalds 	if (odev->type != ARPHRD_ETHER) {
2095f9467eaeSJoe Perches 		pr_err("not an ethernet device: \"%s\"\n", ifname);
209639df232fSStephen Hemminger 		err = -EINVAL;
209739df232fSStephen Hemminger 	} else if (!netif_running(odev)) {
2098f9467eaeSJoe Perches 		pr_err("device is down: \"%s\"\n", ifname);
209939df232fSStephen Hemminger 		err = -ENETDOWN;
210039df232fSStephen Hemminger 	} else {
21011da177e4SLinus Torvalds 		pkt_dev->odev = odev;
210239df232fSStephen Hemminger 		return 0;
210339df232fSStephen Hemminger 	}
21041da177e4SLinus Torvalds 
21051da177e4SLinus Torvalds 	dev_put(odev);
210639df232fSStephen Hemminger 	return err;
21071da177e4SLinus Torvalds }
21081da177e4SLinus Torvalds 
21091da177e4SLinus Torvalds /* Read pkt_dev from the interface and set up internal pktgen_dev
21101da177e4SLinus Torvalds  * structure to have the right information to create/send packets
21111da177e4SLinus Torvalds  */
21121da177e4SLinus Torvalds static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
21131da177e4SLinus Torvalds {
211464c00d81SAndrew Gallatin 	int ntxq;
211564c00d81SAndrew Gallatin 
21161da177e4SLinus Torvalds 	if (!pkt_dev->odev) {
2117f9467eaeSJoe Perches 		pr_err("ERROR: pkt_dev->odev == NULL in setup_inject\n");
2118222f1806SLuiz Capitulino 		sprintf(pkt_dev->result,
2119222f1806SLuiz Capitulino 			"ERROR: pkt_dev->odev == NULL in setup_inject.\n");
21201da177e4SLinus Torvalds 		return;
21211da177e4SLinus Torvalds 	}
21221da177e4SLinus Torvalds 
212364c00d81SAndrew Gallatin 	/* make sure that we don't pick a non-existing transmit queue */
212464c00d81SAndrew Gallatin 	ntxq = pkt_dev->odev->real_num_tx_queues;
2125bfdbc0acSRobert Olsson 
212664c00d81SAndrew Gallatin 	if (ntxq <= pkt_dev->queue_map_min) {
2127294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
212888271660SJesse Brandeburg 			pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
2129593f63b0SEric Dumazet 			pkt_dev->odevname);
213026e29eedSDan Carpenter 		pkt_dev->queue_map_min = (ntxq ?: 1) - 1;
213164c00d81SAndrew Gallatin 	}
213288271660SJesse Brandeburg 	if (pkt_dev->queue_map_max >= ntxq) {
2133294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
213488271660SJesse Brandeburg 			pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
2135593f63b0SEric Dumazet 			pkt_dev->odevname);
213626e29eedSDan Carpenter 		pkt_dev->queue_map_max = (ntxq ?: 1) - 1;
213764c00d81SAndrew Gallatin 	}
213864c00d81SAndrew Gallatin 
21391da177e4SLinus Torvalds 	/* Default to the interface's mac if not explicitly set. */
21401da177e4SLinus Torvalds 
2141f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
21429ea08b12SJoe Perches 		ether_addr_copy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr);
21431da177e4SLinus Torvalds 
21441da177e4SLinus Torvalds 	/* Set up Dest MAC */
21459ea08b12SJoe Perches 	ether_addr_copy(&(pkt_dev->hh[0]), pkt_dev->dst_mac);
21461da177e4SLinus Torvalds 
21471da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
21484c139b8cSAmerigo Wang 		int i, set = 0, err = 1;
21494c139b8cSAmerigo Wang 		struct inet6_dev *idev;
21504c139b8cSAmerigo Wang 
215168bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
215268bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct ipv6hdr)
215368bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
215468bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
215568bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
215668bf9f0bSAmerigo Wang 		}
215768bf9f0bSAmerigo Wang 
21581da177e4SLinus Torvalds 		for (i = 0; i < IN6_ADDR_HSIZE; i++)
21591da177e4SLinus Torvalds 			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
21601da177e4SLinus Torvalds 				set = 1;
21611da177e4SLinus Torvalds 				break;
21621da177e4SLinus Torvalds 			}
21631da177e4SLinus Torvalds 
21641da177e4SLinus Torvalds 		if (!set) {
21651da177e4SLinus Torvalds 
21661da177e4SLinus Torvalds 			/*
21671da177e4SLinus Torvalds 			 * Use linklevel address if unconfigured.
21681da177e4SLinus Torvalds 			 *
21691da177e4SLinus Torvalds 			 * use ipv6_get_lladdr if/when it's get exported
21701da177e4SLinus Torvalds 			 */
21711da177e4SLinus Torvalds 
21728814c4b5SYOSHIFUJI Hideaki 			rcu_read_lock();
217363adc6fbSStephen Hemminger 			idev = __in6_dev_get(pkt_dev->odev);
217463adc6fbSStephen Hemminger 			if (idev) {
21751da177e4SLinus Torvalds 				struct inet6_ifaddr *ifp;
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 				read_lock_bh(&idev->lock);
21784c139b8cSAmerigo Wang 				list_for_each_entry(ifp, &idev->addr_list, if_list) {
21794c139b8cSAmerigo Wang 					if ((ifp->scope & IFA_LINK) &&
2180f64f9e71SJoe Perches 					    !(ifp->flags & IFA_F_TENTATIVE)) {
21814e3fd7a0SAlexey Dobriyan 						pkt_dev->cur_in6_saddr = ifp->addr;
21821da177e4SLinus Torvalds 						err = 0;
21831da177e4SLinus Torvalds 						break;
21841da177e4SLinus Torvalds 					}
21851da177e4SLinus Torvalds 				}
21861da177e4SLinus Torvalds 				read_unlock_bh(&idev->lock);
21871da177e4SLinus Torvalds 			}
21888814c4b5SYOSHIFUJI Hideaki 			rcu_read_unlock();
2189222f1806SLuiz Capitulino 			if (err)
2190f9467eaeSJoe Perches 				pr_err("ERROR: IPv6 link address not available\n");
21911da177e4SLinus Torvalds 		}
2192222f1806SLuiz Capitulino 	} else {
219368bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
219468bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct iphdr)
219568bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
219668bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
219768bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
219868bf9f0bSAmerigo Wang 		}
219968bf9f0bSAmerigo Wang 
22001da177e4SLinus Torvalds 		pkt_dev->saddr_min = 0;
22011da177e4SLinus Torvalds 		pkt_dev->saddr_max = 0;
22021da177e4SLinus Torvalds 		if (strlen(pkt_dev->src_min) == 0) {
22031da177e4SLinus Torvalds 
22041da177e4SLinus Torvalds 			struct in_device *in_dev;
22051da177e4SLinus Torvalds 
22061da177e4SLinus Torvalds 			rcu_read_lock();
2207e5ed6399SHerbert Xu 			in_dev = __in_dev_get_rcu(pkt_dev->odev);
22081da177e4SLinus Torvalds 			if (in_dev) {
22091da177e4SLinus Torvalds 				if (in_dev->ifa_list) {
2210222f1806SLuiz Capitulino 					pkt_dev->saddr_min =
2211222f1806SLuiz Capitulino 					    in_dev->ifa_list->ifa_address;
22121da177e4SLinus Torvalds 					pkt_dev->saddr_max = pkt_dev->saddr_min;
22131da177e4SLinus Torvalds 				}
22141da177e4SLinus Torvalds 			}
22151da177e4SLinus Torvalds 			rcu_read_unlock();
2216222f1806SLuiz Capitulino 		} else {
22171da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
22181da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
22191da177e4SLinus Torvalds 		}
22201da177e4SLinus Torvalds 
22211da177e4SLinus Torvalds 		pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
22221da177e4SLinus Torvalds 		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
22231da177e4SLinus Torvalds 	}
22241da177e4SLinus Torvalds 	/* Initialize current values. */
222568bf9f0bSAmerigo Wang 	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
222668bf9f0bSAmerigo Wang 	if (pkt_dev->min_pkt_size > pkt_dev->max_pkt_size)
222768bf9f0bSAmerigo Wang 		pkt_dev->max_pkt_size = pkt_dev->min_pkt_size;
222868bf9f0bSAmerigo Wang 
22291da177e4SLinus Torvalds 	pkt_dev->cur_dst_mac_offset = 0;
22301da177e4SLinus Torvalds 	pkt_dev->cur_src_mac_offset = 0;
22311da177e4SLinus Torvalds 	pkt_dev->cur_saddr = pkt_dev->saddr_min;
22321da177e4SLinus Torvalds 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
22331da177e4SLinus Torvalds 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
22341da177e4SLinus Torvalds 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
22351da177e4SLinus Torvalds 	pkt_dev->nflows = 0;
22361da177e4SLinus Torvalds }
22371da177e4SLinus Torvalds 
22381da177e4SLinus Torvalds 
2239fd29cf72SStephen Hemminger static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
2240fd29cf72SStephen Hemminger {
2241ef87979cSStephen Hemminger 	ktime_t start_time, end_time;
2242417bc4b8SEric Dumazet 	s64 remaining;
22432bc481cfSStephen Hemminger 	struct hrtimer_sleeper t;
2244fd29cf72SStephen Hemminger 
22452bc481cfSStephen Hemminger 	hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
22462bc481cfSStephen Hemminger 	hrtimer_set_expires(&t.timer, spin_until);
2247fd29cf72SStephen Hemminger 
224843d28b65SDaniel Turull 	remaining = ktime_to_ns(hrtimer_expires_remaining(&t.timer));
2249417bc4b8SEric Dumazet 	if (remaining <= 0) {
2250417bc4b8SEric Dumazet 		pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
22511da177e4SLinus Torvalds 		return;
2252417bc4b8SEric Dumazet 	}
22532bc481cfSStephen Hemminger 
2254398f382cSDaniel Borkmann 	start_time = ktime_get();
225533136d12SEric Dumazet 	if (remaining < 100000) {
225633136d12SEric Dumazet 		/* for small delays (<100us), just loop until limit is reached */
225733136d12SEric Dumazet 		do {
2258398f382cSDaniel Borkmann 			end_time = ktime_get();
2259398f382cSDaniel Borkmann 		} while (ktime_compare(end_time, spin_until) < 0);
226033136d12SEric Dumazet 	} else {
22612bc481cfSStephen Hemminger 		/* see do_nanosleep */
22622bc481cfSStephen Hemminger 		hrtimer_init_sleeper(&t, current);
22632bc481cfSStephen Hemminger 		do {
22642bc481cfSStephen Hemminger 			set_current_state(TASK_INTERRUPTIBLE);
22652bc481cfSStephen Hemminger 			hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
22662bc481cfSStephen Hemminger 			if (!hrtimer_active(&t.timer))
22672bc481cfSStephen Hemminger 				t.task = NULL;
22682bc481cfSStephen Hemminger 
22692bc481cfSStephen Hemminger 			if (likely(t.task))
22701da177e4SLinus Torvalds 				schedule();
22711da177e4SLinus Torvalds 
22722bc481cfSStephen Hemminger 			hrtimer_cancel(&t.timer);
22732bc481cfSStephen Hemminger 		} while (t.task && pkt_dev->running && !signal_pending(current));
22742bc481cfSStephen Hemminger 		__set_current_state(TASK_RUNNING);
2275398f382cSDaniel Borkmann 		end_time = ktime_get();
227633136d12SEric Dumazet 	}
2277ef87979cSStephen Hemminger 
2278ef87979cSStephen Hemminger 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
227907a0f0f0SDaniel Turull 	pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
22801da177e4SLinus Torvalds }
22811da177e4SLinus Torvalds 
228216dab72fSJamal Hadi Salim static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
228316dab72fSJamal Hadi Salim {
2284a553e4a6SJamal Hadi Salim 	pkt_dev->pkt_overhead = 0;
228516dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
228616dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
228716dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
228816dab72fSJamal Hadi Salim }
228916dab72fSJamal Hadi Salim 
2290648fda74SStephen Hemminger static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
2291007a531bSJamal Hadi Salim {
2292648fda74SStephen Hemminger 	return !!(pkt_dev->flows[flow].flags & F_INIT);
2293007a531bSJamal Hadi Salim }
2294007a531bSJamal Hadi Salim 
2295007a531bSJamal Hadi Salim static inline int f_pick(struct pktgen_dev *pkt_dev)
2296007a531bSJamal Hadi Salim {
2297007a531bSJamal Hadi Salim 	int flow = pkt_dev->curfl;
2298007a531bSJamal Hadi Salim 
2299007a531bSJamal Hadi Salim 	if (pkt_dev->flags & F_FLOW_SEQ) {
2300007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2301007a531bSJamal Hadi Salim 			/* reset time */
2302007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
23031211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
2304007a531bSJamal Hadi Salim 			pkt_dev->curfl += 1;
2305007a531bSJamal Hadi Salim 			if (pkt_dev->curfl >= pkt_dev->cflows)
2306007a531bSJamal Hadi Salim 				pkt_dev->curfl = 0; /*reset */
2307007a531bSJamal Hadi Salim 		}
2308007a531bSJamal Hadi Salim 	} else {
230933d7c5e5SAkinobu Mita 		flow = prandom_u32() % pkt_dev->cflows;
23101211a645SRobert Olsson 		pkt_dev->curfl = flow;
2311007a531bSJamal Hadi Salim 
23121211a645SRobert Olsson 		if (pkt_dev->flows[flow].count > pkt_dev->lflow) {
2313007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
23141211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
23151211a645SRobert Olsson 		}
2316007a531bSJamal Hadi Salim 	}
2317007a531bSJamal Hadi Salim 
2318007a531bSJamal Hadi Salim 	return pkt_dev->curfl;
2319007a531bSJamal Hadi Salim }
2320007a531bSJamal Hadi Salim 
2321a553e4a6SJamal Hadi Salim 
2322a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2323a553e4a6SJamal Hadi Salim /* If there was already an IPSEC SA, we keep it as is, else
2324a553e4a6SJamal Hadi Salim  * we go look for it ...
2325a553e4a6SJamal Hadi Salim */
2326bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
2327fea1ab0fSAdrian Bunk static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
2328a553e4a6SJamal Hadi Salim {
2329a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[flow].x;
23304e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id);
2331a553e4a6SJamal Hadi Salim 	if (!x) {
2332c454997eSFan Du 
2333c454997eSFan Du 		if (pkt_dev->spi) {
2334c454997eSFan Du 			/* We need as quick as possible to find the right SA
2335c454997eSFan Du 			 * Searching with minimum criteria to archieve this.
2336c454997eSFan Du 			 */
2337c454997eSFan Du 			x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
2338c454997eSFan Du 		} else {
2339a553e4a6SJamal Hadi Salim 			/* slow path: we dont already have xfrm_state */
23404e58a027SCong Wang 			x = xfrm_stateonly_find(pn->net, DUMMY_MARK,
23415447c5e4SAlexey Dobriyan 						(xfrm_address_t *)&pkt_dev->cur_daddr,
2342a553e4a6SJamal Hadi Salim 						(xfrm_address_t *)&pkt_dev->cur_saddr,
2343a553e4a6SJamal Hadi Salim 						AF_INET,
2344a553e4a6SJamal Hadi Salim 						pkt_dev->ipsmode,
2345a553e4a6SJamal Hadi Salim 						pkt_dev->ipsproto, 0);
2346c454997eSFan Du 		}
2347a553e4a6SJamal Hadi Salim 		if (x) {
2348a553e4a6SJamal Hadi Salim 			pkt_dev->flows[flow].x = x;
2349a553e4a6SJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
2350a553e4a6SJamal Hadi Salim 			pkt_dev->pkt_overhead += x->props.header_len;
2351a553e4a6SJamal Hadi Salim 		}
2352a553e4a6SJamal Hadi Salim 
2353a553e4a6SJamal Hadi Salim 	}
2354a553e4a6SJamal Hadi Salim }
2355a553e4a6SJamal Hadi Salim #endif
2356fd2ea0a7SDavid S. Miller static void set_cur_queue_map(struct pktgen_dev *pkt_dev)
2357fd2ea0a7SDavid S. Miller {
2358e6fce5b9SRobert Olsson 
2359e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
2360e6fce5b9SRobert Olsson 		pkt_dev->cur_queue_map = smp_processor_id();
2361e6fce5b9SRobert Olsson 
2362896a7cf8SEric Dumazet 	else if (pkt_dev->queue_map_min <= pkt_dev->queue_map_max) {
2363fd2ea0a7SDavid S. Miller 		__u16 t;
2364fd2ea0a7SDavid S. Miller 		if (pkt_dev->flags & F_QUEUE_MAP_RND) {
236533d7c5e5SAkinobu Mita 			t = prandom_u32() %
2366fd2ea0a7SDavid S. Miller 				(pkt_dev->queue_map_max -
2367fd2ea0a7SDavid S. Miller 				 pkt_dev->queue_map_min + 1)
2368fd2ea0a7SDavid S. Miller 				+ pkt_dev->queue_map_min;
2369fd2ea0a7SDavid S. Miller 		} else {
2370fd2ea0a7SDavid S. Miller 			t = pkt_dev->cur_queue_map + 1;
2371fd2ea0a7SDavid S. Miller 			if (t > pkt_dev->queue_map_max)
2372fd2ea0a7SDavid S. Miller 				t = pkt_dev->queue_map_min;
2373fd2ea0a7SDavid S. Miller 		}
2374fd2ea0a7SDavid S. Miller 		pkt_dev->cur_queue_map = t;
2375fd2ea0a7SDavid S. Miller 	}
2376bfdbc0acSRobert Olsson 	pkt_dev->cur_queue_map  = pkt_dev->cur_queue_map % pkt_dev->odev->real_num_tx_queues;
2377fd2ea0a7SDavid S. Miller }
2378fd2ea0a7SDavid S. Miller 
23791da177e4SLinus Torvalds /* Increment/randomize headers according to flags and current values
23801da177e4SLinus Torvalds  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
23811da177e4SLinus Torvalds  */
2382222f1806SLuiz Capitulino static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2383222f1806SLuiz Capitulino {
23841da177e4SLinus Torvalds 	__u32 imn;
23851da177e4SLinus Torvalds 	__u32 imx;
23861da177e4SLinus Torvalds 	int flow = 0;
23871da177e4SLinus Torvalds 
2388007a531bSJamal Hadi Salim 	if (pkt_dev->cflows)
2389007a531bSJamal Hadi Salim 		flow = f_pick(pkt_dev);
23901da177e4SLinus Torvalds 
23911da177e4SLinus Torvalds 	/*  Deal with source MAC */
23921da177e4SLinus Torvalds 	if (pkt_dev->src_mac_count > 1) {
23931da177e4SLinus Torvalds 		__u32 mc;
23941da177e4SLinus Torvalds 		__u32 tmp;
23951da177e4SLinus Torvalds 
23961da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACSRC_RND)
239733d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->src_mac_count;
23981da177e4SLinus Torvalds 		else {
23991da177e4SLinus Torvalds 			mc = pkt_dev->cur_src_mac_offset++;
2400ff2a79a5SRobert Olsson 			if (pkt_dev->cur_src_mac_offset >=
2401222f1806SLuiz Capitulino 			    pkt_dev->src_mac_count)
24021da177e4SLinus Torvalds 				pkt_dev->cur_src_mac_offset = 0;
24031da177e4SLinus Torvalds 		}
24041da177e4SLinus Torvalds 
24051da177e4SLinus Torvalds 		tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
24061da177e4SLinus Torvalds 		pkt_dev->hh[11] = tmp;
24071da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
24081da177e4SLinus Torvalds 		pkt_dev->hh[10] = tmp;
24091da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24101da177e4SLinus Torvalds 		pkt_dev->hh[9] = tmp;
24111da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24121da177e4SLinus Torvalds 		pkt_dev->hh[8] = tmp;
24131da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
24141da177e4SLinus Torvalds 		pkt_dev->hh[7] = tmp;
24151da177e4SLinus Torvalds 	}
24161da177e4SLinus Torvalds 
24171da177e4SLinus Torvalds 	/*  Deal with Destination MAC */
24181da177e4SLinus Torvalds 	if (pkt_dev->dst_mac_count > 1) {
24191da177e4SLinus Torvalds 		__u32 mc;
24201da177e4SLinus Torvalds 		__u32 tmp;
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACDST_RND)
242333d7c5e5SAkinobu Mita 			mc = prandom_u32() % pkt_dev->dst_mac_count;
24241da177e4SLinus Torvalds 
24251da177e4SLinus Torvalds 		else {
24261da177e4SLinus Torvalds 			mc = pkt_dev->cur_dst_mac_offset++;
2427ff2a79a5SRobert Olsson 			if (pkt_dev->cur_dst_mac_offset >=
2428222f1806SLuiz Capitulino 			    pkt_dev->dst_mac_count) {
24291da177e4SLinus Torvalds 				pkt_dev->cur_dst_mac_offset = 0;
24301da177e4SLinus Torvalds 			}
24311da177e4SLinus Torvalds 		}
24321da177e4SLinus Torvalds 
24331da177e4SLinus Torvalds 		tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
24341da177e4SLinus Torvalds 		pkt_dev->hh[5] = tmp;
24351da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
24361da177e4SLinus Torvalds 		pkt_dev->hh[4] = tmp;
24371da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24381da177e4SLinus Torvalds 		pkt_dev->hh[3] = tmp;
24391da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24401da177e4SLinus Torvalds 		pkt_dev->hh[2] = tmp;
24411da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
24421da177e4SLinus Torvalds 		pkt_dev->hh[1] = tmp;
24431da177e4SLinus Torvalds 	}
24441da177e4SLinus Torvalds 
2445ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND) {
244695c96174SEric Dumazet 		unsigned int i;
2447ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
2448ca6549afSSteven Whitehouse 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2449ca6549afSSteven Whitehouse 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
245033d7c5e5SAkinobu Mita 					     ((__force __be32)prandom_u32() &
2451ca6549afSSteven Whitehouse 						      htonl(0x000fffff));
2452ca6549afSSteven Whitehouse 	}
2453ca6549afSSteven Whitehouse 
245434954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
245533d7c5e5SAkinobu Mita 		pkt_dev->vlan_id = prandom_u32() & (4096 - 1);
245634954ddcSFrancesco Fondelli 	}
245734954ddcSFrancesco Fondelli 
245834954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
245933d7c5e5SAkinobu Mita 		pkt_dev->svlan_id = prandom_u32() & (4096 - 1);
246034954ddcSFrancesco Fondelli 	}
246134954ddcSFrancesco Fondelli 
24621da177e4SLinus Torvalds 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
24631da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPSRC_RND)
246433d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_src = prandom_u32() %
24655fa6fc76SStephen Hemminger 				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)
24665fa6fc76SStephen Hemminger 				+ pkt_dev->udp_src_min;
24671da177e4SLinus Torvalds 
24681da177e4SLinus Torvalds 		else {
24691da177e4SLinus Torvalds 			pkt_dev->cur_udp_src++;
24701da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
24711da177e4SLinus Torvalds 				pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
24721da177e4SLinus Torvalds 		}
24731da177e4SLinus Torvalds 	}
24741da177e4SLinus Torvalds 
24751da177e4SLinus Torvalds 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
24761da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPDST_RND) {
247733d7c5e5SAkinobu Mita 			pkt_dev->cur_udp_dst = prandom_u32() %
24785fa6fc76SStephen Hemminger 				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
24795fa6fc76SStephen Hemminger 				+ pkt_dev->udp_dst_min;
2480222f1806SLuiz Capitulino 		} else {
24811da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst++;
24821da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
24831da177e4SLinus Torvalds 				pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
24841da177e4SLinus Torvalds 		}
24851da177e4SLinus Torvalds 	}
24861da177e4SLinus Torvalds 
24871da177e4SLinus Torvalds 	if (!(pkt_dev->flags & F_IPV6)) {
24881da177e4SLinus Torvalds 
248963adc6fbSStephen Hemminger 		imn = ntohl(pkt_dev->saddr_min);
249063adc6fbSStephen Hemminger 		imx = ntohl(pkt_dev->saddr_max);
249163adc6fbSStephen Hemminger 		if (imn < imx) {
24921da177e4SLinus Torvalds 			__u32 t;
24931da177e4SLinus Torvalds 			if (pkt_dev->flags & F_IPSRC_RND)
249433d7c5e5SAkinobu Mita 				t = prandom_u32() % (imx - imn) + imn;
24951da177e4SLinus Torvalds 			else {
24961da177e4SLinus Torvalds 				t = ntohl(pkt_dev->cur_saddr);
24971da177e4SLinus Torvalds 				t++;
249863adc6fbSStephen Hemminger 				if (t > imx)
24991da177e4SLinus Torvalds 					t = imn;
250063adc6fbSStephen Hemminger 
25011da177e4SLinus Torvalds 			}
25021da177e4SLinus Torvalds 			pkt_dev->cur_saddr = htonl(t);
25031da177e4SLinus Torvalds 		}
25041da177e4SLinus Torvalds 
2505007a531bSJamal Hadi Salim 		if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
25061da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
25071da177e4SLinus Torvalds 		} else {
2508252e3346SAl Viro 			imn = ntohl(pkt_dev->daddr_min);
2509252e3346SAl Viro 			imx = ntohl(pkt_dev->daddr_max);
2510252e3346SAl Viro 			if (imn < imx) {
25111da177e4SLinus Torvalds 				__u32 t;
2512252e3346SAl Viro 				__be32 s;
25131da177e4SLinus Torvalds 				if (pkt_dev->flags & F_IPDST_RND) {
25141da177e4SLinus Torvalds 
251570e3ba72SAkinobu Mita 					do {
251633d7c5e5SAkinobu Mita 						t = prandom_u32() %
251733d7c5e5SAkinobu Mita 							(imx - imn) + imn;
2518252e3346SAl Viro 						s = htonl(t);
251970e3ba72SAkinobu Mita 					} while (ipv4_is_loopback(s) ||
252070e3ba72SAkinobu Mita 						ipv4_is_multicast(s) ||
252170e3ba72SAkinobu Mita 						ipv4_is_lbcast(s) ||
252270e3ba72SAkinobu Mita 						ipv4_is_zeronet(s) ||
252370e3ba72SAkinobu Mita 						ipv4_is_local_multicast(s));
2524252e3346SAl Viro 					pkt_dev->cur_daddr = s;
2525252e3346SAl Viro 				} else {
25261da177e4SLinus Torvalds 					t = ntohl(pkt_dev->cur_daddr);
25271da177e4SLinus Torvalds 					t++;
25281da177e4SLinus Torvalds 					if (t > imx) {
25291da177e4SLinus Torvalds 						t = imn;
25301da177e4SLinus Torvalds 					}
25311da177e4SLinus Torvalds 					pkt_dev->cur_daddr = htonl(t);
25321da177e4SLinus Torvalds 				}
25331da177e4SLinus Torvalds 			}
25341da177e4SLinus Torvalds 			if (pkt_dev->cflows) {
2535007a531bSJamal Hadi Salim 				pkt_dev->flows[flow].flags |= F_INIT;
2536222f1806SLuiz Capitulino 				pkt_dev->flows[flow].cur_daddr =
2537222f1806SLuiz Capitulino 				    pkt_dev->cur_daddr;
2538a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2539a553e4a6SJamal Hadi Salim 				if (pkt_dev->flags & F_IPSEC_ON)
2540a553e4a6SJamal Hadi Salim 					get_ipsec_sa(pkt_dev, flow);
2541a553e4a6SJamal Hadi Salim #endif
25421da177e4SLinus Torvalds 				pkt_dev->nflows++;
25431da177e4SLinus Torvalds 			}
25441da177e4SLinus Torvalds 		}
2545222f1806SLuiz Capitulino 	} else {		/* IPV6 * */
2546222f1806SLuiz Capitulino 
254706e30411SJoe Perches 		if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {
25481da177e4SLinus Torvalds 			int i;
25491da177e4SLinus Torvalds 
25501da177e4SLinus Torvalds 			/* Only random destinations yet */
25511da177e4SLinus Torvalds 
25521da177e4SLinus Torvalds 			for (i = 0; i < 4; i++) {
25531da177e4SLinus Torvalds 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
255433d7c5e5SAkinobu Mita 				    (((__force __be32)prandom_u32() |
25551da177e4SLinus Torvalds 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
25561da177e4SLinus Torvalds 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
25571da177e4SLinus Torvalds 			}
25581da177e4SLinus Torvalds 		}
25591da177e4SLinus Torvalds 	}
25601da177e4SLinus Torvalds 
25611da177e4SLinus Torvalds 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
25621da177e4SLinus Torvalds 		__u32 t;
25631da177e4SLinus Torvalds 		if (pkt_dev->flags & F_TXSIZE_RND) {
256433d7c5e5SAkinobu Mita 			t = prandom_u32() %
25655fa6fc76SStephen Hemminger 				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
25665fa6fc76SStephen Hemminger 				+ pkt_dev->min_pkt_size;
2567222f1806SLuiz Capitulino 		} else {
25681da177e4SLinus Torvalds 			t = pkt_dev->cur_pkt_size + 1;
25691da177e4SLinus Torvalds 			if (t > pkt_dev->max_pkt_size)
25701da177e4SLinus Torvalds 				t = pkt_dev->min_pkt_size;
25711da177e4SLinus Torvalds 		}
25721da177e4SLinus Torvalds 		pkt_dev->cur_pkt_size = t;
25731da177e4SLinus Torvalds 	}
25741da177e4SLinus Torvalds 
2575fd2ea0a7SDavid S. Miller 	set_cur_queue_map(pkt_dev);
257645b270f8SRobert Olsson 
25771da177e4SLinus Torvalds 	pkt_dev->flows[flow].count++;
25781da177e4SLinus Torvalds }
25791da177e4SLinus Torvalds 
2580a553e4a6SJamal Hadi Salim 
2581a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
25825537a055SFengguang Wu static u32 pktgen_dst_metrics[RTAX_MAX + 1] = {
2583cf93d47eSFan Du 
2584cf93d47eSFan Du 	[RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */
2585cf93d47eSFan Du };
2586cf93d47eSFan Du 
2587a553e4a6SJamal Hadi Salim static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
2588a553e4a6SJamal Hadi Salim {
2589a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2590a553e4a6SJamal Hadi Salim 	int err = 0;
25916de9ace4SFan Du 	struct net *net = dev_net(pkt_dev->odev);
2592a553e4a6SJamal Hadi Salim 
2593a553e4a6SJamal Hadi Salim 	if (!x)
2594a553e4a6SJamal Hadi Salim 		return 0;
2595a553e4a6SJamal Hadi Salim 	/* XXX: we dont support tunnel mode for now until
2596a553e4a6SJamal Hadi Salim 	 * we resolve the dst issue */
2597cf93d47eSFan Du 	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))
2598a553e4a6SJamal Hadi Salim 		return 0;
2599a553e4a6SJamal Hadi Salim 
2600cf93d47eSFan Du 	/* But when user specify an valid SPI, transformation
2601cf93d47eSFan Du 	 * supports both transport/tunnel mode + ESP/AH type.
2602cf93d47eSFan Du 	 */
2603cf93d47eSFan Du 	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0))
2604cf93d47eSFan Du 		skb->_skb_refdst = (unsigned long)&pkt_dev->dst | SKB_DST_NOREF;
2605cf93d47eSFan Du 
2606cf93d47eSFan Du 	rcu_read_lock_bh();
260713996378SHerbert Xu 	err = x->outer_mode->output(x, skb);
2608cf93d47eSFan Du 	rcu_read_unlock_bh();
26096de9ace4SFan Du 	if (err) {
26106de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
2611a553e4a6SJamal Hadi Salim 		goto error;
26126de9ace4SFan Du 	}
2613a553e4a6SJamal Hadi Salim 	err = x->type->output(x, skb);
26146de9ace4SFan Du 	if (err) {
26156de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
2616a553e4a6SJamal Hadi Salim 		goto error;
26176de9ace4SFan Du 	}
26180af0a413SFan Du 	spin_lock_bh(&x->lock);
2619a553e4a6SJamal Hadi Salim 	x->curlft.bytes += skb->len;
2620a553e4a6SJamal Hadi Salim 	x->curlft.packets++;
26210af0a413SFan Du 	spin_unlock_bh(&x->lock);
2622a553e4a6SJamal Hadi Salim error:
2623a553e4a6SJamal Hadi Salim 	return err;
2624a553e4a6SJamal Hadi Salim }
2625a553e4a6SJamal Hadi Salim 
2626475ac1e4SStephen Hemminger static void free_SAs(struct pktgen_dev *pkt_dev)
2627a553e4a6SJamal Hadi Salim {
2628a553e4a6SJamal Hadi Salim 	if (pkt_dev->cflows) {
2629a553e4a6SJamal Hadi Salim 		/* let go of the SAs if we have them */
2630d6182223SPaul Gortmaker 		int i;
2631d6182223SPaul Gortmaker 		for (i = 0; i < pkt_dev->cflows; i++) {
2632a553e4a6SJamal Hadi Salim 			struct xfrm_state *x = pkt_dev->flows[i].x;
2633a553e4a6SJamal Hadi Salim 			if (x) {
2634a553e4a6SJamal Hadi Salim 				xfrm_state_put(x);
2635a553e4a6SJamal Hadi Salim 				pkt_dev->flows[i].x = NULL;
2636a553e4a6SJamal Hadi Salim 			}
2637a553e4a6SJamal Hadi Salim 		}
2638a553e4a6SJamal Hadi Salim 	}
2639a553e4a6SJamal Hadi Salim }
2640a553e4a6SJamal Hadi Salim 
2641475ac1e4SStephen Hemminger static int process_ipsec(struct pktgen_dev *pkt_dev,
2642a553e4a6SJamal Hadi Salim 			      struct sk_buff *skb, __be16 protocol)
2643a553e4a6SJamal Hadi Salim {
2644a553e4a6SJamal Hadi Salim 	if (pkt_dev->flags & F_IPSEC_ON) {
2645a553e4a6SJamal Hadi Salim 		struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2646a553e4a6SJamal Hadi Salim 		int nhead = 0;
2647a553e4a6SJamal Hadi Salim 		if (x) {
2648*d4969581SEric Dumazet 			struct ethhdr *eth;
26493868204dSfan.du 			struct iphdr *iph;
2650*d4969581SEric Dumazet 			int ret;
26513868204dSfan.du 
2652a553e4a6SJamal Hadi Salim 			nhead = x->props.header_len - skb_headroom(skb);
2653a553e4a6SJamal Hadi Salim 			if (nhead > 0) {
2654a553e4a6SJamal Hadi Salim 				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
2655a553e4a6SJamal Hadi Salim 				if (ret < 0) {
2656f9467eaeSJoe Perches 					pr_err("Error expanding ipsec packet %d\n",
2657f9467eaeSJoe Perches 					       ret);
2658b4bb4ac8SIlpo Järvinen 					goto err;
2659a553e4a6SJamal Hadi Salim 				}
2660a553e4a6SJamal Hadi Salim 			}
2661a553e4a6SJamal Hadi Salim 
2662a553e4a6SJamal Hadi Salim 			/* ipsec is not expecting ll header */
2663a553e4a6SJamal Hadi Salim 			skb_pull(skb, ETH_HLEN);
2664a553e4a6SJamal Hadi Salim 			ret = pktgen_output_ipsec(skb, pkt_dev);
2665a553e4a6SJamal Hadi Salim 			if (ret) {
2666f9467eaeSJoe Perches 				pr_err("Error creating ipsec packet %d\n", ret);
2667b4bb4ac8SIlpo Järvinen 				goto err;
2668a553e4a6SJamal Hadi Salim 			}
2669a553e4a6SJamal Hadi Salim 			/* restore ll */
2670*d4969581SEric Dumazet 			eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
2671*d4969581SEric Dumazet 			memcpy(eth, pkt_dev->hh, 2 * ETH_ALEN);
2672*d4969581SEric Dumazet 			eth->h_proto = protocol;
26733868204dSfan.du 
26743868204dSfan.du 			/* Update IPv4 header len as well as checksum value */
26753868204dSfan.du 			iph = ip_hdr(skb);
26763868204dSfan.du 			iph->tot_len = htons(skb->len - ETH_HLEN);
26773868204dSfan.du 			ip_send_check(iph);
2678a553e4a6SJamal Hadi Salim 		}
2679a553e4a6SJamal Hadi Salim 	}
2680a553e4a6SJamal Hadi Salim 	return 1;
2681b4bb4ac8SIlpo Järvinen err:
2682b4bb4ac8SIlpo Järvinen 	kfree_skb(skb);
2683b4bb4ac8SIlpo Järvinen 	return 0;
2684a553e4a6SJamal Hadi Salim }
2685a553e4a6SJamal Hadi Salim #endif
2686a553e4a6SJamal Hadi Salim 
2687ca6549afSSteven Whitehouse static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2688ca6549afSSteven Whitehouse {
268995c96174SEric Dumazet 	unsigned int i;
269063adc6fbSStephen Hemminger 	for (i = 0; i < pkt_dev->nr_labels; i++)
2691ca6549afSSteven Whitehouse 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
269263adc6fbSStephen Hemminger 
2693ca6549afSSteven Whitehouse 	mpls--;
2694ca6549afSSteven Whitehouse 	*mpls |= MPLS_STACK_BOTTOM;
2695ca6549afSSteven Whitehouse }
2696ca6549afSSteven Whitehouse 
26970f37c605SAl Viro static inline __be16 build_tci(unsigned int id, unsigned int cfi,
26980f37c605SAl Viro 			       unsigned int prio)
26990f37c605SAl Viro {
27000f37c605SAl Viro 	return htons(id | (cfi << 12) | (prio << 13));
27010f37c605SAl Viro }
27020f37c605SAl Viro 
270326ad7879SEric Dumazet static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
270426ad7879SEric Dumazet 				int datalen)
270526ad7879SEric Dumazet {
270626ad7879SEric Dumazet 	struct timeval timestamp;
270726ad7879SEric Dumazet 	struct pktgen_hdr *pgh;
270826ad7879SEric Dumazet 
270926ad7879SEric Dumazet 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
271026ad7879SEric Dumazet 	datalen -= sizeof(*pgh);
271126ad7879SEric Dumazet 
271226ad7879SEric Dumazet 	if (pkt_dev->nfrags <= 0) {
271305aebe2eSDaniel Turull 		memset(skb_put(skb, datalen), 0, datalen);
271426ad7879SEric Dumazet 	} else {
271526ad7879SEric Dumazet 		int frags = pkt_dev->nfrags;
271626ad7879SEric Dumazet 		int i, len;
27177d36a991Samit salecha 		int frag_len;
271826ad7879SEric Dumazet 
271926ad7879SEric Dumazet 
272026ad7879SEric Dumazet 		if (frags > MAX_SKB_FRAGS)
272126ad7879SEric Dumazet 			frags = MAX_SKB_FRAGS;
272226ad7879SEric Dumazet 		len = datalen - frags * PAGE_SIZE;
272326ad7879SEric Dumazet 		if (len > 0) {
272426ad7879SEric Dumazet 			memset(skb_put(skb, len), 0, len);
272526ad7879SEric Dumazet 			datalen = frags * PAGE_SIZE;
272626ad7879SEric Dumazet 		}
272726ad7879SEric Dumazet 
272826ad7879SEric Dumazet 		i = 0;
27297d36a991Samit salecha 		frag_len = (datalen/frags) < PAGE_SIZE ?
27307d36a991Samit salecha 			   (datalen/frags) : PAGE_SIZE;
273126ad7879SEric Dumazet 		while (datalen > 0) {
273226ad7879SEric Dumazet 			if (unlikely(!pkt_dev->page)) {
273326ad7879SEric Dumazet 				int node = numa_node_id();
273426ad7879SEric Dumazet 
273526ad7879SEric Dumazet 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
273626ad7879SEric Dumazet 					node = pkt_dev->node;
273726ad7879SEric Dumazet 				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
273826ad7879SEric Dumazet 				if (!pkt_dev->page)
273926ad7879SEric Dumazet 					break;
274026ad7879SEric Dumazet 			}
2741a0bec1cdSIan Campbell 			get_page(pkt_dev->page);
2742ea2ab693SIan Campbell 			skb_frag_set_page(skb, i, pkt_dev->page);
274326ad7879SEric Dumazet 			skb_shinfo(skb)->frags[i].page_offset = 0;
27447d36a991Samit salecha 			/*last fragment, fill rest of data*/
27457d36a991Samit salecha 			if (i == (frags - 1))
27469e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i],
27479e903e08SEric Dumazet 				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
27487d36a991Samit salecha 			else
27499e903e08SEric Dumazet 				skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
27509e903e08SEric Dumazet 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
27519e903e08SEric Dumazet 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
27529e903e08SEric Dumazet 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
275326ad7879SEric Dumazet 			i++;
275426ad7879SEric Dumazet 			skb_shinfo(skb)->nr_frags = i;
275526ad7879SEric Dumazet 		}
275626ad7879SEric Dumazet 	}
275726ad7879SEric Dumazet 
275826ad7879SEric Dumazet 	/* Stamp the time, and sequence number,
275926ad7879SEric Dumazet 	 * convert them to network byte order
276026ad7879SEric Dumazet 	 */
276126ad7879SEric Dumazet 	pgh->pgh_magic = htonl(PKTGEN_MAGIC);
276226ad7879SEric Dumazet 	pgh->seq_num = htonl(pkt_dev->seq_num);
276326ad7879SEric Dumazet 
2764afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP) {
2765afb84b62SJesper Dangaard Brouer 		pgh->tv_sec = 0;
2766afb84b62SJesper Dangaard Brouer 		pgh->tv_usec = 0;
2767afb84b62SJesper Dangaard Brouer 	} else {
276826ad7879SEric Dumazet 		do_gettimeofday(&timestamp);
276926ad7879SEric Dumazet 		pgh->tv_sec = htonl(timestamp.tv_sec);
277026ad7879SEric Dumazet 		pgh->tv_usec = htonl(timestamp.tv_usec);
277126ad7879SEric Dumazet 	}
2772afb84b62SJesper Dangaard Brouer }
277326ad7879SEric Dumazet 
27747a6e288dSDaniel Borkmann static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
27757a6e288dSDaniel Borkmann 					struct pktgen_dev *pkt_dev,
27767a6e288dSDaniel Borkmann 					unsigned int extralen)
27777a6e288dSDaniel Borkmann {
27787a6e288dSDaniel Borkmann 	struct sk_buff *skb = NULL;
27797a6e288dSDaniel Borkmann 	unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
27807a6e288dSDaniel Borkmann 			    pkt_dev->pkt_overhead;
27817a6e288dSDaniel Borkmann 
27827a6e288dSDaniel Borkmann 	if (pkt_dev->flags & F_NODE) {
27837a6e288dSDaniel Borkmann 		int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
27847a6e288dSDaniel Borkmann 
27857a6e288dSDaniel Borkmann 		skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node);
27867a6e288dSDaniel Borkmann 		if (likely(skb)) {
27877a6e288dSDaniel Borkmann 			skb_reserve(skb, NET_SKB_PAD);
27887a6e288dSDaniel Borkmann 			skb->dev = dev;
27897a6e288dSDaniel Borkmann 		}
27907a6e288dSDaniel Borkmann 	} else {
27917a6e288dSDaniel Borkmann 		 skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
27927a6e288dSDaniel Borkmann 	}
27937a6e288dSDaniel Borkmann 
27947a6e288dSDaniel Borkmann 	return skb;
27957a6e288dSDaniel Borkmann }
27967a6e288dSDaniel Borkmann 
27971da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
27981da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
27991da177e4SLinus Torvalds {
28001da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
28011da177e4SLinus Torvalds 	__u8 *eth;
28021da177e4SLinus Torvalds 	struct udphdr *udph;
28031da177e4SLinus Torvalds 	int datalen, iplen;
28041da177e4SLinus Torvalds 	struct iphdr *iph;
2805d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IP);
2806ca6549afSSteven Whitehouse 	__be32 *mpls;
280734954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
280834954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
280934954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
281034954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2811fd2ea0a7SDavid S. Miller 	u16 queue_map;
2812ca6549afSSteven Whitehouse 
2813ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2814d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
28151da177e4SLinus Torvalds 
281634954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2817d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
281834954ddcSFrancesco Fondelli 
281964053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
282064053beeSRobert Olsson 	 * fields.
282164053beeSRobert Olsson 	 */
282264053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2823eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
282464053beeSRobert Olsson 
28257ac5459eSDavid S. Miller 	datalen = (odev->hard_header_len + 16) & ~0xf;
2826e99b99b4SRobert Olsson 
28277a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
28281da177e4SLinus Torvalds 	if (!skb) {
28291da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
28301da177e4SLinus Torvalds 		return NULL;
28311da177e4SLinus Torvalds 	}
28321da177e4SLinus Torvalds 
28337a6e288dSDaniel Borkmann 	prefetchw(skb->data);
28347ac5459eSDavid S. Miller 	skb_reserve(skb, datalen);
28351da177e4SLinus Torvalds 
28361da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
28371da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2838ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2839ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2840ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
284134954ddcSFrancesco Fondelli 
284234954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
284334954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
284434954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28450f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
28460f37c605SAl Viro 					       pkt_dev->svlan_cfi,
28470f37c605SAl Viro 					       pkt_dev->svlan_p);
284834954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2849d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
285034954ddcSFrancesco Fondelli 		}
285134954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
28520f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
28530f37c605SAl Viro 				      pkt_dev->vlan_cfi,
28540f37c605SAl Viro 				      pkt_dev->vlan_p);
285534954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2856d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IP);
285734954ddcSFrancesco Fondelli 	}
285834954ddcSFrancesco Fondelli 
2859525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2860525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2861525cebedSThomas Graf 	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
2862525cebedSThomas Graf 
2863525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2864525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2865fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
28669e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
28679e50e3acSJohn Fastabend 
28681da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2869252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
28701da177e4SLinus Torvalds 
2871ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2872ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
287316dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
28746af773e7SNishank Trivedi 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
28751da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
28761da177e4SLinus Torvalds 
28771da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
28781da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
28791da177e4SLinus Torvalds 	udph->len = htons(datalen + 8);	/* DATA + udphdr */
2880c26bf4a5SThomas Graf 	udph->check = 0;
28811da177e4SLinus Torvalds 
28821da177e4SLinus Torvalds 	iph->ihl = 5;
28831da177e4SLinus Torvalds 	iph->version = 4;
28841da177e4SLinus Torvalds 	iph->ttl = 32;
28851ca7768cSFrancesco Fondelli 	iph->tos = pkt_dev->tos;
28861da177e4SLinus Torvalds 	iph->protocol = IPPROTO_UDP;	/* UDP */
28871da177e4SLinus Torvalds 	iph->saddr = pkt_dev->cur_saddr;
28881da177e4SLinus Torvalds 	iph->daddr = pkt_dev->cur_daddr;
288966ed1e5eSEric Dumazet 	iph->id = htons(pkt_dev->ip_id);
289066ed1e5eSEric Dumazet 	pkt_dev->ip_id++;
28911da177e4SLinus Torvalds 	iph->frag_off = 0;
28921da177e4SLinus Torvalds 	iplen = 20 + 8 + datalen;
28931da177e4SLinus Torvalds 	iph->tot_len = htons(iplen);
289403c633e7SThomas Graf 	ip_send_check(iph);
2895ca6549afSSteven Whitehouse 	skb->protocol = protocol;
28961da177e4SLinus Torvalds 	skb->dev = odev;
28971da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
2898c26bf4a5SThomas Graf 
28997744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
29007744b5f3SSabrina Dubroca 
2901c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
2902c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
2903c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V4_CSUM) {
2904c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
2905c26bf4a5SThomas Graf 		skb->csum = 0;
29067744b5f3SSabrina Dubroca 		udp4_hwcsum(skb, iph->saddr, iph->daddr);
2907c26bf4a5SThomas Graf 	} else {
29087744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), datalen + 8, 0);
2909c26bf4a5SThomas Graf 
2910c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
29117744b5f3SSabrina Dubroca 		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
2912c26bf4a5SThomas Graf 						datalen + 8, IPPROTO_UDP, csum);
2913c26bf4a5SThomas Graf 
2914c26bf4a5SThomas Graf 		if (udph->check == 0)
2915c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
2916c26bf4a5SThomas Graf 	}
2917c26bf4a5SThomas Graf 
2918a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2919a553e4a6SJamal Hadi Salim 	if (!process_ipsec(pkt_dev, skb, protocol))
2920a553e4a6SJamal Hadi Salim 		return NULL;
2921a553e4a6SJamal Hadi Salim #endif
2922a553e4a6SJamal Hadi Salim 
29231da177e4SLinus Torvalds 	return skb;
29241da177e4SLinus Torvalds }
29251da177e4SLinus Torvalds 
29261da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
29271da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
29281da177e4SLinus Torvalds {
29291da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
29301da177e4SLinus Torvalds 	__u8 *eth;
29311da177e4SLinus Torvalds 	struct udphdr *udph;
2932c26bf4a5SThomas Graf 	int datalen, udplen;
29331da177e4SLinus Torvalds 	struct ipv6hdr *iph;
2934d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IPV6);
2935ca6549afSSteven Whitehouse 	__be32 *mpls;
293634954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
293734954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
293834954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
293934954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2940fd2ea0a7SDavid S. Miller 	u16 queue_map;
2941ca6549afSSteven Whitehouse 
2942ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2943d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
29441da177e4SLinus Torvalds 
294534954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2946d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
294734954ddcSFrancesco Fondelli 
294864053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
294964053beeSRobert Olsson 	 * fields.
295064053beeSRobert Olsson 	 */
295164053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2952eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
295364053beeSRobert Olsson 
29547a6e288dSDaniel Borkmann 	skb = pktgen_alloc_skb(odev, pkt_dev, 16);
29551da177e4SLinus Torvalds 	if (!skb) {
29561da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
29571da177e4SLinus Torvalds 		return NULL;
29581da177e4SLinus Torvalds 	}
29591da177e4SLinus Torvalds 
29607a6e288dSDaniel Borkmann 	prefetchw(skb->data);
29611da177e4SLinus Torvalds 	skb_reserve(skb, 16);
29621da177e4SLinus Torvalds 
29631da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
29641da177e4SLinus Torvalds 	eth = (__u8 *) skb_push(skb, 14);
2965ca6549afSSteven Whitehouse 	mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2966ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2967ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
296834954ddcSFrancesco Fondelli 
296934954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
297034954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
297134954ddcSFrancesco Fondelli 			svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29720f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
29730f37c605SAl Viro 					       pkt_dev->svlan_cfi,
29740f37c605SAl Viro 					       pkt_dev->svlan_p);
297534954ddcSFrancesco Fondelli 			svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2976d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
297734954ddcSFrancesco Fondelli 		}
297834954ddcSFrancesco Fondelli 		vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
29790f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
29800f37c605SAl Viro 				      pkt_dev->vlan_cfi,
29810f37c605SAl Viro 				      pkt_dev->vlan_p);
298234954ddcSFrancesco Fondelli 		vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
2983d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
298434954ddcSFrancesco Fondelli 	}
298534954ddcSFrancesco Fondelli 
2986525cebedSThomas Graf 	skb_set_mac_header(skb, 0);
2987525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
2988525cebedSThomas Graf 	iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
2989525cebedSThomas Graf 
2990525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
2991525cebedSThomas Graf 	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
2992fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
29939e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
29941da177e4SLinus Torvalds 
29951da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2996252e3346SAl Viro 	*(__be16 *) &eth[12] = protocol;
29971da177e4SLinus Torvalds 
2998ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2999ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 -
3000ca6549afSSteven Whitehouse 		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
300116dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
30021da177e4SLinus Torvalds 
30035aa8b572SAmerigo Wang 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr)) {
30041da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
3005e87cc472SJoe Perches 		net_info_ratelimited("increased datalen to %d\n", datalen);
30061da177e4SLinus Torvalds 	}
30071da177e4SLinus Torvalds 
3008c26bf4a5SThomas Graf 	udplen = datalen + sizeof(struct udphdr);
30091da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
30101da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
3011c26bf4a5SThomas Graf 	udph->len = htons(udplen);
3012c26bf4a5SThomas Graf 	udph->check = 0;
30131da177e4SLinus Torvalds 
3014d5f1ce9aSStephen Hemminger 	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
30151da177e4SLinus Torvalds 
30161ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
30171ca7768cSFrancesco Fondelli 		/* Version + traffic class + flow (0) */
3018252e3346SAl Viro 		*(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
30191ca7768cSFrancesco Fondelli 	}
30201ca7768cSFrancesco Fondelli 
30211da177e4SLinus Torvalds 	iph->hop_limit = 32;
30221da177e4SLinus Torvalds 
3023c26bf4a5SThomas Graf 	iph->payload_len = htons(udplen);
30241da177e4SLinus Torvalds 	iph->nexthdr = IPPROTO_UDP;
30251da177e4SLinus Torvalds 
30264e3fd7a0SAlexey Dobriyan 	iph->daddr = pkt_dev->cur_in6_daddr;
30274e3fd7a0SAlexey Dobriyan 	iph->saddr = pkt_dev->cur_in6_saddr;
30281da177e4SLinus Torvalds 
3029ca6549afSSteven Whitehouse 	skb->protocol = protocol;
30301da177e4SLinus Torvalds 	skb->dev = odev;
30311da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
30321da177e4SLinus Torvalds 
30337744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
30347744b5f3SSabrina Dubroca 
3035c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
3036c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
3037c26bf4a5SThomas Graf 	} else if (odev->features & NETIF_F_V6_CSUM) {
3038c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
3039c26bf4a5SThomas Graf 		skb->csum_start = skb_transport_header(skb) - skb->head;
3040c26bf4a5SThomas Graf 		skb->csum_offset = offsetof(struct udphdr, check);
3041c26bf4a5SThomas Graf 		udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0);
3042c26bf4a5SThomas Graf 	} else {
30437744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), udplen, 0);
3044c26bf4a5SThomas Graf 
3045c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
3046c26bf4a5SThomas Graf 		udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum);
3047c26bf4a5SThomas Graf 
3048c26bf4a5SThomas Graf 		if (udph->check == 0)
3049c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
3050c26bf4a5SThomas Graf 	}
3051c26bf4a5SThomas Graf 
30521da177e4SLinus Torvalds 	return skb;
30531da177e4SLinus Torvalds }
30541da177e4SLinus Torvalds 
3055475ac1e4SStephen Hemminger static struct sk_buff *fill_packet(struct net_device *odev,
30561da177e4SLinus Torvalds 				   struct pktgen_dev *pkt_dev)
30571da177e4SLinus Torvalds {
30581da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
30591da177e4SLinus Torvalds 		return fill_packet_ipv6(odev, pkt_dev);
30601da177e4SLinus Torvalds 	else
30611da177e4SLinus Torvalds 		return fill_packet_ipv4(odev, pkt_dev);
30621da177e4SLinus Torvalds }
30631da177e4SLinus Torvalds 
30641da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
30651da177e4SLinus Torvalds {
30661da177e4SLinus Torvalds 	pkt_dev->seq_num = 1;
30671da177e4SLinus Torvalds 	pkt_dev->idle_acc = 0;
30681da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
30691da177e4SLinus Torvalds 	pkt_dev->tx_bytes = 0;
30701da177e4SLinus Torvalds 	pkt_dev->errors = 0;
30711da177e4SLinus Torvalds }
30721da177e4SLinus Torvalds 
30731da177e4SLinus Torvalds /* Set up structure for sending pkts, clear counters */
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds static void pktgen_run(struct pktgen_thread *t)
30761da177e4SLinus Torvalds {
3077c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
30781da177e4SLinus Torvalds 	int started = 0;
30791da177e4SLinus Torvalds 
3080f9467eaeSJoe Perches 	func_enter();
30811da177e4SLinus Torvalds 
30828788370aSJesper Dangaard Brouer 	rcu_read_lock();
30838788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
30841da177e4SLinus Torvalds 
30851da177e4SLinus Torvalds 		/*
30861da177e4SLinus Torvalds 		 * setup odev and create initial packet.
30871da177e4SLinus Torvalds 		 */
30881da177e4SLinus Torvalds 		pktgen_setup_inject(pkt_dev);
30891da177e4SLinus Torvalds 
30901da177e4SLinus Torvalds 		if (pkt_dev->odev) {
30911da177e4SLinus Torvalds 			pktgen_clear_counters(pkt_dev);
30921da177e4SLinus Torvalds 			pkt_dev->skb = NULL;
3093398f382cSDaniel Borkmann 			pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
3094fd29cf72SStephen Hemminger 
309516dab72fSJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
30961da177e4SLinus Torvalds 
30971da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Starting");
30988788370aSJesper Dangaard Brouer 			pkt_dev->running = 1;	/* Cranke yeself! */
30991da177e4SLinus Torvalds 			started++;
3100222f1806SLuiz Capitulino 		} else
31011da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Error starting");
31021da177e4SLinus Torvalds 	}
31038788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3104222f1806SLuiz Capitulino 	if (started)
3105222f1806SLuiz Capitulino 		t->control &= ~(T_STOP);
31061da177e4SLinus Torvalds }
31071da177e4SLinus Torvalds 
31084e58a027SCong Wang static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn)
31091da177e4SLinus Torvalds {
3110cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31111da177e4SLinus Torvalds 
3112f9467eaeSJoe Perches 	func_enter();
31131da177e4SLinus Torvalds 
31146146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3115cdcdbe0bSLuiz Capitulino 
31164e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
311795ed63f7SArthur Kepner 		t->control |= T_STOP;
3118cdcdbe0bSLuiz Capitulino 
31196146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31201da177e4SLinus Torvalds }
31211da177e4SLinus Torvalds 
3122648fda74SStephen Hemminger static int thread_is_running(const struct pktgen_thread *t)
31231da177e4SLinus Torvalds {
3124648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
31251da177e4SLinus Torvalds 
31268788370aSJesper Dangaard Brouer 	rcu_read_lock();
31278788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
31288788370aSJesper Dangaard Brouer 		if (pkt_dev->running) {
31298788370aSJesper Dangaard Brouer 			rcu_read_unlock();
3130648fda74SStephen Hemminger 			return 1;
31318788370aSJesper Dangaard Brouer 		}
31328788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3133648fda74SStephen Hemminger 	return 0;
31341da177e4SLinus Torvalds }
31351da177e4SLinus Torvalds 
31361da177e4SLinus Torvalds static int pktgen_wait_thread_run(struct pktgen_thread *t)
31371da177e4SLinus Torvalds {
31381da177e4SLinus Torvalds 	while (thread_is_running(t)) {
31391da177e4SLinus Torvalds 
31401da177e4SLinus Torvalds 		msleep_interruptible(100);
31411da177e4SLinus Torvalds 
31421da177e4SLinus Torvalds 		if (signal_pending(current))
31431da177e4SLinus Torvalds 			goto signal;
31441da177e4SLinus Torvalds 	}
31451da177e4SLinus Torvalds 	return 1;
31461da177e4SLinus Torvalds signal:
31471da177e4SLinus Torvalds 	return 0;
31481da177e4SLinus Torvalds }
31491da177e4SLinus Torvalds 
31504e58a027SCong Wang static int pktgen_wait_all_threads_run(struct pktgen_net *pn)
31511da177e4SLinus Torvalds {
3152cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31531da177e4SLinus Torvalds 	int sig = 1;
31541da177e4SLinus Torvalds 
31556146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3156cdcdbe0bSLuiz Capitulino 
31574e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
31581da177e4SLinus Torvalds 		sig = pktgen_wait_thread_run(t);
3159222f1806SLuiz Capitulino 		if (sig == 0)
3160222f1806SLuiz Capitulino 			break;
31611da177e4SLinus Torvalds 	}
3162cdcdbe0bSLuiz Capitulino 
3163cdcdbe0bSLuiz Capitulino 	if (sig == 0)
31644e58a027SCong Wang 		list_for_each_entry(t, &pn->pktgen_threads, th_list)
31651da177e4SLinus Torvalds 			t->control |= (T_STOP);
3166cdcdbe0bSLuiz Capitulino 
31676146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31681da177e4SLinus Torvalds 	return sig;
31691da177e4SLinus Torvalds }
31701da177e4SLinus Torvalds 
31714e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn)
31721da177e4SLinus Torvalds {
3173cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31741da177e4SLinus Torvalds 
3175f9467eaeSJoe Perches 	func_enter();
31761da177e4SLinus Torvalds 
31776146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
31781da177e4SLinus Torvalds 
31794e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
31801da177e4SLinus Torvalds 		t->control |= (T_RUN);
3181cdcdbe0bSLuiz Capitulino 
31826146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31831da177e4SLinus Torvalds 
318463adc6fbSStephen Hemminger 	/* Propagate thread->control  */
318563adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
31861da177e4SLinus Torvalds 
31874e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
31881da177e4SLinus Torvalds }
31891da177e4SLinus Torvalds 
31904e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn)
3191eb37b41cSJesse Brandeburg {
3192eb37b41cSJesse Brandeburg 	struct pktgen_thread *t;
3193eb37b41cSJesse Brandeburg 
3194f9467eaeSJoe Perches 	func_enter();
3195eb37b41cSJesse Brandeburg 
3196eb37b41cSJesse Brandeburg 	mutex_lock(&pktgen_thread_lock);
3197eb37b41cSJesse Brandeburg 
31984e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
3199eb37b41cSJesse Brandeburg 		t->control |= (T_REMDEVALL);
3200eb37b41cSJesse Brandeburg 
3201eb37b41cSJesse Brandeburg 	mutex_unlock(&pktgen_thread_lock);
3202eb37b41cSJesse Brandeburg 
320363adc6fbSStephen Hemminger 	/* Propagate thread->control  */
320463adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
3205eb37b41cSJesse Brandeburg 
32064e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
3207eb37b41cSJesse Brandeburg }
3208eb37b41cSJesse Brandeburg 
32091da177e4SLinus Torvalds static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
32101da177e4SLinus Torvalds {
3211fd29cf72SStephen Hemminger 	__u64 bps, mbps, pps;
32121da177e4SLinus Torvalds 	char *p = pkt_dev->result;
3213fd29cf72SStephen Hemminger 	ktime_t elapsed = ktime_sub(pkt_dev->stopped_at,
3214fd29cf72SStephen Hemminger 				    pkt_dev->started_at);
3215fd29cf72SStephen Hemminger 	ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
32161da177e4SLinus Torvalds 
321703a14ab1SDaniel Turull 	p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
3218fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(elapsed),
3219fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
3220fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(idle),
32211da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->sofar,
32221da177e4SLinus Torvalds 		     pkt_dev->cur_pkt_size, nr_frags);
32231da177e4SLinus Torvalds 
3224fd29cf72SStephen Hemminger 	pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC,
3225fd29cf72SStephen Hemminger 			ktime_to_ns(elapsed));
32261da177e4SLinus Torvalds 
32271da177e4SLinus Torvalds 	bps = pps * 8 * pkt_dev->cur_pkt_size;
32281da177e4SLinus Torvalds 
32291da177e4SLinus Torvalds 	mbps = bps;
32301da177e4SLinus Torvalds 	do_div(mbps, 1000000);
32311da177e4SLinus Torvalds 	p += sprintf(p, "  %llupps %lluMb/sec (%llubps) errors: %llu",
32321da177e4SLinus Torvalds 		     (unsigned long long)pps,
32331da177e4SLinus Torvalds 		     (unsigned long long)mbps,
32341da177e4SLinus Torvalds 		     (unsigned long long)bps,
32351da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->errors);
32361da177e4SLinus Torvalds }
32371da177e4SLinus Torvalds 
32381da177e4SLinus Torvalds /* Set stopped-at timer, remove from running list, do counters & statistics */
32391da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
32401da177e4SLinus Torvalds {
3241222f1806SLuiz Capitulino 	int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
32421da177e4SLinus Torvalds 
32431da177e4SLinus Torvalds 	if (!pkt_dev->running) {
3244294a0b7fSJoe Perches 		pr_warn("interface: %s is already stopped\n",
3245f9467eaeSJoe Perches 			pkt_dev->odevname);
32461da177e4SLinus Torvalds 		return -EINVAL;
32471da177e4SLinus Torvalds 	}
32481da177e4SLinus Torvalds 
32498788370aSJesper Dangaard Brouer 	pkt_dev->running = 0;
32503bda06a3SStephen Hemminger 	kfree_skb(pkt_dev->skb);
32513bda06a3SStephen Hemminger 	pkt_dev->skb = NULL;
3252398f382cSDaniel Borkmann 	pkt_dev->stopped_at = ktime_get();
32531da177e4SLinus Torvalds 
325495ed63f7SArthur Kepner 	show_results(pkt_dev, nr_frags);
32551da177e4SLinus Torvalds 
32561da177e4SLinus Torvalds 	return 0;
32571da177e4SLinus Torvalds }
32581da177e4SLinus Torvalds 
32591da177e4SLinus Torvalds static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
32601da177e4SLinus Torvalds {
3261c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev, *best = NULL;
32621da177e4SLinus Torvalds 
32638788370aSJesper Dangaard Brouer 	rcu_read_lock();
32648788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3265c26a8016SLuiz Capitulino 		if (!pkt_dev->running)
3266222f1806SLuiz Capitulino 			continue;
3267222f1806SLuiz Capitulino 		if (best == NULL)
3268c26a8016SLuiz Capitulino 			best = pkt_dev;
3269398f382cSDaniel Borkmann 		else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
3270c26a8016SLuiz Capitulino 			best = pkt_dev;
32711da177e4SLinus Torvalds 	}
32728788370aSJesper Dangaard Brouer 	rcu_read_unlock();
32738788370aSJesper Dangaard Brouer 
32741da177e4SLinus Torvalds 	return best;
32751da177e4SLinus Torvalds }
32761da177e4SLinus Torvalds 
3277222f1806SLuiz Capitulino static void pktgen_stop(struct pktgen_thread *t)
3278222f1806SLuiz Capitulino {
3279c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
32801da177e4SLinus Torvalds 
3281f9467eaeSJoe Perches 	func_enter();
32821da177e4SLinus Torvalds 
32838788370aSJesper Dangaard Brouer 	rcu_read_lock();
32841da177e4SLinus Torvalds 
32858788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3286c26a8016SLuiz Capitulino 		pktgen_stop_device(pkt_dev);
328795ed63f7SArthur Kepner 	}
328895ed63f7SArthur Kepner 
32898788370aSJesper Dangaard Brouer 	rcu_read_unlock();
329095ed63f7SArthur Kepner }
329195ed63f7SArthur Kepner 
329295ed63f7SArthur Kepner /*
329395ed63f7SArthur Kepner  * one of our devices needs to be removed - find it
329495ed63f7SArthur Kepner  * and remove it
329595ed63f7SArthur Kepner  */
329695ed63f7SArthur Kepner static void pktgen_rem_one_if(struct pktgen_thread *t)
329795ed63f7SArthur Kepner {
3298c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3299c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
330095ed63f7SArthur Kepner 
3301f9467eaeSJoe Perches 	func_enter();
330295ed63f7SArthur Kepner 
3303c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3304c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
330595ed63f7SArthur Kepner 
3306222f1806SLuiz Capitulino 		if (!cur->removal_mark)
3307222f1806SLuiz Capitulino 			continue;
330895ed63f7SArthur Kepner 
330995ed63f7SArthur Kepner 		kfree_skb(cur->skb);
331095ed63f7SArthur Kepner 		cur->skb = NULL;
331195ed63f7SArthur Kepner 
331295ed63f7SArthur Kepner 		pktgen_remove_device(t, cur);
331395ed63f7SArthur Kepner 
331495ed63f7SArthur Kepner 		break;
331595ed63f7SArthur Kepner 	}
33161da177e4SLinus Torvalds }
33171da177e4SLinus Torvalds 
33181da177e4SLinus Torvalds static void pktgen_rem_all_ifs(struct pktgen_thread *t)
33191da177e4SLinus Torvalds {
3320c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3321c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
33221da177e4SLinus Torvalds 
3323f9467eaeSJoe Perches 	func_enter();
3324f9467eaeSJoe Perches 
33251da177e4SLinus Torvalds 	/* Remove all devices, free mem */
33261da177e4SLinus Torvalds 
3327c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3328c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
332995ed63f7SArthur Kepner 
333095ed63f7SArthur Kepner 		kfree_skb(cur->skb);
333195ed63f7SArthur Kepner 		cur->skb = NULL;
333295ed63f7SArthur Kepner 
33331da177e4SLinus Torvalds 		pktgen_remove_device(t, cur);
33341da177e4SLinus Torvalds 	}
33351da177e4SLinus Torvalds }
33361da177e4SLinus Torvalds 
33371da177e4SLinus Torvalds static void pktgen_rem_thread(struct pktgen_thread *t)
33381da177e4SLinus Torvalds {
33391da177e4SLinus Torvalds 	/* Remove from the thread list */
33404e58a027SCong Wang 	remove_proc_entry(t->tsk->comm, t->net->proc_dir);
33411da177e4SLinus Torvalds }
33421da177e4SLinus Torvalds 
3343ef87979cSStephen Hemminger static void pktgen_resched(struct pktgen_dev *pkt_dev)
33443791decbSStephen Hemminger {
3345398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
33463791decbSStephen Hemminger 	schedule();
3347398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
33483791decbSStephen Hemminger }
33493791decbSStephen Hemminger 
3350ef87979cSStephen Hemminger static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
3351ef87979cSStephen Hemminger {
3352398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
3353ef87979cSStephen Hemminger 
3354ef87979cSStephen Hemminger 	while (atomic_read(&(pkt_dev->skb->users)) != 1) {
3355ef87979cSStephen Hemminger 		if (signal_pending(current))
3356ef87979cSStephen Hemminger 			break;
3357ef87979cSStephen Hemminger 
3358ef87979cSStephen Hemminger 		if (need_resched())
3359ef87979cSStephen Hemminger 			pktgen_resched(pkt_dev);
3360ef87979cSStephen Hemminger 		else
3361ef87979cSStephen Hemminger 			cpu_relax();
3362ef87979cSStephen Hemminger 	}
3363398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
3364ef87979cSStephen Hemminger }
3365fd29cf72SStephen Hemminger 
3366475ac1e4SStephen Hemminger static void pktgen_xmit(struct pktgen_dev *pkt_dev)
33671da177e4SLinus Torvalds {
336838b2cf29SAlexei Starovoitov 	unsigned int burst = ACCESS_ONCE(pkt_dev->burst);
336900829823SStephen Hemminger 	struct net_device *odev = pkt_dev->odev;
3370fd2ea0a7SDavid S. Miller 	struct netdev_queue *txq;
337162f64aedSAlexei Starovoitov 	struct sk_buff *skb;
33721da177e4SLinus Torvalds 	int ret;
33731da177e4SLinus Torvalds 
3374ef87979cSStephen Hemminger 	/* If device is offline, then don't send */
3375ef87979cSStephen Hemminger 	if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
33761da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
33773791decbSStephen Hemminger 		return;
33781da177e4SLinus Torvalds 	}
33791da177e4SLinus Torvalds 
3380ef87979cSStephen Hemminger 	/* This is max DELAY, this has special meaning of
3381ef87979cSStephen Hemminger 	 * "never transmit"
3382ef87979cSStephen Hemminger 	 */
3383ef87979cSStephen Hemminger 	if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
3384398f382cSDaniel Borkmann 		pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
3385ef87979cSStephen Hemminger 		return;
3386ef87979cSStephen Hemminger 	}
3387ef87979cSStephen Hemminger 
3388ef87979cSStephen Hemminger 	/* If no skb or clone count exhausted then get new one */
33897d7bb1cfSStephen Hemminger 	if (!pkt_dev->skb || (pkt_dev->last_ok &&
33907d7bb1cfSStephen Hemminger 			      ++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
33911da177e4SLinus Torvalds 		/* build a new pkt */
33921da177e4SLinus Torvalds 		kfree_skb(pkt_dev->skb);
33931da177e4SLinus Torvalds 
33941da177e4SLinus Torvalds 		pkt_dev->skb = fill_packet(odev, pkt_dev);
33951da177e4SLinus Torvalds 		if (pkt_dev->skb == NULL) {
3396f9467eaeSJoe Perches 			pr_err("ERROR: couldn't allocate skb in fill_packet\n");
33971da177e4SLinus Torvalds 			schedule();
33981da177e4SLinus Torvalds 			pkt_dev->clone_count--;	/* back out increment, OOM */
33993791decbSStephen Hemminger 			return;
34001da177e4SLinus Torvalds 		}
3401baac8564SEric Dumazet 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
34021da177e4SLinus Torvalds 		pkt_dev->allocated_skbs++;
34031da177e4SLinus Torvalds 		pkt_dev->clone_count = 0;	/* reset counter */
34041da177e4SLinus Torvalds 	}
34051da177e4SLinus Torvalds 
3406ef87979cSStephen Hemminger 	if (pkt_dev->delay && pkt_dev->last_ok)
3407ef87979cSStephen Hemminger 		spin(pkt_dev, pkt_dev->next_tx);
3408ef87979cSStephen Hemminger 
340962f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
341062f64aedSAlexei Starovoitov 		skb = pkt_dev->skb;
341162f64aedSAlexei Starovoitov 		skb->protocol = eth_type_trans(skb, skb->dev);
341262f64aedSAlexei Starovoitov 		atomic_add(burst, &skb->users);
341362f64aedSAlexei Starovoitov 		local_bh_disable();
341462f64aedSAlexei Starovoitov 		do {
341562f64aedSAlexei Starovoitov 			ret = netif_receive_skb(skb);
341662f64aedSAlexei Starovoitov 			if (ret == NET_RX_DROP)
341762f64aedSAlexei Starovoitov 				pkt_dev->errors++;
341862f64aedSAlexei Starovoitov 			pkt_dev->sofar++;
341962f64aedSAlexei Starovoitov 			pkt_dev->seq_num++;
342062f64aedSAlexei Starovoitov 			if (atomic_read(&skb->users) != burst) {
342162f64aedSAlexei Starovoitov 				/* skb was queued by rps/rfs or taps,
342262f64aedSAlexei Starovoitov 				 * so cannot reuse this skb
342362f64aedSAlexei Starovoitov 				 */
342462f64aedSAlexei Starovoitov 				atomic_sub(burst - 1, &skb->users);
342562f64aedSAlexei Starovoitov 				/* get out of the loop and wait
342662f64aedSAlexei Starovoitov 				 * until skb is consumed
342762f64aedSAlexei Starovoitov 				 */
342862f64aedSAlexei Starovoitov 				break;
342962f64aedSAlexei Starovoitov 			}
343062f64aedSAlexei Starovoitov 			/* skb was 'freed' by stack, so clean few
343162f64aedSAlexei Starovoitov 			 * bits and reuse it
343262f64aedSAlexei Starovoitov 			 */
343362f64aedSAlexei Starovoitov #ifdef CONFIG_NET_CLS_ACT
343462f64aedSAlexei Starovoitov 			skb->tc_verd = 0; /* reset reclass/redir ttl */
343562f64aedSAlexei Starovoitov #endif
343662f64aedSAlexei Starovoitov 		} while (--burst > 0);
343762f64aedSAlexei Starovoitov 		goto out; /* Skips xmit_mode M_START_XMIT */
343862f64aedSAlexei Starovoitov 	}
343962f64aedSAlexei Starovoitov 
344010c51b56SDaniel Borkmann 	txq = skb_get_tx_queue(odev, pkt_dev->skb);
3441fd2ea0a7SDavid S. Miller 
34420f2eea4bSDaniel Borkmann 	local_bh_disable();
34430f2eea4bSDaniel Borkmann 
34440f2eea4bSDaniel Borkmann 	HARD_TX_LOCK(odev, txq, smp_processor_id());
34455b8db2f5SStephen Hemminger 
34466f25cd47SDaniel Borkmann 	if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) {
3447ef87979cSStephen Hemminger 		ret = NETDEV_TX_BUSY;
34480835acfeSEric Dumazet 		pkt_dev->last_ok = 0;
34490835acfeSEric Dumazet 		goto unlock;
34500835acfeSEric Dumazet 	}
345138b2cf29SAlexei Starovoitov 	atomic_add(burst, &pkt_dev->skb->users);
345238b2cf29SAlexei Starovoitov 
345338b2cf29SAlexei Starovoitov xmit_more:
345438b2cf29SAlexei Starovoitov 	ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0);
3455ef87979cSStephen Hemminger 
34565b8db2f5SStephen Hemminger 	switch (ret) {
34575b8db2f5SStephen Hemminger 	case NETDEV_TX_OK:
34581da177e4SLinus Torvalds 		pkt_dev->last_ok = 1;
34591da177e4SLinus Torvalds 		pkt_dev->sofar++;
34601da177e4SLinus Torvalds 		pkt_dev->seq_num++;
3461baac8564SEric Dumazet 		pkt_dev->tx_bytes += pkt_dev->last_pkt_size;
346238b2cf29SAlexei Starovoitov 		if (burst > 0 && !netif_xmit_frozen_or_drv_stopped(txq))
346338b2cf29SAlexei Starovoitov 			goto xmit_more;
34645b8db2f5SStephen Hemminger 		break;
3465f466dba1SJohn Fastabend 	case NET_XMIT_DROP:
3466f466dba1SJohn Fastabend 	case NET_XMIT_CN:
3467f466dba1SJohn Fastabend 	case NET_XMIT_POLICED:
3468f466dba1SJohn Fastabend 		/* skb has been consumed */
3469f466dba1SJohn Fastabend 		pkt_dev->errors++;
3470f466dba1SJohn Fastabend 		break;
34715b8db2f5SStephen Hemminger 	default: /* Drivers are not supposed to return other values! */
3472e87cc472SJoe Perches 		net_info_ratelimited("%s xmit error: %d\n",
3473e87cc472SJoe Perches 				     pkt_dev->odevname, ret);
34741da177e4SLinus Torvalds 		pkt_dev->errors++;
34755b8db2f5SStephen Hemminger 		/* fallthru */
3476ef87979cSStephen Hemminger 	case NETDEV_TX_LOCKED:
34775b8db2f5SStephen Hemminger 	case NETDEV_TX_BUSY:
34785b8db2f5SStephen Hemminger 		/* Retry it next time */
34795b8db2f5SStephen Hemminger 		atomic_dec(&(pkt_dev->skb->users));
34801da177e4SLinus Torvalds 		pkt_dev->last_ok = 0;
34811da177e4SLinus Torvalds 	}
348238b2cf29SAlexei Starovoitov 	if (unlikely(burst))
348338b2cf29SAlexei Starovoitov 		atomic_sub(burst, &pkt_dev->skb->users);
34840835acfeSEric Dumazet unlock:
34850f2eea4bSDaniel Borkmann 	HARD_TX_UNLOCK(odev, txq);
34860f2eea4bSDaniel Borkmann 
348762f64aedSAlexei Starovoitov out:
34880f2eea4bSDaniel Borkmann 	local_bh_enable();
34891da177e4SLinus Torvalds 
34901da177e4SLinus Torvalds 	/* If pkt_dev->count is zero, then run forever */
34911da177e4SLinus Torvalds 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
3492ef87979cSStephen Hemminger 		pktgen_wait_for_skb(pkt_dev);
34931da177e4SLinus Torvalds 
34941da177e4SLinus Torvalds 		/* Done with this */
34951da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
34961da177e4SLinus Torvalds 	}
34971da177e4SLinus Torvalds }
34981da177e4SLinus Torvalds 
34991da177e4SLinus Torvalds /*
35001da177e4SLinus Torvalds  * Main loop of the thread goes here
35011da177e4SLinus Torvalds  */
35021da177e4SLinus Torvalds 
3503ee74baa7SDavid S. Miller static int pktgen_thread_worker(void *arg)
35041da177e4SLinus Torvalds {
35051da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
3506ee74baa7SDavid S. Miller 	struct pktgen_thread *t = arg;
35071da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
35081da177e4SLinus Torvalds 	int cpu = t->cpu;
35091da177e4SLinus Torvalds 
3510ee74baa7SDavid S. Miller 	BUG_ON(smp_processor_id() != cpu);
35111da177e4SLinus Torvalds 
35121da177e4SLinus Torvalds 	init_waitqueue_head(&t->queue);
3513d3ede327SDenis V. Lunev 	complete(&t->start_done);
35141da177e4SLinus Torvalds 
3515f9467eaeSJoe Perches 	pr_debug("starting pktgen/%d:  pid=%d\n", cpu, task_pid_nr(current));
35161da177e4SLinus Torvalds 
351783144186SRafael J. Wysocki 	set_freezable();
351883144186SRafael J. Wysocki 
3519baac167bSJesper Dangaard Brouer 	__set_current_state(TASK_RUNNING);
3520baac167bSJesper Dangaard Brouer 
3521ee74baa7SDavid S. Miller 	while (!kthread_should_stop()) {
3522ee74baa7SDavid S. Miller 		pkt_dev = next_to_run(t);
3523ee74baa7SDavid S. Miller 
3524ef87979cSStephen Hemminger 		if (unlikely(!pkt_dev && t->control == 0)) {
35254e58a027SCong Wang 			if (t->net->pktgen_exiting)
3526551eaff1SEric Dumazet 				break;
3527ef87979cSStephen Hemminger 			wait_event_interruptible_timeout(t->queue,
3528ef87979cSStephen Hemminger 							 t->control != 0,
3529ef87979cSStephen Hemminger 							 HZ/10);
35301b3f720bSRafael J. Wysocki 			try_to_freeze();
3531ef87979cSStephen Hemminger 			continue;
3532ee74baa7SDavid S. Miller 		}
35331da177e4SLinus Torvalds 
3534ef87979cSStephen Hemminger 		if (likely(pkt_dev)) {
35351da177e4SLinus Torvalds 			pktgen_xmit(pkt_dev);
35361da177e4SLinus Torvalds 
3537ef87979cSStephen Hemminger 			if (need_resched())
3538ef87979cSStephen Hemminger 				pktgen_resched(pkt_dev);
3539ef87979cSStephen Hemminger 			else
3540ef87979cSStephen Hemminger 				cpu_relax();
3541ef87979cSStephen Hemminger 		}
3542ef87979cSStephen Hemminger 
35431da177e4SLinus Torvalds 		if (t->control & T_STOP) {
35441da177e4SLinus Torvalds 			pktgen_stop(t);
35451da177e4SLinus Torvalds 			t->control &= ~(T_STOP);
35461da177e4SLinus Torvalds 		}
35471da177e4SLinus Torvalds 
35481da177e4SLinus Torvalds 		if (t->control & T_RUN) {
35491da177e4SLinus Torvalds 			pktgen_run(t);
35501da177e4SLinus Torvalds 			t->control &= ~(T_RUN);
35511da177e4SLinus Torvalds 		}
35521da177e4SLinus Torvalds 
355395ed63f7SArthur Kepner 		if (t->control & T_REMDEVALL) {
35541da177e4SLinus Torvalds 			pktgen_rem_all_ifs(t);
355595ed63f7SArthur Kepner 			t->control &= ~(T_REMDEVALL);
355695ed63f7SArthur Kepner 		}
355795ed63f7SArthur Kepner 
355895ed63f7SArthur Kepner 		if (t->control & T_REMDEV) {
355995ed63f7SArthur Kepner 			pktgen_rem_one_if(t);
35601da177e4SLinus Torvalds 			t->control &= ~(T_REMDEV);
35611da177e4SLinus Torvalds 		}
35621da177e4SLinus Torvalds 
356309fe3ef4SAndrew Morton 		try_to_freeze();
35641da177e4SLinus Torvalds 	}
3565baac167bSJesper Dangaard Brouer 	set_current_state(TASK_INTERRUPTIBLE);
35661da177e4SLinus Torvalds 
3567f9467eaeSJoe Perches 	pr_debug("%s stopping all device\n", t->tsk->comm);
35681da177e4SLinus Torvalds 	pktgen_stop(t);
35691da177e4SLinus Torvalds 
3570f9467eaeSJoe Perches 	pr_debug("%s removing all device\n", t->tsk->comm);
35711da177e4SLinus Torvalds 	pktgen_rem_all_ifs(t);
35721da177e4SLinus Torvalds 
3573f9467eaeSJoe Perches 	pr_debug("%s removing thread\n", t->tsk->comm);
35741da177e4SLinus Torvalds 	pktgen_rem_thread(t);
3575cdcdbe0bSLuiz Capitulino 
3576551eaff1SEric Dumazet 	/* Wait for kthread_stop */
3577551eaff1SEric Dumazet 	while (!kthread_should_stop()) {
3578551eaff1SEric Dumazet 		set_current_state(TASK_INTERRUPTIBLE);
3579551eaff1SEric Dumazet 		schedule();
3580551eaff1SEric Dumazet 	}
3581551eaff1SEric Dumazet 	__set_current_state(TASK_RUNNING);
3582551eaff1SEric Dumazet 
3583ee74baa7SDavid S. Miller 	return 0;
35841da177e4SLinus Torvalds }
35851da177e4SLinus Torvalds 
3586222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
35873e984840SEric Dumazet 					  const char *ifname, bool exact)
35881da177e4SLinus Torvalds {
3589c26a8016SLuiz Capitulino 	struct pktgen_dev *p, *pkt_dev = NULL;
35903e984840SEric Dumazet 	size_t len = strlen(ifname);
35911da177e4SLinus Torvalds 
35928788370aSJesper Dangaard Brouer 	rcu_read_lock();
35938788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(p, &t->if_list, list)
35943e984840SEric Dumazet 		if (strncmp(p->odevname, ifname, len) == 0) {
35953e984840SEric Dumazet 			if (p->odevname[len]) {
35963e984840SEric Dumazet 				if (exact || p->odevname[len] != '@')
35973e984840SEric Dumazet 					continue;
35983e984840SEric Dumazet 			}
3599c26a8016SLuiz Capitulino 			pkt_dev = p;
36001da177e4SLinus Torvalds 			break;
36011da177e4SLinus Torvalds 		}
36021da177e4SLinus Torvalds 
36038788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3604f9467eaeSJoe Perches 	pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev);
36051da177e4SLinus Torvalds 	return pkt_dev;
36061da177e4SLinus Torvalds }
36071da177e4SLinus Torvalds 
36081da177e4SLinus Torvalds /*
36091da177e4SLinus Torvalds  * Adds a dev at front of if_list.
36101da177e4SLinus Torvalds  */
36111da177e4SLinus Torvalds 
3612222f1806SLuiz Capitulino static int add_dev_to_thread(struct pktgen_thread *t,
3613222f1806SLuiz Capitulino 			     struct pktgen_dev *pkt_dev)
36141da177e4SLinus Torvalds {
36151da177e4SLinus Torvalds 	int rv = 0;
36161da177e4SLinus Torvalds 
36178788370aSJesper Dangaard Brouer 	/* This function cannot be called concurrently, as its called
36188788370aSJesper Dangaard Brouer 	 * under pktgen_thread_lock mutex, but it can run from
36198788370aSJesper Dangaard Brouer 	 * userspace on another CPU than the kthread.  The if_lock()
36208788370aSJesper Dangaard Brouer 	 * is used here to sync with concurrent instances of
36218788370aSJesper Dangaard Brouer 	 * _rem_dev_from_if_list() invoked via kthread, which is also
36228788370aSJesper Dangaard Brouer 	 * updating the if_list */
36231da177e4SLinus Torvalds 	if_lock(t);
36241da177e4SLinus Torvalds 
36251da177e4SLinus Torvalds 	if (pkt_dev->pg_thread) {
3626f9467eaeSJoe Perches 		pr_err("ERROR: already assigned to a thread\n");
36271da177e4SLinus Torvalds 		rv = -EBUSY;
36281da177e4SLinus Torvalds 		goto out;
36291da177e4SLinus Torvalds 	}
3630c26a8016SLuiz Capitulino 
36311da177e4SLinus Torvalds 	pkt_dev->running = 0;
36328788370aSJesper Dangaard Brouer 	pkt_dev->pg_thread = t;
36338788370aSJesper Dangaard Brouer 	list_add_rcu(&pkt_dev->list, &t->if_list);
36341da177e4SLinus Torvalds 
36351da177e4SLinus Torvalds out:
36361da177e4SLinus Torvalds 	if_unlock(t);
36371da177e4SLinus Torvalds 	return rv;
36381da177e4SLinus Torvalds }
36391da177e4SLinus Torvalds 
36401da177e4SLinus Torvalds /* Called under thread lock */
36411da177e4SLinus Torvalds 
36421da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
36431da177e4SLinus Torvalds {
36441da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev;
364539df232fSStephen Hemminger 	int err;
36463291b9dbSEric Dumazet 	int node = cpu_to_node(t->cpu);
36471da177e4SLinus Torvalds 
36481da177e4SLinus Torvalds 	/* We don't allow a device to be on several threads */
36491da177e4SLinus Torvalds 
36504e58a027SCong Wang 	pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND);
3651d50a6b56SStephen Hemminger 	if (pkt_dev) {
3652f9467eaeSJoe Perches 		pr_err("ERROR: interface already used\n");
3653d50a6b56SStephen Hemminger 		return -EBUSY;
3654d50a6b56SStephen Hemminger 	}
36551da177e4SLinus Torvalds 
36563291b9dbSEric Dumazet 	pkt_dev = kzalloc_node(sizeof(struct pktgen_dev), GFP_KERNEL, node);
36571da177e4SLinus Torvalds 	if (!pkt_dev)
36581da177e4SLinus Torvalds 		return -ENOMEM;
36591da177e4SLinus Torvalds 
3660593f63b0SEric Dumazet 	strcpy(pkt_dev->odevname, ifname);
366168d5ac2eSWANG Cong 	pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
36623291b9dbSEric Dumazet 				      node);
36631da177e4SLinus Torvalds 	if (pkt_dev->flows == NULL) {
36641da177e4SLinus Torvalds 		kfree(pkt_dev);
36651da177e4SLinus Torvalds 		return -ENOMEM;
36661da177e4SLinus Torvalds 	}
36671da177e4SLinus Torvalds 
366895ed63f7SArthur Kepner 	pkt_dev->removal_mark = 0;
36691da177e4SLinus Torvalds 	pkt_dev->nfrags = 0;
3670fd29cf72SStephen Hemminger 	pkt_dev->delay = pg_delay_d;
36711da177e4SLinus Torvalds 	pkt_dev->count = pg_count_d;
36721da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
36731da177e4SLinus Torvalds 	pkt_dev->udp_src_min = 9;	/* sink port */
36741da177e4SLinus Torvalds 	pkt_dev->udp_src_max = 9;
36751da177e4SLinus Torvalds 	pkt_dev->udp_dst_min = 9;
36761da177e4SLinus Torvalds 	pkt_dev->udp_dst_max = 9;
367734954ddcSFrancesco Fondelli 	pkt_dev->vlan_p = 0;
367834954ddcSFrancesco Fondelli 	pkt_dev->vlan_cfi = 0;
367934954ddcSFrancesco Fondelli 	pkt_dev->vlan_id = 0xffff;
368034954ddcSFrancesco Fondelli 	pkt_dev->svlan_p = 0;
368134954ddcSFrancesco Fondelli 	pkt_dev->svlan_cfi = 0;
368234954ddcSFrancesco Fondelli 	pkt_dev->svlan_id = 0xffff;
368338b2cf29SAlexei Starovoitov 	pkt_dev->burst = 1;
3684e99b99b4SRobert Olsson 	pkt_dev->node = -1;
368534954ddcSFrancesco Fondelli 
36864e58a027SCong Wang 	err = pktgen_setup_dev(t->net, pkt_dev, ifname);
368739df232fSStephen Hemminger 	if (err)
368839df232fSStephen Hemminger 		goto out1;
3689d8873315SNeil Horman 	if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
3690d8873315SNeil Horman 		pkt_dev->clone_skb = pg_clone_skb_d;
36911da177e4SLinus Torvalds 
36924e58a027SCong Wang 	pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir,
36935efdccbcSDenis V. Lunev 					  &pktgen_if_fops, pkt_dev);
369439df232fSStephen Hemminger 	if (!pkt_dev->entry) {
3695f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3696d50a6b56SStephen Hemminger 		       PG_PROC_DIR, ifname);
369739df232fSStephen Hemminger 		err = -EINVAL;
369839df232fSStephen Hemminger 		goto out2;
369939df232fSStephen Hemminger 	}
3700a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3701a553e4a6SJamal Hadi Salim 	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;
3702a553e4a6SJamal Hadi Salim 	pkt_dev->ipsproto = IPPROTO_ESP;
3703cf93d47eSFan Du 
3704cf93d47eSFan Du 	/* xfrm tunnel mode needs additional dst to extract outter
3705cf93d47eSFan Du 	 * ip header protocol/ttl/id field, here creat a phony one.
3706cf93d47eSFan Du 	 * instead of looking for a valid rt, which definitely hurting
3707cf93d47eSFan Du 	 * performance under such circumstance.
3708cf93d47eSFan Du 	 */
3709cf93d47eSFan Du 	pkt_dev->dstops.family = AF_INET;
3710cf93d47eSFan Du 	pkt_dev->dst.dev = pkt_dev->odev;
3711cf93d47eSFan Du 	dst_init_metrics(&pkt_dev->dst, pktgen_dst_metrics, false);
3712cf93d47eSFan Du 	pkt_dev->dst.child = &pkt_dev->dst;
3713cf93d47eSFan Du 	pkt_dev->dst.ops = &pkt_dev->dstops;
3714a553e4a6SJamal Hadi Salim #endif
371539df232fSStephen Hemminger 
371639df232fSStephen Hemminger 	return add_dev_to_thread(t, pkt_dev);
371739df232fSStephen Hemminger out2:
371839df232fSStephen Hemminger 	dev_put(pkt_dev->odev);
371939df232fSStephen Hemminger out1:
3720a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3721a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3722a553e4a6SJamal Hadi Salim #endif
37231da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
37241da177e4SLinus Torvalds 	kfree(pkt_dev);
372539df232fSStephen Hemminger 	return err;
37261da177e4SLinus Torvalds }
37271da177e4SLinus Torvalds 
37284e58a027SCong Wang static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
37291da177e4SLinus Torvalds {
3730cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3731d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3732ee74baa7SDavid S. Miller 	struct task_struct *p;
37331da177e4SLinus Torvalds 
37343291b9dbSEric Dumazet 	t = kzalloc_node(sizeof(struct pktgen_thread), GFP_KERNEL,
37353291b9dbSEric Dumazet 			 cpu_to_node(cpu));
37361da177e4SLinus Torvalds 	if (!t) {
3737f9467eaeSJoe Perches 		pr_err("ERROR: out of memory, can't create new thread\n");
37381da177e4SLinus Torvalds 		return -ENOMEM;
37391da177e4SLinus Torvalds 	}
37401da177e4SLinus Torvalds 
37411da177e4SLinus Torvalds 	spin_lock_init(&t->if_lock);
37421da177e4SLinus Torvalds 	t->cpu = cpu;
37431da177e4SLinus Torvalds 
3744ee74baa7SDavid S. Miller 	INIT_LIST_HEAD(&t->if_list);
3745ee74baa7SDavid S. Miller 
37464e58a027SCong Wang 	list_add_tail(&t->th_list, &pn->pktgen_threads);
3747d3ede327SDenis V. Lunev 	init_completion(&t->start_done);
3748ee74baa7SDavid S. Miller 
374994dcf29aSEric Dumazet 	p = kthread_create_on_node(pktgen_thread_worker,
375094dcf29aSEric Dumazet 				   t,
375194dcf29aSEric Dumazet 				   cpu_to_node(cpu),
375294dcf29aSEric Dumazet 				   "kpktgend_%d", cpu);
3753ee74baa7SDavid S. Miller 	if (IS_ERR(p)) {
3754f9467eaeSJoe Perches 		pr_err("kernel_thread() failed for cpu %d\n", t->cpu);
3755ee74baa7SDavid S. Miller 		list_del(&t->th_list);
3756ee74baa7SDavid S. Miller 		kfree(t);
3757ee74baa7SDavid S. Miller 		return PTR_ERR(p);
3758ee74baa7SDavid S. Miller 	}
3759ee74baa7SDavid S. Miller 	kthread_bind(p, cpu);
3760ee74baa7SDavid S. Miller 	t->tsk = p;
3761ee74baa7SDavid S. Miller 
37624e58a027SCong Wang 	pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir,
37635efdccbcSDenis V. Lunev 			      &pktgen_thread_fops, t);
3764d50a6b56SStephen Hemminger 	if (!pe) {
3765f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3766ee74baa7SDavid S. Miller 		       PG_PROC_DIR, t->tsk->comm);
3767ee74baa7SDavid S. Miller 		kthread_stop(p);
3768ee74baa7SDavid S. Miller 		list_del(&t->th_list);
37691da177e4SLinus Torvalds 		kfree(t);
37701da177e4SLinus Torvalds 		return -EINVAL;
37711da177e4SLinus Torvalds 	}
3772d50a6b56SStephen Hemminger 
37734e58a027SCong Wang 	t->net = pn;
3774ee74baa7SDavid S. Miller 	wake_up_process(p);
3775d3ede327SDenis V. Lunev 	wait_for_completion(&t->start_done);
37761da177e4SLinus Torvalds 
37771da177e4SLinus Torvalds 	return 0;
37781da177e4SLinus Torvalds }
37791da177e4SLinus Torvalds 
37801da177e4SLinus Torvalds /*
37811da177e4SLinus Torvalds  * Removes a device from the thread if_list.
37821da177e4SLinus Torvalds  */
3783222f1806SLuiz Capitulino static void _rem_dev_from_if_list(struct pktgen_thread *t,
3784222f1806SLuiz Capitulino 				  struct pktgen_dev *pkt_dev)
37851da177e4SLinus Torvalds {
3786c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3787c26a8016SLuiz Capitulino 	struct pktgen_dev *p;
37881da177e4SLinus Torvalds 
37898788370aSJesper Dangaard Brouer 	if_lock(t);
3790c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3791c26a8016SLuiz Capitulino 		p = list_entry(q, struct pktgen_dev, list);
3792c26a8016SLuiz Capitulino 		if (p == pkt_dev)
37938788370aSJesper Dangaard Brouer 			list_del_rcu(&p->list);
37941da177e4SLinus Torvalds 	}
37958788370aSJesper Dangaard Brouer 	if_unlock(t);
37961da177e4SLinus Torvalds }
37971da177e4SLinus Torvalds 
3798222f1806SLuiz Capitulino static int pktgen_remove_device(struct pktgen_thread *t,
3799222f1806SLuiz Capitulino 				struct pktgen_dev *pkt_dev)
38001da177e4SLinus Torvalds {
3801f9467eaeSJoe Perches 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
38021da177e4SLinus Torvalds 
38031da177e4SLinus Torvalds 	if (pkt_dev->running) {
3804294a0b7fSJoe Perches 		pr_warn("WARNING: trying to remove a running interface, stopping it now\n");
38051da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
38061da177e4SLinus Torvalds 	}
38071da177e4SLinus Torvalds 
38081da177e4SLinus Torvalds 	/* Dis-associate from the interface */
38091da177e4SLinus Torvalds 
38101da177e4SLinus Torvalds 	if (pkt_dev->odev) {
38111da177e4SLinus Torvalds 		dev_put(pkt_dev->odev);
38121da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
38131da177e4SLinus Torvalds 	}
38141da177e4SLinus Torvalds 
38158788370aSJesper Dangaard Brouer 	/* Remove proc before if_list entry, because add_device uses
38168788370aSJesper Dangaard Brouer 	 * list to determine if interface already exist, avoid race
38178788370aSJesper Dangaard Brouer 	 * with proc_create_data() */
3818a8ca16eaSDavid Howells 	proc_remove(pkt_dev->entry);
38191da177e4SLinus Torvalds 
38208788370aSJesper Dangaard Brouer 	/* And update the thread if_list */
38218788370aSJesper Dangaard Brouer 	_rem_dev_from_if_list(t, pkt_dev);
38228788370aSJesper Dangaard Brouer 
3823a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3824a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3825a553e4a6SJamal Hadi Salim #endif
38261da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
382726ad7879SEric Dumazet 	if (pkt_dev->page)
382826ad7879SEric Dumazet 		put_page(pkt_dev->page);
38298788370aSJesper Dangaard Brouer 	kfree_rcu(pkt_dev, rcu);
38301da177e4SLinus Torvalds 	return 0;
38311da177e4SLinus Torvalds }
38321da177e4SLinus Torvalds 
38334e58a027SCong Wang static int __net_init pg_net_init(struct net *net)
38341da177e4SLinus Torvalds {
38354e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3836d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
38374e58a027SCong Wang 	int cpu, ret = 0;
3838d50a6b56SStephen Hemminger 
38394e58a027SCong Wang 	pn->net = net;
38404e58a027SCong Wang 	INIT_LIST_HEAD(&pn->pktgen_threads);
38414e58a027SCong Wang 	pn->pktgen_exiting = false;
38424e58a027SCong Wang 	pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net);
38434e58a027SCong Wang 	if (!pn->proc_dir) {
38444e58a027SCong Wang 		pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR);
3845d50a6b56SStephen Hemminger 		return -ENODEV;
38461da177e4SLinus Torvalds 	}
38474e58a027SCong Wang 	pe = proc_create(PGCTRL, 0600, pn->proc_dir, &pktgen_fops);
38484e58a027SCong Wang 	if (pe == NULL) {
38494e58a027SCong Wang 		pr_err("cannot create %s procfs entry\n", PGCTRL);
38504e58a027SCong Wang 		ret = -EINVAL;
38514e58a027SCong Wang 		goto remove;
38524e58a027SCong Wang 	}
38531da177e4SLinus Torvalds 
3854670c02c2SJohn Hawkes 	for_each_online_cpu(cpu) {
38558024bb24SLuiz Capitulino 		int err;
38561da177e4SLinus Torvalds 
38574e58a027SCong Wang 		err = pktgen_create_thread(cpu, pn);
38588024bb24SLuiz Capitulino 		if (err)
38594e58a027SCong Wang 			pr_warn("Cannot create thread for cpu %d (%d)\n",
3860f9467eaeSJoe Perches 				   cpu, err);
38611da177e4SLinus Torvalds 	}
38628024bb24SLuiz Capitulino 
38634e58a027SCong Wang 	if (list_empty(&pn->pktgen_threads)) {
38644e58a027SCong Wang 		pr_err("Initialization failed for all threads\n");
3865ce14f894SWANG Cong 		ret = -ENODEV;
38664e58a027SCong Wang 		goto remove_entry;
38678024bb24SLuiz Capitulino 	}
38688024bb24SLuiz Capitulino 
38691da177e4SLinus Torvalds 	return 0;
3870ce14f894SWANG Cong 
38714e58a027SCong Wang remove_entry:
38724e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
38734e58a027SCong Wang remove:
3874ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
3875ce14f894SWANG Cong 	return ret;
38761da177e4SLinus Torvalds }
38771da177e4SLinus Torvalds 
38784e58a027SCong Wang static void __net_exit pg_net_exit(struct net *net)
38791da177e4SLinus Torvalds {
38804e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3881cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3882cdcdbe0bSLuiz Capitulino 	struct list_head *q, *n;
3883d4b11335SEric Dumazet 	LIST_HEAD(list);
38841da177e4SLinus Torvalds 
38851da177e4SLinus Torvalds 	/* Stop all interfaces & threads */
38864e58a027SCong Wang 	pn->pktgen_exiting = true;
38871da177e4SLinus Torvalds 
3888c57b5468SEric Dumazet 	mutex_lock(&pktgen_thread_lock);
38894e58a027SCong Wang 	list_splice_init(&pn->pktgen_threads, &list);
3890c57b5468SEric Dumazet 	mutex_unlock(&pktgen_thread_lock);
3891c57b5468SEric Dumazet 
3892c57b5468SEric Dumazet 	list_for_each_safe(q, n, &list) {
3893cdcdbe0bSLuiz Capitulino 		t = list_entry(q, struct pktgen_thread, th_list);
3894c57b5468SEric Dumazet 		list_del(&t->th_list);
3895ee74baa7SDavid S. Miller 		kthread_stop(t->tsk);
3896ee74baa7SDavid S. Miller 		kfree(t);
38971da177e4SLinus Torvalds 	}
38981da177e4SLinus Torvalds 
38994e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
3900ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
39014e58a027SCong Wang }
39021da177e4SLinus Torvalds 
39034e58a027SCong Wang static struct pernet_operations pg_net_ops = {
39044e58a027SCong Wang 	.init = pg_net_init,
39054e58a027SCong Wang 	.exit = pg_net_exit,
39064e58a027SCong Wang 	.id   = &pg_net_id,
39074e58a027SCong Wang 	.size = sizeof(struct pktgen_net),
39084e58a027SCong Wang };
39094e58a027SCong Wang 
39104e58a027SCong Wang static int __init pg_init(void)
39114e58a027SCong Wang {
39124e58a027SCong Wang 	int ret = 0;
39134e58a027SCong Wang 
39144e58a027SCong Wang 	pr_info("%s", version);
39154e58a027SCong Wang 	ret = register_pernet_subsys(&pg_net_ops);
39164e58a027SCong Wang 	if (ret)
39174e58a027SCong Wang 		return ret;
39184e58a027SCong Wang 	ret = register_netdevice_notifier(&pktgen_notifier_block);
39194e58a027SCong Wang 	if (ret)
39204e58a027SCong Wang 		unregister_pernet_subsys(&pg_net_ops);
39214e58a027SCong Wang 
39224e58a027SCong Wang 	return ret;
39234e58a027SCong Wang }
39244e58a027SCong Wang 
39254e58a027SCong Wang static void __exit pg_cleanup(void)
39264e58a027SCong Wang {
39274e58a027SCong Wang 	unregister_netdevice_notifier(&pktgen_notifier_block);
39284e58a027SCong Wang 	unregister_pernet_subsys(&pg_net_ops);
39298788370aSJesper Dangaard Brouer 	/* Don't need rcu_barrier() due to use of kfree_rcu() */
39301da177e4SLinus Torvalds }
39311da177e4SLinus Torvalds 
39321da177e4SLinus Torvalds module_init(pg_init);
39331da177e4SLinus Torvalds module_exit(pg_cleanup);
39341da177e4SLinus Torvalds 
3935c3d2f52dSStephen Hemminger MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se>");
39361da177e4SLinus Torvalds MODULE_DESCRIPTION("Packet Generator tool");
39371da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3938c3d2f52dSStephen Hemminger MODULE_VERSION(VERSION);
39391da177e4SLinus Torvalds module_param(pg_count_d, int, 0);
3940c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject");
39411da177e4SLinus Torvalds module_param(pg_delay_d, int, 0);
3942c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)");
39431da177e4SLinus Torvalds module_param(pg_clone_skb_d, int, 0);
3944c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet");
39451da177e4SLinus Torvalds module_param(debug, int, 0);
3946c3d2f52dSStephen Hemminger MODULE_PARM_DESC(debug, "Enable debugging of pktgen module");
3947