pktgen.c 93.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
/*
 * Authors:
 * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
 *                             Uppsala University and
 *                             Swedish University of Agricultural Sciences
 *
 * Alexey Kuznetsov  <kuznet@ms2.inr.ac.ru>
 * Ben Greear <greearb@candelatech.com>
9
 * Jens Låås <jens.laas@data.slu.se>
Linus Torvalds's avatar
Linus Torvalds committed
10
11
12
13
14
15
16
17
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 *
 * A tool for loading the network with preconfigurated packets.
18
 * The tool is implemented as a linux module.  Parameters are output
Linus Torvalds's avatar
Linus Torvalds committed
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 * device, delay (to hard_xmit), number of packets, and whether
 * to use multiple SKBs or just the same one.
 * pktgen uses the installed interface's output routine.
 *
 * Additional hacking by:
 *
 * Jens.Laas@data.slu.se
 * Improved by ANK. 010120.
 * Improved by ANK even more. 010212.
 * MAC address typo fixed. 010417 --ro
 * Integrated.  020301 --DaveM
 * Added multiskb option 020301 --DaveM
 * Scaling of results. 020417--sigurdur@linpro.no
 * Significant re-work of the module:
 *   *  Convert to threaded model to more efficiently be able to transmit
 *       and receive on multiple interfaces at once.
 *   *  Converted many counters to __u64 to allow longer runs.
 *   *  Allow configuration of ranges, like min/max IP address, MACs,
 *       and UDP-ports, for both source and destination, and can
 *       set to use a random distribution or sequentially walk the range.
 *   *  Can now change most values after starting.
 *   *  Place 12-byte packet in UDP payload with magic number,
 *       sequence number, and timestamp.
 *   *  Add receiver code that detects dropped pkts, re-ordered pkts, and
 *       latencies (with micro-second) precision.
 *   *  Add IOCTL interface to easily get counters & configuration.
 *   --Ben Greear <greearb@candelatech.com>
 *
47
48
 * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
 * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
Linus Torvalds's avatar
Linus Torvalds committed
49
 * as a "fastpath" with a configurable number of clones after alloc's.
50
51
 * clone_skb=0 means all packets are allocated this also means ranges time
 * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
Linus Torvalds's avatar
Linus Torvalds committed
52
53
 * clones.
 *
54
 * Also moved to /proc/net/pktgen/
Linus Torvalds's avatar
Linus Torvalds committed
55
56
57
58
59
60
61
62
 * --ro
 *
 * Sept 10:  Fixed threading/locking.  Lots of bone-headed and more clever
 *    mistakes.  Also merged in DaveM's patch in the -pre6 patch.
 * --Ben Greear <greearb@candelatech.com>
 *
 * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br)
 *
63
 *
Linus Torvalds's avatar
Linus Torvalds committed
64
65
66
67
 * 021124 Finished major redesign and rewrite for new functionality.
 * See Documentation/networking/pktgen.txt for how to use this.
 *
 * The new operation:
68
69
70
71
 * For each CPU one thread/process is created at start. This process checks
 * for running devices in the if_list and sends packets until count is 0 it
 * also the thread checks the thread->control which is used for inter-process
 * communication. controlling process "posts" operations to the threads this
72
73
74
 * way.
 * The if_list is RCU protected, and the if_lock remains to protect updating
 * of if_list, from "add_device" as it invoked from userspace (via proc write).
Linus Torvalds's avatar
Linus Torvalds committed
75
 *
76
77
 * By design there should only be *one* "controlling" process. In practice
 * multiple write accesses gives unpredictable result. Understood by "write"
Linus Torvalds's avatar
Linus Torvalds committed
78
 * to /proc gives result code thats should be read be the "writer".
79
 * For practical use this should be no problem.
Linus Torvalds's avatar
Linus Torvalds committed
80
 *
81
82
 * Note when adding devices to a specific CPU there good idea to also assign
 * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
Linus Torvalds's avatar
Linus Torvalds committed
83
84
 * --ro
 *
85
 * Fix refcount off by one if first packet fails, potential null deref,
Linus Torvalds's avatar
Linus Torvalds committed
86
87
88
89
90
91
92
 * memleak 030710- KJP
 *
 * First "ranges" functionality for ipv6 030726 --ro
 *
 * Included flow support. 030802 ANK.
 *
 * Fixed unaligned access on IA-64 Grant Grundler <grundler@parisc-linux.org>
93
 *
Linus Torvalds's avatar
Linus Torvalds committed
94
95
96
 * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
 * ia64 compilation fix from  Aron Griffis <aron@hp.com> 040604
 *
97
 * New xmit() return, do_div and misc clean up by Stephen Hemminger
Linus Torvalds's avatar
Linus Torvalds committed
98
99
 * <shemminger@osdl.org> 040923
 *
100
 * Randy Dunlap fixed u64 printk compiler waring
Linus Torvalds's avatar
Linus Torvalds committed
101
102
103
104
 *
 * Remove FCS from BW calculation.  Lennert Buytenhek <buytenh@wantstofly.org>
 * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213
 *
105
 * Corrections from Nikolai Malykh (nmalykh@bilim.com)
Linus Torvalds's avatar
Linus Torvalds committed
106
107
 * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
 *
108
 * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
Linus Torvalds's avatar
Linus Torvalds committed
109
 * 050103
110
111
112
 *
 * MPLS support by Steven Whitehouse <steve@chygwyn.com>
 *
Francesco Fondelli's avatar
Francesco Fondelli committed
113
114
 * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <francesco.fondelli@gmail.com>
 *
Adit Ranadive's avatar
Adit Ranadive committed
115
116
117
 * Fixed src_mac command to set source mac of packet to value specified in
 * command by Adit Ranadive <adit.262@gmail.com>
 *
Linus Torvalds's avatar
Linus Torvalds committed
118
 */
