1 /* pg3.c: Packet Generator for packet performance testing.
2 *
3 * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se>
4 * Uppsala University, Sweden
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 *
12 *
13 */
14
15 /*
16
17 A tool for loading a network with a preconfigurated packets. The tool is
18 implemented as a linux module. Parameters as output device IPG interpacket
19 packet, number of packets can be configured. pg uses already intalled
20 device driver output routine.
21
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
30
31 TODO:
32 * could release kernel lock yet.
33
34
35 HOWTO:
36
37 1. Compile module pg3.o and install it in the place where modprobe may find it.
38 2. Cut script "ipg" (see below).
39 3. Edit script to set preferred device and destination IP address.
40 4. . ipg
41 5. After this two commands are defined:
42 A. "pg" to start generator and to get results.
43 B. "pgset" to change generator parameters. F.e.
44 pgset "pkt_size 9014" sets packet size to 9014
45 pgset "frags 5" packet will consist of 5 fragments
46 pgset "count 200000" sets number of packets to send
47 pgset "ipg 5000" sets artificial gap inserted between packets
48 to 5000 nanoseconds
49 pgset "dst 10.0.0.1" sets IP destination address
50 (BEWARE! This generator is very aggressive!)
51 pgset "dstmac 00:00:00:00:00:00" sets MAC destination address
52 pgset stop aborts injection
53
54 Also, ^C aborts generator.
55
56 ---- cut here
57
58 #! /bin/sh
59
60 modprobe pg3.o
61
62 function pgset() {
63 local result
64
65 echo $1 > /proc/net/pg
66
67 result=`cat /proc/net/pg | fgrep "Result: OK:"`
68 if [ "$result" = "" ]; then
69 cat /proc/net/pg | fgrep Result:
70 fi
71 }
72
73 function pg() {
74 echo inject > /proc/net/pg
75 cat /proc/net/pg
76 }
77
78 pgset "odev eth0"
79 pgset "dst 0.0.0.0"
80
81 ---- cut here
82 */
83
84 #include <linux/module.h>
85 #include <linux/kernel.h>
86 #include <linux/sched.h>
87 #include <linux/types.h>
88 #include <linux/string.h>
89 #include <linux/ptrace.h>
90 #include <linux/errno.h>
91 #include <linux/ioport.h>
92 #include <linux/malloc.h>
93 #include <linux/interrupt.h>
94 #include <linux/pci.h>
95 #include <linux/delay.h>
96 #include <linux/init.h>
97 #include <linux/inet.h>
98 #include <asm/byteorder.h>
99 #include <asm/bitops.h>
100 #include <asm/io.h>
101 #include <asm/dma.h>
102
103 #include <linux/in.h>
104 #include <linux/ip.h>
105 #include <linux/udp.h>
106 #include <linux/skbuff.h>
107 #include <linux/netdevice.h>
108 #include <linux/inetdevice.h>
109 #include <linux/rtnetlink.h>
110 #include <linux/proc_fs.h>
111 #include <linux/if_arp.h>
112 #include <net/checksum.h>
113
114 static char version[] __initdata =
115 "pg3.c: v1.0 010812: Packet Generator for packet performance testing.\n";
116
117
118
119 /* Parameters */
120
121 char pg_outdev[32], pg_dst[32];
122 int pkt_size=ETH_ZLEN;
123 int nfrags=0;
124 __u32 pg_count = 100000; /* Default No packets to send */
125 __u32 pg_ipg = 0; /* Default Interpacket gap in nsec */
126
127 /* Globar vars */
128
129 int debug;
130 int forced_stop;
131 int pg_cpu_speed;
132 int pg_busy;
133
134 static __u8 hh[14] = {
135 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
136
137 /* We fill in SRC address later */
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x08, 0x00
140 };
141
142 unsigned char *pg_dstmac = hh;
143 char pg_result[512];
144
145
pg_setup_inject(u32 * saddrp)146 static struct net_device *pg_setup_inject(u32 *saddrp)
147 {
148 int p1, p2;
149 struct net_device *odev;
150 u32 saddr;
151
152 rtnl_lock();
153 odev = __dev_get_by_name(pg_outdev);
154 if (!odev) {
155 sprintf(pg_result, "No such netdevice: \"%s\"", pg_outdev);
156 goto out_unlock;
157 }
158
159 if (odev->type != ARPHRD_ETHER) {
160 sprintf(pg_result, "Not ethernet device: \"%s\"", pg_outdev);
161 goto out_unlock;
162 }
163
164 if (!netif_running(odev)) {
165 sprintf(pg_result, "Device is down: \"%s\"", pg_outdev);
166 goto out_unlock;
167 }
168
169 for(p1=6,p2=0; p1 < odev->addr_len+6;p1++)
170 hh[p1]=odev->dev_addr[p2++];
171
172 saddr = 0;
173 if (odev->ip_ptr) {
174 struct in_device *in_dev = odev->ip_ptr;
175
176 if (in_dev->ifa_list)
177 saddr = in_dev->ifa_list->ifa_address;
178 }
179 atomic_inc(&odev->refcnt);
180 rtnl_unlock();
181
182 *saddrp = saddr;
183 return odev;
184
185 out_unlock:
186 rtnl_unlock();
187 return NULL;
188 }
189
190
191 u32 idle_acc_lo, idle_acc_hi;
192
nanospin(int pg_ipg)193 void nanospin(int pg_ipg)
194 {
195 u32 idle_start, idle;
196
197 idle_start = get_cycles();
198
199 for (;;) {
200 barrier();
201 idle = get_cycles() - idle_start;
202 if (idle*1000 >= pg_ipg*pg_cpu_speed)
203 break;
204 }
205 idle_acc_lo += idle;
206 if (idle_acc_lo < idle)
207 idle_acc_hi++;
208 }
209
calc_mhz(void)210 int calc_mhz(void)
211 {
212 struct timeval start, stop;
213 u32 start_s, elapsed;
214
215 do_gettimeofday(&start);
216 start_s = get_cycles();
217 do {
218 barrier();
219 elapsed = get_cycles() - start_s;
220 if (elapsed == 0)
221 return 0;
222 } while (elapsed < 1000*50000);
223 do_gettimeofday(&stop);
224 return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
225 }
226
cycles_calibrate(void)227 static void cycles_calibrate(void)
228 {
229 int i;
230
231 for (i=0; i<3; i++) {
232 int res = calc_mhz();
233 if (res > pg_cpu_speed)
234 pg_cpu_speed = res;
235 }
236 }
237
238 struct sk_buff *
fill_packet(struct net_device * odev,__u32 saddr)239 fill_packet(struct net_device *odev, __u32 saddr)
240 {
241 struct sk_buff *skb;
242 __u8 *eth;
243 struct udphdr *udph;
244 int datalen, iplen;
245 struct iphdr *iph;
246
247 skb = alloc_skb(pkt_size+64+16, GFP_ATOMIC);
248 if (!skb) {
249 sprintf(pg_result, "No memory");
250 return NULL;
251 }
252
253 skb_reserve(skb, 16);
254
255 /* Reserve for ethernet and IP header */
256 eth = (__u8 *) skb_push(skb, 14);
257 iph = (struct iphdr*)skb_put(skb, sizeof( struct iphdr));
258 udph = (struct udphdr*)skb_put(skb, sizeof( struct udphdr));
259
260 /* Copy the ethernet header */
261 memcpy(eth, hh, 14);
262
263 datalen = pkt_size-14-20-8; /* Eth + IPh + UDPh */
264 if (datalen < 0)
265 datalen = 0;
266
267 udph->source= htons(9);
268 udph->dest= htons(9);
269 udph->len= htons(datalen+8); /* DATA + udphdr */
270 udph->check=0; /* No checksum */
271
272 iph->ihl=5;
273 iph->version=4;
274 iph->ttl=3;
275 iph->tos=0;
276 iph->protocol = IPPROTO_UDP; /* UDP */
277 iph->saddr = saddr;
278 iph->daddr = in_aton(pg_dst);
279 iph->frag_off = 0;
280 iplen = 20 + 8 + datalen;
281 iph->tot_len = htons(iplen);
282 iph->check = 0;
283 iph->check = ip_fast_csum((void *)iph, iph->ihl);
284 skb->protocol = __constant_htons(ETH_P_IP);
285 skb->mac.raw = ((u8*)iph) - 14;
286 skb->dev = odev;
287 skb->pkt_type = PACKET_HOST;
288
289 if (nfrags<=0) {
290 skb_put(skb, datalen);
291 } else {
292 int frags = nfrags;
293 int i;
294
295 if (frags > MAX_SKB_FRAGS)
296 frags = MAX_SKB_FRAGS;
297 if (datalen > frags*PAGE_SIZE) {
298 skb_put(skb, datalen-frags*PAGE_SIZE);
299 datalen = frags*PAGE_SIZE;
300 }
301
302 i = 0;
303 while (datalen > 0) {
304 struct page *page = alloc_pages(GFP_KERNEL, 0);
305 skb_shinfo(skb)->frags[i].page = page;
306 skb_shinfo(skb)->frags[i].page_offset = 0;
307 skb_shinfo(skb)->frags[i].size = (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
308 datalen -= skb_shinfo(skb)->frags[i].size;
309 skb->len += skb_shinfo(skb)->frags[i].size;
310 skb->data_len += skb_shinfo(skb)->frags[i].size;
311 i++;
312 skb_shinfo(skb)->nr_frags = i;
313 }
314
315 while (i < frags) {
316 int rem;
317
318 if (i == 0)
319 break;
320
321 rem = skb_shinfo(skb)->frags[i-1].size/2;
322 if (rem == 0)
323 break;
324
325 skb_shinfo(skb)->frags[i-1].size -= rem;
326
327 skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i-1];
328 get_page(skb_shinfo(skb)->frags[i].page);
329 skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i-1].page;
330 skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i-1].size;
331 skb_shinfo(skb)->frags[i].size = rem;
332 i++;
333 skb_shinfo(skb)->nr_frags = i;
334 }
335 }
336
337 return skb;
338 }
339
340
pg_inject(void)341 static void pg_inject(void)
342 {
343 u32 saddr;
344 struct net_device *odev;
345 struct sk_buff *skb;
346 struct timeval start, stop;
347 u32 total, idle;
348 int pc, lcount;
349
350 odev = pg_setup_inject(&saddr);
351 if (!odev)
352 return;
353
354 skb = fill_packet(odev, saddr);
355 if (skb == NULL)
356 goto out_reldev;
357
358 forced_stop = 0;
359 idle_acc_hi = 0;
360 idle_acc_lo = 0;
361 pc = 0;
362 lcount = pg_count;
363 do_gettimeofday(&start);
364
365 for(;;) {
366 spin_lock_bh(&odev->xmit_lock);
367 atomic_inc(&skb->users);
368 if (!netif_queue_stopped(odev)) {
369 if (odev->hard_start_xmit(skb, odev)) {
370 kfree_skb(skb);
371 if (net_ratelimit())
372 printk(KERN_INFO "Hard xmit error\n");
373 }
374 pc++;
375 } else {
376 kfree_skb(skb);
377 }
378 spin_unlock_bh(&odev->xmit_lock);
379
380 if (pg_ipg)
381 nanospin(pg_ipg);
382 if (forced_stop)
383 goto out_intr;
384 if (signal_pending(current))
385 goto out_intr;
386
387 if (--lcount == 0) {
388 if (atomic_read(&skb->users) != 1) {
389 u32 idle_start, idle;
390
391 idle_start = get_cycles();
392 while (atomic_read(&skb->users) != 1) {
393 if (signal_pending(current))
394 goto out_intr;
395 schedule();
396 }
397 idle = get_cycles() - idle_start;
398 idle_acc_lo += idle;
399 if (idle_acc_lo < idle)
400 idle_acc_hi++;
401 }
402 break;
403 }
404
405 if (netif_queue_stopped(odev) || current->need_resched) {
406 u32 idle_start, idle;
407
408 idle_start = get_cycles();
409 do {
410 if (signal_pending(current))
411 goto out_intr;
412 if (!netif_running(odev))
413 goto out_intr;
414 if (current->need_resched)
415 schedule();
416 else
417 do_softirq();
418 } while (netif_queue_stopped(odev));
419 idle = get_cycles() - idle_start;
420 idle_acc_lo += idle;
421 if (idle_acc_lo < idle)
422 idle_acc_hi++;
423 }
424 }
425
426 do_gettimeofday(&stop);
427
428 total = (stop.tv_sec - start.tv_sec)*1000000 +
429 stop.tv_usec - start.tv_usec;
430
431 idle = (((idle_acc_hi<<20)/pg_cpu_speed)<<12)+idle_acc_lo/pg_cpu_speed;
432
433 if (1) {
434 char *p = pg_result;
435
436 p += sprintf(p, "OK: %u(c%u+d%u) usec, %u (%dbyte,%dfrags) %upps %uMB/sec",
437 total, total-idle, idle,
438 pc, skb->len, skb_shinfo(skb)->nr_frags,
439 ((pc*1000)/(total/1000)),
440 (((pc*1000)/(total/1000))*pkt_size)/1024/1024
441 );
442 }
443
444 out_relskb:
445 kfree_skb(skb);
446 out_reldev:
447 dev_put(odev);
448 return;
449
450 out_intr:
451 sprintf(pg_result, "Interrupted");
452 goto out_relskb;
453 }
454
455 /* proc/net/pg */
456
457 static struct proc_dir_entry *pg_proc_ent = 0;
458 static struct proc_dir_entry *pg_busy_proc_ent = 0;
459
proc_pg_busy_read(char * buf,char ** start,off_t offset,int len,int * eof,void * data)460 int proc_pg_busy_read(char *buf , char **start, off_t offset,
461 int len, int *eof, void *data)
462 {
463 char *p;
464
465 p = buf;
466 p += sprintf(p, "%d\n", pg_busy);
467 *eof = 1;
468
469 return p-buf;
470 }
471
proc_pg_read(char * buf,char ** start,off_t offset,int len,int * eof,void * data)472 int proc_pg_read(char *buf , char **start, off_t offset,
473 int len, int *eof, void *data)
474 {
475 char *p;
476 int i;
477
478 p = buf;
479 p += sprintf(p, "Params: count=%u pkt_size=%u frags %d ipg %u odev \"%s\" dst %s dstmac ",
480 pg_count, pkt_size, nfrags, pg_ipg,
481 pg_outdev, pg_dst);
482 for(i=0;i<6;i++)
483 p += sprintf(p, "%02X%s", pg_dstmac[i], i == 5 ? "\n" : ":");
484
485 if(pg_result[0])
486 p += sprintf(p, "Result: %s\n", pg_result);
487 else
488 p += sprintf(p, "Result: Idle\n");
489 *eof = 1;
490 return p-buf;
491 }
492
count_trail_chars(const char * buffer,unsigned int maxlen)493 int count_trail_chars(const char *buffer, unsigned int maxlen)
494 {
495 int i;
496
497 for(i=0; i<maxlen;i++) {
498 switch(buffer[i]) {
499 case '\"':
500 case '\n':
501 case '\r':
502 case '\t':
503 case ' ':
504 case '=':
505 break;
506 default:
507 goto done;
508 }
509 }
510 done:
511 return i;
512 }
513
num_arg(const char * buffer,unsigned long maxlen,unsigned long * num)514 unsigned long num_arg(const char *buffer, unsigned long maxlen,
515 unsigned long *num)
516 {
517 int i=0;
518 *num = 0;
519
520 for(; i<maxlen;i++) {
521 if( (buffer[i] >= '0') && (buffer[i] <= '9')) {
522 *num *= 10;
523 *num += buffer[i] -'0';
524 }
525 else
526 break;
527 }
528 return i;
529 }
530
strn_len(const char * buffer,unsigned int maxlen)531 int strn_len(const char *buffer, unsigned int maxlen)
532 {
533 int i=0;
534
535 for(; i<maxlen;i++)
536 switch(buffer[i]) {
537 case '\"':
538 case '\n':
539 case '\r':
540 case '\t':
541 case ' ':
542 goto done_str;
543 default:
544 }
545 done_str:
546 return i;
547 }
548
proc_pg_write(struct file * file,const char * buffer,unsigned long count,void * data)549 int proc_pg_write(struct file *file, const char *buffer,
550 unsigned long count, void *data)
551 {
552 int i=0, max, len;
553 char name[16], valstr[32];
554 unsigned long value = 0;
555
556 if (count < 1) {
557 sprintf(pg_result, "Wrong command format");
558 return -EINVAL;
559 }
560
561 max = count -i;
562 i += count_trail_chars(&buffer[i], max);
563
564 /* Read variable name */
565
566 len = strn_len(&buffer[i], sizeof(name)-1);
567 memset(name, 0, sizeof(name));
568 strncpy(name, &buffer[i], len);
569 i += len;
570
571 max = count -i;
572 len = count_trail_chars(&buffer[i], max);
573 i += len;
574
575 if (debug)
576 printk("pg: %s,%lu\n", name, count);
577
578 /* Only stop is allowed when we are running */
579
580 if(!strcmp(name, "stop")) {
581 forced_stop=1;
582 if (pg_busy)
583 strcpy(pg_result, "Stopping");
584 return count;
585 }
586
587 if (pg_busy) {
588 strcpy(pg_result, "Busy");
589 return -EINVAL;
590 }
591
592 if(!strcmp(name, "pkt_size")) {
593 len = num_arg(&buffer[i], 10, &value);
594 i += len;
595 if (value < 14+20+8)
596 value = 14+20+8;
597 pkt_size = value;
598 sprintf(pg_result, "OK: pkt_size=%u", pkt_size);
599 return count;
600 }
601 if(!strcmp(name, "frags")) {
602 len = num_arg(&buffer[i], 10, &value);
603 i += len;
604 nfrags = value;
605 sprintf(pg_result, "OK: frags=%u", nfrags);
606 return count;
607 }
608 if(!strcmp(name, "ipg")) {
609 len = num_arg(&buffer[i], 10, &value);
610 i += len;
611 pg_ipg = value;
612 sprintf(pg_result, "OK: ipg=%u", pg_ipg);
613 return count;
614 }
615 if(!strcmp(name, "count")) {
616 len = num_arg(&buffer[i], 10, &value);
617 i += len;
618 pg_count = value;
619 sprintf(pg_result, "OK: count=%u", pg_count);
620 return count;
621 }
622 if(!strcmp(name, "odev")) {
623 len = strn_len(&buffer[i], sizeof(pg_outdev)-1);
624 memset(pg_outdev, 0, sizeof(pg_outdev));
625 strncpy(pg_outdev, &buffer[i], len);
626 i += len;
627 sprintf(pg_result, "OK: odev=%s", pg_outdev);
628 return count;
629 }
630 if(!strcmp(name, "dst")) {
631 len = strn_len(&buffer[i], sizeof(pg_dst)-1);
632 memset(pg_dst, 0, sizeof(pg_dst));
633 strncpy(pg_dst, &buffer[i], len);
634 if(debug)
635 printk("pg: dst set to: %s\n", pg_dst);
636 i += len;
637 sprintf(pg_result, "OK: dst=%s", pg_dst);
638 return count;
639 }
640 if(!strcmp(name, "dstmac")) {
641 char *v = valstr;
642 unsigned char *m = pg_dstmac;
643
644 len = strn_len(&buffer[i], sizeof(valstr)-1);
645 memset(valstr, 0, sizeof(valstr));
646 strncpy(valstr, &buffer[i], len);
647 i += len;
648
649 for(*m = 0;*v && m < pg_dstmac+6;v++) {
650 if(*v >= '0' && *v <= '9') {
651 *m *= 16;
652 *m += *v - '0';
653 }
654 if(*v >= 'A' && *v <= 'F') {
655 *m *= 16;
656 *m += *v - 'A' + 10;
657 }
658 if(*v >= 'a' && *v <= 'f') {
659 *m *= 16;
660 *m += *v - 'a' + 10;
661 }
662 if(*v == ':') {
663 m++;
664 *m = 0;
665 }
666 }
667 sprintf(pg_result, "OK: dstmac");
668 return count;
669 }
670
671 if (!strcmp(name, "inject") || !strcmp(name, "start") ) {
672 MOD_INC_USE_COUNT;
673 pg_busy = 1;
674 strcpy(pg_result, "Starting");
675 pg_inject();
676 pg_busy = 0;
677 MOD_DEC_USE_COUNT;
678 return count;
679 }
680
681 sprintf(pg_result, "No such parameter \"%s\"", name);
682 return -EINVAL;
683 }
684
pg_init(void)685 static int pg_init(void)
686 {
687 printk(version);
688 cycles_calibrate();
689 if (pg_cpu_speed == 0) {
690 printk("pg3: Error: your machine does not have working cycle counter.\n");
691 return -EINVAL;
692 }
693 if(!pg_proc_ent) {
694 pg_proc_ent = create_proc_entry("net/pg", 0600, 0);
695 if (pg_proc_ent) {
696 pg_proc_ent->read_proc = proc_pg_read;
697 pg_proc_ent->write_proc = proc_pg_write;
698 pg_proc_ent->data = 0;
699 }
700 pg_busy_proc_ent = create_proc_entry("net/pg_busy", 0, 0);
701 if (pg_busy_proc_ent) {
702 pg_busy_proc_ent->read_proc = proc_pg_busy_read;
703 pg_busy_proc_ent->data = 0;
704 }
705 }
706 return 0;
707 }
708
pg_cleanup(void)709 void pg_cleanup(void)
710 {
711 if (pg_proc_ent) {
712 remove_proc_entry("net/pg", NULL);
713 pg_proc_ent = 0;
714 remove_proc_entry("net/pg_busy", NULL);
715 pg_busy_proc_ent = 0;
716 }
717 }
718
719 module_init(pg_init);
720 module_exit(pg_cleanup);
721
722
723 #if LINUX_VERSION_CODE > 0x20118
724 MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
725 MODULE_DESCRIPTION("Packet Generator tool");
726 MODULE_PARM(pg_count, "i");
727 MODULE_PARM(pg_ipg, "i");
728 MODULE_PARM(pg_cpu_speed, "i");
729 #endif
730
731 /*
732 * Local variables:
733 * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c pg3.c"
734 * End:
735 */
736