blob: e49b006a76540855d3247a9732b5a41a1d929ca8 [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.
18 * The tool is implemented as a linux module. Parameters are output
19 * 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 *
47 * 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
49 * as a "fastpath" with a configurable number of clones after alloc's.
50 * 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
52 * clones.
53 *
54 * Also moved to /proc/net/pktgen/
55 * --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 *
63 *
64 * 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:
68 * 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
72 * way. The if_lock should be possible to remove when add/rem_device is merged
73 * into this too.
74 *
75 * By design there should only be *one* "controlling" process. In practice
76 * multiple write accesses gives unpredictable result. Understood by "write"
77 * 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 *
80 * 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.
82 * --ro
83 *
84 * Fix refcount off by one if first packet fails, potential null deref,
85 * 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>
92 *
93 * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
94 * ia64 compilation fix from Aron Griffis <aron@hp.com> 040604
95 *
96 * New xmit() return, do_div and misc clean up by Stephen Hemminger
97 * <shemminger@osdl.org> 040923
98 *
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -070099 * 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 *
104 * Corrections from Nikolai Malykh (nmalykh@bilim.com)
105 * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
106 *
107 * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
108 * 050103
109 */
110#include <linux/sys.h>
111#include <linux/types.h>
112#include <linux/module.h>
113#include <linux/moduleparam.h>
114#include <linux/kernel.h>
115#include <linux/smp_lock.h>
116#include <linux/sched.h>
117#include <linux/slab.h>
118#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119#include <linux/unistd.h>
120#include <linux/string.h>
121#include <linux/ptrace.h>
122#include <linux/errno.h>
123#include <linux/ioport.h>
124#include <linux/interrupt.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -0800125#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#include <linux/delay.h>
127#include <linux/timer.h>
128#include <linux/init.h>
129#include <linux/skbuff.h>
130#include <linux/netdevice.h>
131#include <linux/inet.h>
132#include <linux/inetdevice.h>
133#include <linux/rtnetlink.h>
134#include <linux/if_arp.h>
135#include <linux/in.h>
136#include <linux/ip.h>
137#include <linux/ipv6.h>
138#include <linux/udp.h>
139#include <linux/proc_fs.h>
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700140#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141#include <linux/wait.h>
Kris Katterjohnf404e9a2006-01-17 13:04:57 -0800142#include <linux/etherdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143#include <net/checksum.h>
144#include <net/ipv6.h>
145#include <net/addrconf.h>
146#include <asm/byteorder.h>
147#include <linux/rcupdate.h>
148#include <asm/bitops.h>
149#include <asm/io.h>
150#include <asm/dma.h>
151#include <asm/uaccess.h>
Luiz Capitulino222f1802006-03-20 22:16:13 -0800152#include <asm/div64.h> /* do_div */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153#include <asm/timex.h>
154
Arthur Kepner95ed63f2006-03-20 21:26:56 -0800155#define VERSION "pktgen v2.64: Packet Generator for packet performance testing.\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
157/* #define PG_DEBUG(a) a */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800158#define PG_DEBUG(a)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160/* The buckets are exponential in 'width' */
161#define LAT_BUCKETS_MAX 32
162#define IP_NAME_SZ 32
163
164/* Device flag bits */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800165#define F_IPSRC_RND (1<<0) /* IP-Src Random */
166#define F_IPDST_RND (1<<1) /* IP-Dst Random */
167#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
168#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
169#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
170#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
171#define F_TXSIZE_RND (1<<6) /* Transmit size is random */
172#define F_IPV6 (1<<7) /* Interface in IPV6 Mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174/* Thread control flag bits */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800175#define T_TERMINATE (1<<0)
176#define T_STOP (1<<1) /* Stop run */
177#define T_RUN (1<<2) /* Start run */
178#define T_REMDEVALL (1<<3) /* Remove all devs */
179#define T_REMDEV (1<<4) /* Remove one dev */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181/* Locks */
Stephen Hemmingerb7c89212005-10-14 15:26:34 -0700182#define thread_lock() down(&pktgen_sem)
183#define thread_unlock() up(&pktgen_sem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185/* If lock -- can be removed after some work */
186#define if_lock(t) spin_lock(&(t->if_lock));
187#define if_unlock(t) spin_unlock(&(t->if_lock));
188
189/* Used to help with determining the pkts on receive */
190#define PKTGEN_MAGIC 0xbe9be955
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700191#define PG_PROC_DIR "pktgen"
192#define PGCTRL "pgctrl"
193static struct proc_dir_entry *pg_proc_dir = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
195#define MAX_CFLOWS 65536
196
Luiz Capitulino222f1802006-03-20 22:16:13 -0800197struct flow_state {
198 __u32 cur_daddr;
199 int count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200};
201
202struct pktgen_dev {
203
204 /*
205 * Try to keep frequent/infrequent used vars. separated.
206 */
207
Luiz Capitulino222f1802006-03-20 22:16:13 -0800208 char ifname[IFNAMSIZ];
209 char result[512];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
Luiz Capitulino222f1802006-03-20 22:16:13 -0800211 struct pktgen_thread *pg_thread; /* the owner */
212 struct pktgen_dev *next; /* Used for chaining in the thread's run-queue */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Luiz Capitulino222f1802006-03-20 22:16:13 -0800214 int running; /* if this changes to false, the test will stop */
215
216 /* If min != max, then we will either do a linear iteration, or
217 * we will do a random selection from within the range.
218 */
219 __u32 flags;
Arthur Kepner95ed63f2006-03-20 21:26:56 -0800220 int removal_mark; /* non-zero => the device is marked for
221 * removal by worker thread */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
Luiz Capitulino222f1802006-03-20 22:16:13 -0800223 int min_pkt_size; /* = ETH_ZLEN; */
224 int max_pkt_size; /* = ETH_ZLEN; */
225 int nfrags;
226 __u32 delay_us; /* Default delay */
227 __u32 delay_ns;
228 __u64 count; /* Default No packets to send */
229 __u64 sofar; /* How many pkts we've sent so far */
230 __u64 tx_bytes; /* How many bytes we've transmitted */
231 __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Luiz Capitulino222f1802006-03-20 22:16:13 -0800233 /* runtime counters relating to clone_skb */
234 __u64 next_tx_us; /* timestamp of when to tx next */
235 __u32 next_tx_ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
Luiz Capitulino222f1802006-03-20 22:16:13 -0800237 __u64 allocated_skbs;
238 __u32 clone_count;
239 int last_ok; /* Was last skb sent?
240 * Or a failed transmit of some sort? This will keep
241 * sequence numbers in order, for example.
242 */
243 __u64 started_at; /* micro-seconds */
244 __u64 stopped_at; /* micro-seconds */
245 __u64 idle_acc; /* micro-seconds */
246 __u32 seq_num;
247
248 int clone_skb; /* Use multiple SKBs during packet gen. If this number
249 * is greater than 1, then that many copies of the same
250 * packet will be sent before a new packet is allocated.
251 * For instance, if you want to send 1024 identical packets
252 * before creating a new packet, set clone_skb to 1024.
253 */
254
255 char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
256 char dst_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
257 char src_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
258 char src_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
259
260 struct in6_addr in6_saddr;
261 struct in6_addr in6_daddr;
262 struct in6_addr cur_in6_daddr;
263 struct in6_addr cur_in6_saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 /* For ranges */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800265 struct in6_addr min_in6_daddr;
266 struct in6_addr max_in6_daddr;
267 struct in6_addr min_in6_saddr;
268 struct in6_addr max_in6_saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Luiz Capitulino222f1802006-03-20 22:16:13 -0800270 /* If we're doing ranges, random or incremental, then this
271 * defines the min/max for those ranges.
272 */
273 __u32 saddr_min; /* inclusive, source IP address */
274 __u32 saddr_max; /* exclusive, source IP address */
275 __u32 daddr_min; /* inclusive, dest IP address */
276 __u32 daddr_max; /* exclusive, dest IP address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Luiz Capitulino222f1802006-03-20 22:16:13 -0800278 __u16 udp_src_min; /* inclusive, source UDP port */
279 __u16 udp_src_max; /* exclusive, source UDP port */
280 __u16 udp_dst_min; /* inclusive, dest UDP port */
281 __u16 udp_dst_max; /* exclusive, dest UDP port */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Luiz Capitulino222f1802006-03-20 22:16:13 -0800283 __u32 src_mac_count; /* How many MACs to iterate through */
284 __u32 dst_mac_count; /* How many MACs to iterate through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
Luiz Capitulino222f1802006-03-20 22:16:13 -0800286 unsigned char dst_mac[ETH_ALEN];
287 unsigned char src_mac[ETH_ALEN];
288
289 __u32 cur_dst_mac_offset;
290 __u32 cur_src_mac_offset;
291 __u32 cur_saddr;
292 __u32 cur_daddr;
293 __u16 cur_udp_dst;
294 __u16 cur_udp_src;
295 __u32 cur_pkt_size;
296
297 __u8 hh[14];
298 /* = {
299 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
300
301 We fill in SRC address later
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x08, 0x00
304 };
305 */
306 __u16 pad; /* pad out the hh struct to an even 16 bytes */
307
308 struct sk_buff *skb; /* skb we are to transmit next, mainly used for when we
309 * are transmitting the same one multiple times
310 */
311 struct net_device *odev; /* The out-going device. Note that the device should
312 * have it's pg_info pointer pointing back to this
313 * device. This will be set when the user specifies
314 * the out-going device name (not when the inject is
315 * started as it used to do.)
316 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 struct flow_state *flows;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800318 unsigned cflows; /* Concurrent flows (config) */
319 unsigned lflow; /* Flow length (config) */
320 unsigned nflows; /* accumulated flows (stats) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321};
322
323struct pktgen_hdr {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800324 __u32 pgh_magic;
325 __u32 seq_num;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 __u32 tv_sec;
327 __u32 tv_usec;
328};
329
330struct pktgen_thread {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800331 spinlock_t if_lock;
332 struct pktgen_dev *if_list; /* All device here */
333 struct pktgen_thread *next;
334 char name[32];
335 char result[512];
336 u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Luiz Capitulino222f1802006-03-20 22:16:13 -0800338 /* Field for thread to receive "posted" events terminate, stop ifs etc. */
339
340 u32 control;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 int pid;
342 int cpu;
343
Luiz Capitulino222f1802006-03-20 22:16:13 -0800344 wait_queue_head_t queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345};
346
347#define REMOVE 1
348#define FIND 0
349
350/* This code works around the fact that do_div cannot handle two 64-bit
351 numbers, and regular 64-bit division doesn't work on x86 kernels.
352 --Ben
353*/
354
355#define PG_DIV 0
356
357/* This was emailed to LMKL by: Chris Caputo <ccaputo@alt.net>
358 * Function copied/adapted/optimized from:
359 *
360 * nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html
361 *
362 * Copyright 1994, University of Cambridge Computer Laboratory
363 * All Rights Reserved.
364 *
365 */
Jesper Juhl77933d72005-07-27 11:46:09 -0700366static inline s64 divremdi3(s64 x, s64 y, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800368 u64 a = (x < 0) ? -x : x;
369 u64 b = (y < 0) ? -y : y;
370 u64 res = 0, d = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Luiz Capitulino222f1802006-03-20 22:16:13 -0800372 if (b > 0) {
373 while (b < a) {
374 b <<= 1;
375 d <<= 1;
376 }
377 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Luiz Capitulino222f1802006-03-20 22:16:13 -0800379 do {
380 if (a >= b) {
381 a -= b;
382 res += d;
383 }
384 b >>= 1;
385 d >>= 1;
386 }
387 while (d);
388
389 if (PG_DIV == type) {
390 return (((x ^ y) & (1ll << 63)) == 0) ? res : -(s64) res;
391 } else {
392 return ((x & (1ll << 63)) == 0) ? a : -(s64) a;
393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
396/* End of hacks to deal with 64-bit math on x86 */
397
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700398/** Convert to milliseconds */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800399static inline __u64 tv_to_ms(const struct timeval *tv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800401 __u64 ms = tv->tv_usec / 1000;
402 ms += (__u64) tv->tv_sec * (__u64) 1000;
403 return ms;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406/** Convert to micro-seconds */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800407static inline __u64 tv_to_us(const struct timeval *tv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800409 __u64 us = tv->tv_usec;
410 us += (__u64) tv->tv_sec * (__u64) 1000000;
411 return us;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412}
413
Luiz Capitulino222f1802006-03-20 22:16:13 -0800414static inline __u64 pg_div(__u64 n, __u32 base)
415{
416 __u64 tmp = n;
417 do_div(tmp, base);
418 /* printk("pktgen: pg_div, n: %llu base: %d rv: %llu\n",
419 n, base, tmp); */
420 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
Luiz Capitulino222f1802006-03-20 22:16:13 -0800423static inline __u64 pg_div64(__u64 n, __u64 base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800425 __u64 tmp = n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426/*
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700427 * How do we know if the architecture we are running on
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 * supports division with 64 bit base?
429 *
430 */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800431#if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
Luiz Capitulino222f1802006-03-20 22:16:13 -0800433 do_div(tmp, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434#else
Luiz Capitulino222f1802006-03-20 22:16:13 -0800435 tmp = divremdi3(n, base, PG_DIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -0800437 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438}
439
440static inline u32 pktgen_random(void)
441{
442#if 0
443 __u32 n;
444 get_random_bytes(&n, 4);
445 return n;
446#else
447 return net_random();
448#endif
449}
450
Luiz Capitulino222f1802006-03-20 22:16:13 -0800451static inline __u64 getCurMs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800453 struct timeval tv;
454 do_gettimeofday(&tv);
455 return tv_to_ms(&tv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456}
457
Luiz Capitulino222f1802006-03-20 22:16:13 -0800458static inline __u64 getCurUs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800460 struct timeval tv;
461 do_gettimeofday(&tv);
462 return tv_to_us(&tv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
Luiz Capitulino222f1802006-03-20 22:16:13 -0800465static inline __u64 tv_diff(const struct timeval *a, const struct timeval *b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800467 return tv_to_us(a) - tv_to_us(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470/* old include end */
471
472static char version[] __initdata = VERSION;
473
Luiz Capitulino222f1802006-03-20 22:16:13 -0800474static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
475static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
476static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
477 const char *ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
479static void pktgen_run_all_threads(void);
480static void pktgen_stop_all_threads_ifs(void);
481static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800482static void pktgen_stop(struct pktgen_thread *t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800484static int pktgen_mark_device(const char *ifname);
485static unsigned int scan_ip6(const char *s, char ip[16]);
486static unsigned int fmt_ip6(char *s, const char ip[16]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488/* Module parameters, defaults. */
Luiz Capitulino222f1802006-03-20 22:16:13 -0800489static int pg_count_d = 1000; /* 1000 pkts by default */
Jaco Kroonf34fbb92005-12-22 12:51:46 -0800490static int pg_delay_d;
491static int pg_clone_skb_d;
492static int debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Stephen Hemmingerb7c89212005-10-14 15:26:34 -0700494static DECLARE_MUTEX(pktgen_sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495static struct pktgen_thread *pktgen_threads = NULL;
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497static struct notifier_block pktgen_notifier_block = {
498 .notifier_call = pktgen_device_event,
499};
500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501/*
502 * /proc handling functions
503 *
504 */
505
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700506static int pgctrl_show(struct seq_file *seq, void *v)
Luiz Capitulino222f1802006-03-20 22:16:13 -0800507{
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700508 seq_puts(seq, VERSION);
509 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510}
511
Luiz Capitulino222f1802006-03-20 22:16:13 -0800512static ssize_t pgctrl_write(struct file *file, const char __user * buf,
513 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 int err = 0;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700516 char data[128];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
Luiz Capitulino222f1802006-03-20 22:16:13 -0800518 if (!capable(CAP_NET_ADMIN)) {
519 err = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 goto out;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800521 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700523 if (count > sizeof(data))
524 count = sizeof(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 if (copy_from_user(data, buf, count)) {
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -0700527 err = -EFAULT;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700528 goto out;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800529 }
530 data[count - 1] = 0; /* Make string */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Luiz Capitulino222f1802006-03-20 22:16:13 -0800532 if (!strcmp(data, "stop"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 pktgen_stop_all_threads_ifs();
534
Luiz Capitulino222f1802006-03-20 22:16:13 -0800535 else if (!strcmp(data, "start"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 pktgen_run_all_threads();
537
Luiz Capitulino222f1802006-03-20 22:16:13 -0800538 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 printk("pktgen: Unknown command: %s\n", data);
540
541 err = count;
542
Luiz Capitulino222f1802006-03-20 22:16:13 -0800543out:
544 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545}
546
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700547static int pgctrl_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548{
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700549 return single_open(file, pgctrl_show, PDE(inode)->data);
550}
551
552static struct file_operations pktgen_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800553 .owner = THIS_MODULE,
554 .open = pgctrl_open,
555 .read = seq_read,
556 .llseek = seq_lseek,
557 .write = pgctrl_write,
558 .release = single_release,
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700559};
560
561static int pktgen_if_show(struct seq_file *seq, void *v)
562{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 int i;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800564 struct pktgen_dev *pkt_dev = seq->private;
565 __u64 sa;
566 __u64 stopped;
567 __u64 now = getCurUs();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Luiz Capitulino222f1802006-03-20 22:16:13 -0800569 seq_printf(seq,
570 "Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
571 (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
572 pkt_dev->max_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Luiz Capitulino222f1802006-03-20 22:16:13 -0800574 seq_printf(seq,
575 " frags: %d delay: %u clone_skb: %d ifname: %s\n",
576 pkt_dev->nfrags,
577 1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
578 pkt_dev->clone_skb, pkt_dev->ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Luiz Capitulino222f1802006-03-20 22:16:13 -0800580 seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows,
581 pkt_dev->lflow);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
Luiz Capitulino222f1802006-03-20 22:16:13 -0800583 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 char b1[128], b2[128], b3[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800585 fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
586 fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
587 fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
588 seq_printf(seq,
589 " saddr: %s min_saddr: %s max_saddr: %s\n", b1,
590 b2, b3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Luiz Capitulino222f1802006-03-20 22:16:13 -0800592 fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
593 fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
594 fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
595 seq_printf(seq,
596 " daddr: %s min_daddr: %s max_daddr: %s\n", b1,
597 b2, b3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Luiz Capitulino222f1802006-03-20 22:16:13 -0800599 } else
600 seq_printf(seq,
601 " dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n",
602 pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min,
603 pkt_dev->src_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700605 seq_puts(seq, " src_mac: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Kris Katterjohnf404e9a2006-01-17 13:04:57 -0800607 if (is_zero_ether_addr(pkt_dev->src_mac))
Luiz Capitulino222f1802006-03-20 22:16:13 -0800608 for (i = 0; i < 6; i++)
609 seq_printf(seq, "%02X%s", pkt_dev->odev->dev_addr[i],
610 i == 5 ? " " : ":");
611 else
612 for (i = 0; i < 6; i++)
613 seq_printf(seq, "%02X%s", pkt_dev->src_mac[i],
614 i == 5 ? " " : ":");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
Luiz Capitulino222f1802006-03-20 22:16:13 -0800616 seq_printf(seq, "dst_mac: ");
617 for (i = 0; i < 6; i++)
618 seq_printf(seq, "%02X%s", pkt_dev->dst_mac[i],
619 i == 5 ? "\n" : ":");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
Luiz Capitulino222f1802006-03-20 22:16:13 -0800621 seq_printf(seq,
622 " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
623 pkt_dev->udp_src_min, pkt_dev->udp_src_max,
624 pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Luiz Capitulino222f1802006-03-20 22:16:13 -0800626 seq_printf(seq,
627 " src_mac_count: %d dst_mac_count: %d \n Flags: ",
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700628 pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Luiz Capitulino222f1802006-03-20 22:16:13 -0800630 if (pkt_dev->flags & F_IPV6)
631 seq_printf(seq, "IPV6 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Luiz Capitulino222f1802006-03-20 22:16:13 -0800633 if (pkt_dev->flags & F_IPSRC_RND)
634 seq_printf(seq, "IPSRC_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Luiz Capitulino222f1802006-03-20 22:16:13 -0800636 if (pkt_dev->flags & F_IPDST_RND)
637 seq_printf(seq, "IPDST_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Luiz Capitulino222f1802006-03-20 22:16:13 -0800639 if (pkt_dev->flags & F_TXSIZE_RND)
640 seq_printf(seq, "TXSIZE_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Luiz Capitulino222f1802006-03-20 22:16:13 -0800642 if (pkt_dev->flags & F_UDPSRC_RND)
643 seq_printf(seq, "UDPSRC_RND ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Luiz Capitulino222f1802006-03-20 22:16:13 -0800645 if (pkt_dev->flags & F_UDPDST_RND)
646 seq_printf(seq, "UDPDST_RND ");
647
648 if (pkt_dev->flags & F_MACSRC_RND)
649 seq_printf(seq, "MACSRC_RND ");
650
651 if (pkt_dev->flags & F_MACDST_RND)
652 seq_printf(seq, "MACDST_RND ");
653
654 seq_puts(seq, "\n");
655
656 sa = pkt_dev->started_at;
657 stopped = pkt_dev->stopped_at;
658 if (pkt_dev->running)
659 stopped = now; /* not really stopped, more like last-running-at */
660
661 seq_printf(seq,
662 "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n",
663 (unsigned long long)pkt_dev->sofar,
664 (unsigned long long)pkt_dev->errors, (unsigned long long)sa,
665 (unsigned long long)stopped,
666 (unsigned long long)pkt_dev->idle_acc);
667
668 seq_printf(seq,
669 " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700670 pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
671 pkt_dev->cur_src_mac_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Luiz Capitulino222f1802006-03-20 22:16:13 -0800673 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 char b1[128], b2[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800675 fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
676 fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
677 seq_printf(seq, " cur_saddr: %s cur_daddr: %s\n", b2, b1);
678 } else
679 seq_printf(seq, " cur_saddr: 0x%x cur_daddr: 0x%x\n",
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700680 pkt_dev->cur_saddr, pkt_dev->cur_daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
Luiz Capitulino222f1802006-03-20 22:16:13 -0800682 seq_printf(seq, " cur_udp_dst: %d cur_udp_src: %d\n",
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700683 pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Luiz Capitulino222f1802006-03-20 22:16:13 -0800685 seq_printf(seq, " flows: %u\n", pkt_dev->nflows);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687 if (pkt_dev->result[0])
Luiz Capitulino222f1802006-03-20 22:16:13 -0800688 seq_printf(seq, "Result: %s\n", pkt_dev->result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 else
Luiz Capitulino222f1802006-03-20 22:16:13 -0800690 seq_printf(seq, "Result: Idle\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700692 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693}
694
Luiz Capitulino222f1802006-03-20 22:16:13 -0800695static int count_trail_chars(const char __user * user_buffer,
696 unsigned int maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
698 int i;
699
700 for (i = 0; i < maxlen; i++) {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800701 char c;
702 if (get_user(c, &user_buffer[i]))
703 return -EFAULT;
704 switch (c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 case '\"':
706 case '\n':
707 case '\r':
708 case '\t':
709 case ' ':
710 case '=':
711 break;
712 default:
713 goto done;
714 };
715 }
716done:
717 return i;
718}
719
Luiz Capitulino222f1802006-03-20 22:16:13 -0800720static unsigned long num_arg(const char __user * user_buffer,
721 unsigned long maxlen, unsigned long *num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722{
723 int i = 0;
724 *num = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800725
726 for (; i < maxlen; i++) {
727 char c;
728 if (get_user(c, &user_buffer[i]))
729 return -EFAULT;
730 if ((c >= '0') && (c <= '9')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 *num *= 10;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800732 *num += c - '0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 } else
734 break;
735 }
736 return i;
737}
738
Luiz Capitulino222f1802006-03-20 22:16:13 -0800739static int strn_len(const char __user * user_buffer, unsigned int maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 int i = 0;
742
Luiz Capitulino222f1802006-03-20 22:16:13 -0800743 for (; i < maxlen; i++) {
744 char c;
745 if (get_user(c, &user_buffer[i]))
746 return -EFAULT;
747 switch (c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 case '\"':
749 case '\n':
750 case '\r':
751 case '\t':
752 case ' ':
753 goto done_str;
754 break;
755 default:
756 break;
757 };
758 }
759done_str:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return i;
761}
762
Luiz Capitulino222f1802006-03-20 22:16:13 -0800763static ssize_t pktgen_if_write(struct file *file,
764 const char __user * user_buffer, size_t count,
765 loff_t * offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
Luiz Capitulino222f1802006-03-20 22:16:13 -0800767 struct seq_file *seq = (struct seq_file *)file->private_data;
768 struct pktgen_dev *pkt_dev = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 int i = 0, max, len;
770 char name[16], valstr[32];
771 unsigned long value = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800772 char *pg_result = NULL;
773 int tmp = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 char buf[128];
Luiz Capitulino222f1802006-03-20 22:16:13 -0800775
776 pg_result = &(pkt_dev->result[0]);
777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 if (count < 1) {
779 printk("pktgen: wrong command format\n");
780 return -EINVAL;
781 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 max = count - i;
784 tmp = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800785 if (tmp < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 printk("pktgen: illegal format\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -0800787 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800789 i += tmp;
790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 /* Read variable name */
792
793 len = strn_len(&user_buffer[i], sizeof(name) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800794 if (len < 0) {
795 return len;
796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 memset(name, 0, sizeof(name));
Luiz Capitulino222f1802006-03-20 22:16:13 -0800798 if (copy_from_user(name, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 return -EFAULT;
800 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800801
802 max = count - i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 len = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800804 if (len < 0)
805 return len;
806
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 i += len;
808
809 if (debug) {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800810 char tb[count + 1];
811 if (copy_from_user(tb, user_buffer, count))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800813 tb[count] = 0;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -0700814 printk("pktgen: %s,%lu buffer -:%s:-\n", name,
Luiz Capitulino222f1802006-03-20 22:16:13 -0800815 (unsigned long)count, tb);
816 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 if (!strcmp(name, "min_pkt_size")) {
819 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800820 if (len < 0) {
821 return len;
822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800824 if (value < 14 + 20 + 8)
825 value = 14 + 20 + 8;
826 if (value != pkt_dev->min_pkt_size) {
827 pkt_dev->min_pkt_size = value;
828 pkt_dev->cur_pkt_size = value;
829 }
830 sprintf(pg_result, "OK: min_pkt_size=%u",
831 pkt_dev->min_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return count;
833 }
834
Luiz Capitulino222f1802006-03-20 22:16:13 -0800835 if (!strcmp(name, "max_pkt_size")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800837 if (len < 0) {
838 return len;
839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800841 if (value < 14 + 20 + 8)
842 value = 14 + 20 + 8;
843 if (value != pkt_dev->max_pkt_size) {
844 pkt_dev->max_pkt_size = value;
845 pkt_dev->cur_pkt_size = value;
846 }
847 sprintf(pg_result, "OK: max_pkt_size=%u",
848 pkt_dev->max_pkt_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 return count;
850 }
851
Luiz Capitulino222f1802006-03-20 22:16:13 -0800852 /* Shortcut for min = max */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
854 if (!strcmp(name, "pkt_size")) {
855 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800856 if (len < 0) {
857 return len;
858 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800860 if (value < 14 + 20 + 8)
861 value = 14 + 20 + 8;
862 if (value != pkt_dev->min_pkt_size) {
863 pkt_dev->min_pkt_size = value;
864 pkt_dev->max_pkt_size = value;
865 pkt_dev->cur_pkt_size = value;
866 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
868 return count;
869 }
870
Luiz Capitulino222f1802006-03-20 22:16:13 -0800871 if (!strcmp(name, "debug")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800873 if (len < 0) {
874 return len;
875 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800877 debug = value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 sprintf(pg_result, "OK: debug=%u", debug);
879 return count;
880 }
881
Luiz Capitulino222f1802006-03-20 22:16:13 -0800882 if (!strcmp(name, "frags")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800884 if (len < 0) {
885 return len;
886 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 i += len;
888 pkt_dev->nfrags = value;
889 sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
890 return count;
891 }
892 if (!strcmp(name, "delay")) {
893 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800894 if (len < 0) {
895 return len;
896 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 i += len;
898 if (value == 0x7FFFFFFF) {
899 pkt_dev->delay_us = 0x7FFFFFFF;
900 pkt_dev->delay_ns = 0;
901 } else {
902 pkt_dev->delay_us = value / 1000;
903 pkt_dev->delay_ns = value % 1000;
904 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800905 sprintf(pg_result, "OK: delay=%u",
906 1000 * pkt_dev->delay_us + pkt_dev->delay_ns);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return count;
908 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800909 if (!strcmp(name, "udp_src_min")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800911 if (len < 0) {
912 return len;
913 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800915 if (value != pkt_dev->udp_src_min) {
916 pkt_dev->udp_src_min = value;
917 pkt_dev->cur_udp_src = value;
918 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
920 return count;
921 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800922 if (!strcmp(name, "udp_dst_min")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800924 if (len < 0) {
925 return len;
926 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800928 if (value != pkt_dev->udp_dst_min) {
929 pkt_dev->udp_dst_min = value;
930 pkt_dev->cur_udp_dst = value;
931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
933 return count;
934 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800935 if (!strcmp(name, "udp_src_max")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 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 != pkt_dev->udp_src_max) {
942 pkt_dev->udp_src_max = value;
943 pkt_dev->cur_udp_src = value;
944 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
946 return count;
947 }
Luiz Capitulino222f1802006-03-20 22:16:13 -0800948 if (!strcmp(name, "udp_dst_max")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800950 if (len < 0) {
951 return len;
952 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800954 if (value != pkt_dev->udp_dst_max) {
955 pkt_dev->udp_dst_max = value;
956 pkt_dev->cur_udp_dst = value;
957 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
959 return count;
960 }
961 if (!strcmp(name, "clone_skb")) {
962 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800963 if (len < 0) {
964 return len;
965 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -0800967 pkt_dev->clone_skb = value;
968
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
970 return count;
971 }
972 if (!strcmp(name, "count")) {
973 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800974 if (len < 0) {
975 return len;
976 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 i += len;
978 pkt_dev->count = value;
979 sprintf(pg_result, "OK: count=%llu",
Luiz Capitulino222f1802006-03-20 22:16:13 -0800980 (unsigned long long)pkt_dev->count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 return count;
982 }
983 if (!strcmp(name, "src_mac_count")) {
984 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800985 if (len < 0) {
986 return len;
987 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 i += len;
989 if (pkt_dev->src_mac_count != value) {
Luiz Capitulino222f1802006-03-20 22:16:13 -0800990 pkt_dev->src_mac_count = value;
991 pkt_dev->cur_src_mac_offset = 0;
992 }
993 sprintf(pg_result, "OK: src_mac_count=%d",
994 pkt_dev->src_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 return count;
996 }
997 if (!strcmp(name, "dst_mac_count")) {
998 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -0800999 if (len < 0) {
1000 return len;
1001 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 i += len;
1003 if (pkt_dev->dst_mac_count != value) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001004 pkt_dev->dst_mac_count = value;
1005 pkt_dev->cur_dst_mac_offset = 0;
1006 }
1007 sprintf(pg_result, "OK: dst_mac_count=%d",
1008 pkt_dev->dst_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 return count;
1010 }
1011 if (!strcmp(name, "flag")) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001012 char f[32];
1013 memset(f, 0, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 len = strn_len(&user_buffer[i], sizeof(f) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001015 if (len < 0) {
1016 return len;
1017 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 if (copy_from_user(f, &user_buffer[i], len))
1019 return -EFAULT;
1020 i += len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001021 if (strcmp(f, "IPSRC_RND") == 0)
1022 pkt_dev->flags |= F_IPSRC_RND;
1023
1024 else if (strcmp(f, "!IPSRC_RND") == 0)
1025 pkt_dev->flags &= ~F_IPSRC_RND;
1026
1027 else if (strcmp(f, "TXSIZE_RND") == 0)
1028 pkt_dev->flags |= F_TXSIZE_RND;
1029
1030 else if (strcmp(f, "!TXSIZE_RND") == 0)
1031 pkt_dev->flags &= ~F_TXSIZE_RND;
1032
1033 else if (strcmp(f, "IPDST_RND") == 0)
1034 pkt_dev->flags |= F_IPDST_RND;
1035
1036 else if (strcmp(f, "!IPDST_RND") == 0)
1037 pkt_dev->flags &= ~F_IPDST_RND;
1038
1039 else if (strcmp(f, "UDPSRC_RND") == 0)
1040 pkt_dev->flags |= F_UDPSRC_RND;
1041
1042 else if (strcmp(f, "!UDPSRC_RND") == 0)
1043 pkt_dev->flags &= ~F_UDPSRC_RND;
1044
1045 else if (strcmp(f, "UDPDST_RND") == 0)
1046 pkt_dev->flags |= F_UDPDST_RND;
1047
1048 else if (strcmp(f, "!UDPDST_RND") == 0)
1049 pkt_dev->flags &= ~F_UDPDST_RND;
1050
1051 else if (strcmp(f, "MACSRC_RND") == 0)
1052 pkt_dev->flags |= F_MACSRC_RND;
1053
1054 else if (strcmp(f, "!MACSRC_RND") == 0)
1055 pkt_dev->flags &= ~F_MACSRC_RND;
1056
1057 else if (strcmp(f, "MACDST_RND") == 0)
1058 pkt_dev->flags |= F_MACDST_RND;
1059
1060 else if (strcmp(f, "!MACDST_RND") == 0)
1061 pkt_dev->flags &= ~F_MACDST_RND;
1062
1063 else {
1064 sprintf(pg_result,
1065 "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
1066 f,
1067 "IPSRC_RND, IPDST_RND, TXSIZE_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
1068 return count;
1069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
1071 return count;
1072 }
1073 if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
1074 len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001075 if (len < 0) {
1076 return len;
1077 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
Luiz Capitulino222f1802006-03-20 22:16:13 -08001079 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001081 buf[len] = 0;
1082 if (strcmp(buf, pkt_dev->dst_min) != 0) {
1083 memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
1084 strncpy(pkt_dev->dst_min, buf, len);
1085 pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
1086 pkt_dev->cur_daddr = pkt_dev->daddr_min;
1087 }
1088 if (debug)
1089 printk("pktgen: dst_min set to: %s\n",
1090 pkt_dev->dst_min);
1091 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
1093 return count;
1094 }
1095 if (!strcmp(name, "dst_max")) {
1096 len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001097 if (len < 0) {
1098 return len;
1099 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
Luiz Capitulino222f1802006-03-20 22:16:13 -08001101 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 return -EFAULT;
1103
Luiz Capitulino222f1802006-03-20 22:16:13 -08001104 buf[len] = 0;
1105 if (strcmp(buf, pkt_dev->dst_max) != 0) {
1106 memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
1107 strncpy(pkt_dev->dst_max, buf, len);
1108 pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
1109 pkt_dev->cur_daddr = pkt_dev->daddr_max;
1110 }
1111 if (debug)
1112 printk("pktgen: dst_max set to: %s\n",
1113 pkt_dev->dst_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 i += len;
1115 sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
1116 return count;
1117 }
1118 if (!strcmp(name, "dst6")) {
1119 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001120 if (len < 0)
1121 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
1123 pkt_dev->flags |= F_IPV6;
1124
Luiz Capitulino222f1802006-03-20 22:16:13 -08001125 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001127 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
1129 scan_ip6(buf, pkt_dev->in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001130 fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131
1132 ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr);
1133
Luiz Capitulino222f1802006-03-20 22:16:13 -08001134 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 printk("pktgen: dst6 set to: %s\n", buf);
1136
Luiz Capitulino222f1802006-03-20 22:16:13 -08001137 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 sprintf(pg_result, "OK: dst6=%s", buf);
1139 return count;
1140 }
1141 if (!strcmp(name, "dst6_min")) {
1142 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001143 if (len < 0)
1144 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 pkt_dev->flags |= F_IPV6;
1147
Luiz Capitulino222f1802006-03-20 22:16:13 -08001148 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001150 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
1152 scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001153 fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
Luiz Capitulino222f1802006-03-20 22:16:13 -08001155 ipv6_addr_copy(&pkt_dev->cur_in6_daddr,
1156 &pkt_dev->min_in6_daddr);
1157 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 printk("pktgen: dst6_min set to: %s\n", buf);
1159
Luiz Capitulino222f1802006-03-20 22:16:13 -08001160 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 sprintf(pg_result, "OK: dst6_min=%s", buf);
1162 return count;
1163 }
1164 if (!strcmp(name, "dst6_max")) {
1165 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001166 if (len < 0)
1167 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
1169 pkt_dev->flags |= F_IPV6;
1170
Luiz Capitulino222f1802006-03-20 22:16:13 -08001171 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001173 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
1175 scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001176 fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
Luiz Capitulino222f1802006-03-20 22:16:13 -08001178 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 printk("pktgen: dst6_max set to: %s\n", buf);
1180
Luiz Capitulino222f1802006-03-20 22:16:13 -08001181 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 sprintf(pg_result, "OK: dst6_max=%s", buf);
1183 return count;
1184 }
1185 if (!strcmp(name, "src6")) {
1186 len = strn_len(&user_buffer[i], sizeof(buf) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001187 if (len < 0)
1188 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 pkt_dev->flags |= F_IPV6;
1191
Luiz Capitulino222f1802006-03-20 22:16:13 -08001192 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001194 buf[len] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 scan_ip6(buf, pkt_dev->in6_saddr.s6_addr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001197 fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
1199 ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr);
1200
Luiz Capitulino222f1802006-03-20 22:16:13 -08001201 if (debug)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 printk("pktgen: src6 set to: %s\n", buf);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001203
1204 i += len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 sprintf(pg_result, "OK: src6=%s", buf);
1206 return count;
1207 }
1208 if (!strcmp(name, "src_min")) {
1209 len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001210 if (len < 0) {
1211 return len;
1212 }
1213 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001215 buf[len] = 0;
1216 if (strcmp(buf, pkt_dev->src_min) != 0) {
1217 memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
1218 strncpy(pkt_dev->src_min, buf, len);
1219 pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
1220 pkt_dev->cur_saddr = pkt_dev->saddr_min;
1221 }
1222 if (debug)
1223 printk("pktgen: src_min set to: %s\n",
1224 pkt_dev->src_min);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 i += len;
1226 sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
1227 return count;
1228 }
1229 if (!strcmp(name, "src_max")) {
1230 len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001231 if (len < 0) {
1232 return len;
1233 }
1234 if (copy_from_user(buf, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 return -EFAULT;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001236 buf[len] = 0;
1237 if (strcmp(buf, pkt_dev->src_max) != 0) {
1238 memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
1239 strncpy(pkt_dev->src_max, buf, len);
1240 pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
1241 pkt_dev->cur_saddr = pkt_dev->saddr_max;
1242 }
1243 if (debug)
1244 printk("pktgen: src_max set to: %s\n",
1245 pkt_dev->src_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 i += len;
1247 sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
1248 return count;
1249 }
1250 if (!strcmp(name, "dst_mac")) {
1251 char *v = valstr;
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001252 unsigned char old_dmac[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 unsigned char *m = pkt_dev->dst_mac;
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001254 memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001255
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001257 if (len < 0) {
1258 return len;
1259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 memset(valstr, 0, sizeof(valstr));
Luiz Capitulino222f1802006-03-20 22:16:13 -08001261 if (copy_from_user(valstr, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 return -EFAULT;
1263 i += len;
1264
Luiz Capitulino222f1802006-03-20 22:16:13 -08001265 for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 if (*v >= '0' && *v <= '9') {
1267 *m *= 16;
1268 *m += *v - '0';
1269 }
1270 if (*v >= 'A' && *v <= 'F') {
1271 *m *= 16;
1272 *m += *v - 'A' + 10;
1273 }
1274 if (*v >= 'a' && *v <= 'f') {
1275 *m *= 16;
1276 *m += *v - 'a' + 10;
1277 }
1278 if (*v == ':') {
1279 m++;
1280 *m = 0;
1281 }
1282 }
1283
1284 /* Set up Dest MAC */
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001285 if (compare_ether_addr(old_dmac, pkt_dev->dst_mac))
1286 memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 sprintf(pg_result, "OK: dstmac");
1289 return count;
1290 }
1291 if (!strcmp(name, "src_mac")) {
1292 char *v = valstr;
1293 unsigned char *m = pkt_dev->src_mac;
1294
1295 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001296 if (len < 0) {
1297 return len;
1298 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 memset(valstr, 0, sizeof(valstr));
Luiz Capitulino222f1802006-03-20 22:16:13 -08001300 if (copy_from_user(valstr, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 return -EFAULT;
1302 i += len;
1303
Luiz Capitulino222f1802006-03-20 22:16:13 -08001304 for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 if (*v >= '0' && *v <= '9') {
1306 *m *= 16;
1307 *m += *v - '0';
1308 }
1309 if (*v >= 'A' && *v <= 'F') {
1310 *m *= 16;
1311 *m += *v - 'A' + 10;
1312 }
1313 if (*v >= 'a' && *v <= 'f') {
1314 *m *= 16;
1315 *m += *v - 'a' + 10;
1316 }
1317 if (*v == ':') {
1318 m++;
1319 *m = 0;
1320 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001321 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Luiz Capitulino222f1802006-03-20 22:16:13 -08001323 sprintf(pg_result, "OK: srcmac");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 return count;
1325 }
1326
Luiz Capitulino222f1802006-03-20 22:16:13 -08001327 if (!strcmp(name, "clear_counters")) {
1328 pktgen_clear_counters(pkt_dev);
1329 sprintf(pg_result, "OK: Clearing counters.\n");
1330 return count;
1331 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
1333 if (!strcmp(name, "flows")) {
1334 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001335 if (len < 0) {
1336 return len;
1337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 i += len;
1339 if (value > MAX_CFLOWS)
1340 value = MAX_CFLOWS;
1341
1342 pkt_dev->cflows = value;
1343 sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
1344 return count;
1345 }
1346
1347 if (!strcmp(name, "flowlen")) {
1348 len = num_arg(&user_buffer[i], 10, &value);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001349 if (len < 0) {
1350 return len;
1351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 i += len;
1353 pkt_dev->lflow = value;
1354 sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
1355 return count;
1356 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001357
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
1359 return -EINVAL;
1360}
1361
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001362static int pktgen_if_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363{
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001364 return single_open(file, pktgen_if_show, PDE(inode)->data);
1365}
1366
1367static struct file_operations pktgen_if_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001368 .owner = THIS_MODULE,
1369 .open = pktgen_if_open,
1370 .read = seq_read,
1371 .llseek = seq_lseek,
1372 .write = pktgen_if_write,
1373 .release = single_release,
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001374};
1375
1376static int pktgen_thread_show(struct seq_file *seq, void *v)
1377{
Luiz Capitulino222f1802006-03-20 22:16:13 -08001378 struct pktgen_thread *t = seq->private;
1379 struct pktgen_dev *pkt_dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001381 BUG_ON(!t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001383 seq_printf(seq, "Name: %s max_before_softirq: %d\n",
Luiz Capitulino222f1802006-03-20 22:16:13 -08001384 t->name, t->max_before_softirq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
Luiz Capitulino222f1802006-03-20 22:16:13 -08001386 seq_printf(seq, "Running: ");
1387
1388 if_lock(t);
1389 for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
1390 if (pkt_dev->running)
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001391 seq_printf(seq, "%s ", pkt_dev->ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
Luiz Capitulino222f1802006-03-20 22:16:13 -08001393 seq_printf(seq, "\nStopped: ");
1394
1395 for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
1396 if (!pkt_dev->running)
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001397 seq_printf(seq, "%s ", pkt_dev->ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
1399 if (t->result[0])
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001400 seq_printf(seq, "\nResult: %s\n", t->result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 else
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001402 seq_printf(seq, "\nResult: NA\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403
Luiz Capitulino222f1802006-03-20 22:16:13 -08001404 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001406 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407}
1408
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001409static ssize_t pktgen_thread_write(struct file *file,
Luiz Capitulino222f1802006-03-20 22:16:13 -08001410 const char __user * user_buffer,
1411 size_t count, loff_t * offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412{
Luiz Capitulino222f1802006-03-20 22:16:13 -08001413 struct seq_file *seq = (struct seq_file *)file->private_data;
1414 struct pktgen_thread *t = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 int i = 0, max, len, ret;
1416 char name[40];
Luiz Capitulino222f1802006-03-20 22:16:13 -08001417 char *pg_result;
1418 unsigned long value = 0;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (count < 1) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001421 // sprintf(pg_result, "Wrong command format");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 return -EINVAL;
1423 }
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001424
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 max = count - i;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001426 len = count_trail_chars(&user_buffer[i], max);
1427 if (len < 0)
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001428 return len;
1429
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 i += len;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001431
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 /* Read variable name */
1433
1434 len = strn_len(&user_buffer[i], sizeof(name) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001435 if (len < 0)
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001436 return len;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001437
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 memset(name, 0, sizeof(name));
1439 if (copy_from_user(name, &user_buffer[i], len))
1440 return -EFAULT;
1441 i += len;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001442
Luiz Capitulino222f1802006-03-20 22:16:13 -08001443 max = count - i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 len = count_trail_chars(&user_buffer[i], max);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001445 if (len < 0)
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001446 return len;
1447
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 i += len;
1449
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001450 if (debug)
Luiz Capitulino222f1802006-03-20 22:16:13 -08001451 printk("pktgen: t=%s, count=%lu\n", name, (unsigned long)count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
Luiz Capitulino222f1802006-03-20 22:16:13 -08001453 if (!t) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 printk("pktgen: ERROR: No thread\n");
1455 ret = -EINVAL;
1456 goto out;
1457 }
1458
1459 pg_result = &(t->result[0]);
1460
Luiz Capitulino222f1802006-03-20 22:16:13 -08001461 if (!strcmp(name, "add_device")) {
1462 char f[32];
1463 memset(f, 0, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 len = strn_len(&user_buffer[i], sizeof(f) - 1);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001465 if (len < 0) {
1466 ret = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 goto out;
1468 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001469 if (copy_from_user(f, &user_buffer[i], len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 return -EFAULT;
1471 i += len;
1472 thread_lock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001473 pktgen_add_device(t, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 thread_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001475 ret = count;
1476 sprintf(pg_result, "OK: add_device=%s", f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 goto out;
1478 }
1479
Luiz Capitulino222f1802006-03-20 22:16:13 -08001480 if (!strcmp(name, "rem_device_all")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 thread_lock();
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001482 t->control |= T_REMDEVALL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 thread_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001484 schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 ret = count;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001486 sprintf(pg_result, "OK: rem_device_all");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 goto out;
1488 }
1489
Luiz Capitulino222f1802006-03-20 22:16:13 -08001490 if (!strcmp(name, "max_before_softirq")) {
1491 len = num_arg(&user_buffer[i], 10, &value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 thread_lock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001493 t->max_before_softirq = value;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 thread_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001495 ret = count;
1496 sprintf(pg_result, "OK: max_before_softirq=%lu", value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 goto out;
1498 }
1499
1500 ret = -EINVAL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001501out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 return ret;
1503}
1504
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001505static int pktgen_thread_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506{
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001507 return single_open(file, pktgen_thread_show, PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508}
1509
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001510static struct file_operations pktgen_thread_fops = {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001511 .owner = THIS_MODULE,
1512 .open = pktgen_thread_open,
1513 .read = seq_read,
1514 .llseek = seq_lseek,
1515 .write = pktgen_thread_write,
1516 .release = single_release,
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07001517};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
1519/* Think find or remove for NN */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001520static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521{
1522 struct pktgen_thread *t;
1523 struct pktgen_dev *pkt_dev = NULL;
1524
Luiz Capitulino222f1802006-03-20 22:16:13 -08001525 t = pktgen_threads;
1526
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 while (t) {
1528 pkt_dev = pktgen_find_dev(t, ifname);
1529 if (pkt_dev) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001530 if (remove) {
1531 if_lock(t);
1532 pkt_dev->removal_mark = 1;
1533 t->control |= T_REMDEV;
1534 if_unlock(t);
1535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 }
1538 t = t->next;
1539 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001540 return pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541}
1542
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001543/*
1544 * mark a device for removal
1545 */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001546static int pktgen_mark_device(const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547{
1548 struct pktgen_dev *pkt_dev = NULL;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001549 const int max_tries = 10, msec_per_try = 125;
1550 int i = 0;
1551 int ret = 0;
1552
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 thread_lock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001554 PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n",
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001555 ifname));
1556
Luiz Capitulino222f1802006-03-20 22:16:13 -08001557 while (1) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001558
1559 pkt_dev = __pktgen_NN_threads(ifname, REMOVE);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001560 if (pkt_dev == NULL)
1561 break; /* success */
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001562
1563 thread_unlock();
1564 PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s "
Luiz Capitulino222f1802006-03-20 22:16:13 -08001565 "to disappear....\n", ifname));
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001566 schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
1567 thread_lock();
1568
1569 if (++i >= max_tries) {
1570 printk("pktgen_mark_device: timed out after waiting "
Luiz Capitulino222f1802006-03-20 22:16:13 -08001571 "%d msec for device %s to be removed\n",
1572 msec_per_try * i, ifname);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08001573 ret = 1;
1574 break;
1575 }
1576
1577 }
1578
1579 thread_unlock();
1580
1581 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582}
1583
Luiz Capitulino222f1802006-03-20 22:16:13 -08001584static int pktgen_device_event(struct notifier_block *unused,
1585 unsigned long event, void *ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586{
1587 struct net_device *dev = (struct net_device *)(ptr);
1588
1589 /* It is OK that we do not hold the group lock right now,
1590 * as we run under the RTNL lock.
1591 */
1592
1593 switch (event) {
1594 case NETDEV_CHANGEADDR:
1595 case NETDEV_GOING_DOWN:
1596 case NETDEV_DOWN:
1597 case NETDEV_UP:
1598 /* Ignore for now */
1599 break;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001600
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 case NETDEV_UNREGISTER:
Luiz Capitulino222f1802006-03-20 22:16:13 -08001602 pktgen_mark_device(dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 break;
1604 };
1605
1606 return NOTIFY_DONE;
1607}
1608
1609/* Associate pktgen_dev with a device. */
1610
Luiz Capitulino222f1802006-03-20 22:16:13 -08001611static struct net_device *pktgen_setup_dev(struct pktgen_dev *pkt_dev)
1612{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 struct net_device *odev;
1614
1615 /* Clean old setups */
1616
1617 if (pkt_dev->odev) {
1618 dev_put(pkt_dev->odev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001619 pkt_dev->odev = NULL;
1620 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
1622 odev = dev_get_by_name(pkt_dev->ifname);
1623
1624 if (!odev) {
1625 printk("pktgen: no such netdevice: \"%s\"\n", pkt_dev->ifname);
1626 goto out;
1627 }
1628 if (odev->type != ARPHRD_ETHER) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001629 printk("pktgen: not an ethernet device: \"%s\"\n",
1630 pkt_dev->ifname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 goto out_put;
1632 }
1633 if (!netif_running(odev)) {
1634 printk("pktgen: device is down: \"%s\"\n", pkt_dev->ifname);
1635 goto out_put;
1636 }
1637 pkt_dev->odev = odev;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001638
1639 return pkt_dev->odev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641out_put:
1642 dev_put(odev);
1643out:
Luiz Capitulino222f1802006-03-20 22:16:13 -08001644 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646}
1647
1648/* Read pkt_dev from the interface and set up internal pktgen_dev
1649 * structure to have the right information to create/send packets
1650 */
1651static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
1652{
1653 /* Try once more, just in case it works now. */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001654 if (!pkt_dev->odev)
1655 pktgen_setup_dev(pkt_dev);
1656
1657 if (!pkt_dev->odev) {
1658 printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n");
1659 sprintf(pkt_dev->result,
1660 "ERROR: pkt_dev->odev == NULL in setup_inject.\n");
1661 return;
1662 }
1663
1664 /* Default to the interface's mac if not explicitly set. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001666 if (is_zero_ether_addr(pkt_dev->src_mac))
Luiz Capitulino222f1802006-03-20 22:16:13 -08001667 memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
Luiz Capitulino222f1802006-03-20 22:16:13 -08001669 /* Set up Dest MAC */
Kris Katterjohnf404e9a2006-01-17 13:04:57 -08001670 memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
Luiz Capitulino222f1802006-03-20 22:16:13 -08001672 /* Set up pkt size */
1673 pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
1674
1675 if (pkt_dev->flags & F_IPV6) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 /*
1677 * Skip this automatic address setting until locks or functions
1678 * gets exported
1679 */
1680
1681#ifdef NOTNOW
Luiz Capitulino222f1802006-03-20 22:16:13 -08001682 int i, set = 0, err = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 struct inet6_dev *idev;
1684
Luiz Capitulino222f1802006-03-20 22:16:13 -08001685 for (i = 0; i < IN6_ADDR_HSIZE; i++)
1686 if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 set = 1;
1688 break;
1689 }
1690
Luiz Capitulino222f1802006-03-20 22:16:13 -08001691 if (!set) {
1692
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 /*
1694 * Use linklevel address if unconfigured.
1695 *
1696 * use ipv6_get_lladdr if/when it's get exported
1697 */
1698
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 read_lock(&addrconf_lock);
1700 if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) {
1701 struct inet6_ifaddr *ifp;
1702
1703 read_lock_bh(&idev->lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001704 for (ifp = idev->addr_list; ifp;
1705 ifp = ifp->if_next) {
1706 if (ifp->scope == IFA_LINK
1707 && !(ifp->
1708 flags & IFA_F_TENTATIVE)) {
1709 ipv6_addr_copy(&pkt_dev->
1710 cur_in6_saddr,
1711 &ifp->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 err = 0;
1713 break;
1714 }
1715 }
1716 read_unlock_bh(&idev->lock);
1717 }
1718 read_unlock(&addrconf_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08001719 if (err)
1720 printk("pktgen: ERROR: IPv6 link address not availble.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 }
1722#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -08001723 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 pkt_dev->saddr_min = 0;
1725 pkt_dev->saddr_max = 0;
1726 if (strlen(pkt_dev->src_min) == 0) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001727
1728 struct in_device *in_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
1730 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -07001731 in_dev = __in_dev_get_rcu(pkt_dev->odev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 if (in_dev) {
1733 if (in_dev->ifa_list) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08001734 pkt_dev->saddr_min =
1735 in_dev->ifa_list->ifa_address;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 pkt_dev->saddr_max = pkt_dev->saddr_min;
1737 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 }
1739 rcu_read_unlock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08001740 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
1742 pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
1743 }
1744
1745 pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
1746 pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
1747 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001748 /* Initialize current values. */
1749 pkt_dev->cur_dst_mac_offset = 0;
1750 pkt_dev->cur_src_mac_offset = 0;
1751 pkt_dev->cur_saddr = pkt_dev->saddr_min;
1752 pkt_dev->cur_daddr = pkt_dev->daddr_min;
1753 pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
1754 pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 pkt_dev->nflows = 0;
1756}
1757
1758static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
1759{
1760 __u64 start;
1761 __u64 now;
1762
1763 start = now = getCurUs();
1764 printk(KERN_INFO "sleeping for %d\n", (int)(spin_until_us - now));
1765 while (now < spin_until_us) {
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -07001766 /* TODO: optimize sleeping behavior */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001767 if (spin_until_us - now > jiffies_to_usecs(1) + 1)
Nishanth Aravamudan121caf52005-09-12 14:15:34 -07001768 schedule_timeout_interruptible(1);
1769 else if (spin_until_us - now > 100) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 do_softirq();
1771 if (!pkt_dev->running)
1772 return;
1773 if (need_resched())
1774 schedule();
1775 }
1776
1777 now = getCurUs();
1778 }
1779
1780 pkt_dev->idle_acc += now - start;
1781}
1782
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783/* Increment/randomize headers according to flags and current values
1784 * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
1785 */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001786static void mod_cur_headers(struct pktgen_dev *pkt_dev)
1787{
1788 __u32 imn;
1789 __u32 imx;
1790 int flow = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
Luiz Capitulino222f1802006-03-20 22:16:13 -08001792 if (pkt_dev->cflows) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 flow = pktgen_random() % pkt_dev->cflows;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001794
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795 if (pkt_dev->flows[flow].count > pkt_dev->lflow)
1796 pkt_dev->flows[flow].count = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001797 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
1799 /* Deal with source MAC */
Luiz Capitulino222f1802006-03-20 22:16:13 -08001800 if (pkt_dev->src_mac_count > 1) {
1801 __u32 mc;
1802 __u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Luiz Capitulino222f1802006-03-20 22:16:13 -08001804 if (pkt_dev->flags & F_MACSRC_RND)
1805 mc = pktgen_random() % (pkt_dev->src_mac_count);
1806 else {
1807 mc = pkt_dev->cur_src_mac_offset++;
1808 if (pkt_dev->cur_src_mac_offset >
1809 pkt_dev->src_mac_count)
1810 pkt_dev->cur_src_mac_offset = 0;
1811 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Luiz Capitulino222f1802006-03-20 22:16:13 -08001813 tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
1814 pkt_dev->hh[11] = tmp;
1815 tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
1816 pkt_dev->hh[10] = tmp;
1817 tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
1818 pkt_dev->hh[9] = tmp;
1819 tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
1820 pkt_dev->hh[8] = tmp;
1821 tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
1822 pkt_dev->hh[7] = tmp;
1823 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Luiz Capitulino222f1802006-03-20 22:16:13 -08001825 /* Deal with Destination MAC */
1826 if (pkt_dev->dst_mac_count > 1) {
1827 __u32 mc;
1828 __u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829
Luiz Capitulino222f1802006-03-20 22:16:13 -08001830 if (pkt_dev->flags & F_MACDST_RND)
1831 mc = pktgen_random() % (pkt_dev->dst_mac_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832
Luiz Capitulino222f1802006-03-20 22:16:13 -08001833 else {
1834 mc = pkt_dev->cur_dst_mac_offset++;
1835 if (pkt_dev->cur_dst_mac_offset >
1836 pkt_dev->dst_mac_count) {
1837 pkt_dev->cur_dst_mac_offset = 0;
1838 }
1839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840
Luiz Capitulino222f1802006-03-20 22:16:13 -08001841 tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
1842 pkt_dev->hh[5] = tmp;
1843 tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
1844 pkt_dev->hh[4] = tmp;
1845 tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
1846 pkt_dev->hh[3] = tmp;
1847 tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
1848 pkt_dev->hh[2] = tmp;
1849 tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
1850 pkt_dev->hh[1] = tmp;
1851 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852
Luiz Capitulino222f1802006-03-20 22:16:13 -08001853 if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
1854 if (pkt_dev->flags & F_UDPSRC_RND)
1855 pkt_dev->cur_udp_src =
1856 ((pktgen_random() %
1857 (pkt_dev->udp_src_max - pkt_dev->udp_src_min)) +
1858 pkt_dev->udp_src_min);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859
Luiz Capitulino222f1802006-03-20 22:16:13 -08001860 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 pkt_dev->cur_udp_src++;
1862 if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
1863 pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001864 }
1865 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866
Luiz Capitulino222f1802006-03-20 22:16:13 -08001867 if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
1868 if (pkt_dev->flags & F_UDPDST_RND) {
1869 pkt_dev->cur_udp_dst =
1870 ((pktgen_random() %
1871 (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)) +
1872 pkt_dev->udp_dst_min);
1873 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 pkt_dev->cur_udp_dst++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001875 if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001877 }
1878 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
1880 if (!(pkt_dev->flags & F_IPV6)) {
1881
Luiz Capitulino222f1802006-03-20 22:16:13 -08001882 if ((imn = ntohl(pkt_dev->saddr_min)) < (imx =
1883 ntohl(pkt_dev->
1884 saddr_max))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 __u32 t;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001886 if (pkt_dev->flags & F_IPSRC_RND)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 t = ((pktgen_random() % (imx - imn)) + imn);
1888 else {
1889 t = ntohl(pkt_dev->cur_saddr);
1890 t++;
1891 if (t > imx) {
1892 t = imn;
1893 }
1894 }
1895 pkt_dev->cur_saddr = htonl(t);
1896 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 if (pkt_dev->cflows && pkt_dev->flows[flow].count != 0) {
1899 pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
1900 } else {
1901
Luiz Capitulino222f1802006-03-20 22:16:13 -08001902 if ((imn = ntohl(pkt_dev->daddr_min)) < (imx =
1903 ntohl(pkt_dev->
1904 daddr_max)))
1905 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 __u32 t;
1907 if (pkt_dev->flags & F_IPDST_RND) {
1908
Luiz Capitulino222f1802006-03-20 22:16:13 -08001909 t = ((pktgen_random() % (imx - imn)) +
1910 imn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 t = htonl(t);
1912
Luiz Capitulino222f1802006-03-20 22:16:13 -08001913 while (LOOPBACK(t) || MULTICAST(t)
1914 || BADCLASS(t) || ZERONET(t)
1915 || LOCAL_MCAST(t)) {
1916 t = ((pktgen_random() %
1917 (imx - imn)) + imn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 t = htonl(t);
1919 }
1920 pkt_dev->cur_daddr = t;
1921 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001922
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 else {
1924 t = ntohl(pkt_dev->cur_daddr);
1925 t++;
1926 if (t > imx) {
1927 t = imn;
1928 }
1929 pkt_dev->cur_daddr = htonl(t);
1930 }
1931 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001932 if (pkt_dev->cflows) {
1933 pkt_dev->flows[flow].cur_daddr =
1934 pkt_dev->cur_daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 pkt_dev->nflows++;
1936 }
1937 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001938 } else { /* IPV6 * */
1939
1940 if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
1941 pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
1942 pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
1943 pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 else {
1945 int i;
1946
1947 /* Only random destinations yet */
1948
Luiz Capitulino222f1802006-03-20 22:16:13 -08001949 for (i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 pkt_dev->cur_in6_daddr.s6_addr32[i] =
Luiz Capitulino222f1802006-03-20 22:16:13 -08001951 ((pktgen_random() |
1952 pkt_dev->min_in6_daddr.s6_addr32[i]) &
1953 pkt_dev->max_in6_daddr.s6_addr32[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08001955 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 }
1957
Luiz Capitulino222f1802006-03-20 22:16:13 -08001958 if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
1959 __u32 t;
1960 if (pkt_dev->flags & F_TXSIZE_RND) {
1961 t = ((pktgen_random() %
1962 (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size))
1963 + pkt_dev->min_pkt_size);
1964 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965 t = pkt_dev->cur_pkt_size + 1;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001966 if (t > pkt_dev->max_pkt_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 t = pkt_dev->min_pkt_size;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001968 }
1969 pkt_dev->cur_pkt_size = t;
1970 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
1972 pkt_dev->flows[flow].count++;
1973}
1974
Luiz Capitulino222f1802006-03-20 22:16:13 -08001975static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
1976 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977{
1978 struct sk_buff *skb = NULL;
1979 __u8 *eth;
1980 struct udphdr *udph;
1981 int datalen, iplen;
1982 struct iphdr *iph;
Luiz Capitulino222f1802006-03-20 22:16:13 -08001983 struct pktgen_hdr *pgh = NULL;
1984
Robert Olsson64053be2005-06-26 15:27:10 -07001985 /* Update any of the values, used when we're incrementing various
1986 * fields.
1987 */
1988 mod_cur_headers(pkt_dev);
1989
David S. Miller7ac54592006-01-18 14:19:10 -08001990 datalen = (odev->hard_header_len + 16) & ~0xf;
1991 skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 if (!skb) {
1993 sprintf(pkt_dev->result, "No memory");
1994 return NULL;
1995 }
1996
David S. Miller7ac54592006-01-18 14:19:10 -08001997 skb_reserve(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998
1999 /* Reserve for ethernet and IP header */
2000 eth = (__u8 *) skb_push(skb, 14);
2001 iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
2002 udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
2003
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 memcpy(eth, pkt_dev->hh, 12);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002005 *(u16 *) & eth[12] = __constant_htons(ETH_P_IP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006
Luiz Capitulino222f1802006-03-20 22:16:13 -08002007 datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
2008 if (datalen < sizeof(struct pktgen_hdr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 datalen = sizeof(struct pktgen_hdr);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002010
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 udph->source = htons(pkt_dev->cur_udp_src);
2012 udph->dest = htons(pkt_dev->cur_udp_dst);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002013 udph->len = htons(datalen + 8); /* DATA + udphdr */
2014 udph->check = 0; /* No checksum */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015
2016 iph->ihl = 5;
2017 iph->version = 4;
2018 iph->ttl = 32;
2019 iph->tos = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002020 iph->protocol = IPPROTO_UDP; /* UDP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 iph->saddr = pkt_dev->cur_saddr;
2022 iph->daddr = pkt_dev->cur_daddr;
2023 iph->frag_off = 0;
2024 iplen = 20 + 8 + datalen;
2025 iph->tot_len = htons(iplen);
2026 iph->check = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002027 iph->check = ip_fast_csum((void *)iph, iph->ihl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 skb->protocol = __constant_htons(ETH_P_IP);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002029 skb->mac.raw = ((u8 *) iph) - 14;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 skb->dev = odev;
2031 skb->pkt_type = PACKET_HOST;
2032
Luiz Capitulino222f1802006-03-20 22:16:13 -08002033 if (pkt_dev->nfrags <= 0)
2034 pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 else {
2036 int frags = pkt_dev->nfrags;
2037 int i;
2038
Luiz Capitulino222f1802006-03-20 22:16:13 -08002039 pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
2040
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 if (frags > MAX_SKB_FRAGS)
2042 frags = MAX_SKB_FRAGS;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002043 if (datalen > frags * PAGE_SIZE) {
2044 skb_put(skb, datalen - frags * PAGE_SIZE);
2045 datalen = frags * PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 }
2047
2048 i = 0;
2049 while (datalen > 0) {
2050 struct page *page = alloc_pages(GFP_KERNEL, 0);
2051 skb_shinfo(skb)->frags[i].page = page;
2052 skb_shinfo(skb)->frags[i].page_offset = 0;
2053 skb_shinfo(skb)->frags[i].size =
Luiz Capitulino222f1802006-03-20 22:16:13 -08002054 (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 datalen -= skb_shinfo(skb)->frags[i].size;
2056 skb->len += skb_shinfo(skb)->frags[i].size;
2057 skb->data_len += skb_shinfo(skb)->frags[i].size;
2058 i++;
2059 skb_shinfo(skb)->nr_frags = i;
2060 }
2061
2062 while (i < frags) {
2063 int rem;
2064
2065 if (i == 0)
2066 break;
2067
2068 rem = skb_shinfo(skb)->frags[i - 1].size / 2;
2069 if (rem == 0)
2070 break;
2071
2072 skb_shinfo(skb)->frags[i - 1].size -= rem;
2073
Luiz Capitulino222f1802006-03-20 22:16:13 -08002074 skb_shinfo(skb)->frags[i] =
2075 skb_shinfo(skb)->frags[i - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 get_page(skb_shinfo(skb)->frags[i].page);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002077 skb_shinfo(skb)->frags[i].page =
2078 skb_shinfo(skb)->frags[i - 1].page;
2079 skb_shinfo(skb)->frags[i].page_offset +=
2080 skb_shinfo(skb)->frags[i - 1].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 skb_shinfo(skb)->frags[i].size = rem;
2082 i++;
2083 skb_shinfo(skb)->nr_frags = i;
2084 }
2085 }
2086
Luiz Capitulino222f1802006-03-20 22:16:13 -08002087 /* Stamp the time, and sequence number, convert them to network byte order */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088
Luiz Capitulino222f1802006-03-20 22:16:13 -08002089 if (pgh) {
2090 struct timeval timestamp;
2091
2092 pgh->pgh_magic = htonl(PKTGEN_MAGIC);
2093 pgh->seq_num = htonl(pkt_dev->seq_num);
2094
2095 do_gettimeofday(&timestamp);
2096 pgh->tv_sec = htonl(timestamp.tv_sec);
2097 pgh->tv_usec = htonl(timestamp.tv_usec);
2098 }
2099 pkt_dev->seq_num++;
2100
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 return skb;
2102}
2103
2104/*
2105 * scan_ip6, fmt_ip taken from dietlibc-0.21
2106 * Author Felix von Leitner <felix-dietlibc@fefe.de>
2107 *
2108 * Slightly modified for kernel.
2109 * Should be candidate for net/ipv4/utils.c
2110 * --ro
2111 */
2112
Luiz Capitulino222f1802006-03-20 22:16:13 -08002113static unsigned int scan_ip6(const char *s, char ip[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114{
2115 unsigned int i;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002116 unsigned int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 unsigned long u;
2118 char suffix[16];
Luiz Capitulino222f1802006-03-20 22:16:13 -08002119 unsigned int prefixlen = 0;
2120 unsigned int suffixlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 __u32 tmp;
2122
Luiz Capitulino222f1802006-03-20 22:16:13 -08002123 for (i = 0; i < 16; i++)
2124 ip[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
2126 for (;;) {
2127 if (*s == ':') {
2128 len++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002129 if (s[1] == ':') { /* Found "::", skip to part 2 */
2130 s += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 len++;
2132 break;
2133 }
2134 s++;
2135 }
2136 {
2137 char *tmp;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002138 u = simple_strtoul(s, &tmp, 16);
2139 i = tmp - s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 }
2141
Luiz Capitulino222f1802006-03-20 22:16:13 -08002142 if (!i)
2143 return 0;
2144 if (prefixlen == 12 && s[i] == '.') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145
2146 /* the last 4 bytes may be written as IPv4 address */
2147
2148 tmp = in_aton(s);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002149 memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp));
2150 return i + len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 }
2152 ip[prefixlen++] = (u >> 8);
2153 ip[prefixlen++] = (u & 255);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002154 s += i;
2155 len += i;
2156 if (prefixlen == 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 return len;
2158 }
2159
2160/* part 2, after "::" */
2161 for (;;) {
2162 if (*s == ':') {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002163 if (suffixlen == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 break;
2165 s++;
2166 len++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002167 } else if (suffixlen != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 break;
2169 {
2170 char *tmp;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002171 u = simple_strtol(s, &tmp, 16);
2172 i = tmp - s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 }
2174 if (!i) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002175 if (*s)
2176 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 break;
2178 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002179 if (suffixlen + prefixlen <= 12 && s[i] == '.') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 tmp = in_aton(s);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002181 memcpy((struct in_addr *)(suffix + suffixlen), &tmp,
2182 sizeof(tmp));
2183 suffixlen += 4;
2184 len += strlen(s);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 break;
2186 }
2187 suffix[suffixlen++] = (u >> 8);
2188 suffix[suffixlen++] = (u & 255);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002189 s += i;
2190 len += i;
2191 if (prefixlen + suffixlen == 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 break;
2193 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002194 for (i = 0; i < suffixlen; i++)
2195 ip[16 - suffixlen + i] = suffix[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 return len;
2197}
2198
Luiz Capitulino222f1802006-03-20 22:16:13 -08002199static char tohex(char hexdigit)
2200{
2201 return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202}
2203
Luiz Capitulino222f1802006-03-20 22:16:13 -08002204static int fmt_xlong(char *s, unsigned int i)
2205{
2206 char *bak = s;
2207 *s = tohex((i >> 12) & 0xf);
2208 if (s != bak || *s != '0')
2209 ++s;
2210 *s = tohex((i >> 8) & 0xf);
2211 if (s != bak || *s != '0')
2212 ++s;
2213 *s = tohex((i >> 4) & 0xf);
2214 if (s != bak || *s != '0')
2215 ++s;
2216 *s = tohex(i & 0xf);
2217 return s - bak + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218}
2219
Luiz Capitulino222f1802006-03-20 22:16:13 -08002220static unsigned int fmt_ip6(char *s, const char ip[16])
2221{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 unsigned int len;
2223 unsigned int i;
2224 unsigned int temp;
2225 unsigned int compressing;
2226 int j;
2227
Luiz Capitulino222f1802006-03-20 22:16:13 -08002228 len = 0;
2229 compressing = 0;
2230 for (j = 0; j < 16; j += 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231
2232#ifdef V4MAPPEDPREFIX
Luiz Capitulino222f1802006-03-20 22:16:13 -08002233 if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) {
2234 inet_ntoa_r(*(struct in_addr *)(ip + 12), s);
2235 temp = strlen(s);
2236 return len + temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 }
2238#endif
Luiz Capitulino222f1802006-03-20 22:16:13 -08002239 temp = ((unsigned long)(unsigned char)ip[j] << 8) +
2240 (unsigned long)(unsigned char)ip[j + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 if (temp == 0) {
2242 if (!compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002243 compressing = 1;
2244 if (j == 0) {
2245 *s++ = ':';
2246 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 }
2248 }
2249 } else {
2250 if (compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002251 compressing = 0;
2252 *s++ = ':';
2253 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002255 i = fmt_xlong(s, temp);
2256 len += i;
2257 s += i;
2258 if (j < 14) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 *s++ = ':';
2260 ++len;
2261 }
2262 }
2263 }
2264 if (compressing) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002265 *s++ = ':';
2266 ++len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002268 *s = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 return len;
2270}
2271
Luiz Capitulino222f1802006-03-20 22:16:13 -08002272static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
2273 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274{
2275 struct sk_buff *skb = NULL;
2276 __u8 *eth;
2277 struct udphdr *udph;
2278 int datalen;
2279 struct ipv6hdr *iph;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002280 struct pktgen_hdr *pgh = NULL;
Robert Olsson64053be2005-06-26 15:27:10 -07002281
2282 /* Update any of the values, used when we're incrementing various
2283 * fields.
2284 */
2285 mod_cur_headers(pkt_dev);
2286
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16, GFP_ATOMIC);
2288 if (!skb) {
2289 sprintf(pkt_dev->result, "No memory");
2290 return NULL;
2291 }
2292
2293 skb_reserve(skb, 16);
2294
2295 /* Reserve for ethernet and IP header */
2296 eth = (__u8 *) skb_push(skb, 14);
2297 iph = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr));
2298 udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
2299
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 memcpy(eth, pkt_dev->hh, 12);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002301 *(u16 *) & eth[12] = __constant_htons(ETH_P_IPV6);
Robert Olsson64053be2005-06-26 15:27:10 -07002302
Luiz Capitulino222f1802006-03-20 22:16:13 -08002303 datalen = pkt_dev->cur_pkt_size - 14 - sizeof(struct ipv6hdr) - sizeof(struct udphdr); /* Eth + IPh + UDPh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304
Luiz Capitulino222f1802006-03-20 22:16:13 -08002305 if (datalen < sizeof(struct pktgen_hdr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 datalen = sizeof(struct pktgen_hdr);
2307 if (net_ratelimit())
Luiz Capitulino222f1802006-03-20 22:16:13 -08002308 printk(KERN_INFO "pktgen: increased datalen to %d\n",
2309 datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
2311
2312 udph->source = htons(pkt_dev->cur_udp_src);
2313 udph->dest = htons(pkt_dev->cur_udp_dst);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002314 udph->len = htons(datalen + sizeof(struct udphdr));
2315 udph->check = 0; /* No checksum */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
Luiz Capitulino222f1802006-03-20 22:16:13 -08002317 *(u32 *) iph = __constant_htonl(0x60000000); /* Version + flow */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
2319 iph->hop_limit = 32;
2320
2321 iph->payload_len = htons(sizeof(struct udphdr) + datalen);
2322 iph->nexthdr = IPPROTO_UDP;
2323
2324 ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
2325 ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
2326
Luiz Capitulino222f1802006-03-20 22:16:13 -08002327 skb->mac.raw = ((u8 *) iph) - 14;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 skb->protocol = __constant_htons(ETH_P_IPV6);
2329 skb->dev = odev;
2330 skb->pkt_type = PACKET_HOST;
2331
Luiz Capitulino222f1802006-03-20 22:16:13 -08002332 if (pkt_dev->nfrags <= 0)
2333 pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 else {
2335 int frags = pkt_dev->nfrags;
2336 int i;
2337
Luiz Capitulino222f1802006-03-20 22:16:13 -08002338 pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
2339
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 if (frags > MAX_SKB_FRAGS)
2341 frags = MAX_SKB_FRAGS;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002342 if (datalen > frags * PAGE_SIZE) {
2343 skb_put(skb, datalen - frags * PAGE_SIZE);
2344 datalen = frags * PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 }
2346
2347 i = 0;
2348 while (datalen > 0) {
2349 struct page *page = alloc_pages(GFP_KERNEL, 0);
2350 skb_shinfo(skb)->frags[i].page = page;
2351 skb_shinfo(skb)->frags[i].page_offset = 0;
2352 skb_shinfo(skb)->frags[i].size =
Luiz Capitulino222f1802006-03-20 22:16:13 -08002353 (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 datalen -= skb_shinfo(skb)->frags[i].size;
2355 skb->len += skb_shinfo(skb)->frags[i].size;
2356 skb->data_len += skb_shinfo(skb)->frags[i].size;
2357 i++;
2358 skb_shinfo(skb)->nr_frags = i;
2359 }
2360
2361 while (i < frags) {
2362 int rem;
2363
2364 if (i == 0)
2365 break;
2366
2367 rem = skb_shinfo(skb)->frags[i - 1].size / 2;
2368 if (rem == 0)
2369 break;
2370
2371 skb_shinfo(skb)->frags[i - 1].size -= rem;
2372
Luiz Capitulino222f1802006-03-20 22:16:13 -08002373 skb_shinfo(skb)->frags[i] =
2374 skb_shinfo(skb)->frags[i - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 get_page(skb_shinfo(skb)->frags[i].page);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002376 skb_shinfo(skb)->frags[i].page =
2377 skb_shinfo(skb)->frags[i - 1].page;
2378 skb_shinfo(skb)->frags[i].page_offset +=
2379 skb_shinfo(skb)->frags[i - 1].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 skb_shinfo(skb)->frags[i].size = rem;
2381 i++;
2382 skb_shinfo(skb)->nr_frags = i;
2383 }
2384 }
2385
Luiz Capitulino222f1802006-03-20 22:16:13 -08002386 /* Stamp the time, and sequence number, convert them to network byte order */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 /* should we update cloned packets too ? */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002388 if (pgh) {
2389 struct timeval timestamp;
2390
2391 pgh->pgh_magic = htonl(PKTGEN_MAGIC);
2392 pgh->seq_num = htonl(pkt_dev->seq_num);
2393
2394 do_gettimeofday(&timestamp);
2395 pgh->tv_sec = htonl(timestamp.tv_sec);
2396 pgh->tv_usec = htonl(timestamp.tv_usec);
2397 }
2398 pkt_dev->seq_num++;
2399
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 return skb;
2401}
2402
Luiz Capitulino222f1802006-03-20 22:16:13 -08002403static inline struct sk_buff *fill_packet(struct net_device *odev,
2404 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002406 if (pkt_dev->flags & F_IPV6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 return fill_packet_ipv6(odev, pkt_dev);
2408 else
2409 return fill_packet_ipv4(odev, pkt_dev);
2410}
2411
Luiz Capitulino222f1802006-03-20 22:16:13 -08002412static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002414 pkt_dev->seq_num = 1;
2415 pkt_dev->idle_acc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416 pkt_dev->sofar = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002417 pkt_dev->tx_bytes = 0;
2418 pkt_dev->errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419}
2420
2421/* Set up structure for sending pkts, clear counters */
2422
2423static void pktgen_run(struct pktgen_thread *t)
2424{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002425 struct pktgen_dev *pkt_dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 int started = 0;
2427
2428 PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t));
2429
2430 if_lock(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002431 for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432
2433 /*
2434 * setup odev and create initial packet.
2435 */
2436 pktgen_setup_inject(pkt_dev);
2437
Luiz Capitulino222f1802006-03-20 22:16:13 -08002438 if (pkt_dev->odev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 pktgen_clear_counters(pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002440 pkt_dev->running = 1; /* Cranke yeself! */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 pkt_dev->skb = NULL;
2442 pkt_dev->started_at = getCurUs();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002443 pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 pkt_dev->next_tx_ns = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002445
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 strcpy(pkt_dev->result, "Starting");
2447 started++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002448 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 strcpy(pkt_dev->result, "Error starting");
2450 }
2451 if_unlock(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002452 if (started)
2453 t->control &= ~(T_STOP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454}
2455
2456static void pktgen_stop_all_threads_ifs(void)
2457{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002458 struct pktgen_thread *t = pktgen_threads;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002460 PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461
2462 thread_lock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002463 while (t) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002464 t->control |= T_STOP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 t = t->next;
2466 }
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -07002467 thread_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468}
2469
Luiz Capitulino222f1802006-03-20 22:16:13 -08002470static int thread_is_running(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002472 struct pktgen_dev *next;
2473 int res = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474
Luiz Capitulino222f1802006-03-20 22:16:13 -08002475 for (next = t->if_list; next; next = next->next) {
2476 if (next->running) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 res = 1;
2478 break;
2479 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002480 }
2481 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482}
2483
Luiz Capitulino222f1802006-03-20 22:16:13 -08002484static int pktgen_wait_thread_run(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002486 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487
Luiz Capitulino222f1802006-03-20 22:16:13 -08002488 while (thread_is_running(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489
Luiz Capitulino222f1802006-03-20 22:16:13 -08002490 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491
Luiz Capitulino222f1802006-03-20 22:16:13 -08002492 msleep_interruptible(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
Luiz Capitulino222f1802006-03-20 22:16:13 -08002494 if (signal_pending(current))
2495 goto signal;
2496 if_lock(t);
2497 }
2498 if_unlock(t);
2499 return 1;
2500signal:
2501 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502}
2503
2504static int pktgen_wait_all_threads_run(void)
2505{
2506 struct pktgen_thread *t = pktgen_threads;
2507 int sig = 1;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002508
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 while (t) {
2510 sig = pktgen_wait_thread_run(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002511 if (sig == 0)
2512 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 thread_lock();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002514 t = t->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 thread_unlock();
2516 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002517 if (sig == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 thread_lock();
2519 while (t) {
2520 t->control |= (T_STOP);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002521 t = t->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 }
2523 thread_unlock();
2524 }
2525 return sig;
2526}
2527
2528static void pktgen_run_all_threads(void)
2529{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002530 struct pktgen_thread *t = pktgen_threads;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531
2532 PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
2533
2534 thread_lock();
2535
Luiz Capitulino222f1802006-03-20 22:16:13 -08002536 while (t) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 t->control |= (T_RUN);
2538 t = t->next;
2539 }
2540 thread_unlock();
2541
Luiz Capitulino222f1802006-03-20 22:16:13 -08002542 schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
2543
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 pktgen_wait_all_threads_run();
2545}
2546
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
2548{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002549 __u64 total_us, bps, mbps, pps, idle;
2550 char *p = pkt_dev->result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551
Luiz Capitulino222f1802006-03-20 22:16:13 -08002552 total_us = pkt_dev->stopped_at - pkt_dev->started_at;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553
Luiz Capitulino222f1802006-03-20 22:16:13 -08002554 idle = pkt_dev->idle_acc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
Luiz Capitulino222f1802006-03-20 22:16:13 -08002556 p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
2557 (unsigned long long)total_us,
2558 (unsigned long long)(total_us - idle),
2559 (unsigned long long)idle,
2560 (unsigned long long)pkt_dev->sofar,
2561 pkt_dev->cur_pkt_size, nr_frags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
Luiz Capitulino222f1802006-03-20 22:16:13 -08002563 pps = pkt_dev->sofar * USEC_PER_SEC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564
Luiz Capitulino222f1802006-03-20 22:16:13 -08002565 while ((total_us >> 32) != 0) {
2566 pps >>= 1;
2567 total_us >>= 1;
2568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569
Luiz Capitulino222f1802006-03-20 22:16:13 -08002570 do_div(pps, total_us);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571
Luiz Capitulino222f1802006-03-20 22:16:13 -08002572 bps = pps * 8 * pkt_dev->cur_pkt_size;
2573
2574 mbps = bps;
2575 do_div(mbps, 1000000);
2576 p += sprintf(p, " %llupps %lluMb/sec (%llubps) errors: %llu",
2577 (unsigned long long)pps,
2578 (unsigned long long)mbps,
2579 (unsigned long long)bps,
2580 (unsigned long long)pkt_dev->errors);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582
2583/* Set stopped-at timer, remove from running list, do counters & statistics */
2584
Luiz Capitulino222f1802006-03-20 22:16:13 -08002585static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002587 int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002588
Luiz Capitulino222f1802006-03-20 22:16:13 -08002589 if (!pkt_dev->running) {
2590 printk("pktgen: interface: %s is already stopped\n",
2591 pkt_dev->ifname);
2592 return -EINVAL;
2593 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594
Luiz Capitulino222f1802006-03-20 22:16:13 -08002595 pkt_dev->stopped_at = getCurUs();
2596 pkt_dev->running = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002598 show_results(pkt_dev, nr_frags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599
Luiz Capitulino222f1802006-03-20 22:16:13 -08002600 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601}
2602
Luiz Capitulino222f1802006-03-20 22:16:13 -08002603static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604{
2605 struct pktgen_dev *next, *best = NULL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002606
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 if_lock(t);
2608
Luiz Capitulino222f1802006-03-20 22:16:13 -08002609 for (next = t->if_list; next; next = next->next) {
2610 if (!next->running)
2611 continue;
2612 if (best == NULL)
2613 best = next;
2614 else if (next->next_tx_us < best->next_tx_us)
2615 best = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616 }
2617 if_unlock(t);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002618 return best;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619}
2620
Luiz Capitulino222f1802006-03-20 22:16:13 -08002621static void pktgen_stop(struct pktgen_thread *t)
2622{
2623 struct pktgen_dev *next = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002625 PG_DEBUG(printk("pktgen: entering pktgen_stop\n"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626
Luiz Capitulino222f1802006-03-20 22:16:13 -08002627 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628
Luiz Capitulino222f1802006-03-20 22:16:13 -08002629 for (next = t->if_list; next; next = next->next) {
2630 pktgen_stop_device(next);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002631 if (next->skb)
2632 kfree_skb(next->skb);
2633
2634 next->skb = NULL;
2635 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636
Luiz Capitulino222f1802006-03-20 22:16:13 -08002637 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638}
2639
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002640/*
2641 * one of our devices needs to be removed - find it
2642 * and remove it
2643 */
2644static void pktgen_rem_one_if(struct pktgen_thread *t)
2645{
2646 struct pktgen_dev *cur, *next = NULL;
2647
2648 PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n"));
2649
2650 if_lock(t);
2651
Luiz Capitulino222f1802006-03-20 22:16:13 -08002652 for (cur = t->if_list; cur; cur = next) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002653 next = cur->next;
2654
Luiz Capitulino222f1802006-03-20 22:16:13 -08002655 if (!cur->removal_mark)
2656 continue;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002657
2658 if (cur->skb)
2659 kfree_skb(cur->skb);
2660 cur->skb = NULL;
2661
2662 pktgen_remove_device(t, cur);
2663
2664 break;
2665 }
2666
2667 if_unlock(t);
2668}
2669
Luiz Capitulino222f1802006-03-20 22:16:13 -08002670static void pktgen_rem_all_ifs(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002672 struct pktgen_dev *cur, *next = NULL;
2673
2674 /* Remove all devices, free mem */
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002675
2676 PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n"));
Luiz Capitulino222f1802006-03-20 22:16:13 -08002677 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
Luiz Capitulino222f1802006-03-20 22:16:13 -08002679 for (cur = t->if_list; cur; cur = next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 next = cur->next;
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002681
2682 if (cur->skb)
2683 kfree_skb(cur->skb);
2684 cur->skb = NULL;
2685
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 pktgen_remove_device(t, cur);
2687 }
2688
Luiz Capitulino222f1802006-03-20 22:16:13 -08002689 if_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690}
2691
Luiz Capitulino222f1802006-03-20 22:16:13 -08002692static void pktgen_rem_thread(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002694 /* Remove from the thread list */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695
2696 struct pktgen_thread *tmp = pktgen_threads;
2697
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07002698 remove_proc_entry(t->name, pg_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -07002700 thread_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701
2702 if (tmp == t)
2703 pktgen_threads = tmp->next;
2704 else {
2705 while (tmp) {
2706 if (tmp->next == t) {
2707 tmp->next = t->next;
2708 t->next = NULL;
2709 break;
2710 }
2711 tmp = tmp->next;
2712 }
2713 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002714 thread_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715}
2716
2717static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
2718{
2719 struct net_device *odev = NULL;
2720 __u64 idle_start = 0;
2721 int ret;
2722
2723 odev = pkt_dev->odev;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002724
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 if (pkt_dev->delay_us || pkt_dev->delay_ns) {
2726 u64 now;
2727
2728 now = getCurUs();
2729 if (now < pkt_dev->next_tx_us)
2730 spin(pkt_dev, pkt_dev->next_tx_us);
2731
2732 /* This is max DELAY, this has special meaning of
2733 * "never transmit"
2734 */
2735 if (pkt_dev->delay_us == 0x7FFFFFFF) {
2736 pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us;
2737 pkt_dev->next_tx_ns = pkt_dev->delay_ns;
2738 goto out;
2739 }
2740 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002741
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 if (netif_queue_stopped(odev) || need_resched()) {
2743 idle_start = getCurUs();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002744
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 if (!netif_running(odev)) {
2746 pktgen_stop_device(pkt_dev);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002747 if (pkt_dev->skb)
2748 kfree_skb(pkt_dev->skb);
2749 pkt_dev->skb = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 goto out;
2751 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002752 if (need_resched())
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 schedule();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002754
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 pkt_dev->idle_acc += getCurUs() - idle_start;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002756
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 if (netif_queue_stopped(odev)) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002758 pkt_dev->next_tx_us = getCurUs(); /* TODO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 pkt_dev->next_tx_ns = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002760 goto out; /* Try the next interface */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 }
2762 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002763
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 if (pkt_dev->last_ok || !pkt_dev->skb) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002765 if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
2766 || (!pkt_dev->skb)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 /* build a new pkt */
Luiz Capitulino222f1802006-03-20 22:16:13 -08002768 if (pkt_dev->skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 kfree_skb(pkt_dev->skb);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002770
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 pkt_dev->skb = fill_packet(odev, pkt_dev);
2772 if (pkt_dev->skb == NULL) {
2773 printk("pktgen: ERROR: couldn't allocate skb in fill_packet.\n");
2774 schedule();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002775 pkt_dev->clone_count--; /* back out increment, OOM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 goto out;
2777 }
2778 pkt_dev->allocated_skbs++;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002779 pkt_dev->clone_count = 0; /* reset counter */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 }
2781 }
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 spin_lock_bh(&odev->xmit_lock);
2784 if (!netif_queue_stopped(odev)) {
2785
2786 atomic_inc(&(pkt_dev->skb->users));
Luiz Capitulino222f1802006-03-20 22:16:13 -08002787 retry_now:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 ret = odev->hard_start_xmit(pkt_dev->skb, odev);
2789 if (likely(ret == NETDEV_TX_OK)) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08002790 pkt_dev->last_ok = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 pkt_dev->sofar++;
2792 pkt_dev->seq_num++;
2793 pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002794
2795 } else if (ret == NETDEV_TX_LOCKED
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 && (odev->features & NETIF_F_LLTX)) {
2797 cpu_relax();
2798 goto retry_now;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002799 } else { /* Retry it next time */
2800
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 atomic_dec(&(pkt_dev->skb->users));
Luiz Capitulino222f1802006-03-20 22:16:13 -08002802
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 if (debug && net_ratelimit())
2804 printk(KERN_INFO "pktgen: Hard xmit error\n");
Luiz Capitulino222f1802006-03-20 22:16:13 -08002805
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 pkt_dev->errors++;
2807 pkt_dev->last_ok = 0;
2808 }
2809
2810 pkt_dev->next_tx_us = getCurUs();
2811 pkt_dev->next_tx_ns = 0;
2812
2813 pkt_dev->next_tx_us += pkt_dev->delay_us;
2814 pkt_dev->next_tx_ns += pkt_dev->delay_ns;
2815
2816 if (pkt_dev->next_tx_ns > 1000) {
2817 pkt_dev->next_tx_us++;
2818 pkt_dev->next_tx_ns -= 1000;
2819 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821
Luiz Capitulino222f1802006-03-20 22:16:13 -08002822 else { /* Retry it next time */
2823 pkt_dev->last_ok = 0;
2824 pkt_dev->next_tx_us = getCurUs(); /* TODO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 pkt_dev->next_tx_ns = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002826 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827
2828 spin_unlock_bh(&odev->xmit_lock);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002829
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 /* If pkt_dev->count is zero, then run forever */
2831 if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
2832 if (atomic_read(&(pkt_dev->skb->users)) != 1) {
2833 idle_start = getCurUs();
2834 while (atomic_read(&(pkt_dev->skb->users)) != 1) {
2835 if (signal_pending(current)) {
2836 break;
2837 }
2838 schedule();
2839 }
2840 pkt_dev->idle_acc += getCurUs() - idle_start;
2841 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08002842
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 /* Done with this */
2844 pktgen_stop_device(pkt_dev);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002845 if (pkt_dev->skb)
2846 kfree_skb(pkt_dev->skb);
2847 pkt_dev->skb = NULL;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002848 }
2849out:;
2850}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851
2852/*
2853 * Main loop of the thread goes here
2854 */
2855
Luiz Capitulino222f1802006-03-20 22:16:13 -08002856static void pktgen_thread_worker(struct pktgen_thread *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857{
2858 DEFINE_WAIT(wait);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002859 struct pktgen_dev *pkt_dev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 int cpu = t->cpu;
2861 sigset_t tmpsig;
2862 u32 max_before_softirq;
Luiz Capitulino222f1802006-03-20 22:16:13 -08002863 u32 tx_since_softirq = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864
2865 daemonize("pktgen/%d", cpu);
2866
Luiz Capitulino222f1802006-03-20 22:16:13 -08002867 /* Block all signals except SIGKILL, SIGSTOP and SIGTERM */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868
Luiz Capitulino222f1802006-03-20 22:16:13 -08002869 spin_lock_irq(&current->sighand->siglock);
2870 tmpsig = current->blocked;
2871 siginitsetinv(&current->blocked,
2872 sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGTERM));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873
Luiz Capitulino222f1802006-03-20 22:16:13 -08002874 recalc_sigpending();
2875 spin_unlock_irq(&current->sighand->siglock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876
2877 /* Migrate to the right CPU */
2878 set_cpus_allowed(current, cpumask_of_cpu(cpu));
Luiz Capitulino222f1802006-03-20 22:16:13 -08002879 if (smp_processor_id() != cpu)
2880 BUG();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881
2882 init_waitqueue_head(&t->queue);
2883
2884 t->control &= ~(T_TERMINATE);
2885 t->control &= ~(T_RUN);
2886 t->control &= ~(T_STOP);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002887 t->control &= ~(T_REMDEVALL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 t->control &= ~(T_REMDEV);
2889
Luiz Capitulino222f1802006-03-20 22:16:13 -08002890 t->pid = current->pid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891
Luiz Capitulino222f1802006-03-20 22:16:13 -08002892 PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893
2894 max_before_softirq = t->max_before_softirq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895
Luiz Capitulino222f1802006-03-20 22:16:13 -08002896 __set_current_state(TASK_INTERRUPTIBLE);
2897 mb();
2898
2899 while (1) {
2900
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 __set_current_state(TASK_RUNNING);
2902
2903 /*
2904 * Get next dev to xmit -- if any.
2905 */
2906
Luiz Capitulino222f1802006-03-20 22:16:13 -08002907 pkt_dev = next_to_run(t);
2908
2909 if (pkt_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910
2911 pktgen_xmit(pkt_dev);
2912
2913 /*
2914 * We like to stay RUNNING but must also give
2915 * others fair share.
2916 */
2917
2918 tx_since_softirq += pkt_dev->last_ok;
2919
2920 if (tx_since_softirq > max_before_softirq) {
2921 if (local_softirq_pending())
2922 do_softirq();
2923 tx_since_softirq = 0;
2924 }
2925 } else {
2926 prepare_to_wait(&(t->queue), &wait, TASK_INTERRUPTIBLE);
Luiz Capitulino222f1802006-03-20 22:16:13 -08002927 schedule_timeout(HZ / 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 finish_wait(&(t->queue), &wait);
2929 }
2930
Luiz Capitulino222f1802006-03-20 22:16:13 -08002931 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932 * Back from sleep, either due to the timeout or signal.
2933 * We check if we have any "posted" work for us.
2934 */
2935
Luiz Capitulino222f1802006-03-20 22:16:13 -08002936 if (t->control & T_TERMINATE || signal_pending(current))
2937 /* we received a request to terminate ourself */
2938 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939
Luiz Capitulino222f1802006-03-20 22:16:13 -08002940 if (t->control & T_STOP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941 pktgen_stop(t);
2942 t->control &= ~(T_STOP);
2943 }
2944
Luiz Capitulino222f1802006-03-20 22:16:13 -08002945 if (t->control & T_RUN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 pktgen_run(t);
2947 t->control &= ~(T_RUN);
2948 }
2949
Luiz Capitulino222f1802006-03-20 22:16:13 -08002950 if (t->control & T_REMDEVALL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951 pktgen_rem_all_ifs(t);
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002952 t->control &= ~(T_REMDEVALL);
2953 }
2954
Luiz Capitulino222f1802006-03-20 22:16:13 -08002955 if (t->control & T_REMDEV) {
Arthur Kepner95ed63f2006-03-20 21:26:56 -08002956 pktgen_rem_one_if(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 t->control &= ~(T_REMDEV);
2958 }
2959
Luiz Capitulino222f1802006-03-20 22:16:13 -08002960 if (need_resched())
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 schedule();
Luiz Capitulino222f1802006-03-20 22:16:13 -08002962 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963
Luiz Capitulino222f1802006-03-20 22:16:13 -08002964 PG_DEBUG(printk("pktgen: %s stopping all device\n", t->name));
2965 pktgen_stop(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
Luiz Capitulino222f1802006-03-20 22:16:13 -08002967 PG_DEBUG(printk("pktgen: %s removing all device\n", t->name));
2968 pktgen_rem_all_ifs(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969
Luiz Capitulino222f1802006-03-20 22:16:13 -08002970 PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
2971 pktgen_rem_thread(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972}
2973
Luiz Capitulino222f1802006-03-20 22:16:13 -08002974static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
2975 const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976{
Luiz Capitulino222f1802006-03-20 22:16:13 -08002977 struct pktgen_dev *pkt_dev = NULL;
2978 if_lock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979
Luiz Capitulino222f1802006-03-20 22:16:13 -08002980 for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
2981 if (strncmp(pkt_dev->ifname, ifname, IFNAMSIZ) == 0) {
2982 break;
2983 }
2984 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
Luiz Capitulino222f1802006-03-20 22:16:13 -08002986 if_unlock(t);
2987 PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev));
2988 return pkt_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989}
2990
2991/*
2992 * Adds a dev at front of if_list.
2993 */
2994
Luiz Capitulino222f1802006-03-20 22:16:13 -08002995static int add_dev_to_thread(struct pktgen_thread *t,
2996 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997{
2998 int rv = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999
Luiz Capitulino222f1802006-03-20 22:16:13 -08003000 if_lock(t);
3001
3002 if (pkt_dev->pg_thread) {
3003 printk("pktgen: ERROR: already assigned to a thread.\n");
3004 rv = -EBUSY;
3005 goto out;
3006 }
3007 pkt_dev->next = t->if_list;
3008 t->if_list = pkt_dev;
3009 pkt_dev->pg_thread = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010 pkt_dev->running = 0;
3011
Luiz Capitulino222f1802006-03-20 22:16:13 -08003012out:
3013 if_unlock(t);
3014 return rv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015}
3016
3017/* Called under thread lock */
3018
Luiz Capitulino222f1802006-03-20 22:16:13 -08003019static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003021 struct pktgen_dev *pkt_dev;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003022 struct proc_dir_entry *pe;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003023
Linus Torvalds1da177e2005-04-16 15:20:36 -07003024 /* We don't allow a device to be on several threads */
3025
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003026 pkt_dev = __pktgen_NN_threads(ifname, FIND);
3027 if (pkt_dev) {
Luiz Capitulino222f1802006-03-20 22:16:13 -08003028 printk("pktgen: ERROR: interface already used.\n");
3029 return -EBUSY;
3030 }
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003031
3032 pkt_dev = kzalloc(sizeof(struct pktgen_dev), GFP_KERNEL);
3033 if (!pkt_dev)
3034 return -ENOMEM;
3035
Luiz Capitulino222f1802006-03-20 22:16:13 -08003036 pkt_dev->flows = vmalloc(MAX_CFLOWS * sizeof(struct flow_state));
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003037 if (pkt_dev->flows == NULL) {
3038 kfree(pkt_dev);
3039 return -ENOMEM;
3040 }
Luiz Capitulino222f1802006-03-20 22:16:13 -08003041 memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003042
Arthur Kepner95ed63f2006-03-20 21:26:56 -08003043 pkt_dev->removal_mark = 0;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003044 pkt_dev->min_pkt_size = ETH_ZLEN;
3045 pkt_dev->max_pkt_size = ETH_ZLEN;
3046 pkt_dev->nfrags = 0;
3047 pkt_dev->clone_skb = pg_clone_skb_d;
3048 pkt_dev->delay_us = pg_delay_d / 1000;
3049 pkt_dev->delay_ns = pg_delay_d % 1000;
3050 pkt_dev->count = pg_count_d;
3051 pkt_dev->sofar = 0;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003052 pkt_dev->udp_src_min = 9; /* sink port */
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003053 pkt_dev->udp_src_max = 9;
3054 pkt_dev->udp_dst_min = 9;
3055 pkt_dev->udp_dst_max = 9;
3056
3057 strncpy(pkt_dev->ifname, ifname, IFNAMSIZ);
3058
Luiz Capitulino222f1802006-03-20 22:16:13 -08003059 if (!pktgen_setup_dev(pkt_dev)) {
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003060 printk("pktgen: ERROR: pktgen_setup_dev failed.\n");
3061 if (pkt_dev->flows)
3062 vfree(pkt_dev->flows);
3063 kfree(pkt_dev);
3064 return -ENODEV;
3065 }
3066
3067 pe = create_proc_entry(ifname, 0600, pg_proc_dir);
3068 if (!pe) {
3069 printk("pktgen: cannot create %s/%s procfs entry.\n",
3070 PG_PROC_DIR, ifname);
3071 if (pkt_dev->flows)
3072 vfree(pkt_dev->flows);
3073 kfree(pkt_dev);
3074 return -EINVAL;
3075 }
3076 pe->proc_fops = &pktgen_if_fops;
3077 pe->data = pkt_dev;
3078
3079 return add_dev_to_thread(t, pkt_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080}
3081
Luiz Capitulino222f1802006-03-20 22:16:13 -08003082static struct pktgen_thread *__init pktgen_find_thread(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003084 struct pktgen_thread *t = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085
Stephen Hemmingerb4099fa2005-10-14 15:32:22 -07003086 thread_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087
Luiz Capitulino222f1802006-03-20 22:16:13 -08003088 t = pktgen_threads;
3089 while (t) {
3090 if (strcmp(t->name, name) == 0)
3091 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092
Luiz Capitulino222f1802006-03-20 22:16:13 -08003093 t = t->next;
3094 }
3095 thread_unlock();
3096 return t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097}
3098
Luiz Capitulino222f1802006-03-20 22:16:13 -08003099static int __init pktgen_create_thread(const char *name, int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100{
Luiz Capitulino222f1802006-03-20 22:16:13 -08003101 struct pktgen_thread *t = NULL;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003102 struct proc_dir_entry *pe;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103
Luiz Capitulino222f1802006-03-20 22:16:13 -08003104 if (strlen(name) > 31) {
3105 printk("pktgen: ERROR: Thread name cannot be more than 31 characters.\n");
3106 return -EINVAL;
3107 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108
Luiz Capitulino222f1802006-03-20 22:16:13 -08003109 if (pktgen_find_thread(name)) {
3110 printk("pktgen: ERROR: thread: %s already exists\n", name);
3111 return -EINVAL;
3112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113
Luiz Capitulino222f1802006-03-20 22:16:13 -08003114 t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
3115 if (!t) {
3116 printk("pktgen: ERROR: out of memory, can't create new thread.\n");
3117 return -ENOMEM;
3118 }
3119
3120 strcpy(t->name, name);
3121 spin_lock_init(&t->if_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 t->cpu = cpu;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003123
3124 pe = create_proc_entry(t->name, 0600, pg_proc_dir);
3125 if (!pe) {
3126 printk("pktgen: cannot create %s/%s procfs entry.\n",
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003127 PG_PROC_DIR, t->name);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003128 kfree(t);
3129 return -EINVAL;
3130 }
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003131
3132 pe->proc_fops = &pktgen_thread_fops;
3133 pe->data = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134
Luiz Capitulino222f1802006-03-20 22:16:13 -08003135 t->next = pktgen_threads;
3136 pktgen_threads = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137
Luiz Capitulino222f1802006-03-20 22:16:13 -08003138 if (kernel_thread((void *)pktgen_thread_worker, (void *)t,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139 CLONE_FS | CLONE_FILES | CLONE_SIGHAND) < 0)
3140 printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
3141
3142 return 0;
3143}
3144
3145/*
3146 * Removes a device from the thread if_list.
3147 */
Luiz Capitulino222f1802006-03-20 22:16:13 -08003148static void _rem_dev_from_if_list(struct pktgen_thread *t,
3149 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150{
3151 struct pktgen_dev *i, *prev = NULL;
3152
3153 i = t->if_list;
3154
Luiz Capitulino222f1802006-03-20 22:16:13 -08003155 while (i) {
3156 if (i == pkt_dev) {
3157 if (prev)
3158 prev->next = i->next;
3159 else
3160 t->if_list = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003161 break;
3162 }
3163 prev = i;
Luiz Capitulino222f1802006-03-20 22:16:13 -08003164 i = i->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 }
3166}
3167
Luiz Capitulino222f1802006-03-20 22:16:13 -08003168static int pktgen_remove_device(struct pktgen_thread *t,
3169 struct pktgen_dev *pkt_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170{
3171
3172 PG_DEBUG(printk("pktgen: remove_device pkt_dev=%p\n", pkt_dev));
3173
Luiz Capitulino222f1802006-03-20 22:16:13 -08003174 if (pkt_dev->running) {
3175 printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n");
3176 pktgen_stop_device(pkt_dev);
3177 }
3178
3179 /* Dis-associate from the interface */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003180
3181 if (pkt_dev->odev) {
3182 dev_put(pkt_dev->odev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003183 pkt_dev->odev = NULL;
3184 }
3185
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 /* And update the thread if_list */
3187
3188 _rem_dev_from_if_list(t, pkt_dev);
3189
Luiz Capitulino222f1802006-03-20 22:16:13 -08003190 /* Clean up proc file system */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003192 remove_proc_entry(pkt_dev->ifname, pg_proc_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193
3194 if (pkt_dev->flows)
3195 vfree(pkt_dev->flows);
3196 kfree(pkt_dev);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003197 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198}
3199
Luiz Capitulino222f1802006-03-20 22:16:13 -08003200static int __init pg_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003201{
3202 int cpu;
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003203 struct proc_dir_entry *pe;
3204
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205 printk(version);
3206
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003207 pg_proc_dir = proc_mkdir(PG_PROC_DIR, proc_net);
3208 if (!pg_proc_dir)
3209 return -ENODEV;
3210 pg_proc_dir->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003212 pe = create_proc_entry(PGCTRL, 0600, pg_proc_dir);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003213 if (pe == NULL) {
3214 printk("pktgen: ERROR: cannot create %s procfs entry.\n",
3215 PGCTRL);
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003216 proc_net_remove(PG_PROC_DIR);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003217 return -EINVAL;
3218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219
Luiz Capitulino222f1802006-03-20 22:16:13 -08003220 pe->proc_fops = &pktgen_fops;
3221 pe->data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222
3223 /* Register us to receive netdevice events */
3224 register_netdevice_notifier(&pktgen_notifier_block);
Luiz Capitulino222f1802006-03-20 22:16:13 -08003225
John Hawkes670c02c2005-10-13 09:30:31 -07003226 for_each_online_cpu(cpu) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227 char buf[30];
3228
Luiz Capitulino222f1802006-03-20 22:16:13 -08003229 sprintf(buf, "kpktgend_%i", cpu);
3230 pktgen_create_thread(buf, cpu);
3231 }
3232 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003233}
3234
3235static void __exit pg_cleanup(void)
3236{
3237 wait_queue_head_t queue;
3238 init_waitqueue_head(&queue);
3239
Luiz Capitulino222f1802006-03-20 22:16:13 -08003240 /* Stop all interfaces & threads */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241
Luiz Capitulino222f1802006-03-20 22:16:13 -08003242 while (pktgen_threads) {
3243 struct pktgen_thread *t = pktgen_threads;
3244 pktgen_threads->control |= (T_TERMINATE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245
Luiz Capitulino222f1802006-03-20 22:16:13 -08003246 wait_event_interruptible_timeout(queue, (t != pktgen_threads),
3247 HZ);
3248 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003249
Luiz Capitulino222f1802006-03-20 22:16:13 -08003250 /* Un-register us from receiving netdevice events */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003251 unregister_netdevice_notifier(&pktgen_notifier_block);
3252
Luiz Capitulino222f1802006-03-20 22:16:13 -08003253 /* Clean up proc file system */
Stephen Hemmingerd50a6b52005-10-14 15:42:33 -07003254 remove_proc_entry(PGCTRL, pg_proc_dir);
3255 proc_net_remove(PG_PROC_DIR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003256}
3257
Linus Torvalds1da177e2005-04-16 15:20:36 -07003258module_init(pg_init);
3259module_exit(pg_cleanup);
3260
3261MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
3262MODULE_DESCRIPTION("Packet Generator tool");
3263MODULE_LICENSE("GPL");
3264module_param(pg_count_d, int, 0);
3265module_param(pg_delay_d, int, 0);
3266module_param(pg_clone_skb_d, int, 0);
3267module_param(debug, int, 0);