pkt-gen.c revision 257529
1227614Sluigi/* 2257529Sluigi * Copyright (C) 2011-2013 Matteo Landi, Luigi Rizzo. All rights reserved. 3227614Sluigi * 4227614Sluigi * Redistribution and use in source and binary forms, with or without 5227614Sluigi * modification, are permitted provided that the following conditions 6227614Sluigi * are met: 7228276Sluigi * 1. Redistributions of source code must retain the above copyright 8228276Sluigi * notice, this list of conditions and the following disclaimer. 9228276Sluigi * 2. Redistributions in binary form must reproduce the above copyright 10228276Sluigi * notice, this list of conditions and the following disclaimer in the 11227614Sluigi * documentation and/or other materials provided with the distribution. 12227614Sluigi * 13227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16227614Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23227614Sluigi * SUCH DAMAGE. 24227614Sluigi */ 25227614Sluigi 26227614Sluigi/* 27227614Sluigi * $FreeBSD: head/tools/tools/netmap/pkt-gen.c 257529 2013-11-01 21:21:14Z luigi $ 28257529Sluigi * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $ 29227614Sluigi * 30227614Sluigi * Example program to show how to build a multithreaded packet 31227614Sluigi * source/sink using the netmap device. 32227614Sluigi * 33227614Sluigi * In this example we create a programmable number of threads 34227614Sluigi * to take care of all the queues of the interface used to 35227614Sluigi * send or receive traffic. 36227614Sluigi * 37227614Sluigi */ 38227614Sluigi 39246896Sluigi#include "nm_util.h" 40246896Sluigi 41251426Sluigi#include <ctype.h> // isprint() 42251426Sluigi 43257529Sluigiconst char *default_payload="netmap pkt-gen DIRECT payload\n" 44227614Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 45227614Sluigi 46257529Sluigiconst char *indirect_payload="netmap pkt-gen indirect payload\n" 47257529Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 48257529Sluigi 49246896Sluigiint time_second; // support for RD() debugging macro 50227614Sluigi 51227614Sluigiint verbose = 0; 52227614Sluigi 53227614Sluigi#define SKIP_PAYLOAD 1 /* do not check payload. */ 54227614Sluigi 55227614Sluigistruct pkt { 56227614Sluigi struct ether_header eh; 57227614Sluigi struct ip ip; 58227614Sluigi struct udphdr udp; 59231198Sluigi uint8_t body[2048]; // XXX hardwired 60227614Sluigi} __attribute__((__packed__)); 61227614Sluigi 62246896Sluigistruct ip_range { 63246896Sluigi char *name; 64257529Sluigi uint32_t start, end; /* same as struct in_addr */ 65257529Sluigi uint16_t port0, port1; 66246896Sluigi}; 67246896Sluigi 68246896Sluigistruct mac_range { 69246896Sluigi char *name; 70246896Sluigi struct ether_addr start, end; 71246896Sluigi}; 72246896Sluigi 73227614Sluigi/* 74227614Sluigi * global arguments for all threads 75227614Sluigi */ 76246896Sluigi 77227614Sluigistruct glob_arg { 78246896Sluigi struct ip_range src_ip; 79246896Sluigi struct ip_range dst_ip; 80246896Sluigi struct mac_range dst_mac; 81246896Sluigi struct mac_range src_mac; 82227614Sluigi int pkt_size; 83227614Sluigi int burst; 84246896Sluigi int forever; 85227614Sluigi int npackets; /* total packets to send */ 86257529Sluigi int frags; /* fragments per packet */ 87227614Sluigi int nthreads; 88227614Sluigi int cpus; 89234956Sluigi int options; /* testing */ 90234956Sluigi#define OPT_PREFETCH 1 91234956Sluigi#define OPT_ACCESS 2 92234956Sluigi#define OPT_COPY 4 93234956Sluigi#define OPT_MEMCPY 8 94246896Sluigi#define OPT_TS 16 /* add a timestamp */ 95251426Sluigi#define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 96251426Sluigi#define OPT_DUMP 64 /* dump rx/tx traffic */ 97246896Sluigi int dev_type; 98227614Sluigi pcap_t *p; 99227614Sluigi 100251132Sluigi int tx_rate; 101251132Sluigi struct timespec tx_period; 102251132Sluigi 103246896Sluigi int affinity; 104246896Sluigi int main_fd; 105246896Sluigi int report_interval; 106246896Sluigi void *(*td_body)(void *); 107246896Sluigi void *mmap_addr; 108246896Sluigi int mmap_size; 109246896Sluigi char *ifname; 110257529Sluigi char *nmr_config; 111257529Sluigi int dummy_send; 112227614Sluigi}; 113246896Sluigienum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 114227614Sluigi 115246896Sluigi 116227614Sluigi/* 117227614Sluigi * Arguments for a new thread. The same structure is used by 118227614Sluigi * the source and the sink 119227614Sluigi */ 120227614Sluigistruct targ { 121227614Sluigi struct glob_arg *g; 122227614Sluigi int used; 123227614Sluigi int completed; 124238165Semaste int cancel; 125227614Sluigi int fd; 126227614Sluigi struct nmreq nmr; 127227614Sluigi struct netmap_if *nifp; 128227614Sluigi uint16_t qfirst, qlast; /* range of queues to scan */ 129246896Sluigi volatile uint64_t count; 130251132Sluigi struct timespec tic, toc; 131227614Sluigi int me; 132227614Sluigi pthread_t thread; 133227614Sluigi int affinity; 134227614Sluigi 135227614Sluigi struct pkt pkt; 136227614Sluigi}; 137227614Sluigi 138227614Sluigi 139246896Sluigi/* 140246896Sluigi * extract the extremes from a range of ipv4 addresses. 141246896Sluigi * addr_lo[-addr_hi][:port_lo[-port_hi]] 142246896Sluigi */ 143246896Sluigistatic void 144246896Sluigiextract_ip_range(struct ip_range *r) 145246896Sluigi{ 146257529Sluigi char *ap, *pp; 147257529Sluigi struct in_addr a; 148246896Sluigi 149246896Sluigi D("extract IP range from %s", r->name); 150257529Sluigi r->port0 = r->port1 = 0; 151257529Sluigi r->start = r->end = 0; 152257529Sluigi 153257529Sluigi /* the first - splits start/end of range */ 154257529Sluigi ap = index(r->name, '-'); /* do we have ports ? */ 155257529Sluigi if (ap) { 156257529Sluigi *ap++ = '\0'; 157257529Sluigi } 158257529Sluigi /* grab the initial values (mandatory) */ 159257529Sluigi pp = index(r->name, ':'); 160257529Sluigi if (pp) { 161257529Sluigi *pp++ = '\0'; 162257529Sluigi r->port0 = r->port1 = strtol(pp, NULL, 0); 163257529Sluigi }; 164257529Sluigi inet_aton(r->name, &a); 165257529Sluigi r->start = r->end = ntohl(a.s_addr); 166257529Sluigi if (ap) { 167257529Sluigi pp = index(ap, ':'); 168257529Sluigi if (pp) { 169257529Sluigi *pp++ = '\0'; 170257529Sluigi if (*pp) 171257529Sluigi r->port1 = strtol(pp, NULL, 0); 172246896Sluigi } 173257529Sluigi if (*ap) { 174257529Sluigi inet_aton(ap, &a); 175257529Sluigi r->end = ntohl(a.s_addr); 176257529Sluigi } 177246896Sluigi } 178257529Sluigi if (r->port0 > r->port1) { 179257529Sluigi uint16_t tmp = r->port0; 180257529Sluigi r->port0 = r->port1; 181257529Sluigi r->port1 = tmp; 182257529Sluigi } 183257529Sluigi if (r->start > r->end) { 184257529Sluigi uint32_t tmp = r->start; 185246896Sluigi r->start = r->end; 186257529Sluigi r->end = tmp; 187246896Sluigi } 188257529Sluigi { 189257529Sluigi struct in_addr a; 190257529Sluigi char buf1[16]; // one ip address 191257529Sluigi 192257529Sluigi a.s_addr = htonl(r->end); 193257529Sluigi strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 194257529Sluigi a.s_addr = htonl(r->start); 195257529Sluigi D("range is %s:%d to %s:%d", 196257529Sluigi inet_ntoa(a), r->port0, buf1, r->port1); 197257529Sluigi } 198246896Sluigi} 199246896Sluigi 200246896Sluigistatic void 201246896Sluigiextract_mac_range(struct mac_range *r) 202246896Sluigi{ 203246896Sluigi D("extract MAC range from %s", r->name); 204246896Sluigi bcopy(ether_aton(r->name), &r->start, 6); 205246896Sluigi bcopy(ether_aton(r->name), &r->end, 6); 206246896Sluigi#if 0 207246896Sluigi bcopy(targ->src_mac, eh->ether_shost, 6); 208246896Sluigi p = index(targ->g->src_mac, '-'); 209246896Sluigi if (p) 210246896Sluigi targ->src_mac_range = atoi(p+1); 211246896Sluigi 212246896Sluigi bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 213246896Sluigi bcopy(targ->dst_mac, eh->ether_dhost, 6); 214246896Sluigi p = index(targ->g->dst_mac, '-'); 215246896Sluigi if (p) 216246896Sluigi targ->dst_mac_range = atoi(p+1); 217246896Sluigi#endif 218246896Sluigi D("%s starts at %s", r->name, ether_ntoa(&r->start)); 219246896Sluigi} 220246896Sluigi 221227614Sluigistatic struct targ *targs; 222227614Sluigistatic int global_nthreads; 223227614Sluigi 224227614Sluigi/* control-C handler */ 225227614Sluigistatic void 226246896Sluigisigint_h(int sig) 227227614Sluigi{ 228246896Sluigi int i; 229246896Sluigi 230246896Sluigi (void)sig; /* UNUSED */ 231246896Sluigi for (i = 0; i < global_nthreads; i++) { 232238165Semaste targs[i].cancel = 1; 233246896Sluigi } 234227614Sluigi signal(SIGINT, SIG_DFL); 235227614Sluigi} 236227614Sluigi 237227614Sluigi/* sysctl wrapper to return the number of active CPUs */ 238227614Sluigistatic int 239227614Sluigisystem_ncpus(void) 240227614Sluigi{ 241246896Sluigi#ifdef __FreeBSD__ 242227614Sluigi int mib[2], ncpus; 243227614Sluigi size_t len; 244227614Sluigi 245227614Sluigi mib[0] = CTL_HW; 246227614Sluigi mib[1] = HW_NCPU; 247227614Sluigi len = sizeof(mib); 248227614Sluigi sysctl(mib, 2, &ncpus, &len, NULL, 0); 249227614Sluigi 250227614Sluigi return (ncpus); 251246896Sluigi#else 252246896Sluigi return 1; 253246896Sluigi#endif /* !__FreeBSD__ */ 254227614Sluigi} 255227614Sluigi 256246896Sluigi#ifdef __linux__ 257246896Sluigi#define sockaddr_dl sockaddr_ll 258246896Sluigi#define sdl_family sll_family 259246896Sluigi#define AF_LINK AF_PACKET 260246896Sluigi#define LLADDR(s) s->sll_addr; 261246896Sluigi#include <linux/if_tun.h> 262246896Sluigi#define TAP_CLONEDEV "/dev/net/tun" 263246896Sluigi#endif /* __linux__ */ 264246896Sluigi 265246896Sluigi#ifdef __FreeBSD__ 266246896Sluigi#include <net/if_tun.h> 267246896Sluigi#define TAP_CLONEDEV "/dev/tap" 268246896Sluigi#endif /* __FreeBSD */ 269246896Sluigi 270246896Sluigi#ifdef __APPLE__ 271246896Sluigi// #warning TAP not supported on apple ? 272246896Sluigi#include <net/if_utun.h> 273246896Sluigi#define TAP_CLONEDEV "/dev/tap" 274246896Sluigi#endif /* __APPLE__ */ 275246896Sluigi 276246896Sluigi 277227614Sluigi/* 278257529Sluigi * parse the vale configuration in conf and put it in nmr. 279257529Sluigi * The configuration may consist of 0 to 4 numbers separated 280257529Sluigi * by commas: #tx-slots,#rx-slots,#tx-rinzgs,#rx-rings. 281257529Sluigi * Missing numbers or zeroes stand for default values. 282257529Sluigi * As an additional convenience, if exactly one number 283257529Sluigi * is specified, then this is assigned to bot #tx-slots and #rx-slots. 284257529Sluigi * If there is no 4th number, then the 3rd is assigned to bot #tx-rings 285257529Sluigi * and #rx-rings. 286257529Sluigi */ 287257529Sluigivoid parse_nmr_config(const char* conf, struct nmreq *nmr) 288257529Sluigi{ 289257529Sluigi char *w, *tok; 290257529Sluigi int i, v; 291257529Sluigi 292257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 293257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 294257529Sluigi if (conf == NULL || ! *conf) 295257529Sluigi return; 296257529Sluigi w = strdup(conf); 297257529Sluigi for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 298257529Sluigi v = atoi(tok); 299257529Sluigi switch (i) { 300257529Sluigi case 0: 301257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = v; 302257529Sluigi break; 303257529Sluigi case 1: 304257529Sluigi nmr->nr_rx_slots = v; 305257529Sluigi break; 306257529Sluigi case 2: 307257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = v; 308257529Sluigi break; 309257529Sluigi case 3: 310257529Sluigi nmr->nr_rx_rings = v; 311257529Sluigi break; 312257529Sluigi default: 313257529Sluigi D("ignored config: %s", tok); 314257529Sluigi break; 315257529Sluigi } 316257529Sluigi } 317257529Sluigi D("txr %d txd %d rxr %d rxd %d", 318257529Sluigi nmr->nr_tx_rings, nmr->nr_tx_slots, 319257529Sluigi nmr->nr_rx_rings, nmr->nr_rx_slots); 320257529Sluigi free(w); 321257529Sluigi} 322257529Sluigi 323257529Sluigi 324257529Sluigi/* 325227614Sluigi * locate the src mac address for our interface, put it 326227614Sluigi * into the user-supplied buffer. return 0 if ok, -1 on error. 327227614Sluigi */ 328227614Sluigistatic int 329227614Sluigisource_hwaddr(const char *ifname, char *buf) 330227614Sluigi{ 331227614Sluigi struct ifaddrs *ifaphead, *ifap; 332227614Sluigi int l = sizeof(ifap->ifa_name); 333227614Sluigi 334227614Sluigi if (getifaddrs(&ifaphead) != 0) { 335227614Sluigi D("getifaddrs %s failed", ifname); 336227614Sluigi return (-1); 337227614Sluigi } 338227614Sluigi 339227614Sluigi for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 340227614Sluigi struct sockaddr_dl *sdl = 341227614Sluigi (struct sockaddr_dl *)ifap->ifa_addr; 342227614Sluigi uint8_t *mac; 343227614Sluigi 344227614Sluigi if (!sdl || sdl->sdl_family != AF_LINK) 345227614Sluigi continue; 346227614Sluigi if (strncmp(ifap->ifa_name, ifname, l) != 0) 347227614Sluigi continue; 348227614Sluigi mac = (uint8_t *)LLADDR(sdl); 349227614Sluigi sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 350227614Sluigi mac[0], mac[1], mac[2], 351227614Sluigi mac[3], mac[4], mac[5]); 352227614Sluigi if (verbose) 353227614Sluigi D("source hwaddr %s", buf); 354227614Sluigi break; 355227614Sluigi } 356227614Sluigi freeifaddrs(ifaphead); 357227614Sluigi return ifap ? 0 : 1; 358227614Sluigi} 359227614Sluigi 360227614Sluigi 361227614Sluigi/* set the thread affinity. */ 362227614Sluigistatic int 363227614Sluigisetaffinity(pthread_t me, int i) 364227614Sluigi{ 365246896Sluigi#ifdef __FreeBSD__ 366227614Sluigi cpuset_t cpumask; 367227614Sluigi 368227614Sluigi if (i == -1) 369227614Sluigi return 0; 370227614Sluigi 371227614Sluigi /* Set thread affinity affinity.*/ 372227614Sluigi CPU_ZERO(&cpumask); 373227614Sluigi CPU_SET(i, &cpumask); 374227614Sluigi 375227614Sluigi if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 376227614Sluigi D("Unable to set affinity"); 377227614Sluigi return 1; 378227614Sluigi } 379246896Sluigi#else 380246896Sluigi (void)me; /* suppress 'unused' warnings */ 381246896Sluigi (void)i; 382246896Sluigi#endif /* __FreeBSD__ */ 383227614Sluigi return 0; 384227614Sluigi} 385227614Sluigi 386227614Sluigi/* Compute the checksum of the given ip header. */ 387227614Sluigistatic uint16_t 388246896Sluigichecksum(const void *data, uint16_t len, uint32_t sum) 389227614Sluigi{ 390227614Sluigi const uint8_t *addr = data; 391246896Sluigi uint32_t i; 392227614Sluigi 393246896Sluigi /* Checksum all the pairs of bytes first... */ 394246896Sluigi for (i = 0; i < (len & ~1U); i += 2) { 395246896Sluigi sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 396246896Sluigi if (sum > 0xFFFF) 397246896Sluigi sum -= 0xFFFF; 398227614Sluigi } 399246896Sluigi /* 400246896Sluigi * If there's a single byte left over, checksum it, too. 401246896Sluigi * Network byte order is big-endian, so the remaining byte is 402246896Sluigi * the high byte. 403246896Sluigi */ 404246896Sluigi if (i < len) { 405246896Sluigi sum += addr[i] << 8; 406246896Sluigi if (sum > 0xFFFF) 407246896Sluigi sum -= 0xFFFF; 408246896Sluigi } 409246896Sluigi return sum; 410246896Sluigi} 411227614Sluigi 412246896Sluigistatic u_int16_t 413246896Sluigiwrapsum(u_int32_t sum) 414246896Sluigi{ 415246896Sluigi sum = ~sum & 0xFFFF; 416246896Sluigi return (htons(sum)); 417227614Sluigi} 418227614Sluigi 419251426Sluigi/* Check the payload of the packet for errors (use it for debug). 420251426Sluigi * Look for consecutive ascii representations of the size of the packet. 421251426Sluigi */ 422251426Sluigistatic void 423251426Sluigidump_payload(char *p, int len, struct netmap_ring *ring, int cur) 424251426Sluigi{ 425251426Sluigi char buf[128]; 426251426Sluigi int i, j, i0; 427251426Sluigi 428251426Sluigi /* get the length in ASCII of the length of the packet. */ 429251426Sluigi 430257529Sluigi printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 431257529Sluigi ring, cur, ring->slot[cur].buf_idx, 432257529Sluigi ring->slot[cur].flags, len); 433251426Sluigi /* hexdump routine */ 434251426Sluigi for (i = 0; i < len; ) { 435251426Sluigi memset(buf, sizeof(buf), ' '); 436251426Sluigi sprintf(buf, "%5d: ", i); 437251426Sluigi i0 = i; 438251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 439251426Sluigi sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 440251426Sluigi i = i0; 441251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 442251426Sluigi sprintf(buf+7+j + 48, "%c", 443251426Sluigi isprint(p[i]) ? p[i] : '.'); 444251426Sluigi printf("%s\n", buf); 445251426Sluigi } 446251426Sluigi} 447251426Sluigi 448227614Sluigi/* 449227614Sluigi * Fill a packet with some payload. 450246896Sluigi * We create a UDP packet so the payload starts at 451246896Sluigi * 14+20+8 = 42 bytes. 452227614Sluigi */ 453246896Sluigi#ifdef __linux__ 454246896Sluigi#define uh_sport source 455246896Sluigi#define uh_dport dest 456246896Sluigi#define uh_ulen len 457246896Sluigi#define uh_sum check 458246896Sluigi#endif /* linux */ 459251426Sluigi 460257529Sluigi/* 461257529Sluigi * increment the addressed in the packet, 462257529Sluigi * starting from the least significant field. 463257529Sluigi * DST_IP DST_PORT SRC_IP SRC_PORT 464257529Sluigi */ 465227614Sluigistatic void 466257529Sluigiupdate_addresses(struct pkt *pkt, struct glob_arg *g) 467257529Sluigi{ 468257529Sluigi uint32_t a; 469257529Sluigi uint16_t p; 470257529Sluigi struct ip *ip = &pkt->ip; 471257529Sluigi struct udphdr *udp = &pkt->udp; 472257529Sluigi 473257529Sluigi p = ntohs(udp->uh_sport); 474257529Sluigi if (p < g->src_ip.port1) { /* just inc, no wrap */ 475257529Sluigi udp->uh_sport = htons(p + 1); 476257529Sluigi return; 477257529Sluigi } 478257529Sluigi udp->uh_sport = htons(g->src_ip.port0); 479257529Sluigi 480257529Sluigi a = ntohl(ip->ip_src.s_addr); 481257529Sluigi if (a < g->src_ip.end) { /* just inc, no wrap */ 482257529Sluigi ip->ip_src.s_addr = htonl(a + 1); 483257529Sluigi return; 484257529Sluigi } 485257529Sluigi ip->ip_src.s_addr = htonl(g->src_ip.start); 486257529Sluigi 487257529Sluigi udp->uh_sport = htons(g->src_ip.port0); 488257529Sluigi p = ntohs(udp->uh_dport); 489257529Sluigi if (p < g->dst_ip.port1) { /* just inc, no wrap */ 490257529Sluigi udp->uh_dport = htons(p + 1); 491257529Sluigi return; 492257529Sluigi } 493257529Sluigi udp->uh_dport = htons(g->dst_ip.port0); 494257529Sluigi 495257529Sluigi a = ntohl(ip->ip_dst.s_addr); 496257529Sluigi if (a < g->dst_ip.end) { /* just inc, no wrap */ 497257529Sluigi ip->ip_dst.s_addr = htonl(a + 1); 498257529Sluigi return; 499257529Sluigi } 500257529Sluigi ip->ip_dst.s_addr = htonl(g->dst_ip.start); 501257529Sluigi 502257529Sluigi} 503257529Sluigi 504257529Sluigi/* 505257529Sluigi * initialize one packet and prepare for the next one. 506257529Sluigi * The copy could be done better instead of repeating it each time. 507257529Sluigi */ 508257529Sluigistatic void 509227614Sluigiinitialize_packet(struct targ *targ) 510227614Sluigi{ 511227614Sluigi struct pkt *pkt = &targ->pkt; 512227614Sluigi struct ether_header *eh; 513227614Sluigi struct ip *ip; 514227614Sluigi struct udphdr *udp; 515246896Sluigi uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 516251426Sluigi const char *payload = targ->g->options & OPT_INDIRECT ? 517257529Sluigi indirect_payload : default_payload; 518251426Sluigi int i, l, l0 = strlen(payload); 519227614Sluigi 520257529Sluigi /* create a nice NUL-terminated string */ 521227614Sluigi for (i = 0; i < paylen;) { 522227614Sluigi l = min(l0, paylen - i); 523251426Sluigi bcopy(payload, pkt->body + i, l); 524227614Sluigi i += l; 525227614Sluigi } 526227614Sluigi pkt->body[i-1] = '\0'; 527246896Sluigi ip = &pkt->ip; 528227614Sluigi 529257529Sluigi /* prepare the headers */ 530227614Sluigi ip->ip_v = IPVERSION; 531227614Sluigi ip->ip_hl = 5; 532227614Sluigi ip->ip_id = 0; 533227614Sluigi ip->ip_tos = IPTOS_LOWDELAY; 534227614Sluigi ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 535227614Sluigi ip->ip_id = 0; 536227614Sluigi ip->ip_off = htons(IP_DF); /* Don't fragment */ 537227614Sluigi ip->ip_ttl = IPDEFTTL; 538227614Sluigi ip->ip_p = IPPROTO_UDP; 539257529Sluigi ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 540257529Sluigi ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 541246896Sluigi ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 542227614Sluigi 543246896Sluigi 544246896Sluigi udp = &pkt->udp; 545257529Sluigi udp->uh_sport = htons(targ->g->src_ip.port0); 546257529Sluigi udp->uh_dport = htons(targ->g->dst_ip.port0); 547246896Sluigi udp->uh_ulen = htons(paylen); 548246896Sluigi /* Magic: taken from sbin/dhclient/packet.c */ 549246896Sluigi udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 550246896Sluigi checksum(pkt->body, 551246896Sluigi paylen - sizeof(*udp), 552246896Sluigi checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 553246896Sluigi IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 554246896Sluigi ) 555246896Sluigi ) 556246896Sluigi )); 557246896Sluigi 558227614Sluigi eh = &pkt->eh; 559246896Sluigi bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 560246896Sluigi bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 561227614Sluigi eh->ether_type = htons(ETHERTYPE_IP); 562251426Sluigi // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 563227614Sluigi} 564227614Sluigi 565227614Sluigi 566227614Sluigi 567227614Sluigi/* 568227614Sluigi * create and enqueue a batch of packets on a ring. 569227614Sluigi * On the last one set NS_REPORT to tell the driver to generate 570227614Sluigi * an interrupt when done. 571227614Sluigi */ 572227614Sluigistatic int 573227614Sluigisend_packets(struct netmap_ring *ring, struct pkt *pkt, 574257529Sluigi struct glob_arg *g, u_int count, int options, u_int nfrags) 575227614Sluigi{ 576227614Sluigi u_int sent, cur = ring->cur; 577257529Sluigi int fcnt; 578257529Sluigi int size = g->pkt_size; 579227614Sluigi 580227614Sluigi if (ring->avail < count) 581227614Sluigi count = ring->avail; 582257529Sluigi if (count < nfrags) { 583257529Sluigi D("truncating packet, no room for frags %d %d", 584257529Sluigi count, nfrags); 585257529Sluigi } 586234956Sluigi#if 0 587234956Sluigi if (options & (OPT_COPY | OPT_PREFETCH) ) { 588234956Sluigi for (sent = 0; sent < count; sent++) { 589234956Sluigi struct netmap_slot *slot = &ring->slot[cur]; 590234956Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 591234956Sluigi 592234956Sluigi prefetch(p); 593234956Sluigi cur = NETMAP_RING_NEXT(ring, cur); 594234956Sluigi } 595234956Sluigi cur = ring->cur; 596234956Sluigi } 597234956Sluigi#endif 598257529Sluigi for (fcnt = nfrags, sent = 0; sent < count; sent++) { 599227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 600227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 601227614Sluigi 602251426Sluigi slot->flags = 0; 603251426Sluigi if (options & OPT_INDIRECT) { 604251426Sluigi slot->flags |= NS_INDIRECT; 605257529Sluigi slot->ptr = (uint64_t)pkt; 606257529Sluigi } else if (options & OPT_COPY) { 607234956Sluigi pkt_copy(pkt, p, size); 608257529Sluigi if (fcnt == 1) 609257529Sluigi update_addresses(pkt, g); 610257529Sluigi } else if (options & OPT_MEMCPY) { 611227614Sluigi memcpy(p, pkt, size); 612257529Sluigi if (fcnt == 1) 613257529Sluigi update_addresses(pkt, g); 614257529Sluigi } else if (options & OPT_PREFETCH) { 615234956Sluigi prefetch(p); 616257529Sluigi } 617257529Sluigi if (options & OPT_DUMP) 618257529Sluigi dump_payload(p, size, ring, cur); 619227614Sluigi slot->len = size; 620257529Sluigi if (--fcnt > 0) 621257529Sluigi slot->flags |= NS_MOREFRAG; 622257529Sluigi else 623257529Sluigi fcnt = nfrags; 624257529Sluigi if (sent == count - 1) { 625257529Sluigi slot->flags &= ~NS_MOREFRAG; 626227614Sluigi slot->flags |= NS_REPORT; 627257529Sluigi } 628227614Sluigi cur = NETMAP_RING_NEXT(ring, cur); 629227614Sluigi } 630227614Sluigi ring->avail -= sent; 631227614Sluigi ring->cur = cur; 632227614Sluigi 633227614Sluigi return (sent); 634227614Sluigi} 635227614Sluigi 636246896Sluigi/* 637246896Sluigi * Send a packet, and wait for a response. 638246896Sluigi * The payload (after UDP header, ofs 42) has a 4-byte sequence 639246896Sluigi * followed by a struct timeval (or bintime?) 640246896Sluigi */ 641246896Sluigi#define PAY_OFS 42 /* where in the pkt... */ 642246896Sluigi 643227614Sluigistatic void * 644246896Sluigipinger_body(void *data) 645246896Sluigi{ 646246896Sluigi struct targ *targ = (struct targ *) data; 647246896Sluigi struct pollfd fds[1]; 648246896Sluigi struct netmap_if *nifp = targ->nifp; 649246896Sluigi int i, rx = 0, n = targ->g->npackets; 650246896Sluigi 651246896Sluigi fds[0].fd = targ->fd; 652246896Sluigi fds[0].events = (POLLIN); 653246896Sluigi static uint32_t sent; 654246896Sluigi struct timespec ts, now, last_print; 655246896Sluigi uint32_t count = 0, min = 1000000000, av = 0; 656246896Sluigi 657246896Sluigi if (targ->g->nthreads > 1) { 658246896Sluigi D("can only ping with 1 thread"); 659246896Sluigi return NULL; 660246896Sluigi } 661246896Sluigi 662246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 663246896Sluigi while (n == 0 || (int)sent < n) { 664246896Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 665246896Sluigi struct netmap_slot *slot; 666246896Sluigi char *p; 667246896Sluigi for (i = 0; i < 1; i++) { 668246896Sluigi slot = &ring->slot[ring->cur]; 669246896Sluigi slot->len = targ->g->pkt_size; 670246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 671246896Sluigi 672246896Sluigi if (ring->avail == 0) { 673246896Sluigi D("-- ouch, cannot send"); 674246896Sluigi } else { 675246896Sluigi pkt_copy(&targ->pkt, p, targ->g->pkt_size); 676246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 677246896Sluigi bcopy(&sent, p+42, sizeof(sent)); 678246896Sluigi bcopy(&ts, p+46, sizeof(ts)); 679246896Sluigi sent++; 680246896Sluigi ring->cur = NETMAP_RING_NEXT(ring, ring->cur); 681246896Sluigi ring->avail--; 682246896Sluigi } 683246896Sluigi } 684246896Sluigi /* should use a parameter to decide how often to send */ 685246896Sluigi if (poll(fds, 1, 3000) <= 0) { 686246896Sluigi D("poll error/timeout on queue %d", targ->me); 687246896Sluigi continue; 688246896Sluigi } 689246896Sluigi /* see what we got back */ 690246896Sluigi for (i = targ->qfirst; i < targ->qlast; i++) { 691246896Sluigi ring = NETMAP_RXRING(nifp, i); 692246896Sluigi while (ring->avail > 0) { 693246896Sluigi uint32_t seq; 694246896Sluigi slot = &ring->slot[ring->cur]; 695246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 696246896Sluigi 697246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &now); 698246896Sluigi bcopy(p+42, &seq, sizeof(seq)); 699246896Sluigi bcopy(p+46, &ts, sizeof(ts)); 700246896Sluigi ts.tv_sec = now.tv_sec - ts.tv_sec; 701246896Sluigi ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 702246896Sluigi if (ts.tv_nsec < 0) { 703246896Sluigi ts.tv_nsec += 1000000000; 704246896Sluigi ts.tv_sec--; 705246896Sluigi } 706246896Sluigi if (1) D("seq %d/%d delta %d.%09d", seq, sent, 707246896Sluigi (int)ts.tv_sec, (int)ts.tv_nsec); 708246896Sluigi if (ts.tv_nsec < (int)min) 709246896Sluigi min = ts.tv_nsec; 710246896Sluigi count ++; 711246896Sluigi av += ts.tv_nsec; 712246896Sluigi ring->avail--; 713246896Sluigi ring->cur = NETMAP_RING_NEXT(ring, ring->cur); 714246896Sluigi rx++; 715246896Sluigi } 716246896Sluigi } 717246896Sluigi //D("tx %d rx %d", sent, rx); 718246896Sluigi //usleep(100000); 719246896Sluigi ts.tv_sec = now.tv_sec - last_print.tv_sec; 720246896Sluigi ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 721246896Sluigi if (ts.tv_nsec < 0) { 722246896Sluigi ts.tv_nsec += 1000000000; 723246896Sluigi ts.tv_sec--; 724246896Sluigi } 725246896Sluigi if (ts.tv_sec >= 1) { 726246896Sluigi D("count %d min %d av %d", 727246896Sluigi count, min, av/count); 728246896Sluigi count = 0; 729246896Sluigi av = 0; 730246896Sluigi min = 100000000; 731246896Sluigi last_print = now; 732246896Sluigi } 733246896Sluigi } 734246896Sluigi return NULL; 735246896Sluigi} 736246896Sluigi 737246896Sluigi 738246896Sluigi/* 739246896Sluigi * reply to ping requests 740246896Sluigi */ 741246896Sluigistatic void * 742246896Sluigiponger_body(void *data) 743246896Sluigi{ 744246896Sluigi struct targ *targ = (struct targ *) data; 745246896Sluigi struct pollfd fds[1]; 746246896Sluigi struct netmap_if *nifp = targ->nifp; 747246896Sluigi struct netmap_ring *txring, *rxring; 748246896Sluigi int i, rx = 0, sent = 0, n = targ->g->npackets; 749246896Sluigi fds[0].fd = targ->fd; 750246896Sluigi fds[0].events = (POLLIN); 751246896Sluigi 752246896Sluigi if (targ->g->nthreads > 1) { 753246896Sluigi D("can only reply ping with 1 thread"); 754246896Sluigi return NULL; 755246896Sluigi } 756246896Sluigi D("understood ponger %d but don't know how to do it", n); 757246896Sluigi while (n == 0 || sent < n) { 758246896Sluigi uint32_t txcur, txavail; 759246896Sluigi//#define BUSYWAIT 760246896Sluigi#ifdef BUSYWAIT 761246896Sluigi ioctl(fds[0].fd, NIOCRXSYNC, NULL); 762246896Sluigi#else 763246896Sluigi if (poll(fds, 1, 1000) <= 0) { 764246896Sluigi D("poll error/timeout on queue %d", targ->me); 765246896Sluigi continue; 766246896Sluigi } 767246896Sluigi#endif 768246896Sluigi txring = NETMAP_TXRING(nifp, 0); 769246896Sluigi txcur = txring->cur; 770246896Sluigi txavail = txring->avail; 771246896Sluigi /* see what we got back */ 772246896Sluigi for (i = targ->qfirst; i < targ->qlast; i++) { 773246896Sluigi rxring = NETMAP_RXRING(nifp, i); 774246896Sluigi while (rxring->avail > 0) { 775246896Sluigi uint16_t *spkt, *dpkt; 776246896Sluigi uint32_t cur = rxring->cur; 777246896Sluigi struct netmap_slot *slot = &rxring->slot[cur]; 778246896Sluigi char *src, *dst; 779246896Sluigi src = NETMAP_BUF(rxring, slot->buf_idx); 780246896Sluigi //D("got pkt %p of size %d", src, slot->len); 781246896Sluigi rxring->avail--; 782246896Sluigi rxring->cur = NETMAP_RING_NEXT(rxring, cur); 783246896Sluigi rx++; 784246896Sluigi if (txavail == 0) 785246896Sluigi continue; 786246896Sluigi dst = NETMAP_BUF(txring, 787246896Sluigi txring->slot[txcur].buf_idx); 788246896Sluigi /* copy... */ 789246896Sluigi dpkt = (uint16_t *)dst; 790246896Sluigi spkt = (uint16_t *)src; 791246896Sluigi pkt_copy(src, dst, slot->len); 792246896Sluigi dpkt[0] = spkt[3]; 793246896Sluigi dpkt[1] = spkt[4]; 794246896Sluigi dpkt[2] = spkt[5]; 795246896Sluigi dpkt[3] = spkt[0]; 796246896Sluigi dpkt[4] = spkt[1]; 797246896Sluigi dpkt[5] = spkt[2]; 798246896Sluigi txring->slot[txcur].len = slot->len; 799246896Sluigi /* XXX swap src dst mac */ 800246896Sluigi txcur = NETMAP_RING_NEXT(txring, txcur); 801246896Sluigi txavail--; 802246896Sluigi sent++; 803246896Sluigi } 804246896Sluigi } 805246896Sluigi txring->cur = txcur; 806246896Sluigi txring->avail = txavail; 807246896Sluigi targ->count = sent; 808246896Sluigi#ifdef BUSYWAIT 809246896Sluigi ioctl(fds[0].fd, NIOCTXSYNC, NULL); 810246896Sluigi#endif 811246896Sluigi //D("tx %d rx %d", sent, rx); 812246896Sluigi } 813246896Sluigi return NULL; 814246896Sluigi} 815246896Sluigi 816251132Sluigistatic __inline int 817251132Sluigitimespec_ge(const struct timespec *a, const struct timespec *b) 818251132Sluigi{ 819246896Sluigi 820251132Sluigi if (a->tv_sec > b->tv_sec) 821251132Sluigi return (1); 822251132Sluigi if (a->tv_sec < b->tv_sec) 823251132Sluigi return (0); 824251132Sluigi if (a->tv_nsec >= b->tv_nsec) 825251132Sluigi return (1); 826251132Sluigi return (0); 827251132Sluigi} 828251132Sluigi 829251132Sluigistatic __inline struct timespec 830251132Sluigitimeval2spec(const struct timeval *a) 831251132Sluigi{ 832251132Sluigi struct timespec ts = { 833251132Sluigi .tv_sec = a->tv_sec, 834251132Sluigi .tv_nsec = a->tv_usec * 1000 835251132Sluigi }; 836251132Sluigi return ts; 837251132Sluigi} 838251132Sluigi 839251132Sluigistatic __inline struct timeval 840251132Sluigitimespec2val(const struct timespec *a) 841251132Sluigi{ 842251132Sluigi struct timeval tv = { 843251132Sluigi .tv_sec = a->tv_sec, 844251132Sluigi .tv_usec = a->tv_nsec / 1000 845251132Sluigi }; 846251132Sluigi return tv; 847251132Sluigi} 848251132Sluigi 849251132Sluigi 850251132Sluigistatic int 851251132Sluigiwait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited) 852251132Sluigi{ 853251132Sluigi struct timespec curtime; 854251132Sluigi 855251132Sluigi curtime.tv_sec = 0; 856251132Sluigi curtime.tv_nsec = 0; 857251132Sluigi 858251132Sluigi if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) { 859251132Sluigi D("clock_gettime: %s", strerror(errno)); 860251132Sluigi return (-1); 861251132Sluigi } 862251132Sluigi while (timespec_ge(&ts, &curtime)) { 863251132Sluigi if (waited != NULL) 864251132Sluigi (*waited)++; 865251132Sluigi if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) { 866251132Sluigi D("clock_gettime"); 867251132Sluigi return (-1); 868251132Sluigi } 869251132Sluigi } 870251132Sluigi if (wakeup_ts != NULL) 871251132Sluigi *wakeup_ts = curtime; 872251132Sluigi return (0); 873251132Sluigi} 874251132Sluigi 875251132Sluigistatic __inline void 876251132Sluigitimespec_add(struct timespec *tsa, struct timespec *tsb) 877251132Sluigi{ 878251132Sluigi tsa->tv_sec += tsb->tv_sec; 879251132Sluigi tsa->tv_nsec += tsb->tv_nsec; 880251132Sluigi if (tsa->tv_nsec >= 1000000000) { 881251132Sluigi tsa->tv_sec++; 882251132Sluigi tsa->tv_nsec -= 1000000000; 883251132Sluigi } 884251132Sluigi} 885251132Sluigi 886251132Sluigi 887246896Sluigistatic void * 888227614Sluigisender_body(void *data) 889227614Sluigi{ 890227614Sluigi struct targ *targ = (struct targ *) data; 891246896Sluigi 892227614Sluigi struct pollfd fds[1]; 893227614Sluigi struct netmap_if *nifp = targ->nifp; 894227614Sluigi struct netmap_ring *txring; 895246896Sluigi int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 896234956Sluigi int options = targ->g->options | OPT_COPY; 897251132Sluigi struct timespec tmptime, nexttime = { 0, 0}; // XXX silence compiler 898251132Sluigi int rate_limit = targ->g->tx_rate; 899251132Sluigi long long waited = 0; 900251426Sluigi 901251132Sluigi D("start"); 902227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 903227614Sluigi goto quit; 904228975Suqs /* setup poll(2) mechanism. */ 905227614Sluigi memset(fds, 0, sizeof(fds)); 906227614Sluigi fds[0].fd = targ->fd; 907227614Sluigi fds[0].events = (POLLOUT); 908227614Sluigi 909227614Sluigi /* main loop.*/ 910251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 911251132Sluigi if (rate_limit) { 912251132Sluigi tmptime.tv_sec = 2; 913251132Sluigi tmptime.tv_nsec = 0; 914251132Sluigi timespec_add(&targ->tic, &tmptime); 915251132Sluigi targ->tic.tv_nsec = 0; 916251132Sluigi if (wait_time(targ->tic, NULL, NULL) == -1) { 917251132Sluigi D("wait_time: %s", strerror(errno)); 918251132Sluigi goto quit; 919251132Sluigi } 920251132Sluigi nexttime = targ->tic; 921251132Sluigi } 922246896Sluigi if (targ->g->dev_type == DEV_PCAP) { 923246896Sluigi int size = targ->g->pkt_size; 924246896Sluigi void *pkt = &targ->pkt; 925246896Sluigi pcap_t *p = targ->g->p; 926246896Sluigi 927246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 928234956Sluigi if (pcap_inject(p, pkt, size) != -1) 929234956Sluigi sent++; 930257529Sluigi update_addresses(pkt, targ->g); 931234956Sluigi if (i > 10000) { 932234956Sluigi targ->count = sent; 933234956Sluigi i = 0; 934234956Sluigi } 935246896Sluigi } 936246896Sluigi } else if (targ->g->dev_type == DEV_TAP) { /* tap */ 937246896Sluigi int size = targ->g->pkt_size; 938246896Sluigi void *pkt = &targ->pkt; 939246896Sluigi D("writing to file desc %d", targ->g->main_fd); 940246896Sluigi 941246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 942246896Sluigi if (write(targ->g->main_fd, pkt, size) != -1) 943246896Sluigi sent++; 944257529Sluigi update_addresses(pkt, targ->g); 945246896Sluigi if (i > 10000) { 946246896Sluigi targ->count = sent; 947246896Sluigi i = 0; 948246896Sluigi } 949246896Sluigi } 950227614Sluigi } else { 951251132Sluigi int tosend = 0; 952257529Sluigi int frags = targ->g->frags; 953257529Sluigi 954246896Sluigi while (!targ->cancel && (n == 0 || sent < n)) { 955227614Sluigi 956251132Sluigi if (rate_limit && tosend <= 0) { 957251132Sluigi tosend = targ->g->burst; 958251132Sluigi timespec_add(&nexttime, &targ->g->tx_period); 959251132Sluigi if (wait_time(nexttime, &tmptime, &waited) == -1) { 960251132Sluigi D("wait_time"); 961251132Sluigi goto quit; 962251132Sluigi } 963251132Sluigi } 964251132Sluigi 965227614Sluigi /* 966227614Sluigi * wait for available room in the send queue(s) 967227614Sluigi */ 968246896Sluigi if (poll(fds, 1, 2000) <= 0) { 969238165Semaste if (targ->cancel) 970238165Semaste break; 971246896Sluigi D("poll error/timeout on queue %d", targ->me); 972227614Sluigi goto quit; 973227614Sluigi } 974227614Sluigi /* 975227614Sluigi * scan our queues and send on those with room 976227614Sluigi */ 977246896Sluigi if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 978246896Sluigi D("drop copy"); 979234956Sluigi options &= ~OPT_COPY; 980246896Sluigi } 981246896Sluigi for (i = targ->qfirst; i < targ->qlast; i++) { 982251132Sluigi int m, limit = rate_limit ? tosend : targ->g->burst; 983246896Sluigi if (n > 0 && n - sent < limit) 984246896Sluigi limit = n - sent; 985227614Sluigi txring = NETMAP_TXRING(nifp, i); 986227614Sluigi if (txring->avail == 0) 987227614Sluigi continue; 988257529Sluigi if (frags > 1) 989257529Sluigi limit = ((limit + frags - 1) / frags) * frags; 990257529Sluigi 991257529Sluigi m = send_packets(txring, &targ->pkt, targ->g, 992257529Sluigi limit, options, frags); 993257529Sluigi ND("limit %d avail %d frags %d m %d", 994257529Sluigi limit, txring->avail, frags, m); 995227614Sluigi sent += m; 996227614Sluigi targ->count = sent; 997257529Sluigi if (rate_limit) { 998257529Sluigi tosend -= m; 999257529Sluigi if (tosend <= 0) 1000257529Sluigi break; 1001257529Sluigi } 1002227614Sluigi } 1003227614Sluigi } 1004234956Sluigi /* flush any remaining packets */ 1005227614Sluigi ioctl(fds[0].fd, NIOCTXSYNC, NULL); 1006227614Sluigi 1007227614Sluigi /* final part: wait all the TX queues to be empty. */ 1008227614Sluigi for (i = targ->qfirst; i < targ->qlast; i++) { 1009227614Sluigi txring = NETMAP_TXRING(nifp, i); 1010227614Sluigi while (!NETMAP_TX_RING_EMPTY(txring)) { 1011227614Sluigi ioctl(fds[0].fd, NIOCTXSYNC, NULL); 1012227614Sluigi usleep(1); /* wait 1 tick */ 1013227614Sluigi } 1014227614Sluigi } 1015227614Sluigi } 1016227614Sluigi 1017251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1018227614Sluigi targ->completed = 1; 1019227614Sluigi targ->count = sent; 1020227614Sluigi 1021227614Sluigiquit: 1022227614Sluigi /* reset the ``used`` flag. */ 1023227614Sluigi targ->used = 0; 1024227614Sluigi 1025227614Sluigi return (NULL); 1026227614Sluigi} 1027227614Sluigi 1028227614Sluigi 1029227614Sluigistatic void 1030246896Sluigireceive_pcap(u_char *user, const struct pcap_pkthdr * h, 1031246896Sluigi const u_char * bytes) 1032227614Sluigi{ 1033227614Sluigi int *count = (int *)user; 1034246896Sluigi (void)h; /* UNUSED */ 1035246896Sluigi (void)bytes; /* UNUSED */ 1036227614Sluigi (*count)++; 1037227614Sluigi} 1038227614Sluigi 1039227614Sluigistatic int 1040251426Sluigireceive_packets(struct netmap_ring *ring, u_int limit, int dump) 1041227614Sluigi{ 1042227614Sluigi u_int cur, rx; 1043227614Sluigi 1044227614Sluigi cur = ring->cur; 1045227614Sluigi if (ring->avail < limit) 1046227614Sluigi limit = ring->avail; 1047227614Sluigi for (rx = 0; rx < limit; rx++) { 1048227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 1049227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 1050227614Sluigi 1051251426Sluigi if (dump) 1052251426Sluigi dump_payload(p, slot->len, ring, cur); 1053227614Sluigi 1054227614Sluigi cur = NETMAP_RING_NEXT(ring, cur); 1055227614Sluigi } 1056227614Sluigi ring->avail -= rx; 1057227614Sluigi ring->cur = cur; 1058227614Sluigi 1059227614Sluigi return (rx); 1060227614Sluigi} 1061227614Sluigi 1062227614Sluigistatic void * 1063227614Sluigireceiver_body(void *data) 1064227614Sluigi{ 1065227614Sluigi struct targ *targ = (struct targ *) data; 1066227614Sluigi struct pollfd fds[1]; 1067227614Sluigi struct netmap_if *nifp = targ->nifp; 1068227614Sluigi struct netmap_ring *rxring; 1069246896Sluigi int i; 1070246896Sluigi uint64_t received = 0; 1071227614Sluigi 1072227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1073227614Sluigi goto quit; 1074227614Sluigi 1075228975Suqs /* setup poll(2) mechanism. */ 1076227614Sluigi memset(fds, 0, sizeof(fds)); 1077227614Sluigi fds[0].fd = targ->fd; 1078227614Sluigi fds[0].events = (POLLIN); 1079227614Sluigi 1080227614Sluigi /* unbounded wait for the first packet. */ 1081246896Sluigi for (;;) { 1082227614Sluigi i = poll(fds, 1, 1000); 1083227614Sluigi if (i > 0 && !(fds[0].revents & POLLERR)) 1084227614Sluigi break; 1085227614Sluigi D("waiting for initial packets, poll returns %d %d", i, fds[0].revents); 1086227614Sluigi } 1087227614Sluigi 1088227614Sluigi /* main loop, exit after 1s silence */ 1089251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1090246896Sluigi if (targ->g->dev_type == DEV_PCAP) { 1091238165Semaste while (!targ->cancel) { 1092246896Sluigi /* XXX should we poll ? */ 1093227614Sluigi pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 1094227614Sluigi } 1095246896Sluigi } else if (targ->g->dev_type == DEV_TAP) { 1096246896Sluigi D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd); 1097246896Sluigi while (!targ->cancel) { 1098246896Sluigi char buf[2048]; 1099246896Sluigi /* XXX should we poll ? */ 1100246896Sluigi if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 1101246896Sluigi targ->count++; 1102246896Sluigi } 1103227614Sluigi } else { 1104251426Sluigi int dump = targ->g->options & OPT_DUMP; 1105238165Semaste while (!targ->cancel) { 1106227614Sluigi /* Once we started to receive packets, wait at most 1 seconds 1107227614Sluigi before quitting. */ 1108251132Sluigi if (poll(fds, 1, 1 * 1000) <= 0 && !targ->g->forever) { 1109251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1110228975Suqs targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 1111227614Sluigi break; 1112227614Sluigi } 1113227614Sluigi 1114227614Sluigi for (i = targ->qfirst; i < targ->qlast; i++) { 1115227614Sluigi int m; 1116227614Sluigi 1117227614Sluigi rxring = NETMAP_RXRING(nifp, i); 1118227614Sluigi if (rxring->avail == 0) 1119227614Sluigi continue; 1120227614Sluigi 1121251426Sluigi m = receive_packets(rxring, targ->g->burst, dump); 1122227614Sluigi received += m; 1123227614Sluigi } 1124246896Sluigi targ->count = received; 1125227614Sluigi 1126227614Sluigi // tell the card we have read the data 1127227614Sluigi //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 1128227614Sluigi } 1129227614Sluigi } 1130227614Sluigi 1131227614Sluigi targ->completed = 1; 1132227614Sluigi targ->count = received; 1133227614Sluigi 1134227614Sluigiquit: 1135227614Sluigi /* reset the ``used`` flag. */ 1136227614Sluigi targ->used = 0; 1137227614Sluigi 1138227614Sluigi return (NULL); 1139227614Sluigi} 1140227614Sluigi 1141246896Sluigi/* very crude code to print a number in normalized form. 1142246896Sluigi * Caller has to make sure that the buffer is large enough. 1143246896Sluigi */ 1144246896Sluigistatic const char * 1145246896Sluiginorm(char *buf, double val) 1146238170Semaste{ 1147246896Sluigi char *units[] = { "", "K", "M", "G" }; 1148246896Sluigi u_int i; 1149238170Semaste 1150246896Sluigi for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++) 1151238170Semaste val /= 1000; 1152246896Sluigi sprintf(buf, "%.2f %s", val, units[i]); 1153246896Sluigi return buf; 1154238170Semaste} 1155238170Semaste 1156227614Sluigistatic void 1157227614Sluigitx_output(uint64_t sent, int size, double delta) 1158227614Sluigi{ 1159246896Sluigi double bw, raw_bw, pps; 1160246896Sluigi char b1[40], b2[80], b3[80]; 1161227614Sluigi 1162228276Sluigi printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n", 1163227614Sluigi sent, size, delta); 1164246896Sluigi if (delta == 0) 1165246896Sluigi delta = 1e-6; 1166246896Sluigi if (size < 60) /* correct for min packet size */ 1167246896Sluigi size = 60; 1168246896Sluigi pps = sent / delta; 1169246896Sluigi bw = (8.0 * size * sent) / delta; 1170246896Sluigi /* raw packets have4 bytes crc + 20 bytes framing */ 1171246896Sluigi raw_bw = (8.0 * (size + 24) * sent) / delta; 1172238170Semaste 1173246896Sluigi printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1174246896Sluigi norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 1175227614Sluigi} 1176227614Sluigi 1177227614Sluigi 1178227614Sluigistatic void 1179227614Sluigirx_output(uint64_t received, double delta) 1180227614Sluigi{ 1181246896Sluigi double pps; 1182246896Sluigi char b1[40]; 1183227614Sluigi 1184246896Sluigi printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta); 1185227614Sluigi 1186246896Sluigi if (delta == 0) 1187246896Sluigi delta = 1e-6; 1188246896Sluigi pps = received / delta; 1189246896Sluigi printf("Speed: %spps\n", norm(b1, pps)); 1190227614Sluigi} 1191227614Sluigi 1192227614Sluigistatic void 1193227614Sluigiusage(void) 1194227614Sluigi{ 1195227614Sluigi const char *cmd = "pkt-gen"; 1196227614Sluigi fprintf(stderr, 1197227614Sluigi "Usage:\n" 1198227614Sluigi "%s arguments\n" 1199227614Sluigi "\t-i interface interface name\n" 1200246896Sluigi "\t-f function tx rx ping pong\n" 1201246896Sluigi "\t-n count number of iterations (can be 0)\n" 1202246896Sluigi "\t-t pkts_to_send also forces tx mode\n" 1203246896Sluigi "\t-r pkts_to_receive also forces rx mode\n" 1204257529Sluigi "\t-l pkt_size in bytes excluding CRC\n" 1205257529Sluigi "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1206257529Sluigi "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1207257529Sluigi "\t-D dst-mac\n" 1208257529Sluigi "\t-S src-mac\n" 1209246896Sluigi "\t-a cpu_id use setaffinity\n" 1210227614Sluigi "\t-b burst size testing, mostly\n" 1211227614Sluigi "\t-c cores cores to use\n" 1212227614Sluigi "\t-p threads processes/threads to use\n" 1213227614Sluigi "\t-T report_ms milliseconds between reports\n" 1214257529Sluigi "\t-P use libpcap instead of netmap\n" 1215227614Sluigi "\t-w wait_for_link_time in seconds\n" 1216257529Sluigi "\t-R rate in packets per second\n" 1217257529Sluigi "\t-X dump payload\n" 1218227614Sluigi "", 1219227614Sluigi cmd); 1220227614Sluigi 1221227614Sluigi exit(0); 1222227614Sluigi} 1223227614Sluigi 1224246896Sluigistatic void 1225246896Sluigistart_threads(struct glob_arg *g) 1226246896Sluigi{ 1227246896Sluigi int i; 1228227614Sluigi 1229246896Sluigi targs = calloc(g->nthreads, sizeof(*targs)); 1230246896Sluigi /* 1231246896Sluigi * Now create the desired number of threads, each one 1232246896Sluigi * using a single descriptor. 1233246896Sluigi */ 1234246896Sluigi for (i = 0; i < g->nthreads; i++) { 1235246896Sluigi bzero(&targs[i], sizeof(targs[i])); 1236246896Sluigi targs[i].fd = -1; /* default, with pcap */ 1237246896Sluigi targs[i].g = g; 1238246896Sluigi 1239246896Sluigi if (g->dev_type == DEV_NETMAP) { 1240246896Sluigi struct nmreq tifreq; 1241246896Sluigi int tfd; 1242246896Sluigi 1243246896Sluigi /* register interface. */ 1244246896Sluigi tfd = open("/dev/netmap", O_RDWR); 1245246896Sluigi if (tfd == -1) { 1246246896Sluigi D("Unable to open /dev/netmap"); 1247246896Sluigi continue; 1248246896Sluigi } 1249246896Sluigi targs[i].fd = tfd; 1250246896Sluigi 1251246896Sluigi bzero(&tifreq, sizeof(tifreq)); 1252246896Sluigi strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name)); 1253246896Sluigi tifreq.nr_version = NETMAP_API; 1254246896Sluigi tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 1255257529Sluigi parse_nmr_config(g->nmr_config, &tifreq); 1256246896Sluigi 1257246896Sluigi /* 1258246896Sluigi * if we are acting as a receiver only, do not touch the transmit ring. 1259246896Sluigi * This is not the default because many apps may use the interface 1260246896Sluigi * in both directions, but a pure receiver does not. 1261246896Sluigi */ 1262246896Sluigi if (g->td_body == receiver_body) { 1263246896Sluigi tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 1264246896Sluigi } 1265246896Sluigi 1266246896Sluigi if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 1267246896Sluigi D("Unable to register %s", g->ifname); 1268246896Sluigi continue; 1269246896Sluigi } 1270257529Sluigi D("memsize is %d MB", tifreq.nr_memsize >> 20); 1271246896Sluigi targs[i].nmr = tifreq; 1272246896Sluigi targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset); 1273257529Sluigi D("nifp flags 0x%x", targs[i].nifp->ni_flags); 1274246896Sluigi /* start threads. */ 1275246896Sluigi targs[i].qfirst = (g->nthreads > 1) ? i : 0; 1276246896Sluigi targs[i].qlast = (g->nthreads > 1) ? i+1 : 1277246896Sluigi (g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings); 1278246896Sluigi } else { 1279246896Sluigi targs[i].fd = g->main_fd; 1280246896Sluigi } 1281246896Sluigi targs[i].used = 1; 1282246896Sluigi targs[i].me = i; 1283246896Sluigi if (g->affinity >= 0) { 1284246896Sluigi if (g->affinity < g->cpus) 1285246896Sluigi targs[i].affinity = g->affinity; 1286246896Sluigi else 1287246896Sluigi targs[i].affinity = i % g->cpus; 1288246896Sluigi } else 1289246896Sluigi targs[i].affinity = -1; 1290246896Sluigi /* default, init packets */ 1291246896Sluigi initialize_packet(&targs[i]); 1292246896Sluigi 1293246896Sluigi if (pthread_create(&targs[i].thread, NULL, g->td_body, 1294246896Sluigi &targs[i]) == -1) { 1295246896Sluigi D("Unable to create thread %d", i); 1296246896Sluigi targs[i].used = 0; 1297246896Sluigi } 1298246896Sluigi } 1299246896Sluigi} 1300246896Sluigi 1301246896Sluigistatic void 1302246896Sluigimain_thread(struct glob_arg *g) 1303246896Sluigi{ 1304246896Sluigi int i; 1305246896Sluigi 1306246896Sluigi uint64_t prev = 0; 1307246896Sluigi uint64_t count = 0; 1308246896Sluigi double delta_t; 1309246896Sluigi struct timeval tic, toc; 1310246896Sluigi 1311246896Sluigi gettimeofday(&toc, NULL); 1312246896Sluigi for (;;) { 1313246896Sluigi struct timeval now, delta; 1314246896Sluigi uint64_t pps, usec, my_count, npkts; 1315246896Sluigi int done = 0; 1316246896Sluigi 1317246896Sluigi delta.tv_sec = g->report_interval/1000; 1318246896Sluigi delta.tv_usec = (g->report_interval%1000)*1000; 1319246896Sluigi select(0, NULL, NULL, NULL, &delta); 1320246896Sluigi gettimeofday(&now, NULL); 1321246896Sluigi time_second = now.tv_sec; 1322246896Sluigi timersub(&now, &toc, &toc); 1323246896Sluigi my_count = 0; 1324246896Sluigi for (i = 0; i < g->nthreads; i++) { 1325246896Sluigi my_count += targs[i].count; 1326246896Sluigi if (targs[i].used == 0) 1327246896Sluigi done++; 1328246896Sluigi } 1329246896Sluigi usec = toc.tv_sec* 1000000 + toc.tv_usec; 1330246896Sluigi if (usec < 10000) 1331246896Sluigi continue; 1332246896Sluigi npkts = my_count - prev; 1333246896Sluigi pps = (npkts*1000000 + usec/2) / usec; 1334246896Sluigi D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)", 1335246896Sluigi pps, npkts, usec); 1336246896Sluigi prev = my_count; 1337246896Sluigi toc = now; 1338246896Sluigi if (done == g->nthreads) 1339246896Sluigi break; 1340246896Sluigi } 1341246896Sluigi 1342246896Sluigi timerclear(&tic); 1343246896Sluigi timerclear(&toc); 1344246896Sluigi for (i = 0; i < g->nthreads; i++) { 1345251132Sluigi struct timespec t_tic, t_toc; 1346246896Sluigi /* 1347246896Sluigi * Join active threads, unregister interfaces and close 1348246896Sluigi * file descriptors. 1349246896Sluigi */ 1350251132Sluigi if (targs[i].used) 1351251132Sluigi pthread_join(targs[i].thread, NULL); 1352246896Sluigi close(targs[i].fd); 1353246896Sluigi 1354246896Sluigi if (targs[i].completed == 0) 1355246896Sluigi D("ouch, thread %d exited with error", i); 1356246896Sluigi 1357246896Sluigi /* 1358246896Sluigi * Collect threads output and extract information about 1359246896Sluigi * how long it took to send all the packets. 1360246896Sluigi */ 1361246896Sluigi count += targs[i].count; 1362251132Sluigi t_tic = timeval2spec(&tic); 1363251132Sluigi t_toc = timeval2spec(&toc); 1364251132Sluigi if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 1365251132Sluigi tic = timespec2val(&targs[i].tic); 1366251132Sluigi if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 1367251132Sluigi toc = timespec2val(&targs[i].toc); 1368246896Sluigi } 1369246896Sluigi 1370246896Sluigi /* print output. */ 1371246896Sluigi timersub(&toc, &tic, &toc); 1372246896Sluigi delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1373246896Sluigi if (g->td_body == sender_body) 1374246896Sluigi tx_output(count, g->pkt_size, delta_t); 1375246896Sluigi else 1376246896Sluigi rx_output(count, delta_t); 1377246896Sluigi 1378246896Sluigi if (g->dev_type == DEV_NETMAP) { 1379246896Sluigi munmap(g->mmap_addr, g->mmap_size); 1380246896Sluigi close(g->main_fd); 1381246896Sluigi } 1382246896Sluigi} 1383246896Sluigi 1384246896Sluigi 1385246896Sluigistruct sf { 1386246896Sluigi char *key; 1387246896Sluigi void *f; 1388246896Sluigi}; 1389246896Sluigi 1390246896Sluigistatic struct sf func[] = { 1391246896Sluigi { "tx", sender_body }, 1392246896Sluigi { "rx", receiver_body }, 1393246896Sluigi { "ping", pinger_body }, 1394246896Sluigi { "pong", ponger_body }, 1395246896Sluigi { NULL, NULL } 1396246896Sluigi}; 1397246896Sluigi 1398246896Sluigistatic int 1399246896Sluigitap_alloc(char *dev) 1400246896Sluigi{ 1401246896Sluigi struct ifreq ifr; 1402246896Sluigi int fd, err; 1403246896Sluigi char *clonedev = TAP_CLONEDEV; 1404246896Sluigi 1405246896Sluigi (void)err; 1406246896Sluigi (void)dev; 1407246896Sluigi /* Arguments taken by the function: 1408246896Sluigi * 1409246896Sluigi * char *dev: the name of an interface (or '\0'). MUST have enough 1410246896Sluigi * space to hold the interface name if '\0' is passed 1411246896Sluigi * int flags: interface flags (eg, IFF_TUN etc.) 1412246896Sluigi */ 1413246896Sluigi 1414246896Sluigi#ifdef __FreeBSD__ 1415246896Sluigi if (dev[3]) { /* tapSomething */ 1416246896Sluigi static char buf[128]; 1417246896Sluigi snprintf(buf, sizeof(buf), "/dev/%s", dev); 1418246896Sluigi clonedev = buf; 1419246896Sluigi } 1420246896Sluigi#endif 1421246896Sluigi /* open the device */ 1422246896Sluigi if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1423246896Sluigi return fd; 1424246896Sluigi } 1425246896Sluigi D("%s open successful", clonedev); 1426246896Sluigi 1427246896Sluigi /* preparation of the struct ifr, of type "struct ifreq" */ 1428246896Sluigi memset(&ifr, 0, sizeof(ifr)); 1429246896Sluigi 1430246896Sluigi#ifdef linux 1431246896Sluigi ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1432246896Sluigi 1433246896Sluigi if (*dev) { 1434246896Sluigi /* if a device name was specified, put it in the structure; otherwise, 1435246896Sluigi * the kernel will try to allocate the "next" device of the 1436246896Sluigi * specified type */ 1437246896Sluigi strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1438246896Sluigi } 1439246896Sluigi 1440246896Sluigi /* try to create the device */ 1441246896Sluigi if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 1442246896Sluigi D("failed to to a TUNSETIFF"); 1443246896Sluigi close(fd); 1444246896Sluigi return err; 1445246896Sluigi } 1446246896Sluigi 1447246896Sluigi /* if the operation was successful, write back the name of the 1448246896Sluigi * interface to the variable "dev", so the caller can know 1449246896Sluigi * it. Note that the caller MUST reserve space in *dev (see calling 1450246896Sluigi * code below) */ 1451246896Sluigi strcpy(dev, ifr.ifr_name); 1452246896Sluigi D("new name is %s", dev); 1453246896Sluigi#endif /* linux */ 1454246896Sluigi 1455246896Sluigi /* this is the special file descriptor that the caller will use to talk 1456246896Sluigi * with the virtual interface */ 1457246896Sluigi return fd; 1458246896Sluigi} 1459246896Sluigi 1460227614Sluigiint 1461227614Sluigimain(int arc, char **argv) 1462227614Sluigi{ 1463246896Sluigi int i; 1464227614Sluigi 1465227614Sluigi struct glob_arg g; 1466227614Sluigi 1467227614Sluigi struct nmreq nmr; 1468227614Sluigi int ch; 1469227614Sluigi int wait_link = 2; 1470227614Sluigi int devqueues = 1; /* how many device queues */ 1471227614Sluigi 1472227614Sluigi bzero(&g, sizeof(g)); 1473227614Sluigi 1474246896Sluigi g.main_fd = -1; 1475246896Sluigi g.td_body = receiver_body; 1476246896Sluigi g.report_interval = 1000; /* report interval */ 1477246896Sluigi g.affinity = -1; 1478246896Sluigi /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1479246896Sluigi g.src_ip.name = "10.0.0.1"; 1480246896Sluigi g.dst_ip.name = "10.1.0.1"; 1481246896Sluigi g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1482246896Sluigi g.src_mac.name = NULL; 1483227614Sluigi g.pkt_size = 60; 1484227614Sluigi g.burst = 512; // default 1485227614Sluigi g.nthreads = 1; 1486227614Sluigi g.cpus = 1; 1487251426Sluigi g.forever = 1; 1488251132Sluigi g.tx_rate = 0; 1489257529Sluigi g.frags = 1; 1490257529Sluigi g.nmr_config = ""; 1491227614Sluigi 1492227614Sluigi while ( (ch = getopt(arc, argv, 1493257529Sluigi "a:f:F:n:i:It:r:l:d:s:D:S:b:c:o:p:PT:w:WvR:XC:")) != -1) { 1494246896Sluigi struct sf *fn; 1495246896Sluigi 1496227614Sluigi switch(ch) { 1497227614Sluigi default: 1498227614Sluigi D("bad option %c %s", ch, optarg); 1499227614Sluigi usage(); 1500227614Sluigi break; 1501246896Sluigi 1502246896Sluigi case 'n': 1503246896Sluigi g.npackets = atoi(optarg); 1504246896Sluigi break; 1505246896Sluigi 1506257529Sluigi case 'F': 1507257529Sluigi i = atoi(optarg); 1508257529Sluigi if (i < 1 || i > 63) { 1509257529Sluigi D("invalid frags %d [1..63], ignore", i); 1510257529Sluigi break; 1511257529Sluigi } 1512257529Sluigi g.frags = i; 1513257529Sluigi break; 1514257529Sluigi 1515246896Sluigi case 'f': 1516246896Sluigi for (fn = func; fn->key; fn++) { 1517246896Sluigi if (!strcmp(fn->key, optarg)) 1518246896Sluigi break; 1519246896Sluigi } 1520246896Sluigi if (fn->key) 1521246896Sluigi g.td_body = fn->f; 1522246896Sluigi else 1523246896Sluigi D("unrecognised function %s", optarg); 1524246896Sluigi break; 1525246896Sluigi 1526246896Sluigi case 'o': /* data generation options */ 1527234956Sluigi g.options = atoi(optarg); 1528234956Sluigi break; 1529246896Sluigi 1530246896Sluigi case 'a': /* force affinity */ 1531246896Sluigi g.affinity = atoi(optarg); 1532246896Sluigi break; 1533246896Sluigi 1534227614Sluigi case 'i': /* interface */ 1535246896Sluigi g.ifname = optarg; 1536246896Sluigi if (!strncmp(optarg, "tap", 3)) 1537246896Sluigi g.dev_type = DEV_TAP; 1538246896Sluigi else 1539246896Sluigi g.dev_type = DEV_NETMAP; 1540257529Sluigi if (!strcmp(g.ifname, "null")) 1541257529Sluigi g.dummy_send = 1; 1542227614Sluigi break; 1543246896Sluigi 1544251426Sluigi case 'I': 1545251426Sluigi g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1546251426Sluigi break; 1547251426Sluigi 1548246896Sluigi case 't': /* send, deprecated */ 1549246896Sluigi D("-t deprecated, please use -f tx -n %s", optarg); 1550246896Sluigi g.td_body = sender_body; 1551227614Sluigi g.npackets = atoi(optarg); 1552227614Sluigi break; 1553246896Sluigi 1554227614Sluigi case 'r': /* receive */ 1555246896Sluigi D("-r deprecated, please use -f rx -n %s", optarg); 1556246896Sluigi g.td_body = receiver_body; 1557227614Sluigi g.npackets = atoi(optarg); 1558227614Sluigi break; 1559246896Sluigi 1560227614Sluigi case 'l': /* pkt_size */ 1561227614Sluigi g.pkt_size = atoi(optarg); 1562227614Sluigi break; 1563246896Sluigi 1564227614Sluigi case 'd': 1565246896Sluigi g.dst_ip.name = optarg; 1566227614Sluigi break; 1567246896Sluigi 1568227614Sluigi case 's': 1569246896Sluigi g.src_ip.name = optarg; 1570227614Sluigi break; 1571246896Sluigi 1572227614Sluigi case 'T': /* report interval */ 1573246896Sluigi g.report_interval = atoi(optarg); 1574227614Sluigi break; 1575246896Sluigi 1576227614Sluigi case 'w': 1577227614Sluigi wait_link = atoi(optarg); 1578227614Sluigi break; 1579246896Sluigi 1580251426Sluigi case 'W': /* XXX changed default */ 1581251426Sluigi g.forever = 0; /* do not exit rx even with no traffic */ 1582246896Sluigi break; 1583246896Sluigi 1584227614Sluigi case 'b': /* burst */ 1585227614Sluigi g.burst = atoi(optarg); 1586227614Sluigi break; 1587227614Sluigi case 'c': 1588227614Sluigi g.cpus = atoi(optarg); 1589227614Sluigi break; 1590227614Sluigi case 'p': 1591227614Sluigi g.nthreads = atoi(optarg); 1592227614Sluigi break; 1593227614Sluigi 1594227614Sluigi case 'P': 1595246896Sluigi g.dev_type = DEV_PCAP; 1596227614Sluigi break; 1597227614Sluigi 1598227614Sluigi case 'D': /* destination mac */ 1599246896Sluigi g.dst_mac.name = optarg; 1600227614Sluigi break; 1601246896Sluigi 1602227614Sluigi case 'S': /* source mac */ 1603246896Sluigi g.src_mac.name = optarg; 1604227614Sluigi break; 1605227614Sluigi case 'v': 1606227614Sluigi verbose++; 1607251132Sluigi break; 1608251132Sluigi case 'R': 1609251132Sluigi g.tx_rate = atoi(optarg); 1610251132Sluigi break; 1611251426Sluigi case 'X': 1612251426Sluigi g.options |= OPT_DUMP; 1613257529Sluigi break; 1614257529Sluigi case 'C': 1615257529Sluigi g.nmr_config = strdup(optarg); 1616227614Sluigi } 1617227614Sluigi } 1618227614Sluigi 1619246896Sluigi if (g.ifname == NULL) { 1620227614Sluigi D("missing ifname"); 1621227614Sluigi usage(); 1622227614Sluigi } 1623246896Sluigi 1624246896Sluigi i = system_ncpus(); 1625246896Sluigi if (g.cpus < 0 || g.cpus > i) { 1626246896Sluigi D("%d cpus is too high, have only %d cpus", g.cpus, i); 1627246896Sluigi usage(); 1628227614Sluigi } 1629246896Sluigi if (g.cpus == 0) 1630246896Sluigi g.cpus = i; 1631246896Sluigi 1632227614Sluigi if (g.pkt_size < 16 || g.pkt_size > 1536) { 1633227614Sluigi D("bad pktsize %d\n", g.pkt_size); 1634227614Sluigi usage(); 1635227614Sluigi } 1636227614Sluigi 1637246896Sluigi if (g.src_mac.name == NULL) { 1638246896Sluigi static char mybuf[20] = "00:00:00:00:00:00"; 1639234956Sluigi /* retrieve source mac address. */ 1640246896Sluigi if (source_hwaddr(g.ifname, mybuf) == -1) { 1641234956Sluigi D("Unable to retrieve source mac"); 1642234956Sluigi // continue, fail later 1643234956Sluigi } 1644246896Sluigi g.src_mac.name = mybuf; 1645234956Sluigi } 1646246896Sluigi /* extract address ranges */ 1647246896Sluigi extract_ip_range(&g.src_ip); 1648246896Sluigi extract_ip_range(&g.dst_ip); 1649246896Sluigi extract_mac_range(&g.src_mac); 1650246896Sluigi extract_mac_range(&g.dst_mac); 1651234956Sluigi 1652246896Sluigi if (g.dev_type == DEV_TAP) { 1653246896Sluigi D("want to use tap %s", g.ifname); 1654246896Sluigi g.main_fd = tap_alloc(g.ifname); 1655246896Sluigi if (g.main_fd < 0) { 1656246896Sluigi D("cannot open tap %s", g.ifname); 1657246896Sluigi usage(); 1658246896Sluigi } 1659246896Sluigi } else if (g.dev_type > DEV_NETMAP) { 1660246896Sluigi char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1661246896Sluigi 1662246896Sluigi D("using pcap on %s", g.ifname); 1663246896Sluigi pcap_errbuf[0] = '\0'; // init the buffer 1664246896Sluigi g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf); 1665234956Sluigi if (g.p == NULL) { 1666246896Sluigi D("cannot open pcap on %s", g.ifname); 1667234956Sluigi usage(); 1668234956Sluigi } 1669257529Sluigi } else if (g.dummy_send) { 1670257529Sluigi D("using a dummy send routine"); 1671234956Sluigi } else { 1672227614Sluigi bzero(&nmr, sizeof(nmr)); 1673232238Sluigi nmr.nr_version = NETMAP_API; 1674227614Sluigi /* 1675227614Sluigi * Open the netmap device to fetch the number of queues of our 1676227614Sluigi * interface. 1677227614Sluigi * 1678227614Sluigi * The first NIOCREGIF also detaches the card from the 1679227614Sluigi * protocol stack and may cause a reset of the card, 1680227614Sluigi * which in turn may take some time for the PHY to 1681227614Sluigi * reconfigure. 1682227614Sluigi */ 1683246896Sluigi g.main_fd = open("/dev/netmap", O_RDWR); 1684246896Sluigi if (g.main_fd == -1) { 1685227614Sluigi D("Unable to open /dev/netmap"); 1686246896Sluigi // fail later 1687227614Sluigi } 1688257529Sluigi /* 1689257529Sluigi * Register the interface on the netmap device: from now on, 1690257529Sluigi * we can operate on the network interface without any 1691257529Sluigi * interference from the legacy network stack. 1692257529Sluigi * 1693257529Sluigi * We decide to put the first interface registration here to 1694257529Sluigi * give time to cards that take a long time to reset the PHY. 1695257529Sluigi */ 1696257529Sluigi bzero(&nmr, sizeof(nmr)); 1697257529Sluigi nmr.nr_version = NETMAP_API; 1698257529Sluigi strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name)); 1699257529Sluigi nmr.nr_version = NETMAP_API; 1700257529Sluigi parse_nmr_config(g.nmr_config, &nmr); 1701257529Sluigi if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) { 1702257529Sluigi D("Unable to register interface %s", g.ifname); 1703257529Sluigi //continue, fail later 1704257529Sluigi } 1705257529Sluigi ND("%s: txr %d txd %d rxr %d rxd %d", g.ifname, 1706257529Sluigi nmr.nr_tx_rings, nmr.nr_tx_slots, 1707257529Sluigi nmr.nr_rx_rings, nmr.nr_rx_slots); 1708257529Sluigi //if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) { 1709257529Sluigi // D("Unable to get if info without name"); 1710257529Sluigi //} else { 1711257529Sluigi // D("map size is %d Kb", nmr.nr_memsize >> 10); 1712257529Sluigi //} 1713257529Sluigi if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) { 1714257529Sluigi D("Unable to get if info for %s", g.ifname); 1715257529Sluigi } 1716257529Sluigi devqueues = nmr.nr_rx_rings; 1717227614Sluigi 1718227614Sluigi /* validate provided nthreads. */ 1719227614Sluigi if (g.nthreads < 1 || g.nthreads > devqueues) { 1720227614Sluigi D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 1721227614Sluigi // continue, fail later 1722227614Sluigi } 1723227614Sluigi 1724227614Sluigi /* 1725227614Sluigi * Map the netmap shared memory: instead of issuing mmap() 1726227614Sluigi * inside the body of the threads, we prefer to keep this 1727227614Sluigi * operation here to simplify the thread logic. 1728227614Sluigi */ 1729246896Sluigi D("mapping %d Kbytes", nmr.nr_memsize>>10); 1730246896Sluigi g.mmap_size = nmr.nr_memsize; 1731246896Sluigi g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 1732227614Sluigi PROT_WRITE | PROT_READ, 1733246896Sluigi MAP_SHARED, g.main_fd, 0); 1734246896Sluigi if (g.mmap_addr == MAP_FAILED) { 1735227614Sluigi D("Unable to mmap %d KB", nmr.nr_memsize >> 10); 1736227614Sluigi // continue, fail later 1737227614Sluigi } 1738227614Sluigi 1739227614Sluigi 1740227614Sluigi 1741227614Sluigi /* Print some debug information. */ 1742227614Sluigi fprintf(stdout, 1743227614Sluigi "%s %s: %d queues, %d threads and %d cpus.\n", 1744246896Sluigi (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1745246896Sluigi g.ifname, 1746227614Sluigi devqueues, 1747227614Sluigi g.nthreads, 1748227614Sluigi g.cpus); 1749246896Sluigi if (g.td_body == sender_body) { 1750227614Sluigi fprintf(stdout, "%s -> %s (%s -> %s)\n", 1751246896Sluigi g.src_ip.name, g.dst_ip.name, 1752246896Sluigi g.src_mac.name, g.dst_mac.name); 1753227614Sluigi } 1754227614Sluigi 1755227614Sluigi /* Exit if something went wrong. */ 1756246896Sluigi if (g.main_fd < 0) { 1757227614Sluigi D("aborting"); 1758227614Sluigi usage(); 1759227614Sluigi } 1760234956Sluigi } 1761227614Sluigi 1762257529Sluigi 1763234956Sluigi if (g.options) { 1764251426Sluigi D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 1765234956Sluigi g.options & OPT_PREFETCH ? " prefetch" : "", 1766234956Sluigi g.options & OPT_ACCESS ? " access" : "", 1767234956Sluigi g.options & OPT_MEMCPY ? " memcpy" : "", 1768251426Sluigi g.options & OPT_INDIRECT ? " indirect" : "", 1769234956Sluigi g.options & OPT_COPY ? " copy" : ""); 1770234956Sluigi } 1771257529Sluigi 1772257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 1773257529Sluigi if (g.tx_rate > 0) { 1774257529Sluigi /* try to have at least something every second, 1775257529Sluigi * reducing the burst size to 0.5s worth of data 1776257529Sluigi * (but no less than one full set of fragments) 1777257529Sluigi */ 1778257529Sluigi if (g.burst > g.tx_rate/2) 1779257529Sluigi g.burst = g.tx_rate/2; 1780257529Sluigi if (g.burst < g.frags) 1781257529Sluigi g.burst = g.frags; 1782251132Sluigi g.tx_period.tv_nsec = (1e9 / g.tx_rate) * g.burst; 1783257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 1784257529Sluigi g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 1785251132Sluigi } 1786257529Sluigi if (g.td_body == sender_body) 1787257529Sluigi D("Sending %d packets every %ld.%09ld s", 1788257529Sluigi g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 1789227614Sluigi /* Wait for PHY reset. */ 1790227614Sluigi D("Wait %d secs for phy reset", wait_link); 1791227614Sluigi sleep(wait_link); 1792227614Sluigi D("Ready..."); 1793227614Sluigi 1794227614Sluigi /* Install ^C handler. */ 1795227614Sluigi global_nthreads = g.nthreads; 1796227614Sluigi signal(SIGINT, sigint_h); 1797227614Sluigi 1798246896Sluigi#if 0 // XXX this is not needed, i believe 1799246896Sluigi if (g.dev_type > DEV_NETMAP) { 1800246896Sluigi g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL); 1801234956Sluigi if (g.p == NULL) { 1802246896Sluigi D("cannot open pcap on %s", g.ifname); 1803234956Sluigi usage(); 1804234956Sluigi } else 1805246896Sluigi D("using pcap %p on %s", g.p, g.ifname); 1806227614Sluigi } 1807246896Sluigi#endif // XXX 1808246896Sluigi start_threads(&g); 1809246896Sluigi main_thread(&g); 1810246896Sluigi return 0; 1811246896Sluigi} 1812227614Sluigi 1813227614Sluigi/* end of file */ 1814