blob: 683da7065886729943ad30dcf2718c937c02b3f1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Authors:
3 * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
4 * Uppsala University and
5 * Swedish University of Agricultural Sciences
6 *
7 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
8 * Ben Greear <greearb@candelatech.com>
9 * Jens Låås <jens.laas@data.slu.se>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version.
15 *
16 *
17 * A tool for loading the network with preconfigurated packets.
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090018 * The tool is implemented as a linux module. Parameters are output
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 * device, delay (to hard_xmit), number of packets, and whether
20 * to use multiple SKBs or just the same one.
21 * pktgen uses the installed interface's output routine.
22 *
23 * Additional hacking by:
24 *
25 * Jens.Laas@data.slu.se
26 * Improved by ANK. 010120.
27 * Improved by ANK even more. 010212.
28 * MAC address typo fixed. 010417 --ro
29 * Integrated. 020301 --DaveM
30 * Added multiskb option 020301 --DaveM
31 * Scaling of results. 020417--sigurdur@linpro.no
32 * Significant re-work of the module:
33 * * Convert to threaded model to more efficiently be able to transmit
34 * and receive on multiple interfaces at once.
35 * * Converted many counters to __u64 to allow longer runs.
36 * * Allow configuration of ranges, like min/max IP address, MACs,
37 * and UDP-ports, for both source and destination, and can
38 * set to use a random distribution or sequentially walk the range.
39 * * Can now change most values after starting.
40 * * Place 12-byte packet in UDP payload with magic number,
41 * sequence number, and timestamp.
42 * * Add receiver code that detects dropped pkts, re-ordered pkts, and
43 * latencies (with micro-second) precision.
44 * * Add IOCTL interface to easily get counters & configuration.
45 * --Ben Greear <greearb@candelatech.com>
46 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090047 * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
48 * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 * as a "fastpath" with a configurable number of clones after alloc's.
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090050 * clone_skb=0 means all packets are allocated this also means ranges time
51 * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * clones.
53 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090054 * Also moved to /proc/net/pktgen/
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 * --ro
56 *
57 * Sept 10: Fixed threading/locking. Lots of bone-headed and more clever
58 * mistakes. Also merged in DaveM's patch in the -pre6 patch.
59 * --Ben Greear <greearb@candelatech.com>
60 *
61 * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br)
62 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090063 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 * 021124 Finished major redesign and rewrite for new functionality.
65 * See Documentation/networking/pktgen.txt for how to use this.
66 *
67 * The new operation:
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090068 * For each CPU one thread/process is created at start. This process checks
69 * for running devices in the if_list and sends packets until count is 0 it
70 * also the thread checks the thread->control which is used for inter-process
71 * communication. controlling process "posts" operations to the threads this
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 * way. The if_lock should be possible to remove when add/rem_device is merged
73 * into this too.
74 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090075 * By design there should only be *one* "controlling" process. In practice
76 * multiple write accesses gives unpredictable result. Understood by "write"
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 * to /proc gives result code thats should be read be the "writer".
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -070078 * For practical use this should be no problem.
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090080 * Note when adding devices to a specific CPU there good idea to also assign
81 * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 * --ro
83 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090084 * Fix refcount off by one if first packet fails, potential null deref,
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 * memleak 030710- KJP
86 *
87 * First "ranges" functionality for ipv6 030726 --ro
88 *
89 * Included flow support. 030802 ANK.
90 *
91 * Fixed unaligned access on IA-64 Grant Grundler <grundler@parisc-linux.org>
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090092 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
94 * ia64 compilation fix from Aron Griffis <aron@hp.com> 040604
95 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090096 * New xmit() return, do_div and misc clean up by Stephen Hemminger
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 * <shemminger@osdl.org> 040923
98 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090099 * Randy Dunlap fixed u64 printk compiler waring
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 *
101 * Remove FCS from BW calculation. Lennert Buytenhek <buytenh@wantstofly.org>
102 * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213
103 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900104 * Corrections from Nikolai Malykh (nmalykh@bilim.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
106 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900107 * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 * 050103
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800109 *
110 * MPLS support by Steven Whitehouse <steve@chygwyn.com>
111 *
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700112 * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <francesco.fondelli@gmail.com>
113 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 */
115#include <linux/sys.h>
116#include <linux/types.h>
117#include <linux/module.h>
118#include <linux/moduleparam.h>
119#include <linux/kernel.h>
Luiz Capitulino222fa072006-03-20 22:24:27 -0800120#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121#include <linux/sched.h>
122#include <linux/slab.h>
123#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124#include <linux/unistd.h>
125#include <linux/string.h>
126#include <linux/ptrace.h>
127#include <linux/errno.h>
128#include <linux/ioport.h>
129#include <linux/interrupt.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -0800130#include <linux/capability.h>
Andrew Morton09fe3ef2007-04-12 14:45:32 -0700131#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132#include <linux/delay.h>
133#include <linux/timer.h>
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -0800134#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135#include <linux/init.h>
136#include <linux/skbuff.h>
137#include <linux/netdevice.h>
138#include <linux/inet.h>
139#include <linux/inetdevice.h>
140#include <linux/rtnetlink.h>
141#include <linux/if_arp.h>
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700142#include <linux/if_vlan.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143#include <linux/in.h>
144#include <linux/ip.h>
145#include <linux/ipv6.h>
146#include <linux/udp.h>
147#include <linux/proc_fs.h>
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700148#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149#include <linux/wait.h>
Kris Katterjohnf404e9a2006-01-17 13:04:57 -0800150#include <linux/etherdevice.h>
David S. Milleree74baa2007-01-01 20:51:53 -0800151#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152#include <net/checksum.h>
153#include <net/ipv6.h>
154#include <net/addrconf.h>
155#include <asm/byteorder.h>
156#include <linux/rcupdate.h>
157#include <asm/bitops.h>
158#include <asm/io.h>
159#include <asm/dma.h>
160#include <asm/uaccess.h>
Luiz Capitulino222f1802006-03-20 22:16:13 -0800161#include <asm/div64.h> /* do_div */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162#include <asm/timex.h>
163
Francesco Fondelli1ca77682006-09-27 16:32:03 -0700164#define VERSION "pktgen v2.68: Packet Generator for packet performance testing.\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166/* The buckets are exponential in 'width' */
167#define LAT_BUCKETS_MAX 32
168#define IP_NAME_SZ 32
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800169#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -0800170#define MPLS_STACK_BOTTOM htonl(0x00000100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
172/* Device flag bits */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800173#define F_IPSRC_RND (1<<0) /* IP-Src Random */
174#define F_IPDST_RND (1<<1) /* IP-Dst Random */
175#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
176#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
177#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
178#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
179#define F_TXSIZE_RND (1<<6) /* Transmit size is random */
180#define F_IPV6 (1<<7) /* Interface in IPV6 Mode */
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800181#define F_MPLS_RND (1<<8) /* Random MPLS labels */
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700182#define F_VID_RND (1<<9) /* Random VLAN ID */
183#define F_SVID_RND (1<<10) /* Random SVLAN ID */
Jamal Hadi Salim007a5312007-07-02 22:40:36 -0700184#define F_FLOW_SEQ (1<<11) /* Sequential flows */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186/* Thread control flag bits */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800187#define T_TERMINATE (1<<0)
188#define T_STOP (1<<1) /* Stop run */
189#define T_RUN (1<<2) /* Start run */
190#define T_REMDEVALL (1<<3) /* Remove all devs */
191#define T_REMDEV (1<<4) /* Remove one dev */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193/* If lock -- can be removed after some work */
194#define if_lock(t) spin_lock(&(t->if_lock));
195#define if_unlock(t) spin_unlock(&(t->if_lock));
196
197/* Used to help with determining the pkts on receive */
198#define PKTGEN_MAGIC 0xbe9be955
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700199#define PG_PROC_DIR "pktgen"
200#define PGCTRL "pgctrl"
201static struct proc_dir_entry *pg_proc_dir = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203#define MAX_CFLOWS 65536
204
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700205#define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
206#define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
207
Luiz Capitulino222f1802006-03-20 22:16:13 -0800208struct flow_state {
Al Viro252e3342006-11-14 20:48:11 -0800209 __be32 cur_daddr;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800210 int count;
Jamal Hadi Salim007a5312007-07-02 22:40:36 -0700211 __u32 flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212};
213
Jamal Hadi Salim007a5312007-07-02 22:40:36 -0700214/* flow flag bits */
215#define F_INIT (1<<0) /* flow has been initialized */
216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217struct pktgen_dev {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 /*
219 * Try to keep frequent/infrequent used vars. separated.
220 */
Stephen Hemminger39df2322007-03-04 16:11:51 -0800221 struct proc_dir_entry *entry; /* proc file */
222 struct pktgen_thread *pg_thread;/* the owner */
Luiz Capitulinoc26a8012006-03-20 22:18:16 -0800223 struct list_head list; /* Used for chaining in the thread's run-queue */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
Luiz Capitulino222f1802006-03-20 22:16:13 -0800225 int running; /* if this changes to false, the test will stop */
226
227 /* If min != max, then we will either do a linear iteration, or
228 * we will do a random selection from within the range.
229 */
230 __u32 flags;
Arthur Kepner95ed63f2006-03-20 21:26:56 -0800231 int removal_mark; /* non-zero => the device is marked for
232 * removal by worker thread */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Luiz Capitulino222f1802006-03-20 22:16:13 -0800234 int min_pkt_size; /* = ETH_ZLEN; */
235 int max_pkt_size; /* = ETH_ZLEN; */
Jamal Hadi Salim16dab722007-07-02 22:39:50 -0700236 int pkt_overhead; /* overhead for MPLS, VLANs, IPSEC etc */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800237 int nfrags;
238 __u32 delay_us; /* Default delay */
239 __u32 delay_ns;
240 __u64 count; /* Default No packets to send */
241 __u64 sofar; /* How many pkts we've sent so far */
242 __u64 tx_bytes; /* How many bytes we've transmitted */
243 __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Luiz Capitulino222f1802006-03-20 22:16:13 -0800245 /* runtime counters relating to clone_skb */
246 __u64 next_tx_us; /* timestamp of when to tx next */
247 __u32 next_tx_ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Luiz Capitulino222f1802006-03-20 22:16:13 -0800249 __u64 allocated_skbs;
250 __u32 clone_count;
251 int last_ok; /* Was last skb sent?
252 * Or a failed transmit of some sort? This will keep
253 * sequence numbers in order, for example.
254 */
255 __u64 started_at; /* micro-seconds */
256 __u64 stopped_at; /* micro-seconds */
257 __u64 idle_acc; /* micro-seconds */
258 __u32 seq_num;
259
260 int clone_skb; /* Use multiple SKBs during packet gen. If this number
261 * is greater than 1, then that many copies of the same
262 * packet will be sent before a new packet is allocated.
263 * For instance, if you want to send 1024 identical packets
264 * before creating a new packet, set clone_skb to 1024.
265 */
266
267 char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
268 char dst_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
269 char src_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
270 char src_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
271
272 struct in6_addr in6_saddr;
273 struct in6_addr in6_daddr;
274 struct in6_addr cur_in6_daddr;
275 struct in6_addr cur_in6_saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 /* For ranges */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800277 struct in6_addr min_in6_daddr;
278 struct in6_addr max_in6_daddr;
279 struct in6_addr min_in6_saddr;
280 struct in6_addr max_in6_saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Luiz Capitulino222f1802006-03-20 22:16:13 -0800282 /* If we're doing ranges, random or incremental, then this
283 * defines the min/max for those ranges.
284 */
Al Viro252e3342006-11-14 20:48:11 -0800285 __be32 saddr_min; /* inclusive, source IP address */
286 __be32 saddr_max; /* exclusive, source IP address */
287 __be32 daddr_min; /* inclusive, dest IP address */
288 __be32 daddr_max; /* exclusive, dest IP address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
Luiz Capitulino222f1802006-03-20 22:16:13 -0800290 __u16 udp_src_min; /* inclusive, source UDP port */
291 __u16 udp_src_max; /* exclusive, source UDP port */
292 __u16 udp_dst_min; /* inclusive, dest UDP port */
293 __u16 udp_dst_max; /* exclusive, dest UDP port */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Francesco Fondelli1ca77682006-09-27 16:32:03 -0700295 /* DSCP + ECN */
296 __u8 tos; /* six most significant bits of (former) IPv4 TOS are for dscp codepoint */
297 __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 (see RFC 3260, sec. 4) */
298
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800299 /* MPLS */
300 unsigned nr_labels; /* Depth of stack, 0 = no MPLS */
301 __be32 labels[MAX_MPLS_LABELS];
302
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700303 /* VLAN/SVLAN (802.1Q/Q-in-Q) */
304 __u8 vlan_p;
305 __u8 vlan_cfi;
306 __u16 vlan_id; /* 0xffff means no vlan tag */
307
308 __u8 svlan_p;
309 __u8 svlan_cfi;
310 __u16 svlan_id; /* 0xffff means no svlan tag */
311
Luiz Capitulino222f1802006-03-20 22:16:13 -0800312 __u32 src_mac_count; /* How many MACs to iterate through */
313 __u32 dst_mac_count; /* How many MACs to iterate through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Luiz Capitulino222f1802006-03-20 22:16:13 -0800315 unsigned char dst_mac[ETH_ALEN];
316 unsigned char src_mac[ETH_ALEN];
317
318 __u32 cur_dst_mac_offset;
319 __u32 cur_src_mac_offset;
Al Viro252e3342006-11-14 20:48:11 -0800320 __be32 cur_saddr;
321 __be32 cur_daddr;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800322 __u16 cur_udp_dst;
323 __u16 cur_udp_src;
324 __u32 cur_pkt_size;
325
326 __u8 hh[14];
327 /* = {
328 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
329
330 We fill in SRC address later
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x08, 0x00
333 };
334 */
335 __u16 pad; /* pad out the hh struct to an even 16 bytes */
336
337 struct sk_buff *skb; /* skb we are to transmit next, mainly used for when we
338 * are transmitting the same one multiple times
339 */
340 struct net_device *odev; /* The out-going device. Note that the device should
341 * have it's pg_info pointer pointing back to this
342 * device. This will be set when the user specifies
343 * the out-going device name (not when the inject is
344 * started as it used to do.)
345 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 struct flow_state *flows;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800347 unsigned cflows; /* Concurrent flows (config) */
348 unsigned lflow; /* Flow length (config) */
349 unsigned nflows; /* accumulated flows (stats) */
Jamal Hadi Salim007a5312007-07-02 22:40:36 -0700350 unsigned curfl; /* current sequenced flow (state)*/
Stephen Hemminger39df2322007-03-04 16:11:51 -0800351
352 char result[512];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353};
354
355struct pktgen_hdr {
Al Viro252e3342006-11-14 20:48:11 -0800356 __be32 pgh_magic;
357 __be32 seq_num;
358 __be32 tv_sec;
359 __be32 tv_usec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360};
361
362struct pktgen_thread {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800363 spinlock_t if_lock;
Luiz Capitulinoc26a8012006-03-20 22:18:16 -0800364 struct list_head if_list; /* All device here */
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -0800365 struct list_head th_list;
David S. Milleree74baa2007-01-01 20:51:53 -0800366 struct task_struct *tsk;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800367 char result[512];
368 u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Luiz Capitulino222f1802006-03-20 22:16:13 -0800370 /* Field for thread to receive "posted" events terminate, stop ifs etc. */
371
372 u32 control;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 int pid;
374 int cpu;
375
Luiz Capitulino222f1802006-03-20 22:16:13 -0800376 wait_queue_head_t queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377};
378
379#define REMOVE 1
380#define FIND 0
381
382/* This code works around the fact that do_div cannot handle two 64-bit
383 numbers, and regular 64-bit division doesn't work on x86 kernels.
384 --Ben
385*/
386
387#define PG_DIV 0
388
389/* This was emailed to LMKL by: Chris Caputo <ccaputo@alt.net>
390 * Function copied/adapted/optimized from:
391 *
392 * nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html
393 *
394 * Copyright 1994, University of Cambridge Computer Laboratory
395 * All Rights Reserved.
396 *
397 */
Jesper Juhl77933d72005-07-27 11:46:09 -0700398static inline s64 divremdi3(s64 x, s64 y, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800400 u64 a = (x < 0) ? -x : x;
401 u64 b = (y < 0) ? -y : y;
402 u64 res = 0, d = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Luiz Capitulino222f1802006-03-20 22:16:13 -0800404 if (b > 0) {
405 while (b < a) {
406 b <<= 1;
407 d <<= 1;
408 }
409 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Luiz Capitulino222f1802006-03-20 22:16:13 -0800411 do {
412 if (a >= b) {
413 a -= b;
414 res += d;
415 }
416 b >>= 1;
417 d >>= 1;
418 }
419 while (d);
420
421 if (PG_DIV == type) {
422 return (((x ^ y) & (1ll << 63)) == 0) ? res : -(s64) res;
423 } else {
424 return ((x & (1ll << 63)) == 0) ? a : -(s64) a;
425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
428/* End of hacks to deal with 64-bit math on x86 */
429
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700430/** Convert to milliseconds */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800431static inline __u64 tv_to_ms(const struct timeval *tv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800433 __u64 ms = tv->tv_usec / 1000;
434 ms += (__u64) tv->tv_sec * (__u64) 1000;
435 return ms;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436}
437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438/** Convert to micro-seconds */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800439static inline __u64 tv_to_us(const struct timeval *tv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800441 __u64 us = tv->tv_usec;
442 us += (__u64) tv->tv_sec * (__u64) 1000000;
443 return us;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
Luiz Capitulino222f1802006-03-20 22:16:13 -0800446static inline __u64 pg_div(__u64 n, __u32 base)
447{
448 __u64 tmp = n;
449 do_div(tmp, base);
450 /* printk("pktgen: pg_div, n: %llu base: %d rv: %llu\n",
451 n, base, tmp); */
452 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453}
454
Luiz Capitulino222f1802006-03-20 22:16:13 -0800455static inline __u64 pg_div64(__u64 n, __u64 base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800457 __u64 tmp = n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/*
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700459 * How do we know if the architecture we are running on
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 * supports division with 64 bit base?
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900461 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800463#if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
Luiz Capitulino222f1802006-03-20 22:16:13 -0800465 do_div(tmp, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466#else
Luiz Capitulino222f1802006-03-20 22:16:13 -0800467 tmp = divremdi3(n, base, PG_DIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -0800469 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Luiz Capitulino222f1802006-03-20 22:16:13 -0800472static inline __u64 getCurMs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800474 struct timeval tv;
475 do_gettimeofday(&tv);
476 return tv_to_ms(&tv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478
Luiz Capitulino222f1802006-03-20 22:16:13 -0800479static inline __u64 getCurUs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800481 struct timeval tv;
482 do_gettimeofday(&tv);
483 return tv_to_us(&tv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
Luiz Capitulino222f1802006-03-20 22:16:13 -0800486static inline __u64 tv_diff(const struct timeval *a, const struct timeval *b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800488 return tv_to_us(a) - tv_to_us(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491/* old include end */
492
493static char version[] __initdata = VERSION;
494
Luiz Capitulino222f1802006-03-20 22:16:13 -0800495static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
496static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
497static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
498 const char *ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
500static void pktgen_run_all_threads(void);
501static void pktgen_stop_all_threads_ifs(void);
502static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800503static void pktgen_stop(struct pktgen_thread *t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
Stephen Hemminger39df2322007-03-04 16:11:51 -0800505
Luiz Capitulino222f1802006-03-20 22:16:13 -0800506static unsigned int scan_ip6(const char *s, char ip[16]);
507static unsigned int fmt_ip6(char *s, const char ip[16]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509/* Module parameters, defaults. */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800510static int pg_count_d = 1000; /* 1000 pkts by default */
Jaco Kroonf34fbb972005-12-22 12:51:46 -0800511static int pg_delay_d;
512static int pg_clone_skb_d;
513static int debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Luiz Capitulino222fa072006-03-20 22:24:27 -0800515static DEFINE_MUTEX(pktgen_thread_lock);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -0800516static LIST_HEAD(pktgen_threads);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518static struct notifier_block pktgen_notifier_block = {
519 .notifier_call = pktgen_device_event,
520};
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522/*
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900523 * /proc handling functions
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 *
525 */
526
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700527static int pgctrl_show(struct seq_file *seq, void *v)
Luiz Capitulino222f1802006-03-20 22:16:13 -0800528{
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700529 seq_puts(seq, VERSION);
530 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531}
532
Luiz Capitulino222f1802006-03-20 22:16:13 -0800533static ssize_t pgctrl_write(struct file *file, const char __user * buf,
534 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 int err = 0;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700537 char data[128];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Luiz Capitulino222f1802006-03-20 22:16:13 -0800539 if (!capable(CAP_NET_ADMIN)) {
540 err = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 goto out;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800542 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700544 if (count > sizeof(data))
545 count = sizeof(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (copy_from_user(data, buf, count)) {
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700548 err = -EFAULT;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700549 goto out;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800550 }
551 data[count - 1] = 0; /* Make string */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Luiz Capitulino222f1802006-03-20 22:16:13 -0800553 if (!strcmp(data, "stop"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 pktgen_stop_all_threads_ifs();
555
Luiz Capitulino222f1802006-03-20 22:16:13 -0800556 else if (!strcmp(data, "start"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 pktgen_run_all_threads();
558
Luiz Capitulino222f1802006-03-20 22:16:13 -0800559 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 printk("pktgen: Unknown command: %s\n", data);
561
562 err = count;
563
Luiz Capitulino222f1802006-03-20 22:16:13 -0800564out:
565 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700568static int pgctrl_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700570 return single_open(file, pgctrl_show, PDE(inode)->data);
571}
572
Arjan van de Ven9a321442007-02-12 00:55:35 -0800573static const struct file_operations pktgen_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800574 .owner = THIS_MODULE,
575 .open = pgctrl_open,
576 .read = seq_read,
577 .llseek = seq_lseek,
578 .write = pgctrl_write,
579 .release = single_release,
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700580};
581
582static int pktgen_if_show(struct seq_file *seq, void *v)
583{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 int i;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800585 struct pktgen_dev *pkt_dev = seq->private;
586 __u64 sa;
587 __u64 stopped;
588 __u64 now = getCurUs();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Luiz Capitulino222f1802006-03-20 22:16:13 -0800590 seq_printf(seq,
591 "Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
592 (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
593 pkt_dev->max_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Luiz Capitulino222f1802006-03-20 22:16:13 -0800595 seq_printf(seq,
596 " frags: %d delay: %u clone_skb: %d ifname: %s\n",
597 pkt_dev->nfrags,
598 1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
Stephen Hemminger39df2322007-03-04 16:11:51 -0800599 pkt_dev->clone_skb, pkt_dev->odev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Luiz Capitulino222f1802006-03-20 22:16:13 -0800601 seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows,
602 pkt_dev->lflow);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Luiz Capitulino222f1802006-03-20 22:16:13 -0800604 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 char b1[128], b2[128], b3[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800606 fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
607 fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
608 fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
609 seq_printf(seq,
610 " saddr: %s min_saddr: %s max_saddr: %s\n", b1,
611 b2, b3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
Luiz Capitulino222f1802006-03-20 22:16:13 -0800613 fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
614 fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
615 fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
616 seq_printf(seq,
617 " daddr: %s min_daddr: %s max_daddr: %s\n", b1,
618 b2, b3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
Luiz Capitulino222f1802006-03-20 22:16:13 -0800620 } else
621 seq_printf(seq,
622 " dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n",
623 pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min,
624 pkt_dev->src_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700626 seq_puts(seq, " src_mac: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Kris Katterjohnf404e9a2006-01-17 13:04:57 -0800628 if (is_zero_ether_addr(pkt_dev->src_mac))
Luiz Capitulino222f1802006-03-20 22:16:13 -0800629 for (i = 0; i < 6; i++)
630 seq_printf(seq, "%02X%s", pkt_dev->odev->dev_addr[i],
631 i == 5 ? " " : ":");
632 else
633 for (i = 0; i < 6; i++)
634 seq_printf(seq, "%02X%s", pkt_dev->src_mac[i],
635 i == 5 ? " " : ":");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Luiz Capitulino222f1802006-03-20 22:16:13 -0800637 seq_printf(seq, "dst_mac: ");
638 for (i = 0; i < 6; i++)
639 seq_printf(seq, "%02X%s", pkt_dev->dst_mac[i],
640 i == 5 ? "\n" : ":");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Luiz Capitulino222f1802006-03-20 22:16:13 -0800642 seq_printf(seq,
643 " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
644 pkt_dev->udp_src_min, pkt_dev->udp_src_max,
645 pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
Luiz Capitulino222f1802006-03-20 22:16:13 -0800647 seq_printf(seq,
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800648 " src_mac_count: %d dst_mac_count: %d\n",
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700649 pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800651 if (pkt_dev->nr_labels) {
652 unsigned i;
653 seq_printf(seq, " mpls: ");
Stephen Hemmingere71a4782007-04-10 20:10:33 -0700654 for (i = 0; i < pkt_dev->nr_labels; i++)
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800655 seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
656 i == pkt_dev->nr_labels-1 ? "\n" : ", ");
657 }
658
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700659 if (pkt_dev->vlan_id != 0xffff) {
660 seq_printf(seq, " vlan_id: %u vlan_p: %u vlan_cfi: %u\n",
661 pkt_dev->vlan_id, pkt_dev->vlan_p, pkt_dev->vlan_cfi);
662 }
663
664 if (pkt_dev->svlan_id != 0xffff) {
665 seq_printf(seq, " svlan_id: %u vlan_p: %u vlan_cfi: %u\n",
666 pkt_dev->svlan_id, pkt_dev->svlan_p, pkt_dev->svlan_cfi);
667 }
668
Francesco Fondelli1ca77682006-09-27 16:32:03 -0700669 if (pkt_dev->tos) {
670 seq_printf(seq, " tos: 0x%02x\n", pkt_dev->tos);
671 }
672
673 if (pkt_dev->traffic_class) {
674 seq_printf(seq, " traffic_class: 0x%02x\n", pkt_dev->traffic_class);
675 }
676
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800677 seq_printf(seq, " Flags: ");
678
Luiz Capitulino222f1802006-03-20 22:16:13 -0800679 if (pkt_dev->flags & F_IPV6)
680 seq_printf(seq, "IPV6 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
Luiz Capitulino222f1802006-03-20 22:16:13 -0800682 if (pkt_dev->flags & F_IPSRC_RND)
683 seq_printf(seq, "IPSRC_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Luiz Capitulino222f1802006-03-20 22:16:13 -0800685 if (pkt_dev->flags & F_IPDST_RND)
686 seq_printf(seq, "IPDST_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Luiz Capitulino222f1802006-03-20 22:16:13 -0800688 if (pkt_dev->flags & F_TXSIZE_RND)
689 seq_printf(seq, "TXSIZE_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Luiz Capitulino222f1802006-03-20 22:16:13 -0800691 if (pkt_dev->flags & F_UDPSRC_RND)
692 seq_printf(seq, "UDPSRC_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Luiz Capitulino222f1802006-03-20 22:16:13 -0800694 if (pkt_dev->flags & F_UDPDST_RND)
695 seq_printf(seq, "UDPDST_RND ");
696
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800697 if (pkt_dev->flags & F_MPLS_RND)
698 seq_printf(seq, "MPLS_RND ");
699
Jamal Hadi Salim007a5312007-07-02 22:40:36 -0700700 if (pkt_dev->cflows) {
701 if (pkt_dev->flags & F_FLOW_SEQ)
702 seq_printf(seq, "FLOW_SEQ "); /*in sequence flows*/
703 else
704 seq_printf(seq, "FLOW_RND ");
705 }
706
Luiz Capitulino222f1802006-03-20 22:16:13 -0800707 if (pkt_dev->flags & F_MACSRC_RND)
708 seq_printf(seq, "MACSRC_RND ");
709
710 if (pkt_dev->flags & F_MACDST_RND)
711 seq_printf(seq, "MACDST_RND ");
712
Francesco Fondelli34954dd2006-09-27 16:30:44 -0700713 if (pkt_dev->flags & F_VID_RND)
714 seq_printf(seq, "VID_RND ");
715
716 if (pkt_dev->flags & F_SVID_RND)
717 seq_printf(seq, "SVID_RND ");
718
Luiz Capitulino222f1802006-03-20 22:16:13 -0800719 seq_puts(seq, "\n");
720
721 sa = pkt_dev->started_at;
722 stopped = pkt_dev->stopped_at;
723 if (pkt_dev->running)
724 stopped = now; /* not really stopped, more like last-running-at */
725
726 seq_printf(seq,
727 "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n",
728 (unsigned long long)pkt_dev->sofar,
729 (unsigned long long)pkt_dev->errors, (unsigned long long)sa,
730 (unsigned long long)stopped,
731 (unsigned long long)pkt_dev->idle_acc);
732
733 seq_printf(seq,
734 " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700735 pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
736 pkt_dev->cur_src_mac_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Luiz Capitulino222f1802006-03-20 22:16:13 -0800738 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 char b1[128], b2[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800740 fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
741 fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
742 seq_printf(seq, " cur_saddr: %s cur_daddr: %s\n", b2, b1);
743 } else
744 seq_printf(seq, " cur_saddr: 0x%x cur_daddr: 0x%x\n",
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700745 pkt_dev->cur_saddr, pkt_dev->cur_daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Luiz Capitulino222f1802006-03-20 22:16:13 -0800747 seq_printf(seq, " cur_udp_dst: %d cur_udp_src: %d\n",
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700748 pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Luiz Capitulino222f1802006-03-20 22:16:13 -0800750 seq_printf(seq, " flows: %u\n", pkt_dev->nflows);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752 if (pkt_dev->result[0])
Luiz Capitulino222f1802006-03-20 22:16:13 -0800753 seq_printf(seq, "Result: %s\n", pkt_dev->result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 else
Luiz Capitulino222f1802006-03-20 22:16:13 -0800755 seq_printf(seq, "Result: Idle\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700757 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758}
759
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800760
Francesco Fondelli1ca77682006-09-27 16:32:03 -0700761static int hex32_arg(const char __user *user_buffer, unsigned long maxlen, __u32 *num)
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800762{
763 int i = 0;
764 *num = 0;
765
Stephen Hemmingere71a4782007-04-10 20:10:33 -0700766 for (; i < maxlen; i++) {
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800767 char c;
768 *num <<= 4;
769 if (get_user(c, &user_buffer[i]))
770 return -EFAULT;
771 if ((c >= '0') && (c <= '9'))
772 *num |= c - '0';
773 else if ((c >= 'a') && (c <= 'f'))
774 *num |= c - 'a' + 10;
775 else if ((c >= 'A') && (c <= 'F'))
776 *num |= c - 'A' + 10;
777 else
778 break;
779 }
780 return i;
781}
782
Luiz Capitulino222f1802006-03-20 22:16:13 -0800783static int count_trail_chars(const char __user * user_buffer,
784 unsigned int maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
786 int i;
787
788 for (i = 0; i < maxlen; i++) {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800789 char c;
790 if (get_user(c, &user_buffer[i]))
791 return -EFAULT;
792 switch (c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 case '\"':
794 case '\n':
795 case '\r':
796 case '\t':
797 case ' ':
798 case '=':
799 break;
800 default:
801 goto done;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 }
804done:
805 return i;
806}
807
Luiz Capitulino222f1802006-03-20 22:16:13 -0800808static unsigned long num_arg(const char __user * user_buffer,
809 unsigned long maxlen, unsigned long *num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
811 int i = 0;
812 *num = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800813
814 for (; i < maxlen; i++) {
815 char c;
816 if (get_user(c, &user_buffer[i]))
817 return -EFAULT;
818 if ((c >= '0') && (c <= '9')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 *num *= 10;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800820 *num += c - '0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 } else
822 break;
823 }
824 return i;
825}
826
Luiz Capitulino222f1802006-03-20 22:16:13 -0800827static int strn_len(const char __user * user_buffer, unsigned int maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
829 int i = 0;
830
Luiz Capitulino222f1802006-03-20 22:16:13 -0800831 for (; i < maxlen; i++) {
832 char c;
833 if (get_user(c, &user_buffer[i]))
834 return -EFAULT;
835 switch (c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 case '\"':
837 case '\n':
838 case '\r':
839 case '\t':
840 case ' ':
841 goto done_str;
842 break;
843 default:
844 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -0700845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 }
847done_str:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 return i;
849}
850
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800851static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
852{
853 unsigned n = 0;
854 char c;
855 ssize_t i = 0;
856 int len;
857
858 pkt_dev->nr_labels = 0;
859 do {
860 __u32 tmp;
Francesco Fondelli1ca77682006-09-27 16:32:03 -0700861 len = hex32_arg(&buffer[i], 8, &tmp);
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800862 if (len <= 0)
863 return len;
864 pkt_dev->labels[n] = htonl(tmp);
865 if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
866 pkt_dev->flags |= F_MPLS_RND;
867 i += len;
868 if (get_user(c, &buffer[i]))
869 return -EFAULT;
870 i++;
871 n++;
872 if (n >= MAX_MPLS_LABELS)
873 return -E2BIG;
Stephen Hemmingere71a4782007-04-10 20:10:33 -0700874 } while (c == ',');
Steven Whitehouseca6549a2006-03-23 01:10:26 -0800875
876 pkt_dev->nr_labels = n;
877 return i;
878}
879
Luiz Capitulino222f1802006-03-20 22:16:13 -0800880static ssize_t pktgen_if_write(struct file *file,
881 const char __user * user_buffer, size_t count,
882 loff_t * offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800884 struct seq_file *seq = (struct seq_file *)file->private_data;
885 struct pktgen_dev *pkt_dev = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 int i = 0, max, len;
887 char name[16], valstr[32];
888 unsigned long value = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800889 char *pg_result = NULL;
890 int tmp = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 char buf[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800892
893 pg_result = &(pkt_dev->result[0]);
894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 if (count < 1) {
896 printk("pktgen: wrong command format\n");
897 return -EINVAL;
898 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 max = count - i;
901 tmp = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800902 if (tmp < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 printk("pktgen: illegal format\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -0800904 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800906 i += tmp;
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 /* Read variable name */
909
910 len = strn_len(&user_buffer[i], sizeof(name) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800911 if (len < 0) {
912 return len;
913 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 memset(name, 0, sizeof(name));
Luiz Capitulino222f1802006-03-20 22:16:13 -0800915 if (copy_from_user(name, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return -EFAULT;
917 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800918
919 max = count - i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 len = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800921 if (len < 0)
922 return len;
923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 i += len;
925
926 if (debug) {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800927 char tb[count + 1];
928 if (copy_from_user(tb, user_buffer, count))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800930 tb[count] = 0;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -0700931 printk("pktgen: %s,%lu buffer -:%s:-\n", name,
Luiz Capitulino222f1802006-03-20 22:16:13 -0800932 (unsigned long)count, tb);
933 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 if (!strcmp(name, "min_pkt_size")) {
936 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800937 if (len < 0) {
938 return len;
939 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800941 if (value < 14 + 20 + 8)
942 value = 14 + 20 + 8;
943 if (value != pkt_dev->min_pkt_size) {
944 pkt_dev->min_pkt_size = value;
945 pkt_dev->cur_pkt_size = value;
946 }
947 sprintf(pg_result, "OK: min_pkt_size=%u",
948 pkt_dev->min_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 return count;
950 }
951
Luiz Capitulino222f1802006-03-20 22:16:13 -0800952 if (!strcmp(name, "max_pkt_size")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800954 if (len < 0) {
955 return len;
956 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800958 if (value < 14 + 20 + 8)
959 value = 14 + 20 + 8;
960 if (value != pkt_dev->max_pkt_size) {
961 pkt_dev->max_pkt_size = value;
962 pkt_dev->cur_pkt_size = value;
963 }
964 sprintf(pg_result, "OK: max_pkt_size=%u",
965 pkt_dev->max_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 return count;
967 }
968
Luiz Capitulino222f1802006-03-20 22:16:13 -0800969 /* Shortcut for min = max */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 if (!strcmp(name, "pkt_size")) {
972 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800973 if (len < 0) {
974 return len;
975 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800977 if (value < 14 + 20 + 8)
978 value = 14 + 20 + 8;
979 if (value != pkt_dev->min_pkt_size) {
980 pkt_dev->min_pkt_size = value;
981 pkt_dev->max_pkt_size = value;
982 pkt_dev->cur_pkt_size = value;
983 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
985 return count;
986 }
987
Luiz Capitulino222f1802006-03-20 22:16:13 -0800988 if (!strcmp(name, "debug")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800990 if (len < 0) {
991 return len;
992 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800994 debug = value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 sprintf(pg_result, "OK: debug=%u", debug);
996 return count;
997 }
998
Luiz Capitulino222f1802006-03-20 22:16:13 -0800999 if (!strcmp(name, "frags")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001001 if (len < 0) {
1002 return len;
1003 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 i += len;
1005 pkt_dev->nfrags = value;
1006 sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
1007 return count;
1008 }
1009 if (!strcmp(name, "delay")) {
1010 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001011 if (len < 0) {
1012 return len;
1013 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 i += len;
1015 if (value == 0x7FFFFFFF) {
1016 pkt_dev->delay_us = 0x7FFFFFFF;
1017 pkt_dev->delay_ns = 0;
1018 } else {
1019 pkt_dev->delay_us = value / 1000;
1020 pkt_dev->delay_ns = value % 1000;
1021 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001022 sprintf(pg_result, "OK: delay=%u",
1023 1000 * pkt_dev->delay_us + pkt_dev->delay_ns);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 return count;
1025 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001026 if (!strcmp(name, "udp_src_min")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001028 if (len < 0) {
1029 return len;
1030 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001032 if (value != pkt_dev->udp_src_min) {
1033 pkt_dev->udp_src_min = value;
1034 pkt_dev->cur_udp_src = value;
1035 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
1037 return count;
1038 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001039 if (!strcmp(name, "udp_dst_min")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001041 if (len < 0) {
1042 return len;
1043 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001045 if (value != pkt_dev->udp_dst_min) {
1046 pkt_dev->udp_dst_min = value;
1047 pkt_dev->cur_udp_dst = value;
1048 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
1050 return count;
1051 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001052 if (!strcmp(name, "udp_src_max")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001054 if (len < 0) {
1055 return len;
1056 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001058 if (value != pkt_dev->udp_src_max) {
1059 pkt_dev->udp_src_max = value;
1060 pkt_dev->cur_udp_src = value;
1061 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
1063 return count;
1064 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001065 if (!strcmp(name, "udp_dst_max")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001067 if (len < 0) {
1068 return len;
1069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001071 if (value != pkt_dev->udp_dst_max) {
1072 pkt_dev->udp_dst_max = value;
1073 pkt_dev->cur_udp_dst = value;
1074 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
1076 return count;
1077 }
1078 if (!strcmp(name, "clone_skb")) {
1079 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001080 if (len < 0) {
1081 return len;
1082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001084 pkt_dev->clone_skb = value;
1085
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
1087 return count;
1088 }
1089 if (!strcmp(name, "count")) {
1090 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001091 if (len < 0) {
1092 return len;
1093 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 i += len;
1095 pkt_dev->count = value;
1096 sprintf(pg_result, "OK: count=%llu",
Luiz Capitulino222f1802006-03-20 22:16:13 -08001097 (unsigned long long)pkt_dev->count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 return count;
1099 }
1100 if (!strcmp(name, "src_mac_count")) {
1101 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001102 if (len < 0) {
1103 return len;
1104 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 i += len;
1106 if (pkt_dev->src_mac_count != value) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001107 pkt_dev->src_mac_count = value;
1108 pkt_dev->cur_src_mac_offset = 0;
1109 }
1110 sprintf(pg_result, "OK: src_mac_count=%d",
1111 pkt_dev->src_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 return count;
1113 }
1114 if (!strcmp(name, "dst_mac_count")) {
1115 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001116 if (len < 0) {
1117 return len;
1118 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 i += len;
1120 if (pkt_dev->dst_mac_count != value) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001121 pkt_dev->dst_mac_count = value;
1122 pkt_dev->cur_dst_mac_offset = 0;
1123 }
1124 sprintf(pg_result, "OK: dst_mac_count=%d",
1125 pkt_dev->dst_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 return count;
1127 }
1128 if (!strcmp(name, "flag")) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001129 char f[32];
1130 memset(f, 0, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 len = strn_len(&user_buffer[i], sizeof(f) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001132 if (len < 0) {
1133 return len;
1134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 if (copy_from_user(f, &user_buffer[i], len))
1136 return -EFAULT;
1137 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001138 if (strcmp(f, "IPSRC_RND") == 0)
1139 pkt_dev->flags |= F_IPSRC_RND;
1140
1141 else if (strcmp(f, "!IPSRC_RND") == 0)
1142 pkt_dev->flags &= ~F_IPSRC_RND;
1143
1144 else if (strcmp(f, "TXSIZE_RND") == 0)
1145 pkt_dev->flags |= F_TXSIZE_RND;
1146
1147 else if (strcmp(f, "!TXSIZE_RND") == 0)
1148 pkt_dev->flags &= ~F_TXSIZE_RND;
1149
1150 else if (strcmp(f, "IPDST_RND") == 0)
1151 pkt_dev->flags |= F_IPDST_RND;
1152
1153 else if (strcmp(f, "!IPDST_RND") == 0)
1154 pkt_dev->flags &= ~F_IPDST_RND;
1155
1156 else if (strcmp(f, "UDPSRC_RND") == 0)
1157 pkt_dev->flags |= F_UDPSRC_RND;
1158
1159 else if (strcmp(f, "!UDPSRC_RND") == 0)
1160 pkt_dev->flags &= ~F_UDPSRC_RND;
1161
1162 else if (strcmp(f, "UDPDST_RND") == 0)
1163 pkt_dev->flags |= F_UDPDST_RND;
1164
1165 else if (strcmp(f, "!UDPDST_RND") == 0)
1166 pkt_dev->flags &= ~F_UDPDST_RND;
1167
1168 else if (strcmp(f, "MACSRC_RND") == 0)
1169 pkt_dev->flags |= F_MACSRC_RND;
1170
1171 else if (strcmp(f, "!MACSRC_RND") == 0)
1172 pkt_dev->flags &= ~F_MACSRC_RND;
1173
1174 else if (strcmp(f, "MACDST_RND") == 0)
1175 pkt_dev->flags |= F_MACDST_RND;
1176
1177 else if (strcmp(f, "!MACDST_RND") == 0)
1178 pkt_dev->flags &= ~F_MACDST_RND;
1179
Steven Whitehouseca6549a2006-03-23 01:10:26 -08001180 else if (strcmp(f, "MPLS_RND") == 0)
1181 pkt_dev->flags |= F_MPLS_RND;
1182
1183 else if (strcmp(f, "!MPLS_RND") == 0)
1184 pkt_dev->flags &= ~F_MPLS_RND;
1185
Francesco Fondelli34954dd2006-09-27 16:30:44 -07001186 else if (strcmp(f, "VID_RND") == 0)
1187 pkt_dev->flags |= F_VID_RND;
1188
1189 else if (strcmp(f, "!VID_RND") == 0)
1190 pkt_dev->flags &= ~F_VID_RND;
1191
1192 else if (strcmp(f, "SVID_RND") == 0)
1193 pkt_dev->flags |= F_SVID_RND;
1194
1195 else if (strcmp(f, "!SVID_RND") == 0)
1196 pkt_dev->flags &= ~F_SVID_RND;
1197
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07001198 else if (strcmp(f, "FLOW_SEQ") == 0)
1199 pkt_dev->flags |= F_FLOW_SEQ;
1200
Francesco Fondelli1ca77682006-09-27 16:32:03 -07001201 else if (strcmp(f, "!IPV6") == 0)
1202 pkt_dev->flags &= ~F_IPV6;
1203
Luiz Capitulino222f1802006-03-20 22:16:13 -08001204 else {
1205 sprintf(pg_result,
1206 "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
1207 f,
Francesco Fondelli1ca77682006-09-27 16:32:03 -07001208 "IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07001209 "MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -08001210 return count;
1211 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
1213 return count;
1214 }
1215 if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
1216 len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001217 if (len < 0) {
1218 return len;
1219 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
Luiz Capitulino222f1802006-03-20 22:16:13 -08001221 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001223 buf[len] = 0;
1224 if (strcmp(buf, pkt_dev->dst_min) != 0) {
1225 memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
1226 strncpy(pkt_dev->dst_min, buf, len);
1227 pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
1228 pkt_dev->cur_daddr = pkt_dev->daddr_min;
1229 }
1230 if (debug)
1231 printk("pktgen: dst_min set to: %s\n",
1232 pkt_dev->dst_min);
1233 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
1235 return count;
1236 }
1237 if (!strcmp(name, "dst_max")) {
1238 len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001239 if (len < 0) {
1240 return len;
1241 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
Luiz Capitulino222f1802006-03-20 22:16:13 -08001243 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 return -EFAULT;
1245
Luiz Capitulino222f1802006-03-20 22:16:13 -08001246 buf[len] = 0;
1247 if (strcmp(buf, pkt_dev->dst_max) != 0) {
1248 memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
1249 strncpy(pkt_dev->dst_max, buf, len);
1250 pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
1251 pkt_dev->cur_daddr = pkt_dev->daddr_max;
1252 }
1253 if (debug)
1254 printk("pktgen: dst_max set to: %s\n",
1255 pkt_dev->dst_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 i += len;
1257 sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
1258 return count;
1259 }
1260 if (!strcmp(name, "dst6")) {
1261 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001262 if (len < 0)
1263 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
1265 pkt_dev->flags |= F_IPV6;
1266
Luiz Capitulino222f1802006-03-20 22:16:13 -08001267 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001269 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
1271 scan_ip6(buf, pkt_dev->in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001272 fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr);
1275
Luiz Capitulino222f1802006-03-20 22:16:13 -08001276 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 printk("pktgen: dst6 set to: %s\n", buf);
1278
Luiz Capitulino222f1802006-03-20 22:16:13 -08001279 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 sprintf(pg_result, "OK: dst6=%s", buf);
1281 return count;
1282 }
1283 if (!strcmp(name, "dst6_min")) {
1284 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001285 if (len < 0)
1286 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
1288 pkt_dev->flags |= F_IPV6;
1289
Luiz Capitulino222f1802006-03-20 22:16:13 -08001290 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001292 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
1294 scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001295 fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
Luiz Capitulino222f1802006-03-20 22:16:13 -08001297 ipv6_addr_copy(&pkt_dev->cur_in6_daddr,
1298 &pkt_dev->min_in6_daddr);
1299 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 printk("pktgen: dst6_min set to: %s\n", buf);
1301
Luiz Capitulino222f1802006-03-20 22:16:13 -08001302 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 sprintf(pg_result, "OK: dst6_min=%s", buf);
1304 return count;
1305 }
1306 if (!strcmp(name, "dst6_max")) {
1307 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001308 if (len < 0)
1309 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
1311 pkt_dev->flags |= F_IPV6;
1312
Luiz Capitulino222f1802006-03-20 22:16:13 -08001313 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001315 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
1317 scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001318 fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
Luiz Capitulino222f1802006-03-20 22:16:13 -08001320 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 printk("pktgen: dst6_max set to: %s\n", buf);
1322
Luiz Capitulino222f1802006-03-20 22:16:13 -08001323 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 sprintf(pg_result, "OK: dst6_max=%s", buf);
1325 return count;
1326 }
1327 if (!strcmp(name, "src6")) {
1328 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001329 if (len < 0)
1330 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
1332 pkt_dev->flags |= F_IPV6;
1333
Luiz Capitulino222f1802006-03-20 22:16:13 -08001334 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001336 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 scan_ip6(buf, pkt_dev->in6_saddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001339 fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
1341 ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr);
1342
Luiz Capitulino222f1802006-03-20 22:16:13 -08001343 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 printk("pktgen: src6 set to: %s\n", buf);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001345
1346 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 sprintf(pg_result, "OK: src6=%s", buf);
1348 return count;
1349 }
1350 if (!strcmp(name, "src_min")) {
1351 len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001352 if (len < 0) {
1353 return len;
1354 }
1355 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001357 buf[len] = 0;
1358 if (strcmp(buf, pkt_dev->src_min) != 0) {
1359 memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
1360 strncpy(pkt_dev->src_min, buf, len);
1361 pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
1362 pkt_dev->cur_saddr = pkt_dev->saddr_min;
1363 }
1364 if (debug)
1365 printk("pktgen: src_min set to: %s\n",
1366 pkt_dev->src_min);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 i += len;
1368 sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
1369 return count;
1370 }
1371 if (!strcmp(name, "src_max")) {
1372 len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001373 if (len < 0) {
1374 return len;
1375 }
1376 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001378 buf[len] = 0;
1379 if (strcmp(buf, pkt_dev->src_max) != 0) {
1380 memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
1381 strncpy(pkt_dev->src_max, buf, len);
1382 pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
1383 pkt_dev->cur_saddr = pkt_dev->saddr_max;
1384 }
1385 if (debug)
1386 printk("pktgen: src_max set to: %s\n",
1387 pkt_dev->src_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 i += len;
1389 sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
1390 return count;
1391 }
1392 if (!strcmp(name, "dst_mac")) {
1393 char *v = valstr;
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001394 unsigned char old_dmac[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 unsigned char *m = pkt_dev->dst_mac;
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001396 memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001397
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001399 if (len < 0) {
1400 return len;
1401 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 memset(valstr, 0, sizeof(valstr));
Luiz Capitulino222f1802006-03-20 22:16:13 -08001403 if (copy_from_user(valstr, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 return -EFAULT;
1405 i += len;
1406
Luiz Capitulino222f1802006-03-20 22:16:13 -08001407 for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 if (*v >= '0' && *v <= '9') {
1409 *m *= 16;
1410 *m += *v - '0';
1411 }
1412 if (*v >= 'A' && *v <= 'F') {
1413 *m *= 16;
1414 *m += *v - 'A' + 10;
1415 }
1416 if (*v >= 'a' && *v <= 'f') {
1417 *m *= 16;
1418 *m += *v - 'a' + 10;
1419 }
1420 if (*v == ':') {
1421 m++;
1422 *m = 0;
1423 }
1424 }
1425
1426 /* Set up Dest MAC */
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001427 if (compare_ether_addr(old_dmac, pkt_dev->dst_mac))
1428 memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001429
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 sprintf(pg_result, "OK: dstmac");
1431 return count;
1432 }
1433 if (!strcmp(name, "src_mac")) {
1434 char *v = valstr;
1435 unsigned char *m = pkt_dev->src_mac;
1436
1437 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001438 if (len < 0) {
1439 return len;
1440 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 memset(valstr, 0, sizeof(valstr));
Luiz Capitulino222f1802006-03-20 22:16:13 -08001442 if (copy_from_user(valstr, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 return -EFAULT;
1444 i += len;
1445
Luiz Capitulino222f1802006-03-20 22:16:13 -08001446 for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 if (*v >= '0' && *v <= '9') {
1448 *m *= 16;
1449 *m += *v - '0';
1450 }
1451 if (*v >= 'A' && *v <= 'F') {
1452 *m *= 16;
1453 *m += *v - 'A' + 10;
1454 }
1455 if (*v >= 'a' && *v <= 'f') {
1456 *m *= 16;
1457 *m += *v - 'a' + 10;
1458 }
1459 if (*v == ':') {
1460 m++;
1461 *m = 0;
1462 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001463 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Luiz Capitulino222f1802006-03-20 22:16:13 -08001465 sprintf(pg_result, "OK: srcmac");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 return count;
1467 }
1468
Luiz Capitulino222f1802006-03-20 22:16:13 -08001469 if (!strcmp(name, "clear_counters")) {
1470 pktgen_clear_counters(pkt_dev);
1471 sprintf(pg_result, "OK: Clearing counters.\n");
1472 return count;
1473 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
1475 if (!strcmp(name, "flows")) {
1476 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001477 if (len < 0) {
1478 return len;
1479 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 i += len;
1481 if (value > MAX_CFLOWS)
1482 value = MAX_CFLOWS;
1483
1484 pkt_dev->cflows = value;
1485 sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
1486 return count;
1487 }
1488
1489 if (!strcmp(name, "flowlen")) {
1490 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001491 if (len < 0) {
1492 return len;
1493 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 i += len;
1495 pkt_dev->lflow = value;
1496 sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
1497 return count;
1498 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001499
Steven Whitehouseca6549a2006-03-23 01:10:26 -08001500 if (!strcmp(name, "mpls")) {
1501 unsigned n, offset;
1502 len = get_labels(&user_buffer[i], pkt_dev);
1503 if (len < 0) { return len; }
1504 i += len;
1505 offset = sprintf(pg_result, "OK: mpls=");
Stephen Hemmingere71a4782007-04-10 20:10:33 -07001506 for (n = 0; n < pkt_dev->nr_labels; n++)
Steven Whitehouseca6549a2006-03-23 01:10:26 -08001507 offset += sprintf(pg_result + offset,
1508 "%08x%s", ntohl(pkt_dev->labels[n]),
1509 n == pkt_dev->nr_labels-1 ? "" : ",");
Francesco Fondelli34954dd2006-09-27 16:30:44 -07001510
1511 if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
1512 pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
1513 pkt_dev->svlan_id = 0xffff;
1514
1515 if (debug)
1516 printk("pktgen: VLAN/SVLAN auto turned off\n");
1517 }
1518 return count;
1519 }
1520
1521 if (!strcmp(name, "vlan_id")) {
1522 len = num_arg(&user_buffer[i], 4, &value);
1523 if (len < 0) {
1524 return len;
1525 }
1526 i += len;
1527 if (value <= 4095) {
1528 pkt_dev->vlan_id = value; /* turn on VLAN */
1529
1530 if (debug)
1531 printk("pktgen: VLAN turned on\n");
1532
1533 if (debug && pkt_dev->nr_labels)
1534 printk("pktgen: MPLS auto turned off\n");
1535
1536 pkt_dev->nr_labels = 0; /* turn off MPLS */
1537 sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
1538 } else {
1539 pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
1540 pkt_dev->svlan_id = 0xffff;
1541
1542 if (debug)
1543 printk("pktgen: VLAN/SVLAN turned off\n");
1544 }
1545 return count;
1546 }
1547
1548 if (!strcmp(name, "vlan_p")) {
1549 len = num_arg(&user_buffer[i], 1, &value);
1550 if (len < 0) {
1551 return len;
1552 }
1553 i += len;
1554 if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
1555 pkt_dev->vlan_p = value;
1556 sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
1557 } else {
1558 sprintf(pg_result, "ERROR: vlan_p must be 0-7");
1559 }
1560 return count;
1561 }
1562
1563 if (!strcmp(name, "vlan_cfi")) {
1564 len = num_arg(&user_buffer[i], 1, &value);
1565 if (len < 0) {
1566 return len;
1567 }
1568 i += len;
1569 if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
1570 pkt_dev->vlan_cfi = value;
1571 sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
1572 } else {
1573 sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
1574 }
1575 return count;
1576 }
1577
1578 if (!strcmp(name, "svlan_id")) {
1579 len = num_arg(&user_buffer[i], 4, &value);
1580 if (len < 0) {
1581 return len;
1582 }
1583 i += len;
1584 if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
1585 pkt_dev->svlan_id = value; /* turn on SVLAN */
1586
1587 if (debug)
1588 printk("pktgen: SVLAN turned on\n");
1589
1590 if (debug && pkt_dev->nr_labels)
1591 printk("pktgen: MPLS auto turned off\n");
1592
1593 pkt_dev->nr_labels = 0; /* turn off MPLS */
1594 sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
1595 } else {
1596 pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
1597 pkt_dev->svlan_id = 0xffff;
1598
1599 if (debug)
1600 printk("pktgen: VLAN/SVLAN turned off\n");
1601 }
1602 return count;
1603 }
1604
1605 if (!strcmp(name, "svlan_p")) {
1606 len = num_arg(&user_buffer[i], 1, &value);
1607 if (len < 0) {
1608 return len;
1609 }
1610 i += len;
1611 if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
1612 pkt_dev->svlan_p = value;
1613 sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
1614 } else {
1615 sprintf(pg_result, "ERROR: svlan_p must be 0-7");
1616 }
1617 return count;
1618 }
1619
1620 if (!strcmp(name, "svlan_cfi")) {
1621 len = num_arg(&user_buffer[i], 1, &value);
1622 if (len < 0) {
1623 return len;
1624 }
1625 i += len;
1626 if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
1627 pkt_dev->svlan_cfi = value;
1628 sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
1629 } else {
1630 sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
1631 }
Steven Whitehouseca6549a2006-03-23 01:10:26 -08001632 return count;
1633 }
1634
Francesco Fondelli1ca77682006-09-27 16:32:03 -07001635 if (!strcmp(name, "tos")) {
1636 __u32 tmp_value = 0;
1637 len = hex32_arg(&user_buffer[i], 2, &tmp_value);
1638 if (len < 0) {
1639 return len;
1640 }
1641 i += len;
1642 if (len == 2) {
1643 pkt_dev->tos = tmp_value;
1644 sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
1645 } else {
1646 sprintf(pg_result, "ERROR: tos must be 00-ff");
1647 }
1648 return count;
1649 }
1650
1651 if (!strcmp(name, "traffic_class")) {
1652 __u32 tmp_value = 0;
1653 len = hex32_arg(&user_buffer[i], 2, &tmp_value);
1654 if (len < 0) {
1655 return len;
1656 }
1657 i += len;
1658 if (len == 2) {
1659 pkt_dev->traffic_class = tmp_value;
1660 sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
1661 } else {
1662 sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
1663 }
1664 return count;
1665 }
1666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
1668 return -EINVAL;
1669}
1670
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001671static int pktgen_if_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672{
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001673 return single_open(file, pktgen_if_show, PDE(inode)->data);
1674}
1675
Arjan van de Ven9a321442007-02-12 00:55:35 -08001676static const struct file_operations pktgen_if_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001677 .owner = THIS_MODULE,
1678 .open = pktgen_if_open,
1679 .read = seq_read,
1680 .llseek = seq_lseek,
1681 .write = pktgen_if_write,
1682 .release = single_release,
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001683};
1684
1685static int pktgen_thread_show(struct seq_file *seq, void *v)
1686{
Luiz Capitulino222f1802006-03-20 22:16:13 -08001687 struct pktgen_thread *t = seq->private;
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08001688 struct pktgen_dev *pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001690 BUG_ON(!t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001692 seq_printf(seq, "Name: %s max_before_softirq: %d\n",
David S. Milleree74baa2007-01-01 20:51:53 -08001693 t->tsk->comm, t->max_before_softirq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
Luiz Capitulino222f1802006-03-20 22:16:13 -08001695 seq_printf(seq, "Running: ");
1696
1697 if_lock(t);
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08001698 list_for_each_entry(pkt_dev, &t->if_list, list)
Luiz Capitulino222f1802006-03-20 22:16:13 -08001699 if (pkt_dev->running)
Stephen Hemminger39df2322007-03-04 16:11:51 -08001700 seq_printf(seq, "%s ", pkt_dev->odev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
Luiz Capitulino222f1802006-03-20 22:16:13 -08001702 seq_printf(seq, "\nStopped: ");
1703
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08001704 list_for_each_entry(pkt_dev, &t->if_list, list)
Luiz Capitulino222f1802006-03-20 22:16:13 -08001705 if (!pkt_dev->running)
Stephen Hemminger39df2322007-03-04 16:11:51 -08001706 seq_printf(seq, "%s ", pkt_dev->odev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
1708 if (t->result[0])
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001709 seq_printf(seq, "\nResult: %s\n", t->result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 else
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001711 seq_printf(seq, "\nResult: NA\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712
Luiz Capitulino222f1802006-03-20 22:16:13 -08001713 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001715 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716}
1717
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001718static ssize_t pktgen_thread_write(struct file *file,
Luiz Capitulino222f1802006-03-20 22:16:13 -08001719 const char __user * user_buffer,
1720 size_t count, loff_t * offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721{
Luiz Capitulino222f1802006-03-20 22:16:13 -08001722 struct seq_file *seq = (struct seq_file *)file->private_data;
1723 struct pktgen_thread *t = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 int i = 0, max, len, ret;
1725 char name[40];
Luiz Capitulino222f1802006-03-20 22:16:13 -08001726 char *pg_result;
1727 unsigned long value = 0;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001728
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 if (count < 1) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001730 // sprintf(pg_result, "Wrong command format");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 return -EINVAL;
1732 }
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001733
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 max = count - i;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001735 len = count_trail_chars(&user_buffer[i], max);
1736 if (len < 0)
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001737 return len;
1738
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 i += len;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001740
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 /* Read variable name */
1742
1743 len = strn_len(&user_buffer[i], sizeof(name) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001744 if (len < 0)
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001745 return len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001746
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 memset(name, 0, sizeof(name));
1748 if (copy_from_user(name, &user_buffer[i], len))
1749 return -EFAULT;
1750 i += len;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001751
Luiz Capitulino222f1802006-03-20 22:16:13 -08001752 max = count - i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 len = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001754 if (len < 0)
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001755 return len;
1756
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757 i += len;
1758
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001759 if (debug)
Luiz Capitulino222f1802006-03-20 22:16:13 -08001760 printk("pktgen: t=%s, count=%lu\n", name, (unsigned long)count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
Luiz Capitulino222f1802006-03-20 22:16:13 -08001762 if (!t) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 printk("pktgen: ERROR: No thread\n");
1764 ret = -EINVAL;
1765 goto out;
1766 }
1767
1768 pg_result = &(t->result[0]);
1769
Luiz Capitulino222f1802006-03-20 22:16:13 -08001770 if (!strcmp(name, "add_device")) {
1771 char f[32];
1772 memset(f, 0, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 len = strn_len(&user_buffer[i], sizeof(f) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001774 if (len < 0) {
1775 ret = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 goto out;
1777 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001778 if (copy_from_user(f, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 return -EFAULT;
1780 i += len;
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001781 mutex_lock(&pktgen_thread_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001782 pktgen_add_device(t, f);
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001783 mutex_unlock(&pktgen_thread_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001784 ret = count;
1785 sprintf(pg_result, "OK: add_device=%s", f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 goto out;
1787 }
1788
Luiz Capitulino222f1802006-03-20 22:16:13 -08001789 if (!strcmp(name, "rem_device_all")) {
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001790 mutex_lock(&pktgen_thread_lock);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001791 t->control |= T_REMDEVALL;
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001792 mutex_unlock(&pktgen_thread_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001793 schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 ret = count;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001795 sprintf(pg_result, "OK: rem_device_all");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 goto out;
1797 }
1798
Luiz Capitulino222f1802006-03-20 22:16:13 -08001799 if (!strcmp(name, "max_before_softirq")) {
1800 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001801 mutex_lock(&pktgen_thread_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001802 t->max_before_softirq = value;
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001803 mutex_unlock(&pktgen_thread_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001804 ret = count;
1805 sprintf(pg_result, "OK: max_before_softirq=%lu", value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 goto out;
1807 }
1808
1809 ret = -EINVAL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001810out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 return ret;
1812}
1813
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001814static int pktgen_thread_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815{
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001816 return single_open(file, pktgen_thread_show, PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817}
1818
Arjan van de Ven9a321442007-02-12 00:55:35 -08001819static const struct file_operations pktgen_thread_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001820 .owner = THIS_MODULE,
1821 .open = pktgen_thread_open,
1822 .read = seq_read,
1823 .llseek = seq_lseek,
1824 .write = pktgen_thread_write,
1825 .release = single_release,
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07001826};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827
1828/* Think find or remove for NN */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001829static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830{
1831 struct pktgen_thread *t;
1832 struct pktgen_dev *pkt_dev = NULL;
1833
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08001834 list_for_each_entry(t, &pktgen_threads, th_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 pkt_dev = pktgen_find_dev(t, ifname);
1836 if (pkt_dev) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001837 if (remove) {
1838 if_lock(t);
1839 pkt_dev->removal_mark = 1;
1840 t->control |= T_REMDEV;
1841 if_unlock(t);
1842 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 break;
1844 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001846 return pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847}
1848
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001849/*
1850 * mark a device for removal
1851 */
Stephen Hemminger39df2322007-03-04 16:11:51 -08001852static void pktgen_mark_device(const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853{
1854 struct pktgen_dev *pkt_dev = NULL;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001855 const int max_tries = 10, msec_per_try = 125;
1856 int i = 0;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001857
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001858 mutex_lock(&pktgen_thread_lock);
Stephen Hemminger25c4e532007-03-04 16:06:47 -08001859 pr_debug("pktgen: pktgen_mark_device marking %s for removal\n", ifname);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001860
Luiz Capitulino222f1802006-03-20 22:16:13 -08001861 while (1) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001862
1863 pkt_dev = __pktgen_NN_threads(ifname, REMOVE);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001864 if (pkt_dev == NULL)
1865 break; /* success */
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001866
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001867 mutex_unlock(&pktgen_thread_lock);
Stephen Hemminger25c4e532007-03-04 16:06:47 -08001868 pr_debug("pktgen: pktgen_mark_device waiting for %s "
1869 "to disappear....\n", ifname);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001870 schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001871 mutex_lock(&pktgen_thread_lock);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001872
1873 if (++i >= max_tries) {
1874 printk("pktgen_mark_device: timed out after waiting "
Luiz Capitulino222f1802006-03-20 22:16:13 -08001875 "%d msec for device %s to be removed\n",
1876 msec_per_try * i, ifname);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001877 break;
1878 }
1879
1880 }
1881
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08001882 mutex_unlock(&pktgen_thread_lock);
Stephen Hemminger39df2322007-03-04 16:11:51 -08001883}
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001884
Stephen Hemminger39df2322007-03-04 16:11:51 -08001885static void pktgen_change_name(struct net_device *dev)
1886{
1887 struct pktgen_thread *t;
1888
1889 list_for_each_entry(t, &pktgen_threads, th_list) {
1890 struct pktgen_dev *pkt_dev;
1891
1892 list_for_each_entry(pkt_dev, &t->if_list, list) {
1893 if (pkt_dev->odev != dev)
1894 continue;
1895
1896 remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
1897
1898 pkt_dev->entry = create_proc_entry(dev->name, 0600,
1899 pg_proc_dir);
1900 if (!pkt_dev->entry)
1901 printk(KERN_ERR "pktgen: can't move proc "
1902 " entry for '%s'\n", dev->name);
1903 break;
1904 }
1905 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906}
1907
Luiz Capitulino222f1802006-03-20 22:16:13 -08001908static int pktgen_device_event(struct notifier_block *unused,
1909 unsigned long event, void *ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910{
Stephen Hemminger39df2322007-03-04 16:11:51 -08001911 struct net_device *dev = ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
1913 /* It is OK that we do not hold the group lock right now,
1914 * as we run under the RTNL lock.
1915 */
1916
1917 switch (event) {
Stephen Hemminger39df2322007-03-04 16:11:51 -08001918 case NETDEV_CHANGENAME:
1919 pktgen_change_name(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 break;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001921
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 case NETDEV_UNREGISTER:
Luiz Capitulino222f1802006-03-20 22:16:13 -08001923 pktgen_mark_device(dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001925 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
1927 return NOTIFY_DONE;
1928}
1929
1930/* Associate pktgen_dev with a device. */
1931
Stephen Hemminger39df2322007-03-04 16:11:51 -08001932static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname)
Luiz Capitulino222f1802006-03-20 22:16:13 -08001933{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 struct net_device *odev;
Stephen Hemminger39df2322007-03-04 16:11:51 -08001935 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936
1937 /* Clean old setups */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 if (pkt_dev->odev) {
1939 dev_put(pkt_dev->odev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001940 pkt_dev->odev = NULL;
1941 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
Stephen Hemminger39df2322007-03-04 16:11:51 -08001943 odev = dev_get_by_name(ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 if (!odev) {
Stephen Hemminger39df2322007-03-04 16:11:51 -08001945 printk("pktgen: no such netdevice: \"%s\"\n", ifname);
1946 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 }
Stephen Hemminger39df2322007-03-04 16:11:51 -08001948
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 if (odev->type != ARPHRD_ETHER) {
Stephen Hemminger39df2322007-03-04 16:11:51 -08001950 printk("pktgen: not an ethernet device: \"%s\"\n", ifname);
1951 err = -EINVAL;
1952 } else if (!netif_running(odev)) {
1953 printk("pktgen: device is down: \"%s\"\n", ifname);
1954 err = -ENETDOWN;
1955 } else {
1956 pkt_dev->odev = odev;
1957 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001959
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 dev_put(odev);
Stephen Hemminger39df2322007-03-04 16:11:51 -08001961 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962}
1963
1964/* Read pkt_dev from the interface and set up internal pktgen_dev
1965 * structure to have the right information to create/send packets
1966 */
1967static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
1968{
Luiz Capitulino222f1802006-03-20 22:16:13 -08001969 if (!pkt_dev->odev) {
1970 printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n");
1971 sprintf(pkt_dev->result,
1972 "ERROR: pkt_dev->odev == NULL in setup_inject.\n");
1973 return;
1974 }
1975
1976 /* Default to the interface's mac if not explicitly set. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001978 if (is_zero_ether_addr(pkt_dev->src_mac))
Luiz Capitulino222f1802006-03-20 22:16:13 -08001979 memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980
Luiz Capitulino222f1802006-03-20 22:16:13 -08001981 /* Set up Dest MAC */
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001982 memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Luiz Capitulino222f1802006-03-20 22:16:13 -08001984 /* Set up pkt size */
1985 pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
1986
1987 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 /*
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09001989 * Skip this automatic address setting until locks or functions
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 * gets exported
1991 */
1992
1993#ifdef NOTNOW
Luiz Capitulino222f1802006-03-20 22:16:13 -08001994 int i, set = 0, err = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 struct inet6_dev *idev;
1996
Luiz Capitulino222f1802006-03-20 22:16:13 -08001997 for (i = 0; i < IN6_ADDR_HSIZE; i++)
1998 if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 set = 1;
2000 break;
2001 }
2002
Luiz Capitulino222f1802006-03-20 22:16:13 -08002003 if (!set) {
2004
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 /*
2006 * Use linklevel address if unconfigured.
2007 *
2008 * use ipv6_get_lladdr if/when it's get exported
2009 */
2010
YOSHIFUJI Hideaki8814c4b2006-09-22 14:44:24 -07002011 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) {
2013 struct inet6_ifaddr *ifp;
2014
2015 read_lock_bh(&idev->lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002016 for (ifp = idev->addr_list; ifp;
2017 ifp = ifp->if_next) {
2018 if (ifp->scope == IFA_LINK
2019 && !(ifp->
2020 flags & IFA_F_TENTATIVE)) {
2021 ipv6_addr_copy(&pkt_dev->
2022 cur_in6_saddr,
2023 &ifp->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 err = 0;
2025 break;
2026 }
2027 }
2028 read_unlock_bh(&idev->lock);
2029 }
YOSHIFUJI Hideaki8814c4b2006-09-22 14:44:24 -07002030 rcu_read_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002031 if (err)
2032 printk("pktgen: ERROR: IPv6 link address not availble.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 }
2034#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -08002035 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 pkt_dev->saddr_min = 0;
2037 pkt_dev->saddr_max = 0;
2038 if (strlen(pkt_dev->src_min) == 0) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002039
2040 struct in_device *in_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041
2042 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -07002043 in_dev = __in_dev_get_rcu(pkt_dev->odev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 if (in_dev) {
2045 if (in_dev->ifa_list) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002046 pkt_dev->saddr_min =
2047 in_dev->ifa_list->ifa_address;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 pkt_dev->saddr_max = pkt_dev->saddr_min;
2049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 }
2051 rcu_read_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002052 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
2054 pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
2055 }
2056
2057 pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
2058 pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
2059 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002060 /* Initialize current values. */
2061 pkt_dev->cur_dst_mac_offset = 0;
2062 pkt_dev->cur_src_mac_offset = 0;
2063 pkt_dev->cur_saddr = pkt_dev->saddr_min;
2064 pkt_dev->cur_daddr = pkt_dev->daddr_min;
2065 pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
2066 pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 pkt_dev->nflows = 0;
2068}
2069
2070static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
2071{
2072 __u64 start;
2073 __u64 now;
2074
2075 start = now = getCurUs();
2076 printk(KERN_INFO "sleeping for %d\n", (int)(spin_until_us - now));
2077 while (now < spin_until_us) {
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -07002078 /* TODO: optimize sleeping behavior */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002079 if (spin_until_us - now > jiffies_to_usecs(1) + 1)
Nishanth Aravamudan121caf52005-09-12 14:15:34 -07002080 schedule_timeout_interruptible(1);
2081 else if (spin_until_us - now > 100) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 do_softirq();
2083 if (!pkt_dev->running)
2084 return;
2085 if (need_resched())
2086 schedule();
2087 }
2088
2089 now = getCurUs();
2090 }
2091
2092 pkt_dev->idle_acc += now - start;
2093}
2094
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002095static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
2096{
2097 pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
2098 pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
2099 pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
2100}
2101
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07002102static inline int f_seen(struct pktgen_dev *pkt_dev, int flow)
2103{
2104
2105 if (pkt_dev->flows[flow].flags & F_INIT)
2106 return 1;
2107 else
2108 return 0;
2109}
2110
2111static inline int f_pick(struct pktgen_dev *pkt_dev)
2112{
2113 int flow = pkt_dev->curfl;
2114
2115 if (pkt_dev->flags & F_FLOW_SEQ) {
2116 if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2117 /* reset time */
2118 pkt_dev->flows[flow].count = 0;
2119 pkt_dev->curfl += 1;
2120 if (pkt_dev->curfl >= pkt_dev->cflows)
2121 pkt_dev->curfl = 0; /*reset */
2122 }
2123 } else {
2124 flow = random32() % pkt_dev->cflows;
2125
2126 if (pkt_dev->flows[flow].count > pkt_dev->lflow)
2127 pkt_dev->flows[flow].count = 0;
2128 }
2129
2130 return pkt_dev->curfl;
2131}
2132
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133/* Increment/randomize headers according to flags and current values
2134 * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
2135 */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002136static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2137{
2138 __u32 imn;
2139 __u32 imx;
2140 int flow = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07002142 if (pkt_dev->cflows)
2143 flow = f_pick(pkt_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
2145 /* Deal with source MAC */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002146 if (pkt_dev->src_mac_count > 1) {
2147 __u32 mc;
2148 __u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
Luiz Capitulino222f1802006-03-20 22:16:13 -08002150 if (pkt_dev->flags & F_MACSRC_RND)
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002151 mc = random32() % pkt_dev->src_mac_count;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002152 else {
2153 mc = pkt_dev->cur_src_mac_offset++;
2154 if (pkt_dev->cur_src_mac_offset >
2155 pkt_dev->src_mac_count)
2156 pkt_dev->cur_src_mac_offset = 0;
2157 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158
Luiz Capitulino222f1802006-03-20 22:16:13 -08002159 tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
2160 pkt_dev->hh[11] = tmp;
2161 tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
2162 pkt_dev->hh[10] = tmp;
2163 tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
2164 pkt_dev->hh[9] = tmp;
2165 tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
2166 pkt_dev->hh[8] = tmp;
2167 tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
2168 pkt_dev->hh[7] = tmp;
2169 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
Luiz Capitulino222f1802006-03-20 22:16:13 -08002171 /* Deal with Destination MAC */
2172 if (pkt_dev->dst_mac_count > 1) {
2173 __u32 mc;
2174 __u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175
Luiz Capitulino222f1802006-03-20 22:16:13 -08002176 if (pkt_dev->flags & F_MACDST_RND)
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002177 mc = random32() % pkt_dev->dst_mac_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178
Luiz Capitulino222f1802006-03-20 22:16:13 -08002179 else {
2180 mc = pkt_dev->cur_dst_mac_offset++;
2181 if (pkt_dev->cur_dst_mac_offset >
2182 pkt_dev->dst_mac_count) {
2183 pkt_dev->cur_dst_mac_offset = 0;
2184 }
2185 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186
Luiz Capitulino222f1802006-03-20 22:16:13 -08002187 tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
2188 pkt_dev->hh[5] = tmp;
2189 tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
2190 pkt_dev->hh[4] = tmp;
2191 tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
2192 pkt_dev->hh[3] = tmp;
2193 tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
2194 pkt_dev->hh[2] = tmp;
2195 tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
2196 pkt_dev->hh[1] = tmp;
2197 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002199 if (pkt_dev->flags & F_MPLS_RND) {
2200 unsigned i;
Stephen Hemmingere71a4782007-04-10 20:10:33 -07002201 for (i = 0; i < pkt_dev->nr_labels; i++)
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002202 if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2203 pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002204 ((__force __be32)random32() &
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002205 htonl(0x000fffff));
2206 }
2207
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002208 if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002209 pkt_dev->vlan_id = random32() & (4096-1);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002210 }
2211
2212 if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002213 pkt_dev->svlan_id = random32() & (4096 - 1);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002214 }
2215
Luiz Capitulino222f1802006-03-20 22:16:13 -08002216 if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
2217 if (pkt_dev->flags & F_UDPSRC_RND)
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002218 pkt_dev->cur_udp_src = random32() %
2219 (pkt_dev->udp_src_max - pkt_dev->udp_src_min)
2220 + pkt_dev->udp_src_min;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221
Luiz Capitulino222f1802006-03-20 22:16:13 -08002222 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 pkt_dev->cur_udp_src++;
2224 if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
2225 pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002226 }
2227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228
Luiz Capitulino222f1802006-03-20 22:16:13 -08002229 if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
2230 if (pkt_dev->flags & F_UDPDST_RND) {
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002231 pkt_dev->cur_udp_dst = random32() %
2232 (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)
2233 + pkt_dev->udp_dst_min;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002234 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 pkt_dev->cur_udp_dst++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002236 if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002238 }
2239 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240
2241 if (!(pkt_dev->flags & F_IPV6)) {
2242
Luiz Capitulino222f1802006-03-20 22:16:13 -08002243 if ((imn = ntohl(pkt_dev->saddr_min)) < (imx =
2244 ntohl(pkt_dev->
2245 saddr_max))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 __u32 t;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002247 if (pkt_dev->flags & F_IPSRC_RND)
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002248 t = random32() % (imx - imn) + imn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 else {
2250 t = ntohl(pkt_dev->cur_saddr);
2251 t++;
2252 if (t > imx) {
2253 t = imn;
2254 }
2255 }
2256 pkt_dev->cur_saddr = htonl(t);
2257 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002258
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07002259 if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
2261 } else {
Al Viro252e3342006-11-14 20:48:11 -08002262 imn = ntohl(pkt_dev->daddr_min);
2263 imx = ntohl(pkt_dev->daddr_max);
2264 if (imn < imx) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 __u32 t;
Al Viro252e3342006-11-14 20:48:11 -08002266 __be32 s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 if (pkt_dev->flags & F_IPDST_RND) {
2268
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002269 t = random32() % (imx - imn) + imn;
Al Viro252e3342006-11-14 20:48:11 -08002270 s = htonl(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271
Al Viro252e3342006-11-14 20:48:11 -08002272 while (LOOPBACK(s) || MULTICAST(s)
2273 || BADCLASS(s) || ZERONET(s)
2274 || LOCAL_MCAST(s)) {
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002275 t = random32() % (imx - imn) + imn;
Al Viro252e3342006-11-14 20:48:11 -08002276 s = htonl(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 }
Al Viro252e3342006-11-14 20:48:11 -08002278 pkt_dev->cur_daddr = s;
2279 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 t = ntohl(pkt_dev->cur_daddr);
2281 t++;
2282 if (t > imx) {
2283 t = imn;
2284 }
2285 pkt_dev->cur_daddr = htonl(t);
2286 }
2287 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002288 if (pkt_dev->cflows) {
Jamal Hadi Salim007a5312007-07-02 22:40:36 -07002289 pkt_dev->flows[flow].flags |= F_INIT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002290 pkt_dev->flows[flow].cur_daddr =
2291 pkt_dev->cur_daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 pkt_dev->nflows++;
2293 }
2294 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002295 } else { /* IPV6 * */
2296
2297 if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
2298 pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
2299 pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
2300 pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 else {
2302 int i;
2303
2304 /* Only random destinations yet */
2305
Luiz Capitulino222f1802006-03-20 22:16:13 -08002306 for (i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 pkt_dev->cur_in6_daddr.s6_addr32[i] =
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002308 (((__force __be32)random32() |
Luiz Capitulino222f1802006-03-20 22:16:13 -08002309 pkt_dev->min_in6_daddr.s6_addr32[i]) &
2310 pkt_dev->max_in6_daddr.s6_addr32[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002312 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 }
2314
Luiz Capitulino222f1802006-03-20 22:16:13 -08002315 if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
2316 __u32 t;
2317 if (pkt_dev->flags & F_TXSIZE_RND) {
Stephen Hemminger5fa6fc72007-03-04 16:07:28 -08002318 t = random32() %
2319 (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)
2320 + pkt_dev->min_pkt_size;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002321 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 t = pkt_dev->cur_pkt_size + 1;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002323 if (t > pkt_dev->max_pkt_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 t = pkt_dev->min_pkt_size;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002325 }
2326 pkt_dev->cur_pkt_size = t;
2327 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328
2329 pkt_dev->flows[flow].count++;
2330}
2331
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002332static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2333{
2334 unsigned i;
Stephen Hemmingere71a4782007-04-10 20:10:33 -07002335 for (i = 0; i < pkt_dev->nr_labels; i++) {
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002336 *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
2337 }
2338 mpls--;
2339 *mpls |= MPLS_STACK_BOTTOM;
2340}
2341
Al Viro0f37c602006-11-03 03:49:56 -08002342static inline __be16 build_tci(unsigned int id, unsigned int cfi,
2343 unsigned int prio)
2344{
2345 return htons(id | (cfi << 12) | (prio << 13));
2346}
2347
Luiz Capitulino222f1802006-03-20 22:16:13 -08002348static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
2349 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350{
2351 struct sk_buff *skb = NULL;
2352 __u8 *eth;
2353 struct udphdr *udph;
2354 int datalen, iplen;
2355 struct iphdr *iph;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002356 struct pktgen_hdr *pgh = NULL;
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002357 __be16 protocol = htons(ETH_P_IP);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002358 __be32 *mpls;
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002359 __be16 *vlan_tci = NULL; /* Encapsulates priority and VLAN ID */
2360 __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */
2361 __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */
2362 __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2363
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002364
2365 if (pkt_dev->nr_labels)
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002366 protocol = htons(ETH_P_MPLS_UC);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002367
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002368 if (pkt_dev->vlan_id != 0xffff)
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002369 protocol = htons(ETH_P_8021Q);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002370
Robert Olsson64053be2005-06-26 15:27:10 -07002371 /* Update any of the values, used when we're incrementing various
2372 * fields.
2373 */
2374 mod_cur_headers(pkt_dev);
2375
David S. Miller7ac54592006-01-18 14:19:10 -08002376 datalen = (odev->hard_header_len + 16) & ~0xf;
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002377 skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen +
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002378 pkt_dev->pkt_overhead, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 if (!skb) {
2380 sprintf(pkt_dev->result, "No memory");
2381 return NULL;
2382 }
2383
David S. Miller7ac54592006-01-18 14:19:10 -08002384 skb_reserve(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385
2386 /* Reserve for ethernet and IP header */
2387 eth = (__u8 *) skb_push(skb, 14);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002388 mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2389 if (pkt_dev->nr_labels)
2390 mpls_push(mpls, pkt_dev);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002391
2392 if (pkt_dev->vlan_id != 0xffff) {
Stephen Hemmingere71a4782007-04-10 20:10:33 -07002393 if (pkt_dev->svlan_id != 0xffff) {
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002394 svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
Al Viro0f37c602006-11-03 03:49:56 -08002395 *svlan_tci = build_tci(pkt_dev->svlan_id,
2396 pkt_dev->svlan_cfi,
2397 pkt_dev->svlan_p);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002398 svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002399 *svlan_encapsulated_proto = htons(ETH_P_8021Q);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002400 }
2401 vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
Al Viro0f37c602006-11-03 03:49:56 -08002402 *vlan_tci = build_tci(pkt_dev->vlan_id,
2403 pkt_dev->vlan_cfi,
2404 pkt_dev->vlan_p);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002405 vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002406 *vlan_encapsulated_proto = htons(ETH_P_IP);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002407 }
2408
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07002409 skb->network_header = skb->tail;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07002410 skb->transport_header = skb->network_header + sizeof(struct iphdr);
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -03002411 skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr));
2412
2413 iph = ip_hdr(skb);
2414 udph = udp_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416 memcpy(eth, pkt_dev->hh, 12);
Al Viro252e3342006-11-14 20:48:11 -08002417 *(__be16 *) & eth[12] = protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002419 /* Eth + IPh + UDPh + mpls */
2420 datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002421 pkt_dev->pkt_overhead;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002422 if (datalen < sizeof(struct pktgen_hdr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 datalen = sizeof(struct pktgen_hdr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002424
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425 udph->source = htons(pkt_dev->cur_udp_src);
2426 udph->dest = htons(pkt_dev->cur_udp_dst);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002427 udph->len = htons(datalen + 8); /* DATA + udphdr */
2428 udph->check = 0; /* No checksum */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429
2430 iph->ihl = 5;
2431 iph->version = 4;
2432 iph->ttl = 32;
Francesco Fondelli1ca77682006-09-27 16:32:03 -07002433 iph->tos = pkt_dev->tos;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002434 iph->protocol = IPPROTO_UDP; /* UDP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 iph->saddr = pkt_dev->cur_saddr;
2436 iph->daddr = pkt_dev->cur_daddr;
2437 iph->frag_off = 0;
2438 iplen = 20 + 8 + datalen;
2439 iph->tot_len = htons(iplen);
2440 iph->check = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002441 iph->check = ip_fast_csum((void *)iph, iph->ihl);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002442 skb->protocol = protocol;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07002443 skb->mac_header = (skb->network_header - ETH_HLEN -
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002444 pkt_dev->pkt_overhead);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 skb->dev = odev;
2446 skb->pkt_type = PACKET_HOST;
2447
Luiz Capitulino222f1802006-03-20 22:16:13 -08002448 if (pkt_dev->nfrags <= 0)
2449 pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 else {
2451 int frags = pkt_dev->nfrags;
2452 int i;
2453
Luiz Capitulino222f1802006-03-20 22:16:13 -08002454 pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
2455
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 if (frags > MAX_SKB_FRAGS)
2457 frags = MAX_SKB_FRAGS;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002458 if (datalen > frags * PAGE_SIZE) {
2459 skb_put(skb, datalen - frags * PAGE_SIZE);
2460 datalen = frags * PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 }
2462
2463 i = 0;
2464 while (datalen > 0) {
2465 struct page *page = alloc_pages(GFP_KERNEL, 0);
2466 skb_shinfo(skb)->frags[i].page = page;
2467 skb_shinfo(skb)->frags[i].page_offset = 0;
2468 skb_shinfo(skb)->frags[i].size =
Luiz Capitulino222f1802006-03-20 22:16:13 -08002469 (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 datalen -= skb_shinfo(skb)->frags[i].size;
2471 skb->len += skb_shinfo(skb)->frags[i].size;
2472 skb->data_len += skb_shinfo(skb)->frags[i].size;
2473 i++;
2474 skb_shinfo(skb)->nr_frags = i;
2475 }
2476
2477 while (i < frags) {
2478 int rem;
2479
2480 if (i == 0)
2481 break;
2482
2483 rem = skb_shinfo(skb)->frags[i - 1].size / 2;
2484 if (rem == 0)
2485 break;
2486
2487 skb_shinfo(skb)->frags[i - 1].size -= rem;
2488
Luiz Capitulino222f1802006-03-20 22:16:13 -08002489 skb_shinfo(skb)->frags[i] =
2490 skb_shinfo(skb)->frags[i - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 get_page(skb_shinfo(skb)->frags[i].page);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002492 skb_shinfo(skb)->frags[i].page =
2493 skb_shinfo(skb)->frags[i - 1].page;
2494 skb_shinfo(skb)->frags[i].page_offset +=
2495 skb_shinfo(skb)->frags[i - 1].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 skb_shinfo(skb)->frags[i].size = rem;
2497 i++;
2498 skb_shinfo(skb)->nr_frags = i;
2499 }
2500 }
2501
Luiz Capitulino222f1802006-03-20 22:16:13 -08002502 /* Stamp the time, and sequence number, convert them to network byte order */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503
Luiz Capitulino222f1802006-03-20 22:16:13 -08002504 if (pgh) {
2505 struct timeval timestamp;
2506
2507 pgh->pgh_magic = htonl(PKTGEN_MAGIC);
2508 pgh->seq_num = htonl(pkt_dev->seq_num);
2509
2510 do_gettimeofday(&timestamp);
2511 pgh->tv_sec = htonl(timestamp.tv_sec);
2512 pgh->tv_usec = htonl(timestamp.tv_usec);
2513 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002514
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 return skb;
2516}
2517
2518/*
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09002519 * scan_ip6, fmt_ip taken from dietlibc-0.21
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 * Author Felix von Leitner <felix-dietlibc@fefe.de>
2521 *
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09002522 * Slightly modified for kernel.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 * Should be candidate for net/ipv4/utils.c
2524 * --ro
2525 */
2526
Luiz Capitulino222f1802006-03-20 22:16:13 -08002527static unsigned int scan_ip6(const char *s, char ip[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528{
2529 unsigned int i;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002530 unsigned int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 unsigned long u;
2532 char suffix[16];
Luiz Capitulino222f1802006-03-20 22:16:13 -08002533 unsigned int prefixlen = 0;
2534 unsigned int suffixlen = 0;
Al Viro252e3342006-11-14 20:48:11 -08002535 __be32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536
Luiz Capitulino222f1802006-03-20 22:16:13 -08002537 for (i = 0; i < 16; i++)
2538 ip[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539
2540 for (;;) {
2541 if (*s == ':') {
2542 len++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002543 if (s[1] == ':') { /* Found "::", skip to part 2 */
2544 s += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545 len++;
2546 break;
2547 }
2548 s++;
2549 }
2550 {
2551 char *tmp;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002552 u = simple_strtoul(s, &tmp, 16);
2553 i = tmp - s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 }
2555
Luiz Capitulino222f1802006-03-20 22:16:13 -08002556 if (!i)
2557 return 0;
2558 if (prefixlen == 12 && s[i] == '.') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
2560 /* the last 4 bytes may be written as IPv4 address */
2561
2562 tmp = in_aton(s);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002563 memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp));
2564 return i + len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 }
2566 ip[prefixlen++] = (u >> 8);
2567 ip[prefixlen++] = (u & 255);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002568 s += i;
2569 len += i;
2570 if (prefixlen == 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 return len;
2572 }
2573
2574/* part 2, after "::" */
2575 for (;;) {
2576 if (*s == ':') {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002577 if (suffixlen == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 break;
2579 s++;
2580 len++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002581 } else if (suffixlen != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 break;
2583 {
2584 char *tmp;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002585 u = simple_strtol(s, &tmp, 16);
2586 i = tmp - s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 }
2588 if (!i) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002589 if (*s)
2590 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591 break;
2592 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002593 if (suffixlen + prefixlen <= 12 && s[i] == '.') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 tmp = in_aton(s);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002595 memcpy((struct in_addr *)(suffix + suffixlen), &tmp,
2596 sizeof(tmp));
2597 suffixlen += 4;
2598 len += strlen(s);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599 break;
2600 }
2601 suffix[suffixlen++] = (u >> 8);
2602 suffix[suffixlen++] = (u & 255);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002603 s += i;
2604 len += i;
2605 if (prefixlen + suffixlen == 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 break;
2607 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002608 for (i = 0; i < suffixlen; i++)
2609 ip[16 - suffixlen + i] = suffix[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 return len;
2611}
2612
Luiz Capitulino222f1802006-03-20 22:16:13 -08002613static char tohex(char hexdigit)
2614{
2615 return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616}
2617
Luiz Capitulino222f1802006-03-20 22:16:13 -08002618static int fmt_xlong(char *s, unsigned int i)
2619{
2620 char *bak = s;
2621 *s = tohex((i >> 12) & 0xf);
2622 if (s != bak || *s != '0')
2623 ++s;
2624 *s = tohex((i >> 8) & 0xf);
2625 if (s != bak || *s != '0')
2626 ++s;
2627 *s = tohex((i >> 4) & 0xf);
2628 if (s != bak || *s != '0')
2629 ++s;
2630 *s = tohex(i & 0xf);
2631 return s - bak + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632}
2633
Luiz Capitulino222f1802006-03-20 22:16:13 -08002634static unsigned int fmt_ip6(char *s, const char ip[16])
2635{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636 unsigned int len;
2637 unsigned int i;
2638 unsigned int temp;
2639 unsigned int compressing;
2640 int j;
2641
Luiz Capitulino222f1802006-03-20 22:16:13 -08002642 len = 0;
2643 compressing = 0;
2644 for (j = 0; j < 16; j += 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645
2646#ifdef V4MAPPEDPREFIX
Luiz Capitulino222f1802006-03-20 22:16:13 -08002647 if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) {
2648 inet_ntoa_r(*(struct in_addr *)(ip + 12), s);
2649 temp = strlen(s);
2650 return len + temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 }
2652#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -08002653 temp = ((unsigned long)(unsigned char)ip[j] << 8) +
2654 (unsigned long)(unsigned char)ip[j + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 if (temp == 0) {
2656 if (!compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002657 compressing = 1;
2658 if (j == 0) {
2659 *s++ = ':';
2660 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 }
2662 }
2663 } else {
2664 if (compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002665 compressing = 0;
2666 *s++ = ':';
2667 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002669 i = fmt_xlong(s, temp);
2670 len += i;
2671 s += i;
2672 if (j < 14) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 *s++ = ':';
2674 ++len;
2675 }
2676 }
2677 }
2678 if (compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002679 *s++ = ':';
2680 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002682 *s = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 return len;
2684}
2685
Luiz Capitulino222f1802006-03-20 22:16:13 -08002686static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
2687 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688{
2689 struct sk_buff *skb = NULL;
2690 __u8 *eth;
2691 struct udphdr *udph;
2692 int datalen;
2693 struct ipv6hdr *iph;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002694 struct pktgen_hdr *pgh = NULL;
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002695 __be16 protocol = htons(ETH_P_IPV6);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002696 __be32 *mpls;
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002697 __be16 *vlan_tci = NULL; /* Encapsulates priority and VLAN ID */
2698 __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */
2699 __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */
2700 __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002701
2702 if (pkt_dev->nr_labels)
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002703 protocol = htons(ETH_P_MPLS_UC);
Robert Olsson64053be2005-06-26 15:27:10 -07002704
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002705 if (pkt_dev->vlan_id != 0xffff)
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002706 protocol = htons(ETH_P_8021Q);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002707
Robert Olsson64053be2005-06-26 15:27:10 -07002708 /* Update any of the values, used when we're incrementing various
2709 * fields.
2710 */
2711 mod_cur_headers(pkt_dev);
2712
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002713 skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 +
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002714 pkt_dev->pkt_overhead, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 if (!skb) {
2716 sprintf(pkt_dev->result, "No memory");
2717 return NULL;
2718 }
2719
2720 skb_reserve(skb, 16);
2721
2722 /* Reserve for ethernet and IP header */
2723 eth = (__u8 *) skb_push(skb, 14);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002724 mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
2725 if (pkt_dev->nr_labels)
2726 mpls_push(mpls, pkt_dev);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002727
2728 if (pkt_dev->vlan_id != 0xffff) {
Stephen Hemmingere71a4782007-04-10 20:10:33 -07002729 if (pkt_dev->svlan_id != 0xffff) {
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002730 svlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
Al Viro0f37c602006-11-03 03:49:56 -08002731 *svlan_tci = build_tci(pkt_dev->svlan_id,
2732 pkt_dev->svlan_cfi,
2733 pkt_dev->svlan_p);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002734 svlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002735 *svlan_encapsulated_proto = htons(ETH_P_8021Q);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002736 }
2737 vlan_tci = (__be16 *)skb_put(skb, sizeof(__be16));
Al Viro0f37c602006-11-03 03:49:56 -08002738 *vlan_tci = build_tci(pkt_dev->vlan_id,
2739 pkt_dev->vlan_cfi,
2740 pkt_dev->vlan_p);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002741 vlan_encapsulated_proto = (__be16 *)skb_put(skb, sizeof(__be16));
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002742 *vlan_encapsulated_proto = htons(ETH_P_IPV6);
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002743 }
2744
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07002745 skb->network_header = skb->tail;
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07002746 skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -03002747 skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr));
2748
2749 iph = ipv6_hdr(skb);
2750 udph = udp_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752 memcpy(eth, pkt_dev->hh, 12);
Al Viro252e3342006-11-14 20:48:11 -08002753 *(__be16 *) & eth[12] = protocol;
Robert Olsson64053be2005-06-26 15:27:10 -07002754
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002755 /* Eth + IPh + UDPh + mpls */
2756 datalen = pkt_dev->cur_pkt_size - 14 -
2757 sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002758 pkt_dev->pkt_overhead;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759
Luiz Capitulino222f1802006-03-20 22:16:13 -08002760 if (datalen < sizeof(struct pktgen_hdr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 datalen = sizeof(struct pktgen_hdr);
2762 if (net_ratelimit())
Luiz Capitulino222f1802006-03-20 22:16:13 -08002763 printk(KERN_INFO "pktgen: increased datalen to %d\n",
2764 datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 }
2766
2767 udph->source = htons(pkt_dev->cur_udp_src);
2768 udph->dest = htons(pkt_dev->cur_udp_dst);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002769 udph->len = htons(datalen + sizeof(struct udphdr));
2770 udph->check = 0; /* No checksum */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771
Stephen Hemmingerd5f1ce92007-03-04 16:08:08 -08002772 *(__be32 *) iph = htonl(0x60000000); /* Version + flow */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773
Francesco Fondelli1ca77682006-09-27 16:32:03 -07002774 if (pkt_dev->traffic_class) {
2775 /* Version + traffic class + flow (0) */
Al Viro252e3342006-11-14 20:48:11 -08002776 *(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
Francesco Fondelli1ca77682006-09-27 16:32:03 -07002777 }
2778
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 iph->hop_limit = 32;
2780
2781 iph->payload_len = htons(sizeof(struct udphdr) + datalen);
2782 iph->nexthdr = IPPROTO_UDP;
2783
2784 ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
2785 ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
2786
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07002787 skb->mac_header = (skb->network_header - ETH_HLEN -
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002788 pkt_dev->pkt_overhead);
Steven Whitehouseca6549a2006-03-23 01:10:26 -08002789 skb->protocol = protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 skb->dev = odev;
2791 skb->pkt_type = PACKET_HOST;
2792
Luiz Capitulino222f1802006-03-20 22:16:13 -08002793 if (pkt_dev->nfrags <= 0)
2794 pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 else {
2796 int frags = pkt_dev->nfrags;
2797 int i;
2798
Luiz Capitulino222f1802006-03-20 22:16:13 -08002799 pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
2800
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 if (frags > MAX_SKB_FRAGS)
2802 frags = MAX_SKB_FRAGS;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002803 if (datalen > frags * PAGE_SIZE) {
2804 skb_put(skb, datalen - frags * PAGE_SIZE);
2805 datalen = frags * PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 }
2807
2808 i = 0;
2809 while (datalen > 0) {
2810 struct page *page = alloc_pages(GFP_KERNEL, 0);
2811 skb_shinfo(skb)->frags[i].page = page;
2812 skb_shinfo(skb)->frags[i].page_offset = 0;
2813 skb_shinfo(skb)->frags[i].size =
Luiz Capitulino222f1802006-03-20 22:16:13 -08002814 (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 datalen -= skb_shinfo(skb)->frags[i].size;
2816 skb->len += skb_shinfo(skb)->frags[i].size;
2817 skb->data_len += skb_shinfo(skb)->frags[i].size;
2818 i++;
2819 skb_shinfo(skb)->nr_frags = i;
2820 }
2821
2822 while (i < frags) {
2823 int rem;
2824
2825 if (i == 0)
2826 break;
2827
2828 rem = skb_shinfo(skb)->frags[i - 1].size / 2;
2829 if (rem == 0)
2830 break;
2831
2832 skb_shinfo(skb)->frags[i - 1].size -= rem;
2833
Luiz Capitulino222f1802006-03-20 22:16:13 -08002834 skb_shinfo(skb)->frags[i] =
2835 skb_shinfo(skb)->frags[i - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 get_page(skb_shinfo(skb)->frags[i].page);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002837 skb_shinfo(skb)->frags[i].page =
2838 skb_shinfo(skb)->frags[i - 1].page;
2839 skb_shinfo(skb)->frags[i].page_offset +=
2840 skb_shinfo(skb)->frags[i - 1].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 skb_shinfo(skb)->frags[i].size = rem;
2842 i++;
2843 skb_shinfo(skb)->nr_frags = i;
2844 }
2845 }
2846
Luiz Capitulino222f1802006-03-20 22:16:13 -08002847 /* Stamp the time, and sequence number, convert them to network byte order */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 /* should we update cloned packets too ? */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002849 if (pgh) {
2850 struct timeval timestamp;
2851
2852 pgh->pgh_magic = htonl(PKTGEN_MAGIC);
2853 pgh->seq_num = htonl(pkt_dev->seq_num);
2854
2855 do_gettimeofday(&timestamp);
2856 pgh->tv_sec = htonl(timestamp.tv_sec);
2857 pgh->tv_usec = htonl(timestamp.tv_usec);
2858 }
Francesco Fondelli34954dd2006-09-27 16:30:44 -07002859 /* pkt_dev->seq_num++; FF: you really mean this? */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002860
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 return skb;
2862}
2863
Luiz Capitulino222f1802006-03-20 22:16:13 -08002864static inline struct sk_buff *fill_packet(struct net_device *odev,
2865 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002867 if (pkt_dev->flags & F_IPV6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 return fill_packet_ipv6(odev, pkt_dev);
2869 else
2870 return fill_packet_ipv4(odev, pkt_dev);
2871}
2872
Luiz Capitulino222f1802006-03-20 22:16:13 -08002873static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002875 pkt_dev->seq_num = 1;
2876 pkt_dev->idle_acc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 pkt_dev->sofar = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002878 pkt_dev->tx_bytes = 0;
2879 pkt_dev->errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880}
2881
2882/* Set up structure for sending pkts, clear counters */
2883
2884static void pktgen_run(struct pktgen_thread *t)
2885{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08002886 struct pktgen_dev *pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887 int started = 0;
2888
Stephen Hemminger25c4e532007-03-04 16:06:47 -08002889 pr_debug("pktgen: entering pktgen_run. %p\n", t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890
2891 if_lock(t);
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08002892 list_for_each_entry(pkt_dev, &t->if_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893
2894 /*
2895 * setup odev and create initial packet.
2896 */
2897 pktgen_setup_inject(pkt_dev);
2898
Luiz Capitulino222f1802006-03-20 22:16:13 -08002899 if (pkt_dev->odev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 pktgen_clear_counters(pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002901 pkt_dev->running = 1; /* Cranke yeself! */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 pkt_dev->skb = NULL;
2903 pkt_dev->started_at = getCurUs();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002904 pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905 pkt_dev->next_tx_ns = 0;
Jamal Hadi Salim16dab722007-07-02 22:39:50 -07002906 set_pkt_overhead(pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002907
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908 strcpy(pkt_dev->result, "Starting");
2909 started++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002910 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 strcpy(pkt_dev->result, "Error starting");
2912 }
2913 if_unlock(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002914 if (started)
2915 t->control &= ~(T_STOP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916}
2917
2918static void pktgen_stop_all_threads_ifs(void)
2919{
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002920 struct pktgen_thread *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921
Stephen Hemminger25c4e532007-03-04 16:06:47 -08002922 pr_debug("pktgen: entering pktgen_stop_all_threads_ifs.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002924 mutex_lock(&pktgen_thread_lock);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002925
2926 list_for_each_entry(t, &pktgen_threads, th_list)
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002927 t->control |= T_STOP;
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002928
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002929 mutex_unlock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930}
2931
Luiz Capitulino222f1802006-03-20 22:16:13 -08002932static int thread_is_running(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08002934 struct pktgen_dev *pkt_dev;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002935 int res = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08002937 list_for_each_entry(pkt_dev, &t->if_list, list)
2938 if (pkt_dev->running) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 res = 1;
2940 break;
2941 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002942 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943}
2944
Luiz Capitulino222f1802006-03-20 22:16:13 -08002945static int pktgen_wait_thread_run(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002947 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948
Luiz Capitulino222f1802006-03-20 22:16:13 -08002949 while (thread_is_running(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950
Luiz Capitulino222f1802006-03-20 22:16:13 -08002951 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952
Luiz Capitulino222f1802006-03-20 22:16:13 -08002953 msleep_interruptible(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954
Luiz Capitulino222f1802006-03-20 22:16:13 -08002955 if (signal_pending(current))
2956 goto signal;
2957 if_lock(t);
2958 }
2959 if_unlock(t);
2960 return 1;
2961signal:
2962 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963}
2964
2965static int pktgen_wait_all_threads_run(void)
2966{
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002967 struct pktgen_thread *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 int sig = 1;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002969
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002970 mutex_lock(&pktgen_thread_lock);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002971
2972 list_for_each_entry(t, &pktgen_threads, th_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973 sig = pktgen_wait_thread_run(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002974 if (sig == 0)
2975 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976 }
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002977
2978 if (sig == 0)
2979 list_for_each_entry(t, &pktgen_threads, th_list)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002980 t->control |= (T_STOP);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002981
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002982 mutex_unlock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983 return sig;
2984}
2985
2986static void pktgen_run_all_threads(void)
2987{
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002988 struct pktgen_thread *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989
Stephen Hemminger25c4e532007-03-04 16:06:47 -08002990 pr_debug("pktgen: entering pktgen_run_all_threads.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002992 mutex_lock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002993
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002994 list_for_each_entry(t, &pktgen_threads, th_list)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 t->control |= (T_RUN);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08002996
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08002997 mutex_unlock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002998
Luiz Capitulino222f1802006-03-20 22:16:13 -08002999 schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
3000
Linus Torvalds1da177e2005-04-16 15:20:36 -07003001 pktgen_wait_all_threads_run();
3002}
3003
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
3005{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003006 __u64 total_us, bps, mbps, pps, idle;
3007 char *p = pkt_dev->result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008
Luiz Capitulino222f1802006-03-20 22:16:13 -08003009 total_us = pkt_dev->stopped_at - pkt_dev->started_at;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010
Luiz Capitulino222f1802006-03-20 22:16:13 -08003011 idle = pkt_dev->idle_acc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003012
Luiz Capitulino222f1802006-03-20 22:16:13 -08003013 p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
3014 (unsigned long long)total_us,
3015 (unsigned long long)(total_us - idle),
3016 (unsigned long long)idle,
3017 (unsigned long long)pkt_dev->sofar,
3018 pkt_dev->cur_pkt_size, nr_frags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019
Luiz Capitulino222f1802006-03-20 22:16:13 -08003020 pps = pkt_dev->sofar * USEC_PER_SEC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021
Luiz Capitulino222f1802006-03-20 22:16:13 -08003022 while ((total_us >> 32) != 0) {
3023 pps >>= 1;
3024 total_us >>= 1;
3025 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003026
Luiz Capitulino222f1802006-03-20 22:16:13 -08003027 do_div(pps, total_us);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003028
Luiz Capitulino222f1802006-03-20 22:16:13 -08003029 bps = pps * 8 * pkt_dev->cur_pkt_size;
3030
3031 mbps = bps;
3032 do_div(mbps, 1000000);
3033 p += sprintf(p, " %llupps %lluMb/sec (%llubps) errors: %llu",
3034 (unsigned long long)pps,
3035 (unsigned long long)mbps,
3036 (unsigned long long)bps,
3037 (unsigned long long)pkt_dev->errors);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039
3040/* Set stopped-at timer, remove from running list, do counters & statistics */
3041
Luiz Capitulino222f1802006-03-20 22:16:13 -08003042static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003044 int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003045
Luiz Capitulino222f1802006-03-20 22:16:13 -08003046 if (!pkt_dev->running) {
3047 printk("pktgen: interface: %s is already stopped\n",
Stephen Hemminger39df2322007-03-04 16:11:51 -08003048 pkt_dev->odev->name);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003049 return -EINVAL;
3050 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003051
Luiz Capitulino222f1802006-03-20 22:16:13 -08003052 pkt_dev->stopped_at = getCurUs();
3053 pkt_dev->running = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003055 show_results(pkt_dev, nr_frags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056
Luiz Capitulino222f1802006-03-20 22:16:13 -08003057 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058}
3059
Luiz Capitulino222f1802006-03-20 22:16:13 -08003060static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003062 struct pktgen_dev *pkt_dev, *best = NULL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003063
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064 if_lock(t);
3065
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003066 list_for_each_entry(pkt_dev, &t->if_list, list) {
3067 if (!pkt_dev->running)
Luiz Capitulino222f1802006-03-20 22:16:13 -08003068 continue;
3069 if (best == NULL)
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003070 best = pkt_dev;
3071 else if (pkt_dev->next_tx_us < best->next_tx_us)
3072 best = pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003073 }
3074 if_unlock(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003075 return best;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076}
3077
Luiz Capitulino222f1802006-03-20 22:16:13 -08003078static void pktgen_stop(struct pktgen_thread *t)
3079{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003080 struct pktgen_dev *pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003081
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003082 pr_debug("pktgen: entering pktgen_stop\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083
Luiz Capitulino222f1802006-03-20 22:16:13 -08003084 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003086 list_for_each_entry(pkt_dev, &t->if_list, list) {
3087 pktgen_stop_device(pkt_dev);
3088 if (pkt_dev->skb)
3089 kfree_skb(pkt_dev->skb);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003090
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003091 pkt_dev->skb = NULL;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003092 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093
Luiz Capitulino222f1802006-03-20 22:16:13 -08003094 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095}
3096
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003097/*
3098 * one of our devices needs to be removed - find it
3099 * and remove it
3100 */
3101static void pktgen_rem_one_if(struct pktgen_thread *t)
3102{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003103 struct list_head *q, *n;
3104 struct pktgen_dev *cur;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003105
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003106 pr_debug("pktgen: entering pktgen_rem_one_if\n");
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003107
3108 if_lock(t);
3109
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003110 list_for_each_safe(q, n, &t->if_list) {
3111 cur = list_entry(q, struct pktgen_dev, list);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003112
Luiz Capitulino222f1802006-03-20 22:16:13 -08003113 if (!cur->removal_mark)
3114 continue;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003115
3116 if (cur->skb)
3117 kfree_skb(cur->skb);
3118 cur->skb = NULL;
3119
3120 pktgen_remove_device(t, cur);
3121
3122 break;
3123 }
3124
3125 if_unlock(t);
3126}
3127
Luiz Capitulino222f1802006-03-20 22:16:13 -08003128static void pktgen_rem_all_ifs(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003130 struct list_head *q, *n;
3131 struct pktgen_dev *cur;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003132
3133 /* Remove all devices, free mem */
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003134
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003135 pr_debug("pktgen: entering pktgen_rem_all_ifs\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -08003136 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003138 list_for_each_safe(q, n, &t->if_list) {
3139 cur = list_entry(q, struct pktgen_dev, list);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003140
3141 if (cur->skb)
3142 kfree_skb(cur->skb);
3143 cur->skb = NULL;
3144
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 pktgen_remove_device(t, cur);
3146 }
3147
Luiz Capitulino222f1802006-03-20 22:16:13 -08003148 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149}
3150
Luiz Capitulino222f1802006-03-20 22:16:13 -08003151static void pktgen_rem_thread(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003153 /* Remove from the thread list */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154
David S. Milleree74baa2007-01-01 20:51:53 -08003155 remove_proc_entry(t->tsk->comm, pg_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08003157 mutex_lock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08003159 list_del(&t->th_list);
3160
Luiz Capitulino6146e6a2006-03-20 22:24:45 -08003161 mutex_unlock(&pktgen_thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162}
3163
3164static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
3165{
3166 struct net_device *odev = NULL;
3167 __u64 idle_start = 0;
3168 int ret;
3169
3170 odev = pkt_dev->odev;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003171
Linus Torvalds1da177e2005-04-16 15:20:36 -07003172 if (pkt_dev->delay_us || pkt_dev->delay_ns) {
3173 u64 now;
3174
3175 now = getCurUs();
3176 if (now < pkt_dev->next_tx_us)
3177 spin(pkt_dev, pkt_dev->next_tx_us);
3178
3179 /* This is max DELAY, this has special meaning of
3180 * "never transmit"
3181 */
3182 if (pkt_dev->delay_us == 0x7FFFFFFF) {
3183 pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us;
3184 pkt_dev->next_tx_ns = pkt_dev->delay_ns;
3185 goto out;
3186 }
3187 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003188
Peter P Waskiewicz Jrf25f4e42007-07-06 13:36:20 -07003189 if ((netif_queue_stopped(odev) ||
3190 netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) ||
3191 need_resched()) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003192 idle_start = getCurUs();
Luiz Capitulino222f1802006-03-20 22:16:13 -08003193
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 if (!netif_running(odev)) {
3195 pktgen_stop_device(pkt_dev);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003196 if (pkt_dev->skb)
3197 kfree_skb(pkt_dev->skb);
3198 pkt_dev->skb = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 goto out;
3200 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003201 if (need_resched())
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202 schedule();
Luiz Capitulino222f1802006-03-20 22:16:13 -08003203
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 pkt_dev->idle_acc += getCurUs() - idle_start;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003205
Peter P Waskiewicz Jrf25f4e42007-07-06 13:36:20 -07003206 if (netif_queue_stopped(odev) ||
3207 netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08003208 pkt_dev->next_tx_us = getCurUs(); /* TODO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 pkt_dev->next_tx_ns = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003210 goto out; /* Try the next interface */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211 }
3212 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003213
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 if (pkt_dev->last_ok || !pkt_dev->skb) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08003215 if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
3216 || (!pkt_dev->skb)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217 /* build a new pkt */
Luiz Capitulino222f1802006-03-20 22:16:13 -08003218 if (pkt_dev->skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 kfree_skb(pkt_dev->skb);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003220
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221 pkt_dev->skb = fill_packet(odev, pkt_dev);
3222 if (pkt_dev->skb == NULL) {
3223 printk("pktgen: ERROR: couldn't allocate skb in fill_packet.\n");
3224 schedule();
Luiz Capitulino222f1802006-03-20 22:16:13 -08003225 pkt_dev->clone_count--; /* back out increment, OOM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 goto out;
3227 }
3228 pkt_dev->allocated_skbs++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003229 pkt_dev->clone_count = 0; /* reset counter */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230 }
3231 }
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003232
Herbert Xu932ff272006-06-09 12:20:56 -07003233 netif_tx_lock_bh(odev);
Peter P Waskiewicz Jrf25f4e42007-07-06 13:36:20 -07003234 if (!netif_queue_stopped(odev) &&
3235 !netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236
3237 atomic_inc(&(pkt_dev->skb->users));
Luiz Capitulino222f1802006-03-20 22:16:13 -08003238 retry_now:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 ret = odev->hard_start_xmit(pkt_dev->skb, odev);
3240 if (likely(ret == NETDEV_TX_OK)) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08003241 pkt_dev->last_ok = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242 pkt_dev->sofar++;
3243 pkt_dev->seq_num++;
3244 pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003245
3246 } else if (ret == NETDEV_TX_LOCKED
Linus Torvalds1da177e2005-04-16 15:20:36 -07003247 && (odev->features & NETIF_F_LLTX)) {
3248 cpu_relax();
3249 goto retry_now;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003250 } else { /* Retry it next time */
3251
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252 atomic_dec(&(pkt_dev->skb->users));
Luiz Capitulino222f1802006-03-20 22:16:13 -08003253
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254 if (debug && net_ratelimit())
3255 printk(KERN_INFO "pktgen: Hard xmit error\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -08003256
Linus Torvalds1da177e2005-04-16 15:20:36 -07003257 pkt_dev->errors++;
3258 pkt_dev->last_ok = 0;
3259 }
3260
3261 pkt_dev->next_tx_us = getCurUs();
3262 pkt_dev->next_tx_ns = 0;
3263
3264 pkt_dev->next_tx_us += pkt_dev->delay_us;
3265 pkt_dev->next_tx_ns += pkt_dev->delay_ns;
3266
3267 if (pkt_dev->next_tx_ns > 1000) {
3268 pkt_dev->next_tx_us++;
3269 pkt_dev->next_tx_ns -= 1000;
3270 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003271 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003272
Luiz Capitulino222f1802006-03-20 22:16:13 -08003273 else { /* Retry it next time */
3274 pkt_dev->last_ok = 0;
3275 pkt_dev->next_tx_us = getCurUs(); /* TODO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003276 pkt_dev->next_tx_ns = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003277 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003278
Herbert Xu932ff272006-06-09 12:20:56 -07003279 netif_tx_unlock_bh(odev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003280
Linus Torvalds1da177e2005-04-16 15:20:36 -07003281 /* If pkt_dev->count is zero, then run forever */
3282 if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
3283 if (atomic_read(&(pkt_dev->skb->users)) != 1) {
3284 idle_start = getCurUs();
3285 while (atomic_read(&(pkt_dev->skb->users)) != 1) {
3286 if (signal_pending(current)) {
3287 break;
3288 }
3289 schedule();
3290 }
3291 pkt_dev->idle_acc += getCurUs() - idle_start;
3292 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003293
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 /* Done with this */
3295 pktgen_stop_device(pkt_dev);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003296 if (pkt_dev->skb)
3297 kfree_skb(pkt_dev->skb);
3298 pkt_dev->skb = NULL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003299 }
3300out:;
3301}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003302
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09003303/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003304 * Main loop of the thread goes here
3305 */
3306
David S. Milleree74baa2007-01-01 20:51:53 -08003307static int pktgen_thread_worker(void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308{
3309 DEFINE_WAIT(wait);
David S. Milleree74baa2007-01-01 20:51:53 -08003310 struct pktgen_thread *t = arg;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003311 struct pktgen_dev *pkt_dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312 int cpu = t->cpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 u32 max_before_softirq;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003314 u32 tx_since_softirq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315
David S. Milleree74baa2007-01-01 20:51:53 -08003316 BUG_ON(smp_processor_id() != cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317
3318 init_waitqueue_head(&t->queue);
3319
Luiz Capitulino222f1802006-03-20 22:16:13 -08003320 t->pid = current->pid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003322 pr_debug("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323
3324 max_before_softirq = t->max_before_softirq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325
David S. Milleree74baa2007-01-01 20:51:53 -08003326 set_current_state(TASK_INTERRUPTIBLE);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003327
David S. Milleree74baa2007-01-01 20:51:53 -08003328 while (!kthread_should_stop()) {
3329 pkt_dev = next_to_run(t);
3330
3331 if (!pkt_dev &&
3332 (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV))
3333 == 0) {
3334 prepare_to_wait(&(t->queue), &wait,
3335 TASK_INTERRUPTIBLE);
3336 schedule_timeout(HZ / 10);
3337 finish_wait(&(t->queue), &wait);
3338 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003339
Linus Torvalds1da177e2005-04-16 15:20:36 -07003340 __set_current_state(TASK_RUNNING);
3341
Luiz Capitulino222f1802006-03-20 22:16:13 -08003342 if (pkt_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003343
3344 pktgen_xmit(pkt_dev);
3345
3346 /*
3347 * We like to stay RUNNING but must also give
3348 * others fair share.
3349 */
3350
3351 tx_since_softirq += pkt_dev->last_ok;
3352
3353 if (tx_since_softirq > max_before_softirq) {
3354 if (local_softirq_pending())
3355 do_softirq();
3356 tx_since_softirq = 0;
3357 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358 }
3359
Luiz Capitulino222f1802006-03-20 22:16:13 -08003360 if (t->control & T_STOP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361 pktgen_stop(t);
3362 t->control &= ~(T_STOP);
3363 }
3364
Luiz Capitulino222f1802006-03-20 22:16:13 -08003365 if (t->control & T_RUN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366 pktgen_run(t);
3367 t->control &= ~(T_RUN);
3368 }
3369
Luiz Capitulino222f1802006-03-20 22:16:13 -08003370 if (t->control & T_REMDEVALL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003371 pktgen_rem_all_ifs(t);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003372 t->control &= ~(T_REMDEVALL);
3373 }
3374
Luiz Capitulino222f1802006-03-20 22:16:13 -08003375 if (t->control & T_REMDEV) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003376 pktgen_rem_one_if(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 t->control &= ~(T_REMDEV);
3378 }
3379
Andrew Morton09fe3ef2007-04-12 14:45:32 -07003380 try_to_freeze();
3381
David S. Milleree74baa2007-01-01 20:51:53 -08003382 set_current_state(TASK_INTERRUPTIBLE);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003383 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003384
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003385 pr_debug("pktgen: %s stopping all device\n", t->tsk->comm);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003386 pktgen_stop(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003387
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003388 pr_debug("pktgen: %s removing all device\n", t->tsk->comm);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003389 pktgen_rem_all_ifs(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003391 pr_debug("pktgen: %s removing thread.\n", t->tsk->comm);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003392 pktgen_rem_thread(t);
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08003393
David S. Milleree74baa2007-01-01 20:51:53 -08003394 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395}
3396
Luiz Capitulino222f1802006-03-20 22:16:13 -08003397static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
3398 const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003399{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003400 struct pktgen_dev *p, *pkt_dev = NULL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003401 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003403 list_for_each_entry(p, &t->if_list, list)
Stephen Hemminger39df2322007-03-04 16:11:51 -08003404 if (strncmp(p->odev->name, ifname, IFNAMSIZ) == 0) {
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003405 pkt_dev = p;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003406 break;
3407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408
Luiz Capitulino222f1802006-03-20 22:16:13 -08003409 if_unlock(t);
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003410 pr_debug("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003411 return pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003412}
3413
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09003414/*
3415 * Adds a dev at front of if_list.
Linus Torvalds1da177e2005-04-16 15:20:36 -07003416 */
3417
Luiz Capitulino222f1802006-03-20 22:16:13 -08003418static int add_dev_to_thread(struct pktgen_thread *t,
3419 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003420{
3421 int rv = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422
Luiz Capitulino222f1802006-03-20 22:16:13 -08003423 if_lock(t);
3424
3425 if (pkt_dev->pg_thread) {
3426 printk("pktgen: ERROR: already assigned to a thread.\n");
3427 rv = -EBUSY;
3428 goto out;
3429 }
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003430
3431 list_add(&pkt_dev->list, &t->if_list);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003432 pkt_dev->pg_thread = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 pkt_dev->running = 0;
3434
Luiz Capitulino222f1802006-03-20 22:16:13 -08003435out:
3436 if_unlock(t);
3437 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003438}
3439
3440/* Called under thread lock */
3441
Luiz Capitulino222f1802006-03-20 22:16:13 -08003442static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003443{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003444 struct pktgen_dev *pkt_dev;
Stephen Hemminger39df2322007-03-04 16:11:51 -08003445 int err;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003446
Linus Torvalds1da177e2005-04-16 15:20:36 -07003447 /* We don't allow a device to be on several threads */
3448
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003449 pkt_dev = __pktgen_NN_threads(ifname, FIND);
3450 if (pkt_dev) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08003451 printk("pktgen: ERROR: interface already used.\n");
3452 return -EBUSY;
3453 }
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003454
3455 pkt_dev = kzalloc(sizeof(struct pktgen_dev), GFP_KERNEL);
3456 if (!pkt_dev)
3457 return -ENOMEM;
3458
Luiz Capitulino222f1802006-03-20 22:16:13 -08003459 pkt_dev->flows = vmalloc(MAX_CFLOWS * sizeof(struct flow_state));
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003460 if (pkt_dev->flows == NULL) {
3461 kfree(pkt_dev);
3462 return -ENOMEM;
3463 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003464 memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003465
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003466 pkt_dev->removal_mark = 0;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003467 pkt_dev->min_pkt_size = ETH_ZLEN;
3468 pkt_dev->max_pkt_size = ETH_ZLEN;
3469 pkt_dev->nfrags = 0;
3470 pkt_dev->clone_skb = pg_clone_skb_d;
3471 pkt_dev->delay_us = pg_delay_d / 1000;
3472 pkt_dev->delay_ns = pg_delay_d % 1000;
3473 pkt_dev->count = pg_count_d;
3474 pkt_dev->sofar = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003475 pkt_dev->udp_src_min = 9; /* sink port */
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003476 pkt_dev->udp_src_max = 9;
3477 pkt_dev->udp_dst_min = 9;
3478 pkt_dev->udp_dst_max = 9;
3479
Francesco Fondelli34954dd2006-09-27 16:30:44 -07003480 pkt_dev->vlan_p = 0;
3481 pkt_dev->vlan_cfi = 0;
3482 pkt_dev->vlan_id = 0xffff;
3483 pkt_dev->svlan_p = 0;
3484 pkt_dev->svlan_cfi = 0;
3485 pkt_dev->svlan_id = 0xffff;
3486
Stephen Hemminger39df2322007-03-04 16:11:51 -08003487 err = pktgen_setup_dev(pkt_dev, ifname);
3488 if (err)
3489 goto out1;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003490
Stephen Hemminger39df2322007-03-04 16:11:51 -08003491 pkt_dev->entry = create_proc_entry(ifname, 0600, pg_proc_dir);
3492 if (!pkt_dev->entry) {
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003493 printk("pktgen: cannot create %s/%s procfs entry.\n",
3494 PG_PROC_DIR, ifname);
Stephen Hemminger39df2322007-03-04 16:11:51 -08003495 err = -EINVAL;
3496 goto out2;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003497 }
Stephen Hemminger39df2322007-03-04 16:11:51 -08003498 pkt_dev->entry->proc_fops = &pktgen_if_fops;
3499 pkt_dev->entry->data = pkt_dev;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003500
3501 return add_dev_to_thread(t, pkt_dev);
Stephen Hemminger39df2322007-03-04 16:11:51 -08003502out2:
3503 dev_put(pkt_dev->odev);
3504out1:
3505 if (pkt_dev->flows)
3506 vfree(pkt_dev->flows);
3507 kfree(pkt_dev);
3508 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509}
3510
David S. Milleree74baa2007-01-01 20:51:53 -08003511static int __init pktgen_create_thread(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003512{
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08003513 struct pktgen_thread *t;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003514 struct proc_dir_entry *pe;
David S. Milleree74baa2007-01-01 20:51:53 -08003515 struct task_struct *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516
Luiz Capitulino222f1802006-03-20 22:16:13 -08003517 t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
3518 if (!t) {
3519 printk("pktgen: ERROR: out of memory, can't create new thread.\n");
3520 return -ENOMEM;
3521 }
3522
Luiz Capitulino222f1802006-03-20 22:16:13 -08003523 spin_lock_init(&t->if_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524 t->cpu = cpu;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003525
David S. Milleree74baa2007-01-01 20:51:53 -08003526 INIT_LIST_HEAD(&t->if_list);
3527
3528 list_add_tail(&t->th_list, &pktgen_threads);
3529
3530 p = kthread_create(pktgen_thread_worker, t, "kpktgend_%d", cpu);
3531 if (IS_ERR(p)) {
3532 printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
3533 list_del(&t->th_list);
3534 kfree(t);
3535 return PTR_ERR(p);
3536 }
3537 kthread_bind(p, cpu);
3538 t->tsk = p;
3539
3540 pe = create_proc_entry(t->tsk->comm, 0600, pg_proc_dir);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003541 if (!pe) {
3542 printk("pktgen: cannot create %s/%s procfs entry.\n",
David S. Milleree74baa2007-01-01 20:51:53 -08003543 PG_PROC_DIR, t->tsk->comm);
3544 kthread_stop(p);
3545 list_del(&t->th_list);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003546 kfree(t);
3547 return -EINVAL;
3548 }
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003549
3550 pe->proc_fops = &pktgen_thread_fops;
3551 pe->data = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552
David S. Milleree74baa2007-01-01 20:51:53 -08003553 wake_up_process(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554
3555 return 0;
3556}
3557
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09003558/*
3559 * Removes a device from the thread if_list.
Linus Torvalds1da177e2005-04-16 15:20:36 -07003560 */
Luiz Capitulino222f1802006-03-20 22:16:13 -08003561static void _rem_dev_from_if_list(struct pktgen_thread *t,
3562 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003563{
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003564 struct list_head *q, *n;
3565 struct pktgen_dev *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003566
Luiz Capitulinoc26a8012006-03-20 22:18:16 -08003567 list_for_each_safe(q, n, &t->if_list) {
3568 p = list_entry(q, struct pktgen_dev, list);
3569 if (p == pkt_dev)
3570 list_del(&p->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 }
3572}
3573
Luiz Capitulino222f1802006-03-20 22:16:13 -08003574static int pktgen_remove_device(struct pktgen_thread *t,
3575 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003576{
3577
Stephen Hemminger25c4e532007-03-04 16:06:47 -08003578 pr_debug("pktgen: remove_device pkt_dev=%p\n", pkt_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003579
Luiz Capitulino222f1802006-03-20 22:16:13 -08003580 if (pkt_dev->running) {
3581 printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n");
3582 pktgen_stop_device(pkt_dev);
3583 }
3584
3585 /* Dis-associate from the interface */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003586
3587 if (pkt_dev->odev) {
3588 dev_put(pkt_dev->odev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003589 pkt_dev->odev = NULL;
3590 }
3591
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 /* And update the thread if_list */
3593
3594 _rem_dev_from_if_list(t, pkt_dev);
3595
Stephen Hemminger39df2322007-03-04 16:11:51 -08003596 if (pkt_dev->entry)
3597 remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003598
3599 if (pkt_dev->flows)
3600 vfree(pkt_dev->flows);
3601 kfree(pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003602 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603}
3604
Luiz Capitulino222f1802006-03-20 22:16:13 -08003605static int __init pg_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606{
3607 int cpu;
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003608 struct proc_dir_entry *pe;
3609
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 printk(version);
3611
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003612 pg_proc_dir = proc_mkdir(PG_PROC_DIR, proc_net);
3613 if (!pg_proc_dir)
3614 return -ENODEV;
3615 pg_proc_dir->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003616
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003617 pe = create_proc_entry(PGCTRL, 0600, pg_proc_dir);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003618 if (pe == NULL) {
3619 printk("pktgen: ERROR: cannot create %s procfs entry.\n",
3620 PGCTRL);
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003621 proc_net_remove(PG_PROC_DIR);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003622 return -EINVAL;
3623 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003624
Luiz Capitulino222f1802006-03-20 22:16:13 -08003625 pe->proc_fops = &pktgen_fops;
3626 pe->data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627
3628 /* Register us to receive netdevice events */
3629 register_netdevice_notifier(&pktgen_notifier_block);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003630
John Hawkes670c02c2005-10-13 09:30:31 -07003631 for_each_online_cpu(cpu) {
Luiz Capitulino8024bb22006-03-20 22:17:55 -08003632 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003633
David S. Milleree74baa2007-01-01 20:51:53 -08003634 err = pktgen_create_thread(cpu);
Luiz Capitulino8024bb22006-03-20 22:17:55 -08003635 if (err)
3636 printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
3637 cpu, err);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003638 }
Luiz Capitulino8024bb22006-03-20 22:17:55 -08003639
3640 if (list_empty(&pktgen_threads)) {
3641 printk("pktgen: ERROR: Initialization failed for all threads\n");
3642 unregister_netdevice_notifier(&pktgen_notifier_block);
3643 remove_proc_entry(PGCTRL, pg_proc_dir);
3644 proc_net_remove(PG_PROC_DIR);
3645 return -ENODEV;
3646 }
3647
Luiz Capitulino222f1802006-03-20 22:16:13 -08003648 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649}
3650
3651static void __exit pg_cleanup(void)
3652{
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08003653 struct pktgen_thread *t;
3654 struct list_head *q, *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655 wait_queue_head_t queue;
3656 init_waitqueue_head(&queue);
3657
Luiz Capitulino222f1802006-03-20 22:16:13 -08003658 /* Stop all interfaces & threads */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659
Luiz Capitulinocdcdbe02006-03-20 22:16:40 -08003660 list_for_each_safe(q, n, &pktgen_threads) {
3661 t = list_entry(q, struct pktgen_thread, th_list);
David S. Milleree74baa2007-01-01 20:51:53 -08003662 kthread_stop(t->tsk);
3663 kfree(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003664 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665
Luiz Capitulino222f1802006-03-20 22:16:13 -08003666 /* Un-register us from receiving netdevice events */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667 unregister_netdevice_notifier(&pktgen_notifier_block);
3668
Luiz Capitulino222f1802006-03-20 22:16:13 -08003669 /* Clean up proc file system */
Stephen Hemmingerd50a6b562005-10-14 15:42:33 -07003670 remove_proc_entry(PGCTRL, pg_proc_dir);
3671 proc_net_remove(PG_PROC_DIR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003672}
3673
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674module_init(pg_init);
3675module_exit(pg_cleanup);
3676
3677MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
3678MODULE_DESCRIPTION("Packet Generator tool");
3679MODULE_LICENSE("GPL");
3680module_param(pg_count_d, int, 0);
3681module_param(pg_delay_d, int, 0);
3682module_param(pg_clone_skb_d, int, 0);
3683module_param(debug, int, 0);