119
120
121

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Linus Torvalds's avatar
Linus Torvalds committed
122
123
124
125
126
#include <linux/sys.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
127
#include <linux/mutex.h>
Linus Torvalds's avatar
Linus Torvalds committed
128
129
130
131
132
133
134
135
136
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
137
#include <linux/capability.h>
Stephen Hemminger's avatar
Stephen Hemminger committed
138
#include <linux/hrtimer.h>
Andrew Morton's avatar
Andrew Morton committed
139
#include <linux/freezer.h>
Linus Torvalds's avatar
Linus Torvalds committed
140
141
#include <linux/delay.h>
#include <linux/timer.h>
142
#include <linux/list.h>
Linus Torvalds's avatar
Linus Torvalds committed
143
144
145
146
147
148
149
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
Francesco Fondelli's avatar
Francesco Fondelli committed
150
#include <linux/if_vlan.h>
Linus Torvalds's avatar
Linus Torvalds committed
151
152
153
154
155
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <linux/proc_fs.h>
156
#include <linux/seq_file.h>
Linus Torvalds's avatar
Linus Torvalds committed
157
#include <linux/wait.h>
158
#include <linux/etherdevice.h>
159
#include <linux/kthread.h>
160
#include <linux/prefetch.h>
161
#include <net/net_namespace.h>
Linus Torvalds's avatar
Linus Torvalds committed
162
163
#include <net/checksum.h>
#include <net/ipv6.h>
164
#include <net/udp.h>
165
#include <net/ip6_checksum.h>
Linus Torvalds's avatar
Linus Torvalds committed
166
#include <net/addrconf.h>
Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
167
168
169
#ifdef CONFIG_XFRM
#include <net/xfrm.h>
#endif
Cong Wang's avatar
Cong Wang committed
170
#include <net/netns/generic.h>
Linus Torvalds's avatar
Linus Torvalds committed
171
172
#include <asm/byteorder.h>
#include <linux/rcupdate.h>
Jiri Slaby's avatar
Jiri Slaby committed
173
#include <linux/bitops.h>
174
175
176
#include <linux/io.h>
#include <linux/timex.h>
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
177
#include <asm/dma.h>
Luiz Capitulino's avatar
Luiz Capitulino committed
178
#include <asm/div64.h>		/* do_div */
Linus Torvalds's avatar
Linus Torvalds committed
179

180
#define VERSION	"2.74"
Linus Torvalds's avatar
Linus Torvalds committed
181
#define IP_NAME_SZ 32
182
#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
183
#define MPLS_STACK_BOTTOM htonl(0x00000100)
Linus Torvalds's avatar
Linus Torvalds committed
184

185
186
#define func_enter() pr_debug("entering %s\n", __func__);

Linus Torvalds's avatar
Linus Torvalds committed
187
/* Device flag bits */
Luiz Capitulino's avatar
Luiz Capitulino committed
188
189
190
191
192
193
194
195
#define F_IPSRC_RND   (1<<0)	/* IP-Src Random  */
#define F_IPDST_RND   (1<<1)	/* IP-Dst Random  */
#define F_UDPSRC_RND  (1<<2)	/* UDP-Src Random */
#define F_UDPDST_RND  (1<<3)	/* UDP-Dst Random */
#define F_MACSRC_RND  (1<<4)	/* MAC-Src Random */
#define F_MACDST_RND  (1<<5)	/* MAC-Dst Random */
#define F_TXSIZE_RND  (1<<6)	/* Transmit size is random */
#define F_IPV6        (1<<7)	/* Interface in IPV6 Mode */
196
#define F_MPLS_RND    (1<<8)	/* Random MPLS labels */
Francesco Fondelli's avatar
Francesco Fondelli committed
197
198
#define F_VID_RND     (1<<9)	/* Random VLAN ID */
#define F_SVID_RND    (1<<10)	/* Random SVLAN ID */
199
#define F_FLOW_SEQ    (1<<11)	/* Sequential flows */
Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
200
#define F_IPSEC_ON    (1<<12)	/* ipsec on for flows */
Robert Olsson's avatar
Robert Olsson committed
201
#define F_QUEUE_MAP_RND (1<<13)	/* queue map Random */
Robert Olsson's avatar
Robert Olsson committed
202
#define F_QUEUE_MAP_CPU (1<<14)	/* queue map mirrors smp_processor_id() */
Robert Olsson's avatar
Robert Olsson committed
203
#define F_NODE          (1<<15)	/* Node memory alloc*/
204
#define F_UDPCSUM       (1<<16)	/* Include UDP checksum */
Linus Torvalds's avatar
Linus Torvalds committed
205
206

/* Thread control flag bits */
207
208
209
210
#define T_STOP        (1<<0)	/* Stop run */
#define T_RUN         (1<<1)	/* Start run */
#define T_REMDEVALL   (1<<2)	/* Remove all devs */
#define T_REMDEV      (1<<3)	/* Remove one dev */
Linus Torvalds's avatar
Linus Torvalds committed
211

212
/* If lock -- protects updating of if_list */
Linus Torvalds's avatar
Linus Torvalds committed
213
214
215
216
217
#define   if_lock(t)           spin_lock(&(t->if_lock));
#define   if_unlock(t)           spin_unlock(&(t->if_lock));

/* Used to help with determining the pkts on receive */
#define PKTGEN_MAGIC 0xbe9be955
218
219
#define PG_PROC_DIR "pktgen"
#define PGCTRL	    "pgctrl"
Linus Torvalds's avatar
Linus Torvalds committed
220
221
222

#define MAX_CFLOWS  65536

Francesco Fondelli's avatar
Francesco Fondelli committed
223
224
225
#define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
#define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)

Luiz Capitulino's avatar
Luiz Capitulino committed
226
struct flow_state {
Al Viro's avatar
Al Viro committed
227
	__be32 cur_daddr;
Luiz Capitulino's avatar
Luiz Capitulino committed
228
	int count;
Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
229
230
231
#ifdef CONFIG_XFRM
	struct xfrm_state *x;
#endif
232
	__u32 flags;
Linus Torvalds's avatar
Linus Torvalds committed
233
234
};

235
236
237
/* flow flag bits */
#define F_INIT   (1<<0)		/* flow has been initialized */

