1 /* -*-linux-c-*-
2 * $Id: pktgen.c,v 1.8 2002/07/15 19:30:17 robert Exp $
3 * pktgen.c: Packet Generator for performance evaluation.
4 *
5 * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
6 * Uppsala University, Sweden
7 *
8 * A tool for loading the network with preconfigurated packets.
9 * The tool is implemented as a linux module. Parameters are output
10 * device, IPG (interpacket gap), number of packets, and whether
11 * to use multiple SKBs or just the same one.
12 * pktgen uses the installed interface's output routine.
13 *
14 * Additional hacking by:
15 *
16 * Jens.Laas@data.slu.se
17 * Improved by ANK. 010120.
18 * Improved by ANK even more. 010212.
19 * MAC address typo fixed. 010417 --ro
20 * Integrated. 020301 --DaveM
21 * Added multiskb option 020301 --DaveM
22 * Scaling of results. 020417--sigurdur@linpro.no
23 * Significant re-work of the module:
24 * * Updated to support generation over multiple interfaces at once
25 * by creating 32 /proc/net/pg* files. Each file can be manipulated
26 * individually.
27 * * Converted many counters to __u64 to allow longer runs.
28 * * Allow configuration of ranges, like min/max IP address, MACs,
29 * and UDP-ports, for both source and destination, and can
30 * set to use a random distribution or sequentially walk the range.
31 * * Can now change some values after starting.
32 * * Place 12-byte packet in UDP payload with magic number,
33 * sequence number, and timestamp. Will write receiver next.
34 * * The new changes seem to have a performance impact of around 1%,
35 * as far as I can tell.
36 * --Ben Greear <greearb@candelatech.com>
37 *
38 * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
39 * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
40 * as a "fastpath" with a configurable number of clones after alloc's.
41 *
42 * clone_skb=0 means all packets are allocated this also means ranges time
43 * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
44 * clones.
45 *
46 * Also moved to /proc/net/pktgen/
47 * --ro
48 *
49 * Fix refcount off by one if first packet fails, potential null deref,
50 * memleak 030710- KJP
51 *
52 * See Documentation/networking/pktgen.txt for how to use this.
53 */
54
55 #include <linux/module.h>
56 #include <linux/kernel.h>
57 #include <linux/sched.h>
58 #include <linux/types.h>
59 #include <linux/string.h>
60 #include <linux/ptrace.h>
61 #include <linux/errno.h>
62 #include <linux/ioport.h>
63 #include <linux/slab.h>
64 #include <linux/interrupt.h>
65 #include <linux/pci.h>
66 #include <linux/delay.h>
67 #include <linux/init.h>
68 #include <linux/inet.h>
69 #include <asm/byteorder.h>
70 #include <asm/bitops.h>
71 #include <asm/io.h>
72 #include <asm/dma.h>
73 #include <asm/uaccess.h>
74
75 #include <linux/in.h>
76 #include <linux/ip.h>
77 #include <linux/udp.h>
78 #include <linux/skbuff.h>
79 #include <linux/netdevice.h>
80 #include <linux/inetdevice.h>
81 #include <linux/rtnetlink.h>
82 #include <linux/proc_fs.h>
83 #include <linux/if_arp.h>
84 #include <net/checksum.h>
85 #include <asm/timex.h>
86
87 #define cycles() ((u32)get_cycles())
88
89
90 #define VERSION "pktgen version 1.3"
91 static char version[] __initdata =
92 "pktgen.c: v1.3: Packet Generator for packet performance testing.\n";
93
94 /* Used to help with determining the pkts on receive */
95
96 #define PKTGEN_MAGIC 0xbe9be955
97
98
99 /* Keep information per interface */
100 struct pktgen_info {
101 /* Parameters */
102
103 /* If min != max, then we will either do a linear iteration, or
104 * we will do a random selection from within the range.
105 */
106 __u32 flags;
107
108 #define F_IPSRC_RND (1<<0) /* IP-Src Random */
109 #define F_IPDST_RND (1<<1) /* IP-Dst Random */
110 #define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
111 #define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
112 #define F_MACSRC_RND (1<<4) /* MAC-Src Random */
113 #define F_MACDST_RND (1<<5) /* MAC-Dst Random */
114 #define F_SET_SRCMAC (1<<6) /* Specify-Src-Mac
115 (default is to use Interface's MAC Addr) */
116 #define F_SET_SRCIP (1<<7) /* Specify-Src-IP
117 (default is to use Interface's IP Addr) */
118
119
120 int pkt_size; /* = ETH_ZLEN; */
121 int nfrags;
122 __u32 ipg; /* Default Interpacket gap in nsec */
123 __u64 count; /* Default No packets to send */
124 __u64 sofar; /* How many pkts we've sent so far */
125 __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
126 struct timeval started_at;
127 struct timeval stopped_at;
128 __u64 idle_acc;
129 __u32 seq_num;
130
131 int clone_skb; /* Use multiple SKBs during packet gen. If this number
132 * is greater than 1, then that many coppies of the same
133 * packet will be sent before a new packet is allocated.
134 * For instance, if you want to send 1024 identical packets
135 * before creating a new packet, set clone_skb to 1024.
136 */
137 int busy;
138 int do_run_run; /* if this changes to false, the test will stop */
139
140 char outdev[32];
141 char dst_min[32];
142 char dst_max[32];
143 char src_min[32];
144 char src_max[32];
145
146 /* If we're doing ranges, random or incremental, then this
147 * defines the min/max for those ranges.
148 */
149 __u32 saddr_min; /* inclusive, source IP address */
150 __u32 saddr_max; /* exclusive, source IP address */
151 __u32 daddr_min; /* inclusive, dest IP address */
152 __u32 daddr_max; /* exclusive, dest IP address */
153
154 __u16 udp_src_min; /* inclusive, source UDP port */
155 __u16 udp_src_max; /* exclusive, source UDP port */
156 __u16 udp_dst_min; /* inclusive, dest UDP port */
157 __u16 udp_dst_max; /* exclusive, dest UDP port */
158
159 __u32 src_mac_count; /* How many MACs to iterate through */
160 __u32 dst_mac_count; /* How many MACs to iterate through */
161
162 unsigned char dst_mac[6];
163 unsigned char src_mac[6];
164
165 __u32 cur_dst_mac_offset;
166 __u32 cur_src_mac_offset;
167 __u32 cur_saddr;
168 __u32 cur_daddr;
169 __u16 cur_udp_dst;
170 __u16 cur_udp_src;
171
172 __u8 hh[14];
173 /* = {
174 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
175
176 We fill in SRC address later
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x08, 0x00
179 };
180 */
181 __u16 pad; /* pad out the hh struct to an even 16 bytes */
182 char result[512];
183
184 /* proc file names */
185 char fname[80];
186 char busy_fname[80];
187
188 struct proc_dir_entry *proc_ent;
189 struct proc_dir_entry *busy_proc_ent;
190 };
191
192 struct pktgen_hdr {
193 __u32 pgh_magic;
194 __u32 seq_num;
195 struct timeval timestamp;
196 };
197
198 static int cpu_speed;
199 static int debug;
200
201 /* Module parameters, defaults. */
202 static int count_d = 100000;
203 static int ipg_d = 0;
204 static int clone_skb_d = 0;
205
206
207 #define MAX_PKTGEN 8
208 static struct pktgen_info pginfos[MAX_PKTGEN];
209
210
211 /** Convert to miliseconds */
tv_to_ms(const struct timeval * tv)212 inline __u64 tv_to_ms(const struct timeval* tv) {
213 __u64 ms = tv->tv_usec / 1000;
214 ms += (__u64)tv->tv_sec * (__u64)1000;
215 return ms;
216 }
217
getCurMs(void)218 inline __u64 getCurMs(void) {
219 struct timeval tv;
220 do_gettimeofday(&tv);
221 return tv_to_ms(&tv);
222 }
223
224 #define PG_PROC_DIR "pktgen"
225 static struct proc_dir_entry *proc_dir = 0;
226
setup_inject(struct pktgen_info * info)227 static struct net_device *setup_inject(struct pktgen_info* info)
228 {
229 struct net_device *odev;
230
231 rtnl_lock();
232 odev = __dev_get_by_name(info->outdev);
233 if (!odev) {
234 sprintf(info->result, "No such netdevice: \"%s\"", info->outdev);
235 goto out_unlock;
236 }
237
238 if (odev->type != ARPHRD_ETHER) {
239 sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev);
240 goto out_unlock;
241 }
242
243 if (!netif_running(odev)) {
244 sprintf(info->result, "Device is down: \"%s\"", info->outdev);
245 goto out_unlock;
246 }
247
248 /* Default to the interface's mac if not explicitly set. */
249 if (!(info->flags & F_SET_SRCMAC)) {
250 memcpy(&(info->hh[6]), odev->dev_addr, 6);
251 }
252 else {
253 memcpy(&(info->hh[6]), info->src_mac, 6);
254 }
255
256 /* Set up Dest MAC */
257 memcpy(&(info->hh[0]), info->dst_mac, 6);
258
259 info->saddr_min = 0;
260 info->saddr_max = 0;
261 if (strlen(info->src_min) == 0) {
262 if (odev->ip_ptr) {
263 struct in_device *in_dev = odev->ip_ptr;
264
265 if (in_dev->ifa_list) {
266 info->saddr_min = in_dev->ifa_list->ifa_address;
267 info->saddr_max = info->saddr_min;
268 }
269 }
270 }
271 else {
272 info->saddr_min = in_aton(info->src_min);
273 info->saddr_max = in_aton(info->src_max);
274 }
275
276 info->daddr_min = in_aton(info->dst_min);
277 info->daddr_max = in_aton(info->dst_max);
278
279 /* Initialize current values. */
280 info->cur_dst_mac_offset = 0;
281 info->cur_src_mac_offset = 0;
282 info->cur_saddr = info->saddr_min;
283 info->cur_daddr = info->daddr_min;
284 info->cur_udp_dst = info->udp_dst_min;
285 info->cur_udp_src = info->udp_src_min;
286
287 atomic_inc(&odev->refcnt);
288 rtnl_unlock();
289
290 return odev;
291
292 out_unlock:
293 rtnl_unlock();
294 return NULL;
295 }
296
nanospin(int ipg,struct pktgen_info * info)297 static void nanospin(int ipg, struct pktgen_info* info)
298 {
299 u32 idle_start, idle;
300
301 idle_start = cycles();
302
303 for (;;) {
304 barrier();
305 idle = cycles() - idle_start;
306 if (idle * 1000 >= ipg * cpu_speed)
307 break;
308 }
309 info->idle_acc += idle;
310 }
311
calc_mhz(void)312 static int calc_mhz(void)
313 {
314 struct timeval start, stop;
315 u32 start_s, elapsed;
316
317 do_gettimeofday(&start);
318 start_s = cycles();
319 do {
320 barrier();
321 elapsed = cycles() - start_s;
322 if (elapsed == 0)
323 return 0;
324 } while (elapsed < 1000 * 50000);
325 do_gettimeofday(&stop);
326 return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
327 }
328
cycles_calibrate(void)329 static void cycles_calibrate(void)
330 {
331 int i;
332
333 for (i = 0; i < 3; i++) {
334 int res = calc_mhz();
335 if (res > cpu_speed)
336 cpu_speed = res;
337 }
338 }
339
340
341 /* Increment/randomize headers according to flags and current values
342 * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
343 */
mod_cur_headers(struct pktgen_info * info)344 static void mod_cur_headers(struct pktgen_info* info) {
345 __u32 imn;
346 __u32 imx;
347
348 /* Deal with source MAC */
349 if (info->src_mac_count > 1) {
350 __u32 mc;
351 __u32 tmp;
352 if (info->flags & F_MACSRC_RND) {
353 mc = net_random() % (info->src_mac_count);
354 }
355 else {
356 mc = info->cur_src_mac_offset++;
357 if (info->cur_src_mac_offset > info->src_mac_count) {
358 info->cur_src_mac_offset = 0;
359 }
360 }
361
362 tmp = info->src_mac[5] + (mc & 0xFF);
363 info->hh[11] = tmp;
364 tmp = (info->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
365 info->hh[10] = tmp;
366 tmp = (info->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
367 info->hh[9] = tmp;
368 tmp = (info->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
369 info->hh[8] = tmp;
370 tmp = (info->src_mac[1] + (tmp >> 8));
371 info->hh[7] = tmp;
372 }
373
374 /* Deal with Destination MAC */
375 if (info->dst_mac_count > 1) {
376 __u32 mc;
377 __u32 tmp;
378 if (info->flags & F_MACDST_RND) {
379 mc = net_random() % (info->dst_mac_count);
380 }
381 else {
382 mc = info->cur_dst_mac_offset++;
383 if (info->cur_dst_mac_offset > info->dst_mac_count) {
384 info->cur_dst_mac_offset = 0;
385 }
386 }
387
388 tmp = info->dst_mac[5] + (mc & 0xFF);
389 info->hh[5] = tmp;
390 tmp = (info->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
391 info->hh[4] = tmp;
392 tmp = (info->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
393 info->hh[3] = tmp;
394 tmp = (info->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
395 info->hh[2] = tmp;
396 tmp = (info->dst_mac[1] + (tmp >> 8));
397 info->hh[1] = tmp;
398 }
399
400 if (info->udp_src_min < info->udp_src_max) {
401 if (info->flags & F_UDPSRC_RND) {
402 info->cur_udp_src = ((net_random() % (info->udp_src_max - info->udp_src_min))
403 + info->udp_src_min);
404 }
405 else {
406 info->cur_udp_src++;
407 if (info->cur_udp_src >= info->udp_src_max) {
408 info->cur_udp_src = info->udp_src_min;
409 }
410 }
411 }
412
413 if (info->udp_dst_min < info->udp_dst_max) {
414 if (info->flags & F_UDPDST_RND) {
415 info->cur_udp_dst = ((net_random() % (info->udp_dst_max - info->udp_dst_min))
416 + info->udp_dst_min);
417 }
418 else {
419 info->cur_udp_dst++;
420 if (info->cur_udp_dst >= info->udp_dst_max) {
421 info->cur_udp_dst = info->udp_dst_min;
422 }
423 }
424 }
425
426 if ((imn = ntohl(info->saddr_min)) < (imx = ntohl(info->saddr_max))) {
427 __u32 t;
428 if (info->flags & F_IPSRC_RND) {
429 t = ((net_random() % (imx - imn)) + imn);
430 }
431 else {
432 t = ntohl(info->cur_saddr);
433 t++;
434 if (t >= imx) {
435 t = imn;
436 }
437 }
438 info->cur_saddr = htonl(t);
439 }
440
441 if ((imn = ntohl(info->daddr_min)) < (imx = ntohl(info->daddr_max))) {
442 __u32 t;
443 if (info->flags & F_IPDST_RND) {
444 t = ((net_random() % (imx - imn)) + imn);
445 }
446 else {
447 t = ntohl(info->cur_daddr);
448 t++;
449 if (t >= imx) {
450 t = imn;
451 }
452 }
453 info->cur_daddr = htonl(t);
454 }
455 }/* mod_cur_headers */
456
457
fill_packet(struct net_device * odev,struct pktgen_info * info)458 static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_info* info)
459 {
460 struct sk_buff *skb = NULL;
461 __u8 *eth;
462 struct udphdr *udph;
463 int datalen, iplen;
464 struct iphdr *iph;
465 struct pktgen_hdr *pgh = NULL;
466
467 skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC);
468 if (!skb) {
469 sprintf(info->result, "No memory");
470 return NULL;
471 }
472
473 skb_reserve(skb, 16);
474
475 /* Reserve for ethernet and IP header */
476 eth = (__u8 *) skb_push(skb, 14);
477 iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
478 udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
479
480 /* Update any of the values, used when we're incrementing various
481 * fields.
482 */
483 mod_cur_headers(info);
484
485 memcpy(eth, info->hh, 14);
486
487 datalen = info->pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
488 if (datalen < sizeof(struct pktgen_hdr)) {
489 datalen = sizeof(struct pktgen_hdr);
490 }
491
492 udph->source = htons(info->cur_udp_src);
493 udph->dest = htons(info->cur_udp_dst);
494 udph->len = htons(datalen + 8); /* DATA + udphdr */
495 udph->check = 0; /* No checksum */
496
497 iph->ihl = 5;
498 iph->version = 4;
499 iph->ttl = 3;
500 iph->tos = 0;
501 iph->protocol = IPPROTO_UDP; /* UDP */
502 iph->saddr = info->cur_saddr;
503 iph->daddr = info->cur_daddr;
504 iph->frag_off = 0;
505 iplen = 20 + 8 + datalen;
506 iph->tot_len = htons(iplen);
507 iph->check = 0;
508 iph->check = ip_fast_csum((void *) iph, iph->ihl);
509 skb->protocol = __constant_htons(ETH_P_IP);
510 skb->mac.raw = ((u8 *)iph) - 14;
511 skb->dev = odev;
512 skb->pkt_type = PACKET_HOST;
513 skb->nh.iph = iph;
514 skb->h.uh = udph;
515
516 if (info->nfrags <= 0) {
517 pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
518 } else {
519 int frags = info->nfrags;
520 int i;
521
522 /* TODO: Verify this is OK...it sure is ugly. --Ben */
523 pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
524
525 if (frags > MAX_SKB_FRAGS)
526 frags = MAX_SKB_FRAGS;
527 if (datalen > frags*PAGE_SIZE) {
528 skb_put(skb, datalen-frags*PAGE_SIZE);
529 datalen = frags*PAGE_SIZE;
530 }
531
532 i = 0;
533 while (datalen > 0) {
534 struct page *page = alloc_pages(GFP_KERNEL, 0);
535 skb_shinfo(skb)->frags[i].page = page;
536 skb_shinfo(skb)->frags[i].page_offset = 0;
537 skb_shinfo(skb)->frags[i].size =
538 (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
539 datalen -= skb_shinfo(skb)->frags[i].size;
540 skb->len += skb_shinfo(skb)->frags[i].size;
541 skb->data_len += skb_shinfo(skb)->frags[i].size;
542 i++;
543 skb_shinfo(skb)->nr_frags = i;
544 }
545
546 while (i < frags) {
547 int rem;
548
549 if (i == 0)
550 break;
551
552 rem = skb_shinfo(skb)->frags[i - 1].size / 2;
553 if (rem == 0)
554 break;
555
556 skb_shinfo(skb)->frags[i - 1].size -= rem;
557
558 skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
559 get_page(skb_shinfo(skb)->frags[i].page);
560 skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i - 1].page;
561 skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i - 1].size;
562 skb_shinfo(skb)->frags[i].size = rem;
563 i++;
564 skb_shinfo(skb)->nr_frags = i;
565 }
566 }
567
568 /* Stamp the time, and sequence number, convert them to network byte order */
569 if (pgh) {
570 pgh->pgh_magic = htonl(PKTGEN_MAGIC);
571 do_gettimeofday(&(pgh->timestamp));
572 pgh->timestamp.tv_usec = htonl(pgh->timestamp.tv_usec);
573 pgh->timestamp.tv_sec = htonl(pgh->timestamp.tv_sec);
574 pgh->seq_num = htonl(info->seq_num);
575 }
576
577 return skb;
578 }
579
580
inject(struct pktgen_info * info)581 static void inject(struct pktgen_info* info)
582 {
583 struct net_device *odev = NULL;
584 struct sk_buff *skb = NULL;
585 __u64 total = 0;
586 __u64 idle = 0;
587 __u64 lcount = 0;
588 int nr_frags = 0;
589 int last_ok = 1; /* Was last skb sent?
590 * Or a failed transmit of some sort? This will keep
591 * sequence numbers in order, for example.
592 */
593 __u64 fp = 0;
594 __u32 fp_tmp = 0;
595
596 odev = setup_inject(info);
597 if (!odev)
598 return;
599
600 info->do_run_run = 1; /* Cranke yeself! */
601 info->idle_acc = 0;
602 info->sofar = 0;
603 lcount = info->count;
604
605
606 /* Build our initial pkt and place it as a re-try pkt. */
607 skb = fill_packet(odev, info);
608 if (skb == NULL) goto out_reldev;
609
610 do_gettimeofday(&(info->started_at));
611
612 while(info->do_run_run) {
613
614 /* Set a time-stamp, so build a new pkt each time */
615
616 if (last_ok) {
617 if (++fp_tmp >= info->clone_skb ) {
618 kfree_skb(skb);
619 skb = fill_packet(odev, info);
620 if (skb == NULL) {
621 goto out_reldev;
622 }
623 fp++;
624 fp_tmp = 0; /* reset counter */
625 }
626 }
627
628 nr_frags = skb_shinfo(skb)->nr_frags;
629
630 spin_lock_bh(&odev->xmit_lock);
631 if (!netif_queue_stopped(odev)) {
632
633 atomic_inc(&skb->users);
634
635 if (odev->hard_start_xmit(skb, odev)) {
636
637 atomic_dec(&skb->users);
638 if (net_ratelimit()) {
639 printk(KERN_INFO "Hard xmit error\n");
640 }
641 info->errors++;
642 last_ok = 0;
643 }
644 else {
645 last_ok = 1;
646 info->sofar++;
647 info->seq_num++;
648 }
649 }
650 else {
651 /* Re-try it next time */
652 last_ok = 0;
653 }
654
655
656 spin_unlock_bh(&odev->xmit_lock);
657
658 if (info->ipg) {
659 /* Try not to busy-spin if we have larger sleep times.
660 * TODO: Investigate better ways to do this.
661 */
662 if (info->ipg < 10000) { /* 10 usecs or less */
663 nanospin(info->ipg, info);
664 }
665 else if (info->ipg < 10000000) { /* 10ms or less */
666 udelay(info->ipg / 1000);
667 }
668 else {
669 mdelay(info->ipg / 1000000);
670 }
671 }
672
673 if (signal_pending(current)) {
674 break;
675 }
676
677 /* If lcount is zero, then run forever */
678 if ((lcount != 0) && (--lcount == 0)) {
679 if (atomic_read(&skb->users) != 1) {
680 u32 idle_start, idle;
681
682 idle_start = cycles();
683 while (atomic_read(&skb->users) != 1) {
684 if (signal_pending(current)) {
685 break;
686 }
687 schedule();
688 }
689 idle = cycles() - idle_start;
690 info->idle_acc += idle;
691 }
692 break;
693 }
694
695 if (netif_queue_stopped(odev) || current->need_resched) {
696 u32 idle_start, idle;
697
698 idle_start = cycles();
699 do {
700 if (signal_pending(current)) {
701 info->do_run_run = 0;
702 break;
703 }
704 if (!netif_running(odev)) {
705 info->do_run_run = 0;
706 break;
707 }
708 if (current->need_resched)
709 schedule();
710 else
711 do_softirq();
712 } while (netif_queue_stopped(odev));
713 idle = cycles() - idle_start;
714 info->idle_acc += idle;
715 }
716 }/* while we should be running */
717
718 do_gettimeofday(&(info->stopped_at));
719
720 total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 +
721 info->stopped_at.tv_usec - info->started_at.tv_usec;
722
723 idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed);
724
725 {
726 char *p = info->result;
727 __u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000);
728 __u64 bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */
729 p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) %llupps %lluMb/sec (%llubps) errors: %llu",
730 (unsigned long long) total,
731 (unsigned long long) (total - idle),
732 (unsigned long long) idle,
733 (unsigned long long) info->sofar,
734 skb->len + 4, /* Add 4 to account for the ethernet checksum */
735 nr_frags,
736 (unsigned long long) pps,
737 (unsigned long long) (bps / (u64) 1024 / (u64) 1024),
738 (unsigned long long) bps,
739 (unsigned long long) info->errors
740 );
741 }
742
743 kfree_skb(skb);
744
745 out_reldev:
746 if (odev) {
747 dev_put(odev);
748 odev = NULL;
749 }
750
751 return;
752
753 }
754
755 /* proc/net/pktgen/pg */
756
proc_busy_read(char * buf,char ** start,off_t offset,int len,int * eof,void * data)757 static int proc_busy_read(char *buf , char **start, off_t offset,
758 int len, int *eof, void *data)
759 {
760 char *p;
761 int idx = (int)(long)(data);
762 struct pktgen_info* info = NULL;
763
764 if ((idx < 0) || (idx >= MAX_PKTGEN)) {
765 printk("ERROR: idx: %i is out of range in proc_write\n", idx);
766 return -EINVAL;
767 }
768 info = &(pginfos[idx]);
769
770 p = buf;
771 p += sprintf(p, "%d\n", info->busy);
772 *eof = 1;
773
774 return p-buf;
775 }
776
proc_read(char * buf,char ** start,off_t offset,int len,int * eof,void * data)777 static int proc_read(char *buf , char **start, off_t offset,
778 int len, int *eof, void *data)
779 {
780 char *p;
781 int i;
782 int idx = (int)(long)(data);
783 struct pktgen_info* info = NULL;
784 __u64 sa;
785 __u64 stopped;
786 __u64 now = getCurMs();
787
788 if ((idx < 0) || (idx >= MAX_PKTGEN)) {
789 printk("ERROR: idx: %i is out of range in proc_write\n", idx);
790 return -EINVAL;
791 }
792 info = &(pginfos[idx]);
793
794 p = buf;
795 p += sprintf(p, "%s\n", VERSION); /* Help with parsing compatibility */
796 p += sprintf(p, "Params: count %llu pkt_size: %u frags: %d ipg: %u clone_skb: %d odev \"%s\"\n",
797 (unsigned long long) info->count,
798 info->pkt_size, info->nfrags, info->ipg,
799 info->clone_skb, info->outdev);
800 p += sprintf(p, " dst_min: %s dst_max: %s src_min: %s src_max: %s\n",
801 info->dst_min, info->dst_max, info->src_min, info->src_max);
802 p += sprintf(p, " src_mac: ");
803 for (i = 0; i < 6; i++) {
804 p += sprintf(p, "%02X%s", info->src_mac[i], i == 5 ? " " : ":");
805 }
806 p += sprintf(p, "dst_mac: ");
807 for (i = 0; i < 6; i++) {
808 p += sprintf(p, "%02X%s", info->dst_mac[i], i == 5 ? "\n" : ":");
809 }
810 p += sprintf(p, " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
811 info->udp_src_min, info->udp_src_max, info->udp_dst_min,
812 info->udp_dst_max);
813 p += sprintf(p, " src_mac_count: %d dst_mac_count: %d\n Flags: ",
814 info->src_mac_count, info->dst_mac_count);
815 if (info->flags & F_IPSRC_RND) {
816 p += sprintf(p, "IPSRC_RND ");
817 }
818 if (info->flags & F_IPDST_RND) {
819 p += sprintf(p, "IPDST_RND ");
820 }
821 if (info->flags & F_UDPSRC_RND) {
822 p += sprintf(p, "UDPSRC_RND ");
823 }
824 if (info->flags & F_UDPDST_RND) {
825 p += sprintf(p, "UDPDST_RND ");
826 }
827 if (info->flags & F_MACSRC_RND) {
828 p += sprintf(p, "MACSRC_RND ");
829 }
830 if (info->flags & F_MACDST_RND) {
831 p += sprintf(p, "MACDST_RND ");
832 }
833 p += sprintf(p, "\n");
834
835 sa = tv_to_ms(&(info->started_at));
836 stopped = tv_to_ms(&(info->stopped_at));
837 if (info->do_run_run) {
838 stopped = now; /* not really stopped, more like last-running-at */
839 }
840 p += sprintf(p, "Current:\n pkts-sofar: %llu errors: %llu\n started: %llums stopped: %llums now: %llums idle: %lluns\n",
841 (unsigned long long) info->sofar,
842 (unsigned long long) info->errors,
843 (unsigned long long) sa,
844 (unsigned long long) stopped,
845 (unsigned long long) now,
846 (unsigned long long) info->idle_acc);
847 p += sprintf(p, " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
848 info->seq_num, info->cur_dst_mac_offset, info->cur_src_mac_offset);
849 p += sprintf(p, " cur_saddr: 0x%x cur_daddr: 0x%x cur_udp_dst: %d cur_udp_src: %d\n",
850 info->cur_saddr, info->cur_daddr, info->cur_udp_dst, info->cur_udp_src);
851
852 if (info->result[0])
853 p += sprintf(p, "Result: %s\n", info->result);
854 else
855 p += sprintf(p, "Result: Idle\n");
856 *eof = 1;
857
858 return p - buf;
859 }
860
count_trail_chars(const char * user_buffer,unsigned int maxlen)861 static int count_trail_chars(const char *user_buffer, unsigned int maxlen)
862 {
863 int i;
864
865 for (i = 0; i < maxlen; i++) {
866 char c;
867
868 if (get_user(c, &user_buffer[i]))
869 return -EFAULT;
870 switch (c) {
871 case '\"':
872 case '\n':
873 case '\r':
874 case '\t':
875 case ' ':
876 case '=':
877 break;
878 default:
879 goto done;
880 };
881 }
882 done:
883 return i;
884 }
885
num_arg(const char * user_buffer,unsigned long maxlen,unsigned long * num)886 static unsigned long num_arg(const char *user_buffer, unsigned long maxlen,
887 unsigned long *num)
888 {
889 int i = 0;
890
891 *num = 0;
892
893 for(; i < maxlen; i++) {
894 char c;
895
896 if (get_user(c, &user_buffer[i]))
897 return -EFAULT;
898 if ((c >= '0') && (c <= '9')) {
899 *num *= 10;
900 *num += c -'0';
901 } else
902 break;
903 }
904 return i;
905 }
906
strn_len(const char * user_buffer,unsigned int maxlen)907 static int strn_len(const char *user_buffer, unsigned int maxlen)
908 {
909 int i = 0;
910
911 for(; i < maxlen; i++) {
912 char c;
913
914 if (get_user(c, &user_buffer[i]))
915 return -EFAULT;
916 switch (c) {
917 case '\"':
918 case '\n':
919 case '\r':
920 case '\t':
921 case ' ':
922 goto done_str;
923 default:
924 break;
925 };
926 }
927 done_str:
928 return i;
929 }
930
proc_write(struct file * file,const char * user_buffer,unsigned long count,void * data)931 static int proc_write(struct file *file, const char *user_buffer,
932 unsigned long count, void *data)
933 {
934 int i = 0, max, len;
935 char name[16], valstr[32];
936 unsigned long value = 0;
937 int idx = (int)(long)(data);
938 struct pktgen_info* info = NULL;
939 char* result = NULL;
940 int tmp;
941
942 if ((idx < 0) || (idx >= MAX_PKTGEN)) {
943 printk("ERROR: idx: %i is out of range in proc_write\n", idx);
944 return -EINVAL;
945 }
946 info = &(pginfos[idx]);
947 result = &(info->result[0]);
948
949 if (count < 1) {
950 sprintf(result, "Wrong command format");
951 return -EINVAL;
952 }
953
954 max = count - i;
955 tmp = count_trail_chars(&user_buffer[i], max);
956 if (tmp < 0)
957 return tmp;
958 i += tmp;
959
960 /* Read variable name */
961
962 len = strn_len(&user_buffer[i], sizeof(name) - 1);
963 if (len < 0)
964 return len;
965 memset(name, 0, sizeof(name));
966 if (copy_from_user(name, &user_buffer[i], len))
967 return -EFAULT;
968 i += len;
969
970 max = count -i;
971 len = count_trail_chars(&user_buffer[i], max);
972 if (len < 0)
973 return len;
974 i += len;
975
976 if (debug)
977 printk("pg: %s,%lu\n", name, count);
978
979 if (!strcmp(name, "stop")) {
980 if (info->do_run_run) {
981 strcpy(result, "Stopping");
982 }
983 else {
984 strcpy(result, "Already stopped...\n");
985 }
986 info->do_run_run = 0;
987 return count;
988 }
989
990 if (!strcmp(name, "pkt_size")) {
991 len = num_arg(&user_buffer[i], 10, &value);
992 if (len < 0)
993 return len;
994 i += len;
995 if (value < 14+20+8)
996 value = 14+20+8;
997 info->pkt_size = value;
998 sprintf(result, "OK: pkt_size=%u", info->pkt_size);
999 return count;
1000 }
1001 if (!strcmp(name, "frags")) {
1002 len = num_arg(&user_buffer[i], 10, &value);
1003 if (len < 0)
1004 return len;
1005 i += len;
1006 info->nfrags = value;
1007 sprintf(result, "OK: frags=%u", info->nfrags);
1008 return count;
1009 }
1010 if (!strcmp(name, "ipg")) {
1011 len = num_arg(&user_buffer[i], 10, &value);
1012 if (len < 0)
1013 return len;
1014 i += len;
1015 info->ipg = value;
1016 sprintf(result, "OK: ipg=%u", info->ipg);
1017 return count;
1018 }
1019 if (!strcmp(name, "udp_src_min")) {
1020 len = num_arg(&user_buffer[i], 10, &value);
1021 if (len < 0)
1022 return len;
1023 i += len;
1024 info->udp_src_min = value;
1025 sprintf(result, "OK: udp_src_min=%u", info->udp_src_min);
1026 return count;
1027 }
1028 if (!strcmp(name, "udp_dst_min")) {
1029 len = num_arg(&user_buffer[i], 10, &value);
1030 if (len < 0)
1031 return len;
1032 i += len;
1033 info->udp_dst_min = value;
1034 sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min);
1035 return count;
1036 }
1037 if (!strcmp(name, "udp_src_max")) {
1038 len = num_arg(&user_buffer[i], 10, &value);
1039 if (len < 0)
1040 return len;
1041 i += len;
1042 info->udp_src_max = value;
1043 sprintf(result, "OK: udp_src_max=%u", info->udp_src_max);
1044 return count;
1045 }
1046 if (!strcmp(name, "udp_dst_max")) {
1047 len = num_arg(&user_buffer[i], 10, &value);
1048 if (len < 0)
1049 return len;
1050 i += len;
1051 info->udp_dst_max = value;
1052 sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max);
1053 return count;
1054 }
1055 if (!strcmp(name, "clone_skb")) {
1056 len = num_arg(&user_buffer[i], 10, &value);
1057 if (len < 0)
1058 return len;
1059 i += len;
1060 info->clone_skb = value;
1061
1062 sprintf(result, "OK: clone_skb=%d", info->clone_skb);
1063 return count;
1064 }
1065 if (!strcmp(name, "count")) {
1066 len = num_arg(&user_buffer[i], 10, &value);
1067 if (len < 0)
1068 return len;
1069 i += len;
1070 info->count = value;
1071 sprintf(result, "OK: count=%llu", (unsigned long long) info->count);
1072 return count;
1073 }
1074 if (!strcmp(name, "src_mac_count")) {
1075 len = num_arg(&user_buffer[i], 10, &value);
1076 if (len < 0)
1077 return len;
1078 i += len;
1079 info->src_mac_count = value;
1080 sprintf(result, "OK: src_mac_count=%d", info->src_mac_count);
1081 return count;
1082 }
1083 if (!strcmp(name, "dst_mac_count")) {
1084 len = num_arg(&user_buffer[i], 10, &value);
1085 if (len < 0)
1086 return len;
1087 i += len;
1088 info->dst_mac_count = value;
1089 sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count);
1090 return count;
1091 }
1092 if (!strcmp(name, "odev")) {
1093 len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1);
1094 if (len < 0)
1095 return len;
1096 memset(info->outdev, 0, sizeof(info->outdev));
1097 if (copy_from_user(info->outdev, &user_buffer[i], len))
1098 return -EFAULT;
1099 i += len;
1100 sprintf(result, "OK: odev=%s", info->outdev);
1101 return count;
1102 }
1103 if (!strcmp(name, "flag")) {
1104 char f[32];
1105 len = strn_len(&user_buffer[i], sizeof(f) - 1);
1106 if (len < 0)
1107 return len;
1108 memset(f, 0, 32);
1109 if (copy_from_user(f, &user_buffer[i], len))
1110 return -EFAULT;
1111 i += len;
1112 if (strcmp(f, "IPSRC_RND") == 0) {
1113 info->flags |= F_IPSRC_RND;
1114 }
1115 else if (strcmp(f, "!IPSRC_RND") == 0) {
1116 info->flags &= ~F_IPSRC_RND;
1117 }
1118 else if (strcmp(f, "IPDST_RND") == 0) {
1119 info->flags |= F_IPDST_RND;
1120 }
1121 else if (strcmp(f, "!IPDST_RND") == 0) {
1122 info->flags &= ~F_IPDST_RND;
1123 }
1124 else if (strcmp(f, "UDPSRC_RND") == 0) {
1125 info->flags |= F_UDPSRC_RND;
1126 }
1127 else if (strcmp(f, "!UDPSRC_RND") == 0) {
1128 info->flags &= ~F_UDPSRC_RND;
1129 }
1130 else if (strcmp(f, "UDPDST_RND") == 0) {
1131 info->flags |= F_UDPDST_RND;
1132 }
1133 else if (strcmp(f, "!UDPDST_RND") == 0) {
1134 info->flags &= ~F_UDPDST_RND;
1135 }
1136 else if (strcmp(f, "MACSRC_RND") == 0) {
1137 info->flags |= F_MACSRC_RND;
1138 }
1139 else if (strcmp(f, "!MACSRC_RND") == 0) {
1140 info->flags &= ~F_MACSRC_RND;
1141 }
1142 else if (strcmp(f, "MACDST_RND") == 0) {
1143 info->flags |= F_MACDST_RND;
1144 }
1145 else if (strcmp(f, "!MACDST_RND") == 0) {
1146 info->flags &= ~F_MACDST_RND;
1147 }
1148 else {
1149 sprintf(result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
1150 f,
1151 "IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
1152 return count;
1153 }
1154 sprintf(result, "OK: flags=0x%x", info->flags);
1155 return count;
1156 }
1157 if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
1158 len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1);
1159 if (len < 0)
1160 return len;
1161 memset(info->dst_min, 0, sizeof(info->dst_min));
1162 if (copy_from_user(info->dst_min, &user_buffer[i], len))
1163 return -EFAULT;
1164 if(debug)
1165 printk("pg: dst_min set to: %s\n", info->dst_min);
1166 i += len;
1167 sprintf(result, "OK: dst_min=%s", info->dst_min);
1168 return count;
1169 }
1170 if (!strcmp(name, "dst_max")) {
1171 len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1);
1172 if (len < 0)
1173 return len;
1174 memset(info->dst_max, 0, sizeof(info->dst_max));
1175 if (copy_from_user(info->dst_max, &user_buffer[i], len))
1176 return -EFAULT;
1177 if(debug)
1178 printk("pg: dst_max set to: %s\n", info->dst_max);
1179 i += len;
1180 sprintf(result, "OK: dst_max=%s", info->dst_max);
1181 return count;
1182 }
1183 if (!strcmp(name, "src_min")) {
1184 len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1);
1185 if (len < 0)
1186 return len;
1187 memset(info->src_min, 0, sizeof(info->src_min));
1188 if (copy_from_user(info->src_min, &user_buffer[i], len))
1189 return -EFAULT;
1190 if(debug)
1191 printk("pg: src_min set to: %s\n", info->src_min);
1192 i += len;
1193 sprintf(result, "OK: src_min=%s", info->src_min);
1194 return count;
1195 }
1196 if (!strcmp(name, "src_max")) {
1197 len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1);
1198 if (len < 0)
1199 return len;
1200 memset(info->src_max, 0, sizeof(info->src_max));
1201 if (copy_from_user(info->src_max, &user_buffer[i], len))
1202 return -EFAULT;
1203 if(debug)
1204 printk("pg: src_max set to: %s\n", info->src_max);
1205 i += len;
1206 sprintf(result, "OK: src_max=%s", info->src_max);
1207 return count;
1208 }
1209 if (!strcmp(name, "dstmac")) {
1210 char *v = valstr;
1211 unsigned char *m = info->dst_mac;
1212
1213 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1214 if (len < 0)
1215 return len;
1216 memset(valstr, 0, sizeof(valstr));
1217 if (copy_from_user(valstr, &user_buffer[i], len))
1218 return -EFAULT;
1219 i += len;
1220
1221 for(*m = 0;*v && m < info->dst_mac + 6; v++) {
1222 if (*v >= '0' && *v <= '9') {
1223 *m *= 16;
1224 *m += *v - '0';
1225 }
1226 if (*v >= 'A' && *v <= 'F') {
1227 *m *= 16;
1228 *m += *v - 'A' + 10;
1229 }
1230 if (*v >= 'a' && *v <= 'f') {
1231 *m *= 16;
1232 *m += *v - 'a' + 10;
1233 }
1234 if (*v == ':') {
1235 m++;
1236 *m = 0;
1237 }
1238 }
1239 sprintf(result, "OK: dstmac");
1240 return count;
1241 }
1242 if (!strcmp(name, "srcmac")) {
1243 char *v = valstr;
1244 unsigned char *m = info->src_mac;
1245
1246 len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
1247 if (len < 0)
1248 return len;
1249 memset(valstr, 0, sizeof(valstr));
1250 if (copy_from_user(valstr, &user_buffer[i], len))
1251 return -EFAULT;
1252 i += len;
1253
1254 for(*m = 0;*v && m < info->src_mac + 6; v++) {
1255 if (*v >= '0' && *v <= '9') {
1256 *m *= 16;
1257 *m += *v - '0';
1258 }
1259 if (*v >= 'A' && *v <= 'F') {
1260 *m *= 16;
1261 *m += *v - 'A' + 10;
1262 }
1263 if (*v >= 'a' && *v <= 'f') {
1264 *m *= 16;
1265 *m += *v - 'a' + 10;
1266 }
1267 if (*v == ':') {
1268 m++;
1269 *m = 0;
1270 }
1271 }
1272 sprintf(result, "OK: srcmac");
1273 return count;
1274 }
1275
1276 if (!strcmp(name, "inject") || !strcmp(name, "start")) {
1277 MOD_INC_USE_COUNT;
1278 if (info->busy) {
1279 strcpy(info->result, "Already running...\n");
1280 }
1281 else {
1282 info->busy = 1;
1283 strcpy(info->result, "Starting");
1284 inject(info);
1285 info->busy = 0;
1286 }
1287 MOD_DEC_USE_COUNT;
1288 return count;
1289 }
1290
1291 sprintf(info->result, "No such parameter \"%s\"", name);
1292 return -EINVAL;
1293 }
1294
1295
create_proc_dir(void)1296 int create_proc_dir(void)
1297 {
1298 int len;
1299 /* does proc_dir already exists */
1300 len = strlen(PG_PROC_DIR);
1301
1302 for (proc_dir = proc_net->subdir; proc_dir;
1303 proc_dir=proc_dir->next) {
1304 if ((proc_dir->namelen == len) &&
1305 (! memcmp(proc_dir->name, PG_PROC_DIR, len)))
1306 break;
1307 }
1308 if (!proc_dir)
1309 proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
1310 if (!proc_dir) return -ENODEV;
1311 return 1;
1312 }
1313
remove_proc_dir(void)1314 int remove_proc_dir(void)
1315 {
1316 remove_proc_entry(PG_PROC_DIR, proc_net);
1317 return 1;
1318 }
1319
init(void)1320 static int __init init(void)
1321 {
1322 int i;
1323 printk(version);
1324 cycles_calibrate();
1325 if (cpu_speed == 0) {
1326 printk("pktgen: Error: your machine does not have working cycle counter.\n");
1327 return -EINVAL;
1328 }
1329
1330 create_proc_dir();
1331
1332 for (i = 0; i<MAX_PKTGEN; i++) {
1333 memset(&(pginfos[i]), 0, sizeof(pginfos[i]));
1334 pginfos[i].pkt_size = ETH_ZLEN;
1335 pginfos[i].nfrags = 0;
1336 pginfos[i].clone_skb = clone_skb_d;
1337 pginfos[i].ipg = ipg_d;
1338 pginfos[i].count = count_d;
1339 pginfos[i].sofar = 0;
1340 pginfos[i].hh[12] = 0x08; /* fill in protocol. Rest is filled in later. */
1341 pginfos[i].hh[13] = 0x00;
1342 pginfos[i].udp_src_min = 9; /* sink NULL */
1343 pginfos[i].udp_src_max = 9;
1344 pginfos[i].udp_dst_min = 9;
1345 pginfos[i].udp_dst_max = 9;
1346
1347 sprintf(pginfos[i].fname, "net/%s/pg%i", PG_PROC_DIR, i);
1348 pginfos[i].proc_ent = create_proc_entry(pginfos[i].fname, 0600, 0);
1349 if (!pginfos[i].proc_ent) {
1350 printk("pktgen: Error: cannot create net/%s/pg procfs entry.\n", PG_PROC_DIR);
1351 goto cleanup_mem;
1352 }
1353 pginfos[i].proc_ent->read_proc = proc_read;
1354 pginfos[i].proc_ent->write_proc = proc_write;
1355 pginfos[i].proc_ent->data = (void*)(long)(i);
1356
1357 sprintf(pginfos[i].busy_fname, "net/%s/pg_busy%i", PG_PROC_DIR, i);
1358 pginfos[i].busy_proc_ent = create_proc_entry(pginfos[i].busy_fname, 0, 0);
1359 if (!pginfos[i].busy_proc_ent) {
1360 printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR);
1361 goto cleanup_mem;
1362 }
1363 pginfos[i].busy_proc_ent->read_proc = proc_busy_read;
1364 pginfos[i].busy_proc_ent->data = (void*)(long)(i);
1365 }
1366 return 0;
1367
1368 cleanup_mem:
1369 for (i = 0; i<MAX_PKTGEN; i++) {
1370 if (strlen(pginfos[i].fname)) {
1371 remove_proc_entry(pginfos[i].fname, NULL);
1372 }
1373 if (strlen(pginfos[i].busy_fname)) {
1374 remove_proc_entry(pginfos[i].busy_fname, NULL);
1375 }
1376 }
1377 return -ENOMEM;
1378 }
1379
1380
cleanup(void)1381 static void __exit cleanup(void)
1382 {
1383 int i;
1384 for (i = 0; i<MAX_PKTGEN; i++) {
1385 if (strlen(pginfos[i].fname)) {
1386 remove_proc_entry(pginfos[i].fname, NULL);
1387 }
1388 if (strlen(pginfos[i].busy_fname)) {
1389 remove_proc_entry(pginfos[i].busy_fname, NULL);
1390 }
1391 }
1392 remove_proc_dir();
1393 }
1394
1395 module_init(init);
1396 module_exit(cleanup);
1397
1398 MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
1399 MODULE_DESCRIPTION("Packet Generator tool");
1400 MODULE_LICENSE("GPL");
1401 MODULE_PARM(count_d, "i");
1402 MODULE_PARM(ipg_d, "i");
1403 MODULE_PARM(cpu_speed, "i");
1404 MODULE_PARM(clone_skb_d, "i");
1405
1406
1407
1408