1 /* pg3.c: Packet Generator for packet performance testing. 2 * 3 * Copyright 2001 by Robert Olsson <robert.olsson (at) 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 (at) 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 (at) 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