Linus Torvalds's avatar
Linus Torvalds committed
238
239
240
241
struct pktgen_dev {
	/*
	 * Try to keep frequent/infrequent used vars. separated.
	 */
242
243
	struct proc_dir_entry *entry;	/* proc file */
	struct pktgen_thread *pg_thread;/* the owner */
244
	struct list_head list;		/* chaining in the thread's run-queue */
245
	struct rcu_head	 rcu;		/* freed by RCU */
Linus Torvalds's avatar
Linus Torvalds committed
246

247
	int running;		/* if false, the test will stop */
Linus Torvalds's avatar
Linus Torvalds committed
248

Luiz Capitulino's avatar
Luiz Capitulino committed
249
250
251
252
	/* If min != max, then we will either do a linear iteration, or
	 * we will do a random selection from within the range.
	 */
	__u32 flags;
253
254
	int removal_mark;	/* non-zero => the device is marked for
				 * removal by worker thread */
Linus Torvalds's avatar
Linus Torvalds committed
255

256
257
	int min_pkt_size;
	int max_pkt_size;
258
	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
Luiz Capitulino's avatar
Luiz Capitulino committed
259
	int nfrags;
Eric Dumazet's avatar
Eric Dumazet committed
260
	struct page *page;
261
262
	u64 delay;		/* nano-seconds */

Luiz Capitulino's avatar
Luiz Capitulino committed
263
264
265
	__u64 count;		/* Default No packets to send */
	__u64 sofar;		/* How many pkts we've sent so far */
	__u64 tx_bytes;		/* How many bytes we've transmitted */
266
	__u64 errors;		/* Errors when trying to transmit, */
Luiz Capitulino's avatar
Luiz Capitulino committed
267
268
269
270
271
272

	/* runtime counters relating to clone_skb */

	__u64 allocated_skbs;
	__u32 clone_count;
	int last_ok;		/* Was last skb sent?
273
274
				 * Or a failed transmit of some sort?
				 * This will keep sequence numbers in order
Luiz Capitulino's avatar
Luiz Capitulino committed
275
				 */
276
277
278
279
280
	ktime_t next_tx;
	ktime_t started_at;
	ktime_t stopped_at;
	u64	idle_acc;	/* nano-seconds */

Luiz Capitulino's avatar
Luiz Capitulino committed
281
282
	__u32 seq_num;

283
284
285
286
287
288
289
290
	int clone_skb;		/*
				 * Use multiple SKBs during packet gen.
				 * If this number is greater than 1, then
				 * that many copies of the same packet will be
				 * sent before a new packet is allocated.
				 * If you want to send 1024 identical packets
				 * before creating a new packet,
				 * set clone_skb to 1024.
Luiz Capitulino's avatar
Luiz Capitulino committed
291
292
293
294
295
296
297
298
299
300
301
				 */

	char dst_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
	char dst_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
	char src_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
	char src_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */

	struct in6_addr in6_saddr;
	struct in6_addr in6_daddr;
	struct in6_addr cur_in6_daddr;
	struct in6_addr cur_in6_saddr;
Linus Torvalds's avatar
Linus Torvalds committed
302
	/* For ranges */
Luiz Capitulino's avatar
Luiz Capitulino committed
303
304
305
306
307
308
309
310
	struct in6_addr min_in6_daddr;
	struct in6_addr max_in6_daddr;
	struct in6_addr min_in6_saddr;
	struct in6_addr max_in6_saddr;

	/* If we're doing ranges, random or incremental, then this
	 * defines the min/max for those ranges.
	 */
Al Viro's avatar
Al Viro committed
311
312
313
314
	__be32 saddr_min;	/* inclusive, source IP address */
	__be32 saddr_max;	/* exclusive, source IP address */
	__be32 daddr_min;	/* inclusive, dest IP address */
	__be32 daddr_max;	/* exclusive, dest IP address */
Luiz Capitulino's avatar
Luiz Capitulino committed
315
316
317
318
319
320

	__u16 udp_src_min;	/* inclusive, source UDP port */
	__u16 udp_src_max;	/* exclusive, source UDP port */
	__u16 udp_dst_min;	/* inclusive, dest UDP port */
	__u16 udp_dst_max;	/* exclusive, dest UDP port */

Francesco Fondelli's avatar
Francesco Fondelli committed
321
	/* DSCP + ECN */
322
323
324
325
	__u8 tos;            /* six MSB of (former) IPv4 TOS
				are for dscp codepoint */
	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6
				(see RFC 3260, sec. 4) */
Francesco Fondelli's avatar
Francesco Fondelli committed
326

327
	/* MPLS */
328
	unsigned int nr_labels;	/* Depth of stack, 0 = no MPLS */
329
330
	__be32 labels[MAX_MPLS_LABELS];

Francesco Fondelli's avatar
Francesco Fondelli committed
331
332
333
334
335
336
337
338
339
	/* VLAN/SVLAN (802.1Q/Q-in-Q) */
	__u8  vlan_p;
	__u8  vlan_cfi;
	__u16 vlan_id;  /* 0xffff means no vlan tag */

	__u8  svlan_p;
	__u8  svlan_cfi;
	__u16 svlan_id; /* 0xffff means no svlan tag */

Luiz Capitulino's avatar
Luiz Capitulino committed
340
341
342
343
344
345
346
347
	__u32 src_mac_count;	/* How many MACs to iterate through */
	__u32 dst_mac_count;	/* How many MACs to iterate through */

	unsigned char dst_mac[ETH_ALEN];
	unsigned char src_mac[ETH_ALEN];

	__u32 cur_dst_mac_offset;
	__u32 cur_src_mac_offset;
Al Viro's avatar
Al Viro committed
348
349
	__be32 cur_saddr;
	__be32 cur_daddr;
Eric Dumazet's avatar
Eric Dumazet committed
350
	__u16 ip_id;
Luiz Capitulino's avatar
Luiz Capitulino committed
351
352
	__u16 cur_udp_dst;
	__u16 cur_udp_src;
Robert Olsson's avatar
Robert Olsson committed
353
	__u16 cur_queue_map;
Luiz Capitulino's avatar
Luiz Capitulino committed
354
	__u32 cur_pkt_size;
355
	__u32 last_pkt_size;
Luiz Capitulino's avatar
Luiz Capitulino committed
356
357
358
359
360
361
362
363
364
365
366
367

