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