pkt-gen.c revision 228276
1/* 2 * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26/* 27 * $FreeBSD: head/tools/tools/netmap/pkt-gen.c 228276 2011-12-05 12:06:53Z luigi $ 28 * $Id: pkt-gen.c 9827 2011-12-05 11:29:34Z luigi $ 29 * 30 * Example program to show how to build a multithreaded packet 31 * source/sink using the netmap device. 32 * 33 * In this example we create a programmable number of threads 34 * to take care of all the queues of the interface used to 35 * send or receive traffic. 36 * 37 */ 38 39const char *default_payload="netmap pkt-gen Luigi Rizzo and Matteo Landi\n" 40 "http://info.iet.unipi.it/~luigi/netmap/ "; 41 42#include <errno.h> 43#include <pthread.h> /* pthread_* */ 44#include <pthread_np.h> /* pthread w/ affinity */ 45#include <signal.h> /* signal */ 46#include <stdlib.h> 47#include <stdio.h> 48#include <inttypes.h> /* PRI* macros */ 49#include <string.h> /* strcmp */ 50#include <fcntl.h> /* open */ 51#include <unistd.h> /* close */ 52#include <ifaddrs.h> /* getifaddrs */ 53 54#include <sys/mman.h> /* PROT_* */ 55#include <sys/ioctl.h> /* ioctl */ 56#include <sys/poll.h> 57#include <sys/socket.h> /* sockaddr.. */ 58#include <arpa/inet.h> /* ntohs */ 59#include <sys/param.h> 60#include <sys/cpuset.h> /* cpu_set */ 61#include <sys/sysctl.h> /* sysctl */ 62#include <sys/time.h> /* timersub */ 63 64#include <net/ethernet.h> 65#include <net/if.h> /* ifreq */ 66#include <net/if_dl.h> /* LLADDR */ 67 68#include <netinet/in.h> 69#include <netinet/ip.h> 70#include <netinet/udp.h> 71 72#include <net/netmap.h> 73#include <net/netmap_user.h> 74#include <pcap/pcap.h> 75 76 77static inline int min(int a, int b) { return a < b ? a : b; } 78 79/* debug support */ 80#define D(format, ...) \ 81 fprintf(stderr, "%s [%d] " format "\n", \ 82 __FUNCTION__, __LINE__, ##__VA_ARGS__) 83 84#ifndef EXPERIMENTAL 85#define EXPERIMENTAL 0 86#endif 87 88int verbose = 0; 89#define MAX_QUEUES 64 /* no need to limit */ 90 91#define SKIP_PAYLOAD 1 /* do not check payload. */ 92 93#if EXPERIMENTAL 94/* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */ 95#define netmap_rdtsc(t) \ 96 do { \ 97 u_int __regs[4]; \ 98 \ 99 do_cpuid(0, __regs); \ 100 (t) = rdtsc(); \ 101 } while (0) 102 103static __inline void 104do_cpuid(u_int ax, u_int *p) 105{ 106 __asm __volatile("cpuid" 107 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) 108 : "0" (ax)); 109} 110 111static __inline uint64_t 112rdtsc(void) 113{ 114 uint64_t rv; 115 116 __asm __volatile("rdtsc" : "=A" (rv)); 117 return (rv); 118} 119#define MAX_SAMPLES 100000 120#endif /* EXPERIMENTAL */ 121 122 123struct pkt { 124 struct ether_header eh; 125 struct ip ip; 126 struct udphdr udp; 127 uint8_t body[NETMAP_BUF_SIZE]; 128} __attribute__((__packed__)); 129 130/* 131 * global arguments for all threads 132 */ 133struct glob_arg { 134 const char *src_ip; 135 const char *dst_ip; 136 const char *src_mac; 137 const char *dst_mac; 138 int pkt_size; 139 int burst; 140 int npackets; /* total packets to send */ 141 int nthreads; 142 int cpus; 143 int use_pcap; 144 pcap_t *p; 145}; 146 147struct mystat { 148 uint64_t containers[8]; 149}; 150 151/* 152 * Arguments for a new thread. The same structure is used by 153 * the source and the sink 154 */ 155struct targ { 156 struct glob_arg *g; 157 int used; 158 int completed; 159 int fd; 160 struct nmreq nmr; 161 struct netmap_if *nifp; 162 uint16_t qfirst, qlast; /* range of queues to scan */ 163 uint64_t count; 164 struct timeval tic, toc; 165 int me; 166 pthread_t thread; 167 int affinity; 168 169 uint8_t dst_mac[6]; 170 uint8_t src_mac[6]; 171 u_int dst_mac_range; 172 u_int src_mac_range; 173 uint32_t dst_ip; 174 uint32_t src_ip; 175 u_int dst_ip_range; 176 u_int src_ip_range; 177 178 struct pkt pkt; 179}; 180 181 182static struct targ *targs; 183static int global_nthreads; 184 185/* control-C handler */ 186static void 187sigint_h(__unused int sig) 188{ 189 for (int i = 0; i < global_nthreads; i++) { 190 /* cancel active threads. */ 191 if (targs[i].used == 0) 192 continue; 193 194 D("Cancelling thread #%d\n", i); 195 pthread_cancel(targs[i].thread); 196 targs[i].used = 0; 197 } 198 199 signal(SIGINT, SIG_DFL); 200} 201 202 203/* sysctl wrapper to return the number of active CPUs */ 204static int 205system_ncpus(void) 206{ 207 int mib[2], ncpus; 208 size_t len; 209 210 mib[0] = CTL_HW; 211 mib[1] = HW_NCPU; 212 len = sizeof(mib); 213 sysctl(mib, 2, &ncpus, &len, NULL, 0); 214 215 return (ncpus); 216} 217 218/* 219 * locate the src mac address for our interface, put it 220 * into the user-supplied buffer. return 0 if ok, -1 on error. 221 */ 222static int 223source_hwaddr(const char *ifname, char *buf) 224{ 225 struct ifaddrs *ifaphead, *ifap; 226 int l = sizeof(ifap->ifa_name); 227 228 if (getifaddrs(&ifaphead) != 0) { 229 D("getifaddrs %s failed", ifname); 230 return (-1); 231 } 232 233 for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 234 struct sockaddr_dl *sdl = 235 (struct sockaddr_dl *)ifap->ifa_addr; 236 uint8_t *mac; 237 238 if (!sdl || sdl->sdl_family != AF_LINK) 239 continue; 240 if (strncmp(ifap->ifa_name, ifname, l) != 0) 241 continue; 242 mac = (uint8_t *)LLADDR(sdl); 243 sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 244 mac[0], mac[1], mac[2], 245 mac[3], mac[4], mac[5]); 246 if (verbose) 247 D("source hwaddr %s", buf); 248 break; 249 } 250 freeifaddrs(ifaphead); 251 return ifap ? 0 : 1; 252} 253 254 255/* set the thread affinity. */ 256static int 257setaffinity(pthread_t me, int i) 258{ 259 cpuset_t cpumask; 260 261 if (i == -1) 262 return 0; 263 264 /* Set thread affinity affinity.*/ 265 CPU_ZERO(&cpumask); 266 CPU_SET(i, &cpumask); 267 268 if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 269 D("Unable to set affinity"); 270 return 1; 271 } 272 return 0; 273} 274 275/* Compute the checksum of the given ip header. */ 276static uint16_t 277checksum(const void *data, uint16_t len) 278{ 279 const uint8_t *addr = data; 280 uint32_t sum = 0; 281 282 while (len > 1) { 283 sum += addr[0] * 256 + addr[1]; 284 addr += 2; 285 len -= 2; 286 } 287 288 if (len == 1) 289 sum += *addr * 256; 290 291 sum = (sum >> 16) + (sum & 0xffff); 292 sum += (sum >> 16); 293 294 sum = htons(sum); 295 296 return ~sum; 297} 298 299/* 300 * Fill a packet with some payload. 301 */ 302static void 303initialize_packet(struct targ *targ) 304{ 305 struct pkt *pkt = &targ->pkt; 306 struct ether_header *eh; 307 struct ip *ip; 308 struct udphdr *udp; 309 uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip); 310 int i, l, l0 = strlen(default_payload); 311 char *p; 312 313 for (i = 0; i < paylen;) { 314 l = min(l0, paylen - i); 315 bcopy(default_payload, pkt->body + i, l); 316 i += l; 317 } 318 pkt->body[i-1] = '\0'; 319 320 udp = &pkt->udp; 321 udp->uh_sport = htons(1234); 322 udp->uh_dport = htons(4321); 323 udp->uh_ulen = htons(paylen); 324 udp->uh_sum = 0; // checksum(udp, sizeof(*udp)); 325 326 ip = &pkt->ip; 327 ip->ip_v = IPVERSION; 328 ip->ip_hl = 5; 329 ip->ip_id = 0; 330 ip->ip_tos = IPTOS_LOWDELAY; 331 ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 332 ip->ip_id = 0; 333 ip->ip_off = htons(IP_DF); /* Don't fragment */ 334 ip->ip_ttl = IPDEFTTL; 335 ip->ip_p = IPPROTO_UDP; 336 inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src); 337 inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst); 338 targ->dst_ip = ip->ip_dst.s_addr; 339 targ->src_ip = ip->ip_src.s_addr; 340 p = index(targ->g->src_ip, '-'); 341 if (p) { 342 targ->dst_ip_range = atoi(p+1); 343 D("dst-ip sweep %d addresses", targ->dst_ip_range); 344 } 345 ip->ip_sum = checksum(ip, sizeof(*ip)); 346 347 eh = &pkt->eh; 348 bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6); 349 bcopy(targ->src_mac, eh->ether_shost, 6); 350 p = index(targ->g->src_mac, '-'); 351 if (p) 352 targ->src_mac_range = atoi(p+1); 353 354 bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 355 bcopy(targ->dst_mac, eh->ether_dhost, 6); 356 p = index(targ->g->dst_mac, '-'); 357 if (p) 358 targ->dst_mac_range = atoi(p+1); 359 eh->ether_type = htons(ETHERTYPE_IP); 360} 361 362/* Check the payload of the packet for errors (use it for debug). 363 * Look for consecutive ascii representations of the size of the packet. 364 */ 365static void 366check_payload(char *p, int psize) 367{ 368 char temp[64]; 369 int n_read, size, sizelen; 370 371 /* get the length in ASCII of the length of the packet. */ 372 sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace 373 374 /* dummy payload. */ 375 p += 14; /* skip packet header. */ 376 n_read = 14; 377 while (psize - n_read >= sizelen) { 378 sscanf(p, "%d", &size); 379 if (size != psize) { 380 D("Read %d instead of %d", size, psize); 381 break; 382 } 383 384 p += sizelen; 385 n_read += sizelen; 386 } 387} 388 389 390/* 391 * create and enqueue a batch of packets on a ring. 392 * On the last one set NS_REPORT to tell the driver to generate 393 * an interrupt when done. 394 */ 395static int 396send_packets(struct netmap_ring *ring, struct pkt *pkt, 397 int size, u_int count, int fill_all) 398{ 399 u_int sent, cur = ring->cur; 400 401 if (ring->avail < count) 402 count = ring->avail; 403 404 for (sent = 0; sent < count; sent++) { 405 struct netmap_slot *slot = &ring->slot[cur]; 406 char *p = NETMAP_BUF(ring, slot->buf_idx); 407 408 if (fill_all) 409 memcpy(p, pkt, size); 410 411 slot->len = size; 412 if (sent == count - 1) 413 slot->flags |= NS_REPORT; 414 cur = NETMAP_RING_NEXT(ring, cur); 415 } 416 ring->avail -= sent; 417 ring->cur = cur; 418 419 return (sent); 420} 421 422static void * 423sender_body(void *data) 424{ 425 struct targ *targ = (struct targ *) data; 426 427 struct pollfd fds[1]; 428 struct netmap_if *nifp = targ->nifp; 429 struct netmap_ring *txring; 430 int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 431 int fill_all = 1; 432 433 if (setaffinity(targ->thread, targ->affinity)) 434 goto quit; 435 /* setup poll(2) machanism. */ 436 memset(fds, 0, sizeof(fds)); 437 fds[0].fd = targ->fd; 438 fds[0].events = (POLLOUT); 439 440 /* main loop.*/ 441 gettimeofday(&targ->tic, NULL); 442 if (targ->g->use_pcap) { 443 int size = targ->g->pkt_size; 444 void *pkt = &targ->pkt; 445 pcap_t *p = targ->g->p; 446 447 for (; sent < n; sent++) { 448 if (pcap_inject(p, pkt, size) == -1) 449 break; 450 } 451 } else { 452 while (sent < n) { 453 454 /* 455 * wait for available room in the send queue(s) 456 */ 457 if (poll(fds, 1, 2000) <= 0) { 458 D("poll error/timeout on queue %d\n", targ->me); 459 goto quit; 460 } 461 /* 462 * scan our queues and send on those with room 463 */ 464 if (sent > 100000) 465 fill_all = 0; 466 for (i = targ->qfirst; i < targ->qlast; i++) { 467 int m, limit = MIN(n - sent, targ->g->burst); 468 469 txring = NETMAP_TXRING(nifp, i); 470 if (txring->avail == 0) 471 continue; 472 m = send_packets(txring, &targ->pkt, targ->g->pkt_size, 473 limit, fill_all); 474 sent += m; 475 targ->count = sent; 476 } 477 } 478 /* Tell the interface that we have new packets. */ 479 ioctl(fds[0].fd, NIOCTXSYNC, NULL); 480 481 /* final part: wait all the TX queues to be empty. */ 482 for (i = targ->qfirst; i < targ->qlast; i++) { 483 txring = NETMAP_TXRING(nifp, i); 484 while (!NETMAP_TX_RING_EMPTY(txring)) { 485 ioctl(fds[0].fd, NIOCTXSYNC, NULL); 486 usleep(1); /* wait 1 tick */ 487 } 488 } 489 } 490 491 gettimeofday(&targ->toc, NULL); 492 targ->completed = 1; 493 targ->count = sent; 494 495quit: 496 /* reset the ``used`` flag. */ 497 targ->used = 0; 498 499 return (NULL); 500} 501 502 503static void 504receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h, 505 __unused const u_char * bytes) 506{ 507 int *count = (int *)user; 508 (*count)++; 509} 510 511static int 512receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload) 513{ 514 u_int cur, rx; 515 516 cur = ring->cur; 517 if (ring->avail < limit) 518 limit = ring->avail; 519 for (rx = 0; rx < limit; rx++) { 520 struct netmap_slot *slot = &ring->slot[cur]; 521 char *p = NETMAP_BUF(ring, slot->buf_idx); 522 523 if (!skip_payload) 524 check_payload(p, slot->len); 525 526 cur = NETMAP_RING_NEXT(ring, cur); 527 } 528 ring->avail -= rx; 529 ring->cur = cur; 530 531 return (rx); 532} 533 534static void * 535receiver_body(void *data) 536{ 537 struct targ *targ = (struct targ *) data; 538 struct pollfd fds[1]; 539 struct netmap_if *nifp = targ->nifp; 540 struct netmap_ring *rxring; 541 int i, received = 0; 542 543 if (setaffinity(targ->thread, targ->affinity)) 544 goto quit; 545 546 /* setup poll(2) machanism. */ 547 memset(fds, 0, sizeof(fds)); 548 fds[0].fd = targ->fd; 549 fds[0].events = (POLLIN); 550 551 /* unbounded wait for the first packet. */ 552 for (;;) { 553 i = poll(fds, 1, 1000); 554 if (i > 0 && !(fds[0].revents & POLLERR)) 555 break; 556 D("waiting for initial packets, poll returns %d %d", i, fds[0].revents); 557 } 558 559 /* main loop, exit after 1s silence */ 560 gettimeofday(&targ->tic, NULL); 561 if (targ->g->use_pcap) { 562 for (;;) { 563 pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 564 } 565 } else { 566 while (1) { 567 /* Once we started to receive packets, wait at most 1 seconds 568 before quitting. */ 569 if (poll(fds, 1, 1 * 1000) <= 0) { 570 gettimeofday(&targ->toc, NULL); 571 targ->toc.tv_sec -= 1; /* Substract timeout time. */ 572 break; 573 } 574 575 for (i = targ->qfirst; i < targ->qlast; i++) { 576 int m; 577 578 rxring = NETMAP_RXRING(nifp, i); 579 if (rxring->avail == 0) 580 continue; 581 582 m = receive_packets(rxring, targ->g->burst, 583 SKIP_PAYLOAD); 584 received += m; 585 targ->count = received; 586 } 587 588 // tell the card we have read the data 589 //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 590 } 591 } 592 593 targ->completed = 1; 594 targ->count = received; 595 596quit: 597 /* reset the ``used`` flag. */ 598 targ->used = 0; 599 600 return (NULL); 601} 602 603static void 604tx_output(uint64_t sent, int size, double delta) 605{ 606 double amount = 8.0 * (1.0 * size * sent) / delta; 607 double pps = sent / delta; 608 char units[4] = { '\0', 'K', 'M', 'G' }; 609 int aunit = 0, punit = 0; 610 611 while (amount >= 1000) { 612 amount /= 1000; 613 aunit += 1; 614 } 615 while (pps >= 1000) { 616 pps /= 1000; 617 punit += 1; 618 } 619 620 printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n", 621 sent, size, delta); 622 printf("Speed: %.2f%cpps. Bandwidth: %.2f%cbps.\n", 623 pps, units[punit], amount, units[aunit]); 624} 625 626 627static void 628rx_output(uint64_t received, double delta) 629{ 630 631 double pps = received / delta; 632 char units[4] = { '\0', 'K', 'M', 'G' }; 633 int punit = 0; 634 635 while (pps >= 1000) { 636 pps /= 1000; 637 punit += 1; 638 } 639 640 printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta); 641 printf("Speed: %.2f%cpps.\n", pps, units[punit]); 642} 643 644static void 645usage(void) 646{ 647 const char *cmd = "pkt-gen"; 648 fprintf(stderr, 649 "Usage:\n" 650 "%s arguments\n" 651 "\t-i interface interface name\n" 652 "\t-t pkts_to_send also forces send mode\n" 653 "\t-r pkts_to_receive also forces receive mode\n" 654 "\t-l pkts_size in bytes excluding CRC\n" 655 "\t-d dst-ip end with %%n to sweep n addresses\n" 656 "\t-s src-ip end with %%n to sweep n addresses\n" 657 "\t-D dst-mac end with %%n to sweep n addresses\n" 658 "\t-S src-mac end with %%n to sweep n addresses\n" 659 "\t-b burst size testing, mostly\n" 660 "\t-c cores cores to use\n" 661 "\t-p threads processes/threads to use\n" 662 "\t-T report_ms milliseconds between reports\n" 663 "\t-w wait_for_link_time in seconds\n" 664 "", 665 cmd); 666 667 exit(0); 668} 669 670 671int 672main(int arc, char **argv) 673{ 674 int i, fd; 675 676 struct glob_arg g; 677 678 struct nmreq nmr; 679 void *mmap_addr; /* the mmap address */ 680 void *(*td_body)(void *) = receiver_body; 681 int ch; 682 int report_interval = 1000; /* report interval */ 683 char *ifname = NULL; 684 int wait_link = 2; 685 int devqueues = 1; /* how many device queues */ 686 687 bzero(&g, sizeof(g)); 688 689 g.src_ip = "10.0.0.1"; 690 g.dst_ip = "10.1.0.1"; 691 g.dst_mac = "ff:ff:ff:ff:ff:ff"; 692 g.src_mac = NULL; 693 g.pkt_size = 60; 694 g.burst = 512; // default 695 g.nthreads = 1; 696 g.cpus = 1; 697 698 while ( (ch = getopt(arc, argv, 699 "i:t:r:l:d:s:D:S:b:c:p:T:w:v")) != -1) { 700 switch(ch) { 701 default: 702 D("bad option %c %s", ch, optarg); 703 usage(); 704 break; 705 case 'i': /* interface */ 706 ifname = optarg; 707 break; 708 case 't': /* send */ 709 td_body = sender_body; 710 g.npackets = atoi(optarg); 711 break; 712 case 'r': /* receive */ 713 td_body = receiver_body; 714 g.npackets = atoi(optarg); 715 break; 716 case 'l': /* pkt_size */ 717 g.pkt_size = atoi(optarg); 718 break; 719 case 'd': 720 g.dst_ip = optarg; 721 break; 722 case 's': 723 g.src_ip = optarg; 724 break; 725 case 'T': /* report interval */ 726 report_interval = atoi(optarg); 727 break; 728 case 'w': 729 wait_link = atoi(optarg); 730 break; 731 case 'b': /* burst */ 732 g.burst = atoi(optarg); 733 break; 734 case 'c': 735 g.cpus = atoi(optarg); 736 break; 737 case 'p': 738 g.nthreads = atoi(optarg); 739 break; 740 741 case 'P': 742 g.use_pcap = 1; 743 break; 744 745 case 'D': /* destination mac */ 746 g.dst_mac = optarg; 747 { 748 struct ether_addr *mac = ether_aton(g.dst_mac); 749 D("ether_aton(%s) gives %p", g.dst_mac, mac); 750 } 751 break; 752 case 'S': /* source mac */ 753 g.src_mac = optarg; 754 break; 755 case 'v': 756 verbose++; 757 } 758 } 759 760 if (ifname == NULL) { 761 D("missing ifname"); 762 usage(); 763 } 764 { 765 int n = system_ncpus(); 766 if (g.cpus < 0 || g.cpus > n) { 767 D("%d cpus is too high, have only %d cpus", g.cpus, n); 768 usage(); 769 } 770 if (g.cpus == 0) 771 g.cpus = n; 772 } 773 if (g.pkt_size < 16 || g.pkt_size > 1536) { 774 D("bad pktsize %d\n", g.pkt_size); 775 usage(); 776 } 777 778 bzero(&nmr, sizeof(nmr)); 779 /* 780 * Open the netmap device to fetch the number of queues of our 781 * interface. 782 * 783 * The first NIOCREGIF also detaches the card from the 784 * protocol stack and may cause a reset of the card, 785 * which in turn may take some time for the PHY to 786 * reconfigure. 787 */ 788 fd = open("/dev/netmap", O_RDWR); 789 if (fd == -1) { 790 D("Unable to open /dev/netmap"); 791 // fail later 792 } else { 793 if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 794 D("Unable to get if info without name"); 795 } else { 796 D("map size is %d Kb", nmr.nr_memsize >> 10); 797 } 798 bzero(&nmr, sizeof(nmr)); 799 strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name)); 800 if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 801 D("Unable to get if info for %s", ifname); 802 } 803 devqueues = nmr.nr_numrings; 804 } 805 806 /* validate provided nthreads. */ 807 if (g.nthreads < 1 || g.nthreads > devqueues) { 808 D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 809 // continue, fail later 810 } 811 812 if (td_body == sender_body && g.src_mac == NULL) { 813 static char mybuf[20] = "ff:ff:ff:ff:ff:ff"; 814 /* retrieve source mac address. */ 815 if (source_hwaddr(ifname, mybuf) == -1) { 816 D("Unable to retrieve source mac"); 817 // continue, fail later 818 } 819 g.src_mac = mybuf; 820 } 821 822 /* 823 * Map the netmap shared memory: instead of issuing mmap() 824 * inside the body of the threads, we prefer to keep this 825 * operation here to simplify the thread logic. 826 */ 827 D("mmapping %d Kbytes", nmr.nr_memsize>>10); 828 mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 829 PROT_WRITE | PROT_READ, 830 MAP_SHARED, fd, 0); 831 if (mmap_addr == MAP_FAILED) { 832 D("Unable to mmap %d KB", nmr.nr_memsize >> 10); 833 // continue, fail later 834 } 835 836 /* 837 * Register the interface on the netmap device: from now on, 838 * we can operate on the network interface without any 839 * interference from the legacy network stack. 840 * 841 * We decide to put the first interface registration here to 842 * give time to cards that take a long time to reset the PHY. 843 */ 844 if (ioctl(fd, NIOCREGIF, &nmr) == -1) { 845 D("Unable to register interface %s", ifname); 846 //continue, fail later 847 } 848 849 850 /* Print some debug information. */ 851 fprintf(stdout, 852 "%s %s: %d queues, %d threads and %d cpus.\n", 853 (td_body == sender_body) ? "Sending on" : "Receiving from", 854 ifname, 855 devqueues, 856 g.nthreads, 857 g.cpus); 858 if (td_body == sender_body) { 859 fprintf(stdout, "%s -> %s (%s -> %s)\n", 860 g.src_ip, g.dst_ip, 861 g.src_mac, g.dst_mac); 862 } 863 864 /* Exit if something went wrong. */ 865 if (fd < 0) { 866 D("aborting"); 867 usage(); 868 } 869 870 871 /* Wait for PHY reset. */ 872 D("Wait %d secs for phy reset", wait_link); 873 sleep(wait_link); 874 D("Ready..."); 875 876 /* Install ^C handler. */ 877 global_nthreads = g.nthreads; 878 signal(SIGINT, sigint_h); 879 880 if (g.use_pcap) { 881 // XXX g.p = pcap_open_live(..); 882 } 883 884 targs = calloc(g.nthreads, sizeof(*targs)); 885 /* 886 * Now create the desired number of threads, each one 887 * using a single descriptor. 888 */ 889 for (i = 0; i < g.nthreads; i++) { 890 struct netmap_if *tnifp; 891 struct nmreq tifreq; 892 int tfd; 893 894 if (g.use_pcap) { 895 tfd = -1; 896 tnifp = NULL; 897 } else { 898 /* register interface. */ 899 tfd = open("/dev/netmap", O_RDWR); 900 if (tfd == -1) { 901 D("Unable to open /dev/netmap"); 902 continue; 903 } 904 905 bzero(&tifreq, sizeof(tifreq)); 906 strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name)); 907 tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 908 909 /* 910 * if we are acting as a receiver only, do not touch the transmit ring. 911 * This is not the default because many apps may use the interface 912 * in both directions, but a pure receiver does not. 913 */ 914 if (td_body == receiver_body) { 915 tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 916 } 917 918 if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 919 D("Unable to register %s", ifname); 920 continue; 921 } 922 tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset); 923 } 924 /* start threads. */ 925 bzero(&targs[i], sizeof(targs[i])); 926 targs[i].g = &g; 927 targs[i].used = 1; 928 targs[i].completed = 0; 929 targs[i].fd = tfd; 930 targs[i].nmr = tifreq; 931 targs[i].nifp = tnifp; 932 targs[i].qfirst = (g.nthreads > 1) ? i : 0; 933 targs[i].qlast = (g.nthreads > 1) ? i+1 : tifreq.nr_numrings; 934 targs[i].me = i; 935 targs[i].affinity = g.cpus ? i % g.cpus : -1; 936 if (td_body == sender_body) { 937 /* initialize the packet to send. */ 938 initialize_packet(&targs[i]); 939 } 940 941 if (pthread_create(&targs[i].thread, NULL, td_body, 942 &targs[i]) == -1) { 943 D("Unable to create thread %d", i); 944 targs[i].used = 0; 945 } 946 } 947 948 { 949 uint64_t my_count = 0, prev = 0; 950 uint64_t count = 0; 951 double delta_t; 952 struct timeval tic, toc; 953 954 gettimeofday(&toc, NULL); 955 for (;;) { 956 struct timeval now, delta; 957 uint64_t pps; 958 int done = 0; 959 960 delta.tv_sec = report_interval/1000; 961 delta.tv_usec = (report_interval%1000)*1000; 962 select(0, NULL, NULL, NULL, &delta); 963 gettimeofday(&now, NULL); 964 timersub(&now, &toc, &toc); 965 my_count = 0; 966 for (i = 0; i < g.nthreads; i++) { 967 my_count += targs[i].count; 968 if (targs[i].used == 0) 969 done++; 970 } 971 pps = toc.tv_sec* 1000000 + toc.tv_usec; 972 if (pps < 10000) 973 continue; 974 pps = (my_count - prev)*1000000 / pps; 975 D("%" PRIu64 " pps", pps); 976 prev = my_count; 977 toc = now; 978 if (done == g.nthreads) 979 break; 980 } 981 982 timerclear(&tic); 983 timerclear(&toc); 984 for (i = 0; i < g.nthreads; i++) { 985 /* 986 * Join active threads, unregister interfaces and close 987 * file descriptors. 988 */ 989 pthread_join(targs[i].thread, NULL); 990 ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr); 991 close(targs[i].fd); 992 993 if (targs[i].completed == 0) 994 continue; 995 996 /* 997 * Collect threads o1utput and extract information about 998 * how log it took to send all the packets. 999 */ 1000 count += targs[i].count; 1001 if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <)) 1002 tic = targs[i].tic; 1003 if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >)) 1004 toc = targs[i].toc; 1005 } 1006 1007 /* print output. */ 1008 timersub(&toc, &tic, &toc); 1009 delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1010 if (td_body == sender_body) 1011 tx_output(count, g.pkt_size, delta_t); 1012 else 1013 rx_output(count, delta_t); 1014 } 1015 1016 ioctl(fd, NIOCUNREGIF, &nmr); 1017 munmap(mmap_addr, nmr.nr_memsize); 1018 close(fd); 1019 1020 return (0); 1021} 1022/* end of file */ 1023