	__u8 hh[14];
	/* = {
	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,

	   We fill in SRC address later
	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	   0x08, 0x00
	   };
	 */
	__u16 pad;		/* pad out the hh struct to an even 16 bytes */

368
	struct sk_buff *skb;	/* skb we are to transmit next, used for when we
Luiz Capitulino's avatar
Luiz Capitulino committed
369
370
				 * are transmitting the same one multiple times
				 */
371
372
373
374
375
376
377
378
	struct net_device *odev; /* The out-going device.
				  * Note that the device should have it's
				  * pg_info pointer pointing back to this
				  * device.
				  * Set when the user specifies the out-going
				  * device name (not when the inject is
				  * started as it used to do.)
				  */
379
	char odevname[32];
Linus Torvalds's avatar
Linus Torvalds committed
380
	struct flow_state *flows;
381
382
383
384
	unsigned int cflows;	/* Concurrent flows (config) */
	unsigned int lflow;		/* Flow length  (config) */
	unsigned int nflows;	/* accumulated flows (stats) */
	unsigned int curfl;		/* current sequenced flow (state)*/
Robert Olsson's avatar
Robert Olsson committed
385
386
387

	u16 queue_map_min;
	u16 queue_map_max;
388
	__u32 skb_priority;	/* skb priority field */
Robert Olsson's avatar
Robert Olsson committed
389
	int node;               /* Memory node */
Robert Olsson's avatar
Robert Olsson committed
390

Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
391
392
393
#ifdef CONFIG_XFRM
	__u8	ipsmode;		/* IPSEC mode (config) */
	__u8	ipsproto;		/* IPSEC type (config) */
394
	__u32	spi;
395
396
	struct dst_entry dst;
	struct dst_ops dstops;
Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
397
#endif
398
	char result[512];
Linus Torvalds's avatar
Linus Torvalds committed
399
400
401
};

struct pktgen_hdr {
Al Viro's avatar
Al Viro committed
402
403
404
405
	__be32 pgh_magic;
	__be32 seq_num;
	__be32 tv_sec;
	__be32 tv_usec;
Linus Torvalds's avatar
Linus Torvalds committed
406
407
};

Cong Wang's avatar
Cong Wang committed
408
409
410
411
412
413
414
415
416

static int pg_net_id __read_mostly;

struct pktgen_net {
	struct net		*net;
	struct proc_dir_entry	*proc_dir;
	struct list_head	pktgen_threads;
	bool			pktgen_exiting;
};
417

Linus Torvalds's avatar
Linus Torvalds committed
418
struct pktgen_thread {
419
	spinlock_t if_lock;		/* for list of devices */
420
	struct list_head if_list;	/* All device here */
421
	struct list_head th_list;
422
	struct task_struct *tsk;
Luiz Capitulino's avatar
Luiz Capitulino committed
423
424
	char result[512];

425
426
	/* Field for thread to receive "posted" events terminate,
	   stop ifs etc. */
Luiz Capitulino's avatar
Luiz Capitulino committed
427
428

	u32 control;
Linus Torvalds's avatar
Linus Torvalds committed
429
430
	int cpu;

Luiz Capitulino's avatar
Luiz Capitulino committed
431
	wait_queue_head_t queue;
432
	struct completion start_done;
Cong Wang's avatar
Cong Wang committed
433
	struct pktgen_net *net;
Linus Torvalds's avatar
Linus Torvalds committed
434
435
436
437
438
};

#define REMOVE 1
#define FIND   0

Stephen Hemminger's avatar
Stephen Hemminger committed
439
static const char version[] =
440
441
	"Packet Generator for packet performance testing. "
	"Version: " VERSION "\n";
Linus Torvalds's avatar
Linus Torvalds committed
442

Luiz Capitulino's avatar
Luiz Capitulino committed
443
444
445
static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
446
					  const char *ifname, bool exact);
Linus Torvalds's avatar
Linus Torvalds committed
447
static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
Cong Wang's avatar
Cong Wang committed
448
449
450
static void pktgen_run_all_threads(struct pktgen_net *pn);
static void pktgen_reset_all_threads(struct pktgen_net *pn);
static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn);
451

Luiz Capitulino's avatar
Luiz Capitulino committed
452
static void pktgen_stop(struct pktgen_thread *t);
Linus Torvalds's avatar
Linus Torvalds committed
453
static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
454

Linus Torvalds's avatar
Linus Torvalds committed
455
/* Module parameters, defaults. */
456
457
458
459
static int pg_count_d __read_mostly = 1000;
static int pg_delay_d __read_mostly;
static int pg_clone_skb_d  __read_mostly;
static int debug  __read_mostly;
Linus Torvalds's avatar
Linus Torvalds committed
460

461
static DEFINE_MUTEX(pktgen_thread_lock);
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
465
466
467

static struct notifier_block pktgen_notifier_block = {
	.notifier_call = pktgen_device_event,
};

/*
468
 * /proc handling functions
Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
 *
 */

472
static int pgctrl_show(struct seq_file *seq, void *v)
Luiz Capitulino's avatar
Luiz Capitulino committed
473
{
Stephen Hemminger's avatar
Stephen Hemminger committed
474
	seq_puts(seq, version);
475
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
476
477
}

478
479
static ssize_t pgctrl_write(struct file *file, const char __user *buf,
			    size_t count, loff_t *ppos)
Linus Torvalds's avatar
Linus Torvalds committed
480
{
481
	char data[128];
Cong Wang's avatar
Cong Wang committed
482
	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
Linus Torvalds's avatar
Linus Torvalds committed
483

484
485
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
486

487
488
489
	if (count == 0)
		return -EINVAL;

490
491
	if (count > sizeof(data))
		count = sizeof(data);
Linus Torvalds's avatar
Linus Torvalds committed
492

493
494
495
	if (copy_from_user(data, buf, count))
		return -EFAULT;

496
	data[count - 1] = 0;	/* Strip trailing '\n' and terminate string */
Linus Torvalds's avatar
Linus Torvalds committed
497

Luiz Capitulino's avatar
Luiz Capitulino committed
498
	if (!strcmp(data, "stop"))
Cong Wang's avatar
Cong Wang committed
499
		pktgen_stop_all_threads_ifs(pn);
Linus Torvalds's avatar
Linus Torvalds committed
500

Luiz Capitulino's avatar
Luiz Capitulino committed
501
	else if (!strcmp(data, "start"))
Cong Wang's avatar
Cong Wang committed
502
		pktgen_run_all_threads(pn);
Linus Torvalds's avatar
Linus Torvalds committed
503

504
	else if (!strcmp(data, "reset"))
Cong Wang's avatar
Cong Wang committed
505
		pktgen_reset_all_threads(pn);
506

Luiz Capitulino's avatar
Luiz Capitulino committed
507
	else
508
		pr_warning("Unknown command: %s\n", data);
Linus Torvalds's avatar
Linus Torvalds committed
509

510
	return count;
Linus Torvalds's avatar
Linus Torvalds committed
511
512
}

513
514
static int pgctrl_open(struct inode *inode, struct file *file)
{
Al Viro's avatar
Al Viro committed
515
	return single_open(file, pgctrl_show, PDE_DATA(inode));
516
517
}

518
static const struct file_operations pktgen_fops = {
Luiz Capitulino's avatar
Luiz Capitulino committed
519
520
521
522
523
524
	.owner   = THIS_MODULE,
	.open    = pgctrl_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.write   = pgctrl_write,
	.release = single_release,
525
526
527
};

static int pktgen_if_show(struct seq_file *seq, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
528
{
Stephen Hemminger's avatar
Stephen Hemminger committed
529
	const struct pktgen_dev *pkt_dev = seq->private;
530
531
	ktime_t stopped;
	u64 idle;
Luiz Capitulino's avatar
Luiz Capitulino committed
532
533
534
535
536
537
538

	seq_printf(seq,
		   "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n",
		   (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
		   pkt_dev->max_pkt_size);

	seq_printf(seq,
539
540
		   "     frags: %d  delay: %llu  clone_skb: %d  ifname: %s\n",
		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
541
		   pkt_dev->clone_skb, pkt_dev->odevname);
Luiz Capitulino's avatar
Luiz Capitulino committed
542
543
544
545

	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
		   pkt_dev->lflow);

Robert Olsson's avatar
Robert Olsson committed
546
547
548
549
550
	seq_printf(seq,
		   "     queue_map_min: %u  queue_map_max: %u\n",
		   pkt_dev->queue_map_min,
		   pkt_dev->queue_map_max);

551
552
553
554
	if (pkt_dev->skb_priority)
		seq_printf(seq, "     skb_priority: %u\n",
			   pkt_dev->skb_priority);

Luiz Capitulino's avatar
Luiz Capitulino committed
555
556
	if (pkt_dev->flags & F_IPV6) {
		seq_printf(seq,
557
558
559
560
561
562
			   "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n"
			   "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n",
			   &pkt_dev->in6_saddr,
			   &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr,
			   &pkt_dev->in6_daddr,
			   &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);
563
564
565
566
	} else {
		seq_printf(seq,
			   "     dst_min: %s  dst_max: %s\n",
			   pkt_dev->dst_min, pkt_dev->dst_max);
Luiz Capitulino's avatar
Luiz Capitulino committed
567
		seq_printf(seq,
568
569
570
			   "        src_min: %s  src_max: %s\n",
			   pkt_dev->src_min, pkt_dev->src_max);
	}
Linus Torvalds's avatar
Linus Torvalds committed
571

Luiz Capitulino's avatar
Luiz Capitulino committed
572
	seq_puts(seq, "     src_mac: ");
Linus Torvalds's avatar
Linus Torvalds committed
573

Johannes Berg's avatar
Johannes Berg committed
574
575
576
	seq_printf(seq, "%pM ",
		   is_zero_ether_addr(pkt_dev->src_mac) ?
			     pkt_dev->odev->dev_addr : pkt_dev->src_mac);
Luiz Capitulino's avatar
Luiz Capitulino committed
577

578
	seq_puts(seq, "dst_mac: ");
Johannes Berg's avatar
Johannes Berg committed
579
	seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
Luiz Capitulino's avatar
Luiz Capitulino committed
580
581

	seq_printf(seq,
582
583
		   "     udp_src_min: %d  udp_src_max: %d"
		   "  udp_dst_min: %d  udp_dst_max: %d\n",
Luiz Capitulino's avatar
Luiz Capitulino committed
584
585
586
587
		   pkt_dev->udp_src_min, pkt_dev->udp_src_max,
		   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);

	seq_printf(seq,
588
		   "     src_mac_count: %d  dst_mac_count: %d\n",
Luiz Capitulino's avatar
Luiz Capitulino committed
589
		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
Linus Torvalds's avatar
Linus Torvalds committed
590

591
	if (pkt_dev->nr_labels) {
592
		unsigned int i;
593
		seq_puts(seq, "     mpls: ");
594
		for (i = 0; i < pkt_dev->nr_labels; i++)
595
596
597
598
			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
	}

599
	if (pkt_dev->vlan_id != 0xffff)
Francesco Fondelli's avatar
Francesco Fondelli committed
600
		seq_printf(seq, "     vlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
601
602
			   pkt_dev->vlan_id, pkt_dev->vlan_p,
			   pkt_dev->vlan_cfi);
Francesco Fondelli's avatar
Francesco Fondelli committed
603

604
	if (pkt_dev->svlan_id != 0xffff)
Francesco Fondelli's avatar
Francesco Fondelli committed
605
		seq_printf(seq, "     svlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
606
607
			   pkt_dev->svlan_id, pkt_dev->svlan_p,
			   pkt_dev->svlan_cfi);
Francesco Fondelli's avatar
Francesco Fondelli committed
608

609
	if (pkt_dev->tos)
Francesco Fondelli's avatar
Francesco Fondelli committed
610
611
		seq_printf(seq, "     tos: 0x%02x\n", pkt_dev->tos);

612
	if (pkt_dev->traffic_class)
Francesco Fondelli's avatar
Francesco Fondelli committed
613
614
		seq_printf(seq, "     traffic_class: 0x%02x\n", pkt_dev->traffic_class);

Robert Olsson's avatar
Robert Olsson committed
615
616
617
	if (pkt_dev->node >= 0)
		seq_printf(seq, "     node: %d\n", pkt_dev->node);

618
	seq_puts(seq, "     Flags: ");
619

Luiz Capitulino's avatar
Luiz Capitulino committed
620
	if (pkt_dev->flags & F_IPV6)
621
		seq_puts(seq, "IPV6  ");
Linus Torvalds's avatar
Linus Torvalds committed
622

Luiz Capitulino's avatar
Luiz Capitulino committed
623
	if (pkt_dev->flags & F_IPSRC_RND)
624
		seq_puts(seq, "IPSRC_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
625

Luiz Capitulino's avatar
Luiz Capitulino committed
626
	if (pkt_dev->flags & F_IPDST_RND)
627
		seq_puts(seq, "IPDST_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
628

Luiz Capitulino's avatar
Luiz Capitulino committed
629
	if (pkt_dev->flags & F_TXSIZE_RND)
630
		seq_puts(seq, "TXSIZE_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
631

Luiz Capitulino's avatar
Luiz Capitulino committed
632
	if (pkt_dev->flags & F_UDPSRC_RND)
633
		seq_puts(seq, "UDPSRC_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
634

Luiz Capitulino's avatar
Luiz Capitulino committed
635
	if (pkt_dev->flags & F_UDPDST_RND)
636
		seq_puts(seq, "UDPDST_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
637

638
	if (pkt_dev->flags & F_UDPCSUM)
639
		seq_puts(seq, "UDPCSUM  ");
640

641
	if (pkt_dev->flags & F_MPLS_RND)
642
		seq_puts(seq,  "MPLS_RND  ");
643

Robert Olsson's avatar
Robert Olsson committed
644
	if (pkt_dev->flags & F_QUEUE_MAP_RND)
645
		seq_puts(seq,  "QUEUE_MAP_RND  ");
Robert Olsson's avatar
Robert Olsson committed
646

Robert Olsson's avatar
Robert Olsson committed
647
	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
648
		seq_puts(seq,  "QUEUE_MAP_CPU  ");
Robert Olsson's avatar
Robert Olsson committed
649

650
651
	if (pkt_dev->cflows) {
		if (pkt_dev->flags & F_FLOW_SEQ)
652
			seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/
653
		else
654
			seq_puts(seq,  "FLOW_RND  ");
655
656
	}

Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
657
#ifdef CONFIG_XFRM
658
	if (pkt_dev->flags & F_IPSEC_ON) {
659
		seq_puts(seq,  "IPSEC  ");
660
661
662
		if (pkt_dev->spi)
			seq_printf(seq, "spi:%u", pkt_dev->spi);
	}
Jamal Hadi Salim's avatar
Jamal Hadi Salim committed
663
664
#endif

Luiz Capitulino's avatar
Luiz Capitulino committed
665
	if (pkt_dev->flags & F_MACSRC_RND)
666
		seq_puts(seq, "MACSRC_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
667

Luiz Capitulino's avatar
Luiz Capitulino committed
668
	if (pkt_dev->flags & F_MACDST_RND)
669
		seq_puts(seq, "MACDST_RND  ");
Linus Torvalds's avatar
Linus Torvalds committed
670

Francesco Fondelli's avatar
Francesco Fondelli committed
671
	if (pkt_dev->flags & F_VID_RND)
672
		seq_puts(seq, "VID_RND  ");
Francesco Fondelli's avatar
Francesco Fondelli committed
673
674

	if (pkt_dev->flags & F_SVID_RND)
675
		seq_puts(seq, "SVID_RND  ");
Francesco Fondelli's avatar
Francesco Fondelli committed
676

Robert Olsson's avatar
Robert Olsson committed
677
	if (pkt_dev->flags & F_NODE)
678
		seq_puts(seq, "NODE_ALLOC  ");
Robert Olsson's avatar
Robert Olsson committed
679

Luiz Capitulino's avatar
Luiz Capitulino committed
680
681
	seq_puts(seq, "\n");

682
	/* not really stopped, more like last-running-at */
683
	stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
684
685
	idle = pkt_dev->idle_acc;
	do_div(idle, NSEC_PER_USEC);
Linus Torvalds's avatar
Linus Torvalds committed
686

Luiz Capitulino's avatar
Luiz Capitulino committed
687
	seq_printf(seq,
688
		   "Current:\n     pkts-sofar: %llu  errors: %llu\n",
Luiz Capitulino's avatar
Luiz Capitulino committed
689
		   (unsigned long long)pkt_dev->sofar,
690
691
692
693
694
695
696
		   (unsigned long long)pkt_dev->errors);

	seq_printf(seq,
		   "     started: %lluus  stopped: %lluus idle: %lluus\n",
		   (unsigned long long) ktime_to_us(pkt_dev->started_at),
		   (unsigned long long) ktime_to_us(stopped),
		   (unsigned long long) idle);
Linus Torvalds's avatar
Linus Torvalds committed
697

Luiz Capitulino's avatar
Luiz Capitulino committed
698
699
	seq_printf(seq,
		   "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
700
701
		   pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
		   pkt_dev->cur_src_mac_offset);
Linus Torvalds's avatar
Linus Torvalds committed
702

Luiz Capitulino's avatar
Luiz Capitulino committed
703
	if (pkt_dev->flags & F_IPV6) {
704
705
706
		seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n",
				&pkt_dev->cur_in6_saddr,
				&pkt_dev->cur_in6_daddr);
Luiz Capitulino's avatar
Luiz Capitulino committed
707
	} else
708
709
		seq_printf(seq, "     cur_saddr: %pI4  cur_daddr: %pI4\n",
			   &pkt_dev->cur_saddr, &pkt_dev->cur_daddr);
Linus Torvalds's avatar
Linus Torvalds committed
710

Luiz Capitulino's avatar
Luiz Capitulino committed
711
	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",
712
		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
Linus Torvalds's avatar
Linus Torvalds committed
713

Robert Olsson's avatar
Robert Olsson committed
714
715
	seq_printf(seq, "     cur_queue_map: %u\n", pkt_dev->cur_queue_map);

Luiz Capitulino's avatar
Luiz Capitulino committed
716
	seq_printf(seq, "     flows: %u\n", pkt_dev->nflows);
Linus Torvalds's avatar
Linus Torvalds committed
717
718

	if (pkt_dev->result[0])
Luiz Capitulino's avatar
Luiz Capitulino committed
719
		seq_printf(seq, "Result: %s\n", pkt_dev->result);
Linus Torvalds's avatar
Linus Torvalds committed
720
	else
721
		seq_puts(seq, "Result: Idle\n");
Linus Torvalds's avatar
Linus Torvalds committed
722

723
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
724
725
}

726

727
728
static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
		     __u32 *num)
729
730
731
732
{
	int i = 0;
	*num = 0;

733
	for (; i < maxlen; i++) {
734
		int value;
735
736
737
738
		char c;
		*num <<= 4;
		if (get_user(c, &user_buffer[i]))
			return -EFAULT;
739
740
741
		value = hex_to_bin(c);
		if (value >= 0)
			*num |= value;
742
743
744
745
746
747
		else
			break;
	}
	return i;
}

Luiz Capitulino's avatar
Luiz Capitulino committed
748
749
static int count_trail_chars(const char __user * user_buffer,
			     unsigned int maxlen)
Linus Torvalds's avatar
Linus Torvalds committed
750
751
752
753
{
	int i;

	for (i = 0; i < maxlen; i++) {
Luiz Capitulino's avatar
Luiz Capitulino committed
754
755
756
757
		char c;
		if (get_user(c, &user_buffer[i]))
			return -EFAULT;
		switch (c) {
Linus Torvalds's avatar
Linus Torvalds committed
758
759
760
761
762
763
764
765
766
		case '\"':
		case '\n':
		case '\r':
		case '\t':
		case ' ':
		case '=':
			break;
		default:
			goto done;
767
		}
Linus Torvalds's avatar
Linus Torvalds committed
768
769
770
771
772
	}
done:
	return i;
}

773
774
static long num_arg(const char __user *user_buffer, unsigned long maxlen,
				unsigned long *num)
Linus Torvalds's avatar
Linus Torvalds committed
775
{
776
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
777
	*num = 0;
Luiz Capitulino's avatar
Luiz Capitulino committed
778

779
	for (i = 0; i < maxlen; i++) {
Luiz Capitulino's avatar
Luiz Capitulino committed
780
781
782
783
		char c;
		if (get_user(c, &user_buffer[i]))
			return -EFAULT;
		if ((c >= '0') && (c <= '9')) {
Linus Torvalds's avatar
Linus Torvalds committed
784
			*num *= 10;
Luiz Capitulino's avatar
Luiz Capitulino committed
785
			*num += c - '0';
Linus Torvalds's avatar
Linus Torvalds committed
786
787
788
789
790
791
		} else
			break;
	}
	return i;
}

Luiz Capitulino's avatar
Luiz Capitulino committed
792
static int strn_len(const char __user * user_buffer, unsigned int maxlen)
Linus Torvalds's avatar
Linus Torvalds committed
793
{
794
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
795

796
	for (i = 0; i < maxlen; i++) {
Luiz Capitulino's avatar
Luiz Capitulino committed
797
798
799
800
		char c;
		if (get_user(c, &user_buffer[i]))
			return -EFAULT;
		switch (c) {
Linus Torvalds's avatar
Linus Torvalds committed
801
802
803
804
805
806
807
808
		case '\"':
		case '\n':
		case '\r':
		case '\t':
		case ' ':
			goto done_str;
		default:
			break;
809
		}
Linus Torvalds's avatar
Linus Torvalds committed
810
811
812
813
814
	}
done_str:
	return i;
}

815
816
static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
{
817
	unsigned int n = 0;
818
819
820
821
822
823
824
	char c;
	ssize_t i = 0;
	int len;

	pkt_dev->nr_labels = 0;
	do {
		__u32 tmp;
Francesco Fondelli's avatar
Francesco Fondelli committed
825
		len = hex32_arg(&buffer[i], 8, &tmp);
826
827
828
829
830
831
832
833
834
835
836
837
		if (len <= 0)
			return len;
		pkt_dev->labels[n] = htonl(tmp);
		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
			pkt_dev->flags |= F_MPLS_RND;
		i += len;
		if (get_user(c, &buffer[i]))
			return -EFAULT;
		i++;
		n++;
		if (n >= MAX_MPLS_LABELS)
			return -E2BIG;
838
	} while (c == ',');
839
840
841
842
843

	pkt_dev->nr_labels = n;
	return i;
}

Luiz Capitulino's avatar
Luiz Capitulino committed
844
845
846
static ssize_t pktgen_if_write(struct file *file,
			       const char __user * user_buffer, size_t count,
			       loff_t * offset)
Linus Torvalds's avatar
Linus Torvalds committed
847
{
848
	struct seq_file *seq = file->private_data;
Luiz Capitulino's avatar
Luiz Capitulino committed
849
	struct pktgen_dev *pkt_dev = seq->private;
850
	int i, max, len;
Linus Torvalds's avatar
Linus Torvalds committed
851
852
	char name[16], valstr[32];
	unsigned long value = 0;
Luiz Capitulino's avatar
Luiz Capitulino committed
853
854
	char *pg_result = NULL;
	int tmp = 0;
Linus Torvalds's avatar
Linus Torvalds committed
855
	char buf[128];
Luiz Capitulino's avatar
Luiz Capitulino committed
856
857
858

	pg_result = &(pkt_dev->result[0]);

Linus Torvalds's avatar
Linus Torvalds committed
859
	if (count < 1) {
860
		pr_warning("wrong command format\n");
Linus Torvalds's avatar
Linus Torvalds committed
861
862
		return -EINVAL;
	}
Luiz Capitulino's avatar
Luiz Capitulino committed
863

864
865
	max = count;
	tmp = count_trail_chars(user_buffer, max);
Luiz Capitulino's avatar
Luiz Capitulino committed
866
	if (tmp < 0) {
867
		pr_warning("illegal format\n");
Luiz Capitulino's avatar
Luiz Capitulino committed
868
		return tmp;
Linus Torvalds's avatar
Linus Torvalds committed
869
	}
870
	i = tmp;
Luiz Capitulino's avatar
Luiz Capitulino committed
871

Linus Torvalds's avatar
Linus Torvalds committed
872
873
874
	/* Read variable name */

	len = strn_len(&user_buffer[i], sizeof(name) - 1);
875
	if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
876
		return len;
877

Linus Torvalds's avatar
Linus Torvalds committed
878
	memset(name, 0, sizeof(name));
Luiz Capitulino's avatar
Luiz Capitulino committed
879
	if (copy_from_user(name, &user_buffer[i], len))
Linus Torvalds's avatar
Linus Torvalds committed
880
881
		return -EFAULT;
	i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
882
883

	max = count - i;
Linus Torvalds's avatar
Linus Torvalds committed
884
	len = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino's avatar
Luiz Capitulino committed
885
886
887
	if (len < 0)
		return len;

Linus Torvalds's avatar
Linus Torvalds committed
888
889
890
	i += len;

	if (debug) {
891
		size_t copy = min_t(size_t, count, 1023);
892
893
		char tb[copy + 1];
		if (copy_from_user(tb, user_buffer, copy))
Linus Torvalds's avatar
Linus Torvalds committed
894
			return -EFAULT;
895
		tb[copy] = 0;
Joe Perches's avatar
Joe Perches committed
896
897
		pr_debug("%s,%lu  buffer -:%s:-\n",
			 name, (unsigned long)count, tb);
Luiz Capitulino's avatar
Luiz Capitulino committed
898
	}
Linus Torvalds's avatar
Linus Torvalds committed
899
900
901

	if (!strcmp(name, "min_pkt_size")) {
		len = num_arg(&user_buffer[i], 10, &value);
902
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
903
			return len;
904

Linus Torvalds's avatar
Linus Torvalds committed
905
		i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
906
907
908
909
910
911
912
913
		if (value < 14 + 20 + 8)
			value = 14 + 20 + 8;
		if (value != pkt_dev->min_pkt_size) {
			pkt_dev->min_pkt_size = value;
			pkt_dev->cur_pkt_size = value;
		}
		sprintf(pg_result, "OK: min_pkt_size=%u",
			pkt_dev->min_pkt_size);
Linus Torvalds's avatar
Linus Torvalds committed
914
915
916
		return count;
	}

Luiz Capitulino's avatar
Luiz Capitulino committed
917
	if (!strcmp(name, "max_pkt_size")) {
Linus Torvalds's avatar
Linus Torvalds committed
918
		len = num_arg(&user_buffer[i], 10, &value);
919
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
920
			return len;
921

Linus Torvalds's avatar
Linus Torvalds committed
922
		i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
923
924
925
926
927
928
929
930
		if (value < 14 + 20 + 8)
			value = 14 + 20 + 8;
		if (value != pkt_dev->max_pkt_size) {
			pkt_dev->max_pkt_size = value;
			pkt_dev->cur_pkt_size = value;
		}
		sprintf(pg_result, "OK: max_pkt_size=%u",
			pkt_dev->max_pkt_size);
Linus Torvalds's avatar
Linus Torvalds committed
931
932
933
		return count;
	}

Luiz Capitulino's avatar
Luiz Capitulino committed
934
	/* Shortcut for min = max */
Linus Torvalds's avatar
Linus Torvalds committed
935
936
937

	if (!strcmp(name, "pkt_size")) {
		len = num_arg(&user_buffer[i], 10, &value);
938
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
939
			return len;
940

Linus Torvalds's avatar
Linus Torvalds committed
941
		i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
942
943
944
945
946
947
948
		if (value < 14 + 20 + 8)
			value = 14 + 20 + 8;
		if (value != pkt_dev->min_pkt_size) {
			pkt_dev->min_pkt_size = value;
			pkt_dev->max_pkt_size = value;
			pkt_dev->cur_pkt_size = value;
		}
Linus Torvalds's avatar
Linus Torvalds committed
949
950
951
952
		sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
		return count;
	}

Luiz Capitulino's avatar
Luiz Capitulino committed
953
	if (!strcmp(name, "debug")) {
Linus Torvalds's avatar
Linus Torvalds committed
954
		len = num_arg(&user_buffer[i], 10, &value);
955
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
956
			return len;
957

Linus Torvalds's avatar
Linus Torvalds committed
958
		i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
959
		debug = value;
Linus Torvalds's avatar
Linus Torvalds committed
960
961
962
963
		sprintf(pg_result, "OK: debug=%u", debug);
		return count;
	}

Luiz Capitulino's avatar
Luiz Capitulino committed
964
	if (!strcmp(name, "frags")) {
Linus Torvalds's avatar
Linus Torvalds committed
965
		len = num_arg(&user_buffer[i], 10, &value);
966
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
967
			return len;
968

Linus Torvalds's avatar
Linus Torvalds committed
969
970
971
972
973
974
975
		i += len;
		pkt_dev->nfrags = value;
		sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
		return count;
	}
	if (!strcmp(name, "delay")) {
		len = num_arg(&user_buffer[i], 10, &value);
976
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
977
			return len;
978

Linus Torvalds's avatar
Linus Torvalds committed
979
		i += len;
980
981
982
		if (value == 0x7FFFFFFF)
			pkt_dev->delay = ULLONG_MAX;
		else
Eric Dumazet's avatar
Eric Dumazet committed
983
			pkt_dev->delay = (u64)value;
984
985
986

		sprintf(pg_result, "OK: delay=%llu",
			(unsigned long long) pkt_dev->delay);
Linus Torvalds's avatar
Linus Torvalds committed
987
988
		return count;
	}
989
990
991
992
993
994
995
996
997
998
	if (!strcmp(name, "rate")) {
		len = num_arg(&user_buffer[i], 10, &value);
		if (len < 0)
			return len;

		i += len;
		if (!value)
			return len;
		pkt_dev->delay = pkt_dev->min_pkt_size*8*NSEC_PER_USEC/value;
		if (debug)
999
			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013

		sprintf(pg_result, "OK: rate=%lu", value);
		return count;
	}
	if (!strcmp(name, "ratep")) {
		len = num_arg(&user_buffer[i], 10, &value);
		if (len < 0)
			return len;

		i += len;
		if (!value)
			return len;
		pkt_dev->delay = NSEC_PER_SEC/value;
		if (debug)
1014
			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
1015
1016
1017
1018

		sprintf(pg_result, "OK: rate=%lu", value);
		return count;
	}
Luiz Capitulino's avatar
Luiz Capitulino committed
1019
	if (!strcmp(name, "udp_src_min")) {
Linus Torvalds's avatar
Linus Torvalds committed
1020
		len = num_arg(&user_buffer[i], 10, &value);
1021
		if (len < 0)
Luiz Capitulino's avatar
Luiz Capitulino committed
1022
			return len;
1023

Linus Torvalds's avatar
Linus Torvalds committed
1024
		i += len;
Luiz Capitulino's avatar
Luiz Capitulino committed
1025
1026
1027
1028
		if (value != pkt_dev->udp_src_min) {
			pkt_dev->udp_src_min = value;
			pkt_dev->cur_udp_src = value;
		}
Linus Torvalds's avatar
Linus Torvalds committed
1029
1030
1031
		sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
		return count;
	}