1227614Sluigi/* 2260368Sluigi * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. 3260368Sluigi * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved. 4227614Sluigi * 5227614Sluigi * Redistribution and use in source and binary forms, with or without 6227614Sluigi * modification, are permitted provided that the following conditions 7227614Sluigi * are met: 8228276Sluigi * 1. Redistributions of source code must retain the above copyright 9228276Sluigi * notice, this list of conditions and the following disclaimer. 10228276Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11228276Sluigi * notice, this list of conditions and the following disclaimer in the 12227614Sluigi * documentation and/or other materials provided with the distribution. 13227614Sluigi * 14227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17227614Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24227614Sluigi * SUCH DAMAGE. 25227614Sluigi */ 26227614Sluigi 27227614Sluigi/* 28227614Sluigi * $FreeBSD: releng/11.0/tools/tools/netmap/pkt-gen.c 281746 2015-04-19 17:07:51Z adrian $ 29257529Sluigi * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $ 30227614Sluigi * 31227614Sluigi * Example program to show how to build a multithreaded packet 32227614Sluigi * source/sink using the netmap device. 33227614Sluigi * 34227614Sluigi * In this example we create a programmable number of threads 35227614Sluigi * to take care of all the queues of the interface used to 36227614Sluigi * send or receive traffic. 37227614Sluigi * 38227614Sluigi */ 39227614Sluigi 40270063Sluigi// #define TRASH_VHOST_HDR 41270063Sluigi 42261909Sluigi#define _GNU_SOURCE /* for CPU_SET() */ 43261909Sluigi#include <stdio.h> 44261909Sluigi#define NETMAP_WITH_LIBS 45261909Sluigi#include <net/netmap_user.h> 46246896Sluigi 47261909Sluigi 48251426Sluigi#include <ctype.h> // isprint() 49261909Sluigi#include <unistd.h> // sysconf() 50261909Sluigi#include <sys/poll.h> 51261909Sluigi#include <arpa/inet.h> /* ntohs */ 52261909Sluigi#include <sys/sysctl.h> /* sysctl */ 53261909Sluigi#include <ifaddrs.h> /* getifaddrs */ 54261909Sluigi#include <net/ethernet.h> 55261909Sluigi#include <netinet/in.h> 56261909Sluigi#include <netinet/ip.h> 57261909Sluigi#include <netinet/udp.h> 58251426Sluigi 59261909Sluigi#include <pthread.h> 60261909Sluigi 61260700Sluigi#ifndef NO_PCAP 62260700Sluigi#include <pcap/pcap.h> 63260700Sluigi#endif 64261909Sluigi 65261909Sluigi#ifdef linux 66261909Sluigi 67261909Sluigi#define cpuset_t cpu_set_t 68261909Sluigi 69261909Sluigi#define ifr_flagshigh ifr_flags /* only the low 16 bits here */ 70261909Sluigi#define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */ 71261909Sluigi#include <linux/ethtool.h> 72261909Sluigi#include <linux/sockios.h> 73261909Sluigi 74261909Sluigi#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 75261909Sluigi#include <netinet/ether.h> /* ether_aton */ 76261909Sluigi#include <linux/if_packet.h> /* sockaddr_ll */ 77261909Sluigi#endif /* linux */ 78261909Sluigi 79261909Sluigi#ifdef __FreeBSD__ 80261909Sluigi#include <sys/endian.h> /* le64toh */ 81261909Sluigi#include <machine/param.h> 82261909Sluigi 83261909Sluigi#include <pthread_np.h> /* pthread w/ affinity */ 84261909Sluigi#include <sys/cpuset.h> /* cpu_set */ 85261909Sluigi#include <net/if_dl.h> /* LLADDR */ 86261909Sluigi#endif /* __FreeBSD__ */ 87261909Sluigi 88261909Sluigi#ifdef __APPLE__ 89261909Sluigi 90261909Sluigi#define cpuset_t uint64_t // XXX 91261909Sluigistatic inline void CPU_ZERO(cpuset_t *p) 92261909Sluigi{ 93261909Sluigi *p = 0; 94261909Sluigi} 95261909Sluigi 96261909Sluigistatic inline void CPU_SET(uint32_t i, cpuset_t *p) 97261909Sluigi{ 98261909Sluigi *p |= 1<< (i & 0x3f); 99261909Sluigi} 100261909Sluigi 101261909Sluigi#define pthread_setaffinity_np(a, b, c) ((void)a, 0) 102261909Sluigi 103261909Sluigi#define ifr_flagshigh ifr_flags // XXX 104261909Sluigi#define IFF_PPROMISC IFF_PROMISC 105261909Sluigi#include <net/if_dl.h> /* LLADDR */ 106261909Sluigi#define clock_gettime(a,b) \ 107261909Sluigi do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) 108261909Sluigi#endif /* __APPLE__ */ 109261909Sluigi 110257529Sluigiconst char *default_payload="netmap pkt-gen DIRECT payload\n" 111227614Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 112227614Sluigi 113257529Sluigiconst char *indirect_payload="netmap pkt-gen indirect payload\n" 114257529Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 115257529Sluigi 116227614Sluigiint verbose = 0; 117227614Sluigi 118261909Sluigi#define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */ 119227614Sluigi 120260368Sluigi 121260368Sluigi#define VIRT_HDR_1 10 /* length of a base vnet-hdr */ 122260368Sluigi#define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ 123260368Sluigi#define VIRT_HDR_MAX VIRT_HDR_2 124260368Sluigistruct virt_header { 125260368Sluigi uint8_t fields[VIRT_HDR_MAX]; 126260368Sluigi}; 127260368Sluigi 128270063Sluigi#define MAX_BODYSIZE 16384 129270063Sluigi 130227614Sluigistruct pkt { 131260368Sluigi struct virt_header vh; 132227614Sluigi struct ether_header eh; 133227614Sluigi struct ip ip; 134227614Sluigi struct udphdr udp; 135270063Sluigi uint8_t body[MAX_BODYSIZE]; // XXX hardwired 136227614Sluigi} __attribute__((__packed__)); 137227614Sluigi 138246896Sluigistruct ip_range { 139246896Sluigi char *name; 140257529Sluigi uint32_t start, end; /* same as struct in_addr */ 141257529Sluigi uint16_t port0, port1; 142246896Sluigi}; 143246896Sluigi 144246896Sluigistruct mac_range { 145246896Sluigi char *name; 146246896Sluigi struct ether_addr start, end; 147246896Sluigi}; 148246896Sluigi 149261909Sluigi/* ifname can be netmap:foo-xxxx */ 150261909Sluigi#define MAX_IFNAMELEN 64 /* our buffer for ifname */ 151270063Sluigi//#define MAX_PKTSIZE 1536 152270063Sluigi#define MAX_PKTSIZE MAX_BODYSIZE /* XXX: + IP_HDR + ETH_HDR */ 153270063Sluigi 154270063Sluigi/* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */ 155270063Sluigistruct tstamp { 156270063Sluigi uint32_t sec; 157270063Sluigi uint32_t nsec; 158270063Sluigi}; 159270063Sluigi 160227614Sluigi/* 161227614Sluigi * global arguments for all threads 162227614Sluigi */ 163246896Sluigi 164227614Sluigistruct glob_arg { 165246896Sluigi struct ip_range src_ip; 166246896Sluigi struct ip_range dst_ip; 167246896Sluigi struct mac_range dst_mac; 168246896Sluigi struct mac_range src_mac; 169227614Sluigi int pkt_size; 170227614Sluigi int burst; 171246896Sluigi int forever; 172227614Sluigi int npackets; /* total packets to send */ 173257529Sluigi int frags; /* fragments per packet */ 174227614Sluigi int nthreads; 175227614Sluigi int cpus; 176234956Sluigi int options; /* testing */ 177234956Sluigi#define OPT_PREFETCH 1 178234956Sluigi#define OPT_ACCESS 2 179234956Sluigi#define OPT_COPY 4 180234956Sluigi#define OPT_MEMCPY 8 181246896Sluigi#define OPT_TS 16 /* add a timestamp */ 182251426Sluigi#define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 183251426Sluigi#define OPT_DUMP 64 /* dump rx/tx traffic */ 184270063Sluigi#define OPT_MONITOR_TX 128 185270063Sluigi#define OPT_MONITOR_RX 256 186281746Sadrian#define OPT_RANDOM_SRC 512 187281746Sadrian#define OPT_RANDOM_DST 1024 188246896Sluigi int dev_type; 189260700Sluigi#ifndef NO_PCAP 190227614Sluigi pcap_t *p; 191260700Sluigi#endif 192227614Sluigi 193251132Sluigi int tx_rate; 194251132Sluigi struct timespec tx_period; 195251132Sluigi 196246896Sluigi int affinity; 197246896Sluigi int main_fd; 198261909Sluigi struct nm_desc *nmd; 199260700Sluigi int report_interval; /* milliseconds between prints */ 200246896Sluigi void *(*td_body)(void *); 201246896Sluigi void *mmap_addr; 202261909Sluigi char ifname[MAX_IFNAMELEN]; 203257529Sluigi char *nmr_config; 204257529Sluigi int dummy_send; 205260368Sluigi int virt_header; /* send also the virt_header */ 206261909Sluigi int extra_bufs; /* goes in nr_arg3 */ 207272962Sgnn char *packet_file; /* -P option */ 208227614Sluigi}; 209246896Sluigienum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 210227614Sluigi 211246896Sluigi 212227614Sluigi/* 213227614Sluigi * Arguments for a new thread. The same structure is used by 214227614Sluigi * the source and the sink 215227614Sluigi */ 216227614Sluigistruct targ { 217227614Sluigi struct glob_arg *g; 218227614Sluigi int used; 219227614Sluigi int completed; 220238165Semaste int cancel; 221227614Sluigi int fd; 222261909Sluigi struct nm_desc *nmd; 223246896Sluigi volatile uint64_t count; 224251132Sluigi struct timespec tic, toc; 225227614Sluigi int me; 226227614Sluigi pthread_t thread; 227227614Sluigi int affinity; 228227614Sluigi 229227614Sluigi struct pkt pkt; 230272962Sgnn void *frame; 231227614Sluigi}; 232227614Sluigi 233227614Sluigi 234246896Sluigi/* 235246896Sluigi * extract the extremes from a range of ipv4 addresses. 236246896Sluigi * addr_lo[-addr_hi][:port_lo[-port_hi]] 237246896Sluigi */ 238246896Sluigistatic void 239246896Sluigiextract_ip_range(struct ip_range *r) 240246896Sluigi{ 241257529Sluigi char *ap, *pp; 242257529Sluigi struct in_addr a; 243246896Sluigi 244260368Sluigi if (verbose) 245260368Sluigi D("extract IP range from %s", r->name); 246257529Sluigi r->port0 = r->port1 = 0; 247257529Sluigi r->start = r->end = 0; 248257529Sluigi 249257529Sluigi /* the first - splits start/end of range */ 250257529Sluigi ap = index(r->name, '-'); /* do we have ports ? */ 251257529Sluigi if (ap) { 252257529Sluigi *ap++ = '\0'; 253257529Sluigi } 254257529Sluigi /* grab the initial values (mandatory) */ 255257529Sluigi pp = index(r->name, ':'); 256257529Sluigi if (pp) { 257257529Sluigi *pp++ = '\0'; 258257529Sluigi r->port0 = r->port1 = strtol(pp, NULL, 0); 259257529Sluigi }; 260257529Sluigi inet_aton(r->name, &a); 261257529Sluigi r->start = r->end = ntohl(a.s_addr); 262257529Sluigi if (ap) { 263257529Sluigi pp = index(ap, ':'); 264257529Sluigi if (pp) { 265257529Sluigi *pp++ = '\0'; 266261909Sluigi if (*pp) 267257529Sluigi r->port1 = strtol(pp, NULL, 0); 268246896Sluigi } 269257529Sluigi if (*ap) { 270257529Sluigi inet_aton(ap, &a); 271257529Sluigi r->end = ntohl(a.s_addr); 272257529Sluigi } 273246896Sluigi } 274257529Sluigi if (r->port0 > r->port1) { 275257529Sluigi uint16_t tmp = r->port0; 276257529Sluigi r->port0 = r->port1; 277257529Sluigi r->port1 = tmp; 278257529Sluigi } 279257529Sluigi if (r->start > r->end) { 280257529Sluigi uint32_t tmp = r->start; 281246896Sluigi r->start = r->end; 282257529Sluigi r->end = tmp; 283246896Sluigi } 284257529Sluigi { 285257529Sluigi struct in_addr a; 286257529Sluigi char buf1[16]; // one ip address 287257529Sluigi 288257529Sluigi a.s_addr = htonl(r->end); 289257529Sluigi strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 290257529Sluigi a.s_addr = htonl(r->start); 291260368Sluigi if (1) 292260368Sluigi D("range is %s:%d to %s:%d", 293257529Sluigi inet_ntoa(a), r->port0, buf1, r->port1); 294257529Sluigi } 295246896Sluigi} 296246896Sluigi 297246896Sluigistatic void 298246896Sluigiextract_mac_range(struct mac_range *r) 299246896Sluigi{ 300260368Sluigi if (verbose) 301260368Sluigi D("extract MAC range from %s", r->name); 302246896Sluigi bcopy(ether_aton(r->name), &r->start, 6); 303246896Sluigi bcopy(ether_aton(r->name), &r->end, 6); 304246896Sluigi#if 0 305246896Sluigi bcopy(targ->src_mac, eh->ether_shost, 6); 306246896Sluigi p = index(targ->g->src_mac, '-'); 307246896Sluigi if (p) 308246896Sluigi targ->src_mac_range = atoi(p+1); 309246896Sluigi 310246896Sluigi bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 311246896Sluigi bcopy(targ->dst_mac, eh->ether_dhost, 6); 312246896Sluigi p = index(targ->g->dst_mac, '-'); 313246896Sluigi if (p) 314246896Sluigi targ->dst_mac_range = atoi(p+1); 315246896Sluigi#endif 316260368Sluigi if (verbose) 317260368Sluigi D("%s starts at %s", r->name, ether_ntoa(&r->start)); 318246896Sluigi} 319246896Sluigi 320227614Sluigistatic struct targ *targs; 321227614Sluigistatic int global_nthreads; 322227614Sluigi 323227614Sluigi/* control-C handler */ 324227614Sluigistatic void 325246896Sluigisigint_h(int sig) 326227614Sluigi{ 327246896Sluigi int i; 328246896Sluigi 329246896Sluigi (void)sig; /* UNUSED */ 330270063Sluigi D("received control-C on thread %p", pthread_self()); 331246896Sluigi for (i = 0; i < global_nthreads; i++) { 332238165Semaste targs[i].cancel = 1; 333246896Sluigi } 334227614Sluigi signal(SIGINT, SIG_DFL); 335227614Sluigi} 336227614Sluigi 337227614Sluigi/* sysctl wrapper to return the number of active CPUs */ 338227614Sluigistatic int 339227614Sluigisystem_ncpus(void) 340227614Sluigi{ 341261909Sluigi int ncpus; 342261909Sluigi#if defined (__FreeBSD__) 343261909Sluigi int mib[2] = { CTL_HW, HW_NCPU }; 344261909Sluigi size_t len = sizeof(mib); 345227614Sluigi sysctl(mib, 2, &ncpus, &len, NULL, 0); 346261909Sluigi#elif defined(linux) 347261909Sluigi ncpus = sysconf(_SC_NPROCESSORS_ONLN); 348261909Sluigi#else /* others */ 349261909Sluigi ncpus = 1; 350261909Sluigi#endif /* others */ 351227614Sluigi return (ncpus); 352227614Sluigi} 353227614Sluigi 354246896Sluigi#ifdef __linux__ 355246896Sluigi#define sockaddr_dl sockaddr_ll 356246896Sluigi#define sdl_family sll_family 357246896Sluigi#define AF_LINK AF_PACKET 358246896Sluigi#define LLADDR(s) s->sll_addr; 359246896Sluigi#include <linux/if_tun.h> 360246896Sluigi#define TAP_CLONEDEV "/dev/net/tun" 361246896Sluigi#endif /* __linux__ */ 362246896Sluigi 363246896Sluigi#ifdef __FreeBSD__ 364246896Sluigi#include <net/if_tun.h> 365246896Sluigi#define TAP_CLONEDEV "/dev/tap" 366246896Sluigi#endif /* __FreeBSD */ 367246896Sluigi 368246896Sluigi#ifdef __APPLE__ 369246896Sluigi// #warning TAP not supported on apple ? 370246896Sluigi#include <net/if_utun.h> 371246896Sluigi#define TAP_CLONEDEV "/dev/tap" 372246896Sluigi#endif /* __APPLE__ */ 373246896Sluigi 374246896Sluigi 375227614Sluigi/* 376257529Sluigi * parse the vale configuration in conf and put it in nmr. 377261909Sluigi * Return the flag set if necessary. 378257529Sluigi * The configuration may consist of 0 to 4 numbers separated 379257906Shiren * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 380257529Sluigi * Missing numbers or zeroes stand for default values. 381257529Sluigi * As an additional convenience, if exactly one number 382257906Shiren * is specified, then this is assigned to both #tx-slots and #rx-slots. 383261909Sluigi * If there is no 4th number, then the 3rd is assigned to both #tx-rings 384257529Sluigi * and #rx-rings. 385257529Sluigi */ 386261909Sluigiint 387261909Sluigiparse_nmr_config(const char* conf, struct nmreq *nmr) 388257529Sluigi{ 389257529Sluigi char *w, *tok; 390257529Sluigi int i, v; 391257529Sluigi 392257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 393257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 394257529Sluigi if (conf == NULL || ! *conf) 395261909Sluigi return 0; 396257529Sluigi w = strdup(conf); 397257529Sluigi for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 398257529Sluigi v = atoi(tok); 399257529Sluigi switch (i) { 400257529Sluigi case 0: 401257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = v; 402257529Sluigi break; 403257529Sluigi case 1: 404257529Sluigi nmr->nr_rx_slots = v; 405257529Sluigi break; 406257529Sluigi case 2: 407257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = v; 408257529Sluigi break; 409257529Sluigi case 3: 410257529Sluigi nmr->nr_rx_rings = v; 411257529Sluigi break; 412257529Sluigi default: 413257529Sluigi D("ignored config: %s", tok); 414257529Sluigi break; 415257529Sluigi } 416257529Sluigi } 417257529Sluigi D("txr %d txd %d rxr %d rxd %d", 418257529Sluigi nmr->nr_tx_rings, nmr->nr_tx_slots, 419257529Sluigi nmr->nr_rx_rings, nmr->nr_rx_slots); 420257529Sluigi free(w); 421261909Sluigi return (nmr->nr_tx_rings || nmr->nr_tx_slots || 422261909Sluigi nmr->nr_rx_rings || nmr->nr_rx_slots) ? 423261909Sluigi NM_OPEN_RING_CFG : 0; 424257529Sluigi} 425257529Sluigi 426257529Sluigi 427257529Sluigi/* 428227614Sluigi * locate the src mac address for our interface, put it 429227614Sluigi * into the user-supplied buffer. return 0 if ok, -1 on error. 430227614Sluigi */ 431227614Sluigistatic int 432227614Sluigisource_hwaddr(const char *ifname, char *buf) 433227614Sluigi{ 434227614Sluigi struct ifaddrs *ifaphead, *ifap; 435227614Sluigi int l = sizeof(ifap->ifa_name); 436227614Sluigi 437227614Sluigi if (getifaddrs(&ifaphead) != 0) { 438227614Sluigi D("getifaddrs %s failed", ifname); 439227614Sluigi return (-1); 440227614Sluigi } 441227614Sluigi 442227614Sluigi for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 443227614Sluigi struct sockaddr_dl *sdl = 444227614Sluigi (struct sockaddr_dl *)ifap->ifa_addr; 445227614Sluigi uint8_t *mac; 446227614Sluigi 447227614Sluigi if (!sdl || sdl->sdl_family != AF_LINK) 448227614Sluigi continue; 449227614Sluigi if (strncmp(ifap->ifa_name, ifname, l) != 0) 450227614Sluigi continue; 451227614Sluigi mac = (uint8_t *)LLADDR(sdl); 452227614Sluigi sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 453227614Sluigi mac[0], mac[1], mac[2], 454227614Sluigi mac[3], mac[4], mac[5]); 455227614Sluigi if (verbose) 456227614Sluigi D("source hwaddr %s", buf); 457227614Sluigi break; 458227614Sluigi } 459227614Sluigi freeifaddrs(ifaphead); 460227614Sluigi return ifap ? 0 : 1; 461227614Sluigi} 462227614Sluigi 463227614Sluigi 464227614Sluigi/* set the thread affinity. */ 465227614Sluigistatic int 466227614Sluigisetaffinity(pthread_t me, int i) 467227614Sluigi{ 468227614Sluigi cpuset_t cpumask; 469227614Sluigi 470227614Sluigi if (i == -1) 471227614Sluigi return 0; 472227614Sluigi 473227614Sluigi /* Set thread affinity affinity.*/ 474227614Sluigi CPU_ZERO(&cpumask); 475227614Sluigi CPU_SET(i, &cpumask); 476227614Sluigi 477227614Sluigi if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 478260368Sluigi D("Unable to set affinity: %s", strerror(errno)); 479227614Sluigi return 1; 480227614Sluigi } 481227614Sluigi return 0; 482227614Sluigi} 483227614Sluigi 484227614Sluigi/* Compute the checksum of the given ip header. */ 485227614Sluigistatic uint16_t 486246896Sluigichecksum(const void *data, uint16_t len, uint32_t sum) 487227614Sluigi{ 488227614Sluigi const uint8_t *addr = data; 489246896Sluigi uint32_t i; 490227614Sluigi 491246896Sluigi /* Checksum all the pairs of bytes first... */ 492246896Sluigi for (i = 0; i < (len & ~1U); i += 2) { 493246896Sluigi sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 494246896Sluigi if (sum > 0xFFFF) 495246896Sluigi sum -= 0xFFFF; 496227614Sluigi } 497246896Sluigi /* 498246896Sluigi * If there's a single byte left over, checksum it, too. 499246896Sluigi * Network byte order is big-endian, so the remaining byte is 500246896Sluigi * the high byte. 501246896Sluigi */ 502246896Sluigi if (i < len) { 503246896Sluigi sum += addr[i] << 8; 504246896Sluigi if (sum > 0xFFFF) 505246896Sluigi sum -= 0xFFFF; 506246896Sluigi } 507246896Sluigi return sum; 508246896Sluigi} 509227614Sluigi 510246896Sluigistatic u_int16_t 511246896Sluigiwrapsum(u_int32_t sum) 512246896Sluigi{ 513246896Sluigi sum = ~sum & 0xFFFF; 514246896Sluigi return (htons(sum)); 515227614Sluigi} 516227614Sluigi 517251426Sluigi/* Check the payload of the packet for errors (use it for debug). 518251426Sluigi * Look for consecutive ascii representations of the size of the packet. 519251426Sluigi */ 520251426Sluigistatic void 521251426Sluigidump_payload(char *p, int len, struct netmap_ring *ring, int cur) 522251426Sluigi{ 523251426Sluigi char buf[128]; 524251426Sluigi int i, j, i0; 525251426Sluigi 526251426Sluigi /* get the length in ASCII of the length of the packet. */ 527261909Sluigi 528257529Sluigi printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 529257529Sluigi ring, cur, ring->slot[cur].buf_idx, 530257529Sluigi ring->slot[cur].flags, len); 531251426Sluigi /* hexdump routine */ 532251426Sluigi for (i = 0; i < len; ) { 533251426Sluigi memset(buf, sizeof(buf), ' '); 534251426Sluigi sprintf(buf, "%5d: ", i); 535251426Sluigi i0 = i; 536251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 537251426Sluigi sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 538251426Sluigi i = i0; 539251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 540251426Sluigi sprintf(buf+7+j + 48, "%c", 541251426Sluigi isprint(p[i]) ? p[i] : '.'); 542251426Sluigi printf("%s\n", buf); 543251426Sluigi } 544251426Sluigi} 545251426Sluigi 546227614Sluigi/* 547227614Sluigi * Fill a packet with some payload. 548246896Sluigi * We create a UDP packet so the payload starts at 549246896Sluigi * 14+20+8 = 42 bytes. 550227614Sluigi */ 551246896Sluigi#ifdef __linux__ 552246896Sluigi#define uh_sport source 553246896Sluigi#define uh_dport dest 554246896Sluigi#define uh_ulen len 555246896Sluigi#define uh_sum check 556246896Sluigi#endif /* linux */ 557251426Sluigi 558257529Sluigi/* 559257529Sluigi * increment the addressed in the packet, 560257529Sluigi * starting from the least significant field. 561257529Sluigi * DST_IP DST_PORT SRC_IP SRC_PORT 562257529Sluigi */ 563227614Sluigistatic void 564257529Sluigiupdate_addresses(struct pkt *pkt, struct glob_arg *g) 565257529Sluigi{ 566257529Sluigi uint32_t a; 567257529Sluigi uint16_t p; 568257529Sluigi struct ip *ip = &pkt->ip; 569257529Sluigi struct udphdr *udp = &pkt->udp; 570257529Sluigi 571260700Sluigi do { 572281746Sadrian /* XXX for now it doesn't handle non-random src, random dst */ 573281746Sadrian if (g->options & OPT_RANDOM_SRC) { 574281746Sadrian udp->uh_sport = random(); 575281746Sadrian ip->ip_src.s_addr = random(); 576281746Sadrian } else { 577281746Sadrian p = ntohs(udp->uh_sport); 578281746Sadrian if (p < g->src_ip.port1) { /* just inc, no wrap */ 579281746Sadrian udp->uh_sport = htons(p + 1); 580281746Sadrian break; 581281746Sadrian } 582281746Sadrian udp->uh_sport = htons(g->src_ip.port0); 583257529Sluigi 584281746Sadrian a = ntohl(ip->ip_src.s_addr); 585281746Sadrian if (a < g->src_ip.end) { /* just inc, no wrap */ 586281746Sadrian ip->ip_src.s_addr = htonl(a + 1); 587281746Sadrian break; 588281746Sadrian } 589281746Sadrian ip->ip_src.s_addr = htonl(g->src_ip.start); 590257529Sluigi 591281746Sadrian udp->uh_sport = htons(g->src_ip.port0); 592257529Sluigi } 593257529Sluigi 594281746Sadrian if (g->options & OPT_RANDOM_DST) { 595281746Sadrian udp->uh_dport = random(); 596281746Sadrian ip->ip_dst.s_addr = random(); 597281746Sadrian } else { 598281746Sadrian p = ntohs(udp->uh_dport); 599281746Sadrian if (p < g->dst_ip.port1) { /* just inc, no wrap */ 600281746Sadrian udp->uh_dport = htons(p + 1); 601281746Sadrian break; 602281746Sadrian } 603281746Sadrian udp->uh_dport = htons(g->dst_ip.port0); 604281746Sadrian 605281746Sadrian a = ntohl(ip->ip_dst.s_addr); 606281746Sadrian if (a < g->dst_ip.end) { /* just inc, no wrap */ 607281746Sadrian ip->ip_dst.s_addr = htonl(a + 1); 608281746Sadrian break; 609281746Sadrian } 610257529Sluigi } 611257529Sluigi ip->ip_dst.s_addr = htonl(g->dst_ip.start); 612260700Sluigi } while (0); 613260700Sluigi // update checksum 614257529Sluigi} 615257529Sluigi 616257529Sluigi/* 617257529Sluigi * initialize one packet and prepare for the next one. 618257529Sluigi * The copy could be done better instead of repeating it each time. 619257529Sluigi */ 620257529Sluigistatic void 621227614Sluigiinitialize_packet(struct targ *targ) 622227614Sluigi{ 623227614Sluigi struct pkt *pkt = &targ->pkt; 624227614Sluigi struct ether_header *eh; 625227614Sluigi struct ip *ip; 626227614Sluigi struct udphdr *udp; 627246896Sluigi uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 628251426Sluigi const char *payload = targ->g->options & OPT_INDIRECT ? 629257529Sluigi indirect_payload : default_payload; 630260700Sluigi int i, l0 = strlen(payload); 631227614Sluigi 632272962Sgnn char errbuf[PCAP_ERRBUF_SIZE]; 633272962Sgnn pcap_t *file; 634272962Sgnn struct pcap_pkthdr *header; 635272962Sgnn const unsigned char *packet; 636272962Sgnn 637272962Sgnn /* Read a packet from a PCAP file if asked. */ 638272962Sgnn if (targ->g->packet_file != NULL) { 639272962Sgnn if ((file = pcap_open_offline(targ->g->packet_file, 640272962Sgnn errbuf)) == NULL) 641272962Sgnn D("failed to open pcap file %s", 642272962Sgnn targ->g->packet_file); 643272962Sgnn if (pcap_next_ex(file, &header, &packet) < 0) 644272962Sgnn D("failed to read packet from %s", 645272962Sgnn targ->g->packet_file); 646272962Sgnn if ((targ->frame = malloc(header->caplen)) == NULL) 647272962Sgnn D("out of memory"); 648272962Sgnn bcopy(packet, (unsigned char *)targ->frame, header->caplen); 649272962Sgnn targ->g->pkt_size = header->caplen; 650272962Sgnn pcap_close(file); 651272962Sgnn return; 652272962Sgnn } 653272962Sgnn 654257529Sluigi /* create a nice NUL-terminated string */ 655260700Sluigi for (i = 0; i < paylen; i += l0) { 656260700Sluigi if (l0 > paylen - i) 657260700Sluigi l0 = paylen - i; // last round 658260700Sluigi bcopy(payload, pkt->body + i, l0); 659227614Sluigi } 660227614Sluigi pkt->body[i-1] = '\0'; 661246896Sluigi ip = &pkt->ip; 662227614Sluigi 663257529Sluigi /* prepare the headers */ 664227614Sluigi ip->ip_v = IPVERSION; 665227614Sluigi ip->ip_hl = 5; 666227614Sluigi ip->ip_id = 0; 667227614Sluigi ip->ip_tos = IPTOS_LOWDELAY; 668227614Sluigi ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 669227614Sluigi ip->ip_id = 0; 670227614Sluigi ip->ip_off = htons(IP_DF); /* Don't fragment */ 671227614Sluigi ip->ip_ttl = IPDEFTTL; 672227614Sluigi ip->ip_p = IPPROTO_UDP; 673257529Sluigi ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 674257529Sluigi ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 675246896Sluigi ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 676227614Sluigi 677246896Sluigi 678246896Sluigi udp = &pkt->udp; 679257529Sluigi udp->uh_sport = htons(targ->g->src_ip.port0); 680257529Sluigi udp->uh_dport = htons(targ->g->dst_ip.port0); 681246896Sluigi udp->uh_ulen = htons(paylen); 682246896Sluigi /* Magic: taken from sbin/dhclient/packet.c */ 683246896Sluigi udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 684246896Sluigi checksum(pkt->body, 685246896Sluigi paylen - sizeof(*udp), 686246896Sluigi checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 687246896Sluigi IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 688246896Sluigi ) 689246896Sluigi ) 690246896Sluigi )); 691246896Sluigi 692227614Sluigi eh = &pkt->eh; 693246896Sluigi bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 694246896Sluigi bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 695227614Sluigi eh->ether_type = htons(ETHERTYPE_IP); 696260368Sluigi 697260368Sluigi bzero(&pkt->vh, sizeof(pkt->vh)); 698270063Sluigi#ifdef TRASH_VHOST_HDR 699270063Sluigi /* set bogus content */ 700270063Sluigi pkt->vh.fields[0] = 0xff; 701270063Sluigi pkt->vh.fields[1] = 0xff; 702270063Sluigi pkt->vh.fields[2] = 0xff; 703270063Sluigi pkt->vh.fields[3] = 0xff; 704270063Sluigi pkt->vh.fields[4] = 0xff; 705270063Sluigi pkt->vh.fields[5] = 0xff; 706270063Sluigi#endif /* TRASH_VHOST_HDR */ 707251426Sluigi // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 708227614Sluigi} 709227614Sluigi 710270063Sluigistatic void 711270063Sluigiset_vnet_hdr_len(struct targ *t) 712270063Sluigi{ 713270063Sluigi int err, l = t->g->virt_header; 714270063Sluigi struct nmreq req; 715227614Sluigi 716270063Sluigi if (l == 0) 717270063Sluigi return; 718227614Sluigi 719270063Sluigi memset(&req, 0, sizeof(req)); 720270063Sluigi bcopy(t->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 721270063Sluigi req.nr_version = NETMAP_API; 722270063Sluigi req.nr_cmd = NETMAP_BDG_VNET_HDR; 723270063Sluigi req.nr_arg1 = l; 724270063Sluigi err = ioctl(t->fd, NIOCREGIF, &req); 725270063Sluigi if (err) { 726270063Sluigi D("Unable to set vnet header length %d", l); 727270063Sluigi } 728270063Sluigi} 729270063Sluigi 730270063Sluigi 731227614Sluigi/* 732227614Sluigi * create and enqueue a batch of packets on a ring. 733227614Sluigi * On the last one set NS_REPORT to tell the driver to generate 734227614Sluigi * an interrupt when done. 735227614Sluigi */ 736227614Sluigistatic int 737260368Sluigisend_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 738260368Sluigi int size, struct glob_arg *g, u_int count, int options, 739260368Sluigi u_int nfrags) 740227614Sluigi{ 741260368Sluigi u_int n, sent, cur = ring->cur; 742260700Sluigi u_int fcnt; 743227614Sluigi 744260368Sluigi n = nm_ring_space(ring); 745260368Sluigi if (n < count) 746260368Sluigi count = n; 747257529Sluigi if (count < nfrags) { 748257529Sluigi D("truncating packet, no room for frags %d %d", 749260368Sluigi count, nfrags); 750257529Sluigi } 751234956Sluigi#if 0 752234956Sluigi if (options & (OPT_COPY | OPT_PREFETCH) ) { 753234956Sluigi for (sent = 0; sent < count; sent++) { 754234956Sluigi struct netmap_slot *slot = &ring->slot[cur]; 755234956Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 756234956Sluigi 757260700Sluigi __builtin_prefetch(p); 758260368Sluigi cur = nm_ring_next(ring, cur); 759234956Sluigi } 760234956Sluigi cur = ring->cur; 761234956Sluigi } 762234956Sluigi#endif 763257529Sluigi for (fcnt = nfrags, sent = 0; sent < count; sent++) { 764227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 765227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 766227614Sluigi 767251426Sluigi slot->flags = 0; 768251426Sluigi if (options & OPT_INDIRECT) { 769251426Sluigi slot->flags |= NS_INDIRECT; 770260368Sluigi slot->ptr = (uint64_t)frame; 771257529Sluigi } else if (options & OPT_COPY) { 772261909Sluigi nm_pkt_copy(frame, p, size); 773260700Sluigi if (fcnt == nfrags) 774257529Sluigi update_addresses(pkt, g); 775257529Sluigi } else if (options & OPT_MEMCPY) { 776260368Sluigi memcpy(p, frame, size); 777260700Sluigi if (fcnt == nfrags) 778257529Sluigi update_addresses(pkt, g); 779257529Sluigi } else if (options & OPT_PREFETCH) { 780260700Sluigi __builtin_prefetch(p); 781257529Sluigi } 782257529Sluigi if (options & OPT_DUMP) 783257529Sluigi dump_payload(p, size, ring, cur); 784227614Sluigi slot->len = size; 785257529Sluigi if (--fcnt > 0) 786257529Sluigi slot->flags |= NS_MOREFRAG; 787257529Sluigi else 788257529Sluigi fcnt = nfrags; 789257529Sluigi if (sent == count - 1) { 790257529Sluigi slot->flags &= ~NS_MOREFRAG; 791227614Sluigi slot->flags |= NS_REPORT; 792257529Sluigi } 793260368Sluigi cur = nm_ring_next(ring, cur); 794227614Sluigi } 795260368Sluigi ring->head = ring->cur = cur; 796227614Sluigi 797227614Sluigi return (sent); 798227614Sluigi} 799227614Sluigi 800246896Sluigi/* 801246896Sluigi * Send a packet, and wait for a response. 802246896Sluigi * The payload (after UDP header, ofs 42) has a 4-byte sequence 803246896Sluigi * followed by a struct timeval (or bintime?) 804246896Sluigi */ 805246896Sluigi#define PAY_OFS 42 /* where in the pkt... */ 806246896Sluigi 807227614Sluigistatic void * 808246896Sluigipinger_body(void *data) 809246896Sluigi{ 810246896Sluigi struct targ *targ = (struct targ *) data; 811261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 812261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 813246896Sluigi int i, rx = 0, n = targ->g->npackets; 814260368Sluigi void *frame; 815260368Sluigi int size; 816261909Sluigi uint32_t sent = 0; 817261909Sluigi struct timespec ts, now, last_print; 818261909Sluigi uint32_t count = 0, min = 1000000000, av = 0; 819246896Sluigi 820260368Sluigi frame = &targ->pkt; 821260368Sluigi frame += sizeof(targ->pkt.vh) - targ->g->virt_header; 822260368Sluigi size = targ->g->pkt_size + targ->g->virt_header; 823260368Sluigi 824246896Sluigi if (targ->g->nthreads > 1) { 825246896Sluigi D("can only ping with 1 thread"); 826246896Sluigi return NULL; 827246896Sluigi } 828246896Sluigi 829246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 830260368Sluigi now = last_print; 831246896Sluigi while (n == 0 || (int)sent < n) { 832246896Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 833246896Sluigi struct netmap_slot *slot; 834246896Sluigi char *p; 835260368Sluigi for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ 836246896Sluigi slot = &ring->slot[ring->cur]; 837260368Sluigi slot->len = size; 838246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 839246896Sluigi 840260368Sluigi if (nm_ring_empty(ring)) { 841246896Sluigi D("-- ouch, cannot send"); 842246896Sluigi } else { 843270063Sluigi struct tstamp *tp; 844261909Sluigi nm_pkt_copy(frame, p, size); 845246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 846246896Sluigi bcopy(&sent, p+42, sizeof(sent)); 847270063Sluigi tp = (struct tstamp *)(p+46); 848270063Sluigi tp->sec = (uint32_t)ts.tv_sec; 849270063Sluigi tp->nsec = (uint32_t)ts.tv_nsec; 850246896Sluigi sent++; 851260368Sluigi ring->head = ring->cur = nm_ring_next(ring, ring->cur); 852246896Sluigi } 853246896Sluigi } 854246896Sluigi /* should use a parameter to decide how often to send */ 855261909Sluigi if (poll(&pfd, 1, 3000) <= 0) { 856260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 857260368Sluigi strerror(errno)); 858246896Sluigi continue; 859246896Sluigi } 860246896Sluigi /* see what we got back */ 861261909Sluigi for (i = targ->nmd->first_tx_ring; 862261909Sluigi i <= targ->nmd->last_tx_ring; i++) { 863246896Sluigi ring = NETMAP_RXRING(nifp, i); 864260368Sluigi while (!nm_ring_empty(ring)) { 865246896Sluigi uint32_t seq; 866270063Sluigi struct tstamp *tp; 867246896Sluigi slot = &ring->slot[ring->cur]; 868246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 869246896Sluigi 870246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &now); 871246896Sluigi bcopy(p+42, &seq, sizeof(seq)); 872270063Sluigi tp = (struct tstamp *)(p+46); 873270063Sluigi ts.tv_sec = (time_t)tp->sec; 874270063Sluigi ts.tv_nsec = (long)tp->nsec; 875246896Sluigi ts.tv_sec = now.tv_sec - ts.tv_sec; 876246896Sluigi ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 877246896Sluigi if (ts.tv_nsec < 0) { 878246896Sluigi ts.tv_nsec += 1000000000; 879246896Sluigi ts.tv_sec--; 880246896Sluigi } 881246896Sluigi if (1) D("seq %d/%d delta %d.%09d", seq, sent, 882246896Sluigi (int)ts.tv_sec, (int)ts.tv_nsec); 883246896Sluigi if (ts.tv_nsec < (int)min) 884246896Sluigi min = ts.tv_nsec; 885246896Sluigi count ++; 886246896Sluigi av += ts.tv_nsec; 887260368Sluigi ring->head = ring->cur = nm_ring_next(ring, ring->cur); 888246896Sluigi rx++; 889246896Sluigi } 890246896Sluigi } 891246896Sluigi //D("tx %d rx %d", sent, rx); 892246896Sluigi //usleep(100000); 893246896Sluigi ts.tv_sec = now.tv_sec - last_print.tv_sec; 894246896Sluigi ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 895246896Sluigi if (ts.tv_nsec < 0) { 896246896Sluigi ts.tv_nsec += 1000000000; 897246896Sluigi ts.tv_sec--; 898246896Sluigi } 899246896Sluigi if (ts.tv_sec >= 1) { 900246896Sluigi D("count %d min %d av %d", 901246896Sluigi count, min, av/count); 902246896Sluigi count = 0; 903246896Sluigi av = 0; 904246896Sluigi min = 100000000; 905246896Sluigi last_print = now; 906246896Sluigi } 907246896Sluigi } 908246896Sluigi return NULL; 909246896Sluigi} 910246896Sluigi 911246896Sluigi 912246896Sluigi/* 913246896Sluigi * reply to ping requests 914246896Sluigi */ 915246896Sluigistatic void * 916246896Sluigiponger_body(void *data) 917246896Sluigi{ 918246896Sluigi struct targ *targ = (struct targ *) data; 919261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 920261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 921246896Sluigi struct netmap_ring *txring, *rxring; 922246896Sluigi int i, rx = 0, sent = 0, n = targ->g->npackets; 923246896Sluigi 924246896Sluigi if (targ->g->nthreads > 1) { 925246896Sluigi D("can only reply ping with 1 thread"); 926246896Sluigi return NULL; 927246896Sluigi } 928246896Sluigi D("understood ponger %d but don't know how to do it", n); 929246896Sluigi while (n == 0 || sent < n) { 930246896Sluigi uint32_t txcur, txavail; 931246896Sluigi//#define BUSYWAIT 932246896Sluigi#ifdef BUSYWAIT 933261909Sluigi ioctl(pfd.fd, NIOCRXSYNC, NULL); 934246896Sluigi#else 935261909Sluigi if (poll(&pfd, 1, 1000) <= 0) { 936260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 937260368Sluigi strerror(errno)); 938246896Sluigi continue; 939246896Sluigi } 940246896Sluigi#endif 941246896Sluigi txring = NETMAP_TXRING(nifp, 0); 942246896Sluigi txcur = txring->cur; 943260368Sluigi txavail = nm_ring_space(txring); 944246896Sluigi /* see what we got back */ 945261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 946246896Sluigi rxring = NETMAP_RXRING(nifp, i); 947260368Sluigi while (!nm_ring_empty(rxring)) { 948246896Sluigi uint16_t *spkt, *dpkt; 949246896Sluigi uint32_t cur = rxring->cur; 950246896Sluigi struct netmap_slot *slot = &rxring->slot[cur]; 951246896Sluigi char *src, *dst; 952246896Sluigi src = NETMAP_BUF(rxring, slot->buf_idx); 953246896Sluigi //D("got pkt %p of size %d", src, slot->len); 954260368Sluigi rxring->head = rxring->cur = nm_ring_next(rxring, cur); 955246896Sluigi rx++; 956246896Sluigi if (txavail == 0) 957246896Sluigi continue; 958246896Sluigi dst = NETMAP_BUF(txring, 959246896Sluigi txring->slot[txcur].buf_idx); 960246896Sluigi /* copy... */ 961246896Sluigi dpkt = (uint16_t *)dst; 962246896Sluigi spkt = (uint16_t *)src; 963261909Sluigi nm_pkt_copy(src, dst, slot->len); 964246896Sluigi dpkt[0] = spkt[3]; 965246896Sluigi dpkt[1] = spkt[4]; 966246896Sluigi dpkt[2] = spkt[5]; 967246896Sluigi dpkt[3] = spkt[0]; 968246896Sluigi dpkt[4] = spkt[1]; 969246896Sluigi dpkt[5] = spkt[2]; 970246896Sluigi txring->slot[txcur].len = slot->len; 971246896Sluigi /* XXX swap src dst mac */ 972260368Sluigi txcur = nm_ring_next(txring, txcur); 973246896Sluigi txavail--; 974246896Sluigi sent++; 975246896Sluigi } 976246896Sluigi } 977260368Sluigi txring->head = txring->cur = txcur; 978246896Sluigi targ->count = sent; 979246896Sluigi#ifdef BUSYWAIT 980261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 981246896Sluigi#endif 982246896Sluigi //D("tx %d rx %d", sent, rx); 983246896Sluigi } 984246896Sluigi return NULL; 985246896Sluigi} 986246896Sluigi 987251132Sluigistatic __inline int 988251132Sluigitimespec_ge(const struct timespec *a, const struct timespec *b) 989251132Sluigi{ 990246896Sluigi 991251132Sluigi if (a->tv_sec > b->tv_sec) 992251132Sluigi return (1); 993251132Sluigi if (a->tv_sec < b->tv_sec) 994251132Sluigi return (0); 995251132Sluigi if (a->tv_nsec >= b->tv_nsec) 996251132Sluigi return (1); 997251132Sluigi return (0); 998251132Sluigi} 999251132Sluigi 1000251132Sluigistatic __inline struct timespec 1001251132Sluigitimeval2spec(const struct timeval *a) 1002251132Sluigi{ 1003251132Sluigi struct timespec ts = { 1004251132Sluigi .tv_sec = a->tv_sec, 1005251132Sluigi .tv_nsec = a->tv_usec * 1000 1006251132Sluigi }; 1007251132Sluigi return ts; 1008251132Sluigi} 1009251132Sluigi 1010251132Sluigistatic __inline struct timeval 1011251132Sluigitimespec2val(const struct timespec *a) 1012251132Sluigi{ 1013251132Sluigi struct timeval tv = { 1014251132Sluigi .tv_sec = a->tv_sec, 1015251132Sluigi .tv_usec = a->tv_nsec / 1000 1016251132Sluigi }; 1017251132Sluigi return tv; 1018251132Sluigi} 1019251132Sluigi 1020251132Sluigi 1021260368Sluigistatic __inline struct timespec 1022260368Sluigitimespec_add(struct timespec a, struct timespec b) 1023251132Sluigi{ 1024260368Sluigi struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; 1025260368Sluigi if (ret.tv_nsec >= 1000000000) { 1026260368Sluigi ret.tv_sec++; 1027260368Sluigi ret.tv_nsec -= 1000000000; 1028260368Sluigi } 1029260368Sluigi return ret; 1030260368Sluigi} 1031251132Sluigi 1032260368Sluigistatic __inline struct timespec 1033260368Sluigitimespec_sub(struct timespec a, struct timespec b) 1034260368Sluigi{ 1035260368Sluigi struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; 1036260368Sluigi if (ret.tv_nsec < 0) { 1037260368Sluigi ret.tv_sec--; 1038260368Sluigi ret.tv_nsec += 1000000000; 1039251132Sluigi } 1040260368Sluigi return ret; 1041251132Sluigi} 1042251132Sluigi 1043260368Sluigi 1044260368Sluigi/* 1045260368Sluigi * wait until ts, either busy or sleeping if more than 1ms. 1046260368Sluigi * Return wakeup time. 1047260368Sluigi */ 1048260368Sluigistatic struct timespec 1049260368Sluigiwait_time(struct timespec ts) 1050251132Sluigi{ 1051260368Sluigi for (;;) { 1052260368Sluigi struct timespec w, cur; 1053260368Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 1054260368Sluigi w = timespec_sub(ts, cur); 1055260368Sluigi if (w.tv_sec < 0) 1056260368Sluigi return cur; 1057260368Sluigi else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 1058260368Sluigi poll(NULL, 0, 1); 1059251132Sluigi } 1060251132Sluigi} 1061251132Sluigi 1062246896Sluigistatic void * 1063227614Sluigisender_body(void *data) 1064227614Sluigi{ 1065227614Sluigi struct targ *targ = (struct targ *) data; 1066261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 1067270063Sluigi struct netmap_if *nifp; 1068227614Sluigi struct netmap_ring *txring; 1069261909Sluigi int i, n = targ->g->npackets / targ->g->nthreads; 1070261909Sluigi int64_t sent = 0; 1071234956Sluigi int options = targ->g->options | OPT_COPY; 1072260368Sluigi struct timespec nexttime = { 0, 0}; // XXX silence compiler 1073251132Sluigi int rate_limit = targ->g->tx_rate; 1074260368Sluigi struct pkt *pkt = &targ->pkt; 1075260368Sluigi void *frame; 1076260368Sluigi int size; 1077251426Sluigi 1078272962Sgnn if (targ->frame == NULL) { 1079272962Sgnn frame = pkt; 1080272962Sgnn frame += sizeof(pkt->vh) - targ->g->virt_header; 1081272962Sgnn size = targ->g->pkt_size + targ->g->virt_header; 1082272962Sgnn } else { 1083272962Sgnn frame = targ->frame; 1084272962Sgnn size = targ->g->pkt_size; 1085272962Sgnn } 1086272962Sgnn 1087270063Sluigi D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 1088227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1089227614Sluigi goto quit; 1090227614Sluigi 1091227614Sluigi /* main loop.*/ 1092251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1093251132Sluigi if (rate_limit) { 1094260368Sluigi targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 1095251132Sluigi targ->tic.tv_nsec = 0; 1096260368Sluigi wait_time(targ->tic); 1097251132Sluigi nexttime = targ->tic; 1098251132Sluigi } 1099261909Sluigi if (targ->g->dev_type == DEV_TAP) { 1100260700Sluigi D("writing to file desc %d", targ->g->main_fd); 1101246896Sluigi 1102246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1103260700Sluigi if (write(targ->g->main_fd, frame, size) != -1) 1104234956Sluigi sent++; 1105257529Sluigi update_addresses(pkt, targ->g); 1106234956Sluigi if (i > 10000) { 1107234956Sluigi targ->count = sent; 1108234956Sluigi i = 0; 1109234956Sluigi } 1110246896Sluigi } 1111260700Sluigi#ifndef NO_PCAP 1112260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1113260700Sluigi pcap_t *p = targ->g->p; 1114246896Sluigi 1115246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1116260700Sluigi if (pcap_inject(p, frame, size) != -1) 1117246896Sluigi sent++; 1118257529Sluigi update_addresses(pkt, targ->g); 1119246896Sluigi if (i > 10000) { 1120246896Sluigi targ->count = sent; 1121246896Sluigi i = 0; 1122246896Sluigi } 1123246896Sluigi } 1124260700Sluigi#endif /* NO_PCAP */ 1125227614Sluigi } else { 1126251132Sluigi int tosend = 0; 1127257529Sluigi int frags = targ->g->frags; 1128257529Sluigi 1129270063Sluigi nifp = targ->nmd->nifp; 1130246896Sluigi while (!targ->cancel && (n == 0 || sent < n)) { 1131227614Sluigi 1132251132Sluigi if (rate_limit && tosend <= 0) { 1133251132Sluigi tosend = targ->g->burst; 1134260368Sluigi nexttime = timespec_add(nexttime, targ->g->tx_period); 1135260368Sluigi wait_time(nexttime); 1136251132Sluigi } 1137251132Sluigi 1138227614Sluigi /* 1139227614Sluigi * wait for available room in the send queue(s) 1140227614Sluigi */ 1141261909Sluigi if (poll(&pfd, 1, 2000) <= 0) { 1142238165Semaste if (targ->cancel) 1143238165Semaste break; 1144260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 1145260368Sluigi strerror(errno)); 1146261909Sluigi // goto quit; 1147227614Sluigi } 1148261909Sluigi if (pfd.revents & POLLERR) { 1149260368Sluigi D("poll error"); 1150260368Sluigi goto quit; 1151260368Sluigi } 1152227614Sluigi /* 1153227614Sluigi * scan our queues and send on those with room 1154227614Sluigi */ 1155246896Sluigi if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 1156246896Sluigi D("drop copy"); 1157234956Sluigi options &= ~OPT_COPY; 1158246896Sluigi } 1159261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1160251132Sluigi int m, limit = rate_limit ? tosend : targ->g->burst; 1161246896Sluigi if (n > 0 && n - sent < limit) 1162246896Sluigi limit = n - sent; 1163227614Sluigi txring = NETMAP_TXRING(nifp, i); 1164260368Sluigi if (nm_ring_empty(txring)) 1165227614Sluigi continue; 1166257529Sluigi if (frags > 1) 1167257529Sluigi limit = ((limit + frags - 1) / frags) * frags; 1168261909Sluigi 1169260368Sluigi m = send_packets(txring, pkt, frame, size, targ->g, 1170257529Sluigi limit, options, frags); 1171261909Sluigi ND("limit %d tail %d frags %d m %d", 1172260700Sluigi limit, txring->tail, frags, m); 1173227614Sluigi sent += m; 1174227614Sluigi targ->count = sent; 1175257529Sluigi if (rate_limit) { 1176257529Sluigi tosend -= m; 1177257529Sluigi if (tosend <= 0) 1178257529Sluigi break; 1179257529Sluigi } 1180227614Sluigi } 1181227614Sluigi } 1182234956Sluigi /* flush any remaining packets */ 1183270063Sluigi D("flush tail %d head %d on thread %p", 1184270063Sluigi txring->tail, txring->head, 1185270063Sluigi pthread_self()); 1186261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1187227614Sluigi 1188227614Sluigi /* final part: wait all the TX queues to be empty. */ 1189261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1190227614Sluigi txring = NETMAP_TXRING(nifp, i); 1191260368Sluigi while (nm_tx_pending(txring)) { 1192270063Sluigi RD(5, "pending tx tail %d head %d on ring %d", 1193270063Sluigi txring->tail, txring->head, i); 1194261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1195227614Sluigi usleep(1); /* wait 1 tick */ 1196227614Sluigi } 1197227614Sluigi } 1198260700Sluigi } /* end DEV_NETMAP */ 1199227614Sluigi 1200251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1201227614Sluigi targ->completed = 1; 1202227614Sluigi targ->count = sent; 1203227614Sluigi 1204227614Sluigiquit: 1205227614Sluigi /* reset the ``used`` flag. */ 1206227614Sluigi targ->used = 0; 1207227614Sluigi 1208227614Sluigi return (NULL); 1209227614Sluigi} 1210227614Sluigi 1211227614Sluigi 1212260700Sluigi#ifndef NO_PCAP 1213227614Sluigistatic void 1214246896Sluigireceive_pcap(u_char *user, const struct pcap_pkthdr * h, 1215246896Sluigi const u_char * bytes) 1216227614Sluigi{ 1217227614Sluigi int *count = (int *)user; 1218246896Sluigi (void)h; /* UNUSED */ 1219246896Sluigi (void)bytes; /* UNUSED */ 1220227614Sluigi (*count)++; 1221227614Sluigi} 1222260700Sluigi#endif /* !NO_PCAP */ 1223227614Sluigi 1224227614Sluigistatic int 1225251426Sluigireceive_packets(struct netmap_ring *ring, u_int limit, int dump) 1226227614Sluigi{ 1227260368Sluigi u_int cur, rx, n; 1228227614Sluigi 1229227614Sluigi cur = ring->cur; 1230260368Sluigi n = nm_ring_space(ring); 1231260368Sluigi if (n < limit) 1232260368Sluigi limit = n; 1233227614Sluigi for (rx = 0; rx < limit; rx++) { 1234227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 1235227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 1236227614Sluigi 1237251426Sluigi if (dump) 1238251426Sluigi dump_payload(p, slot->len, ring, cur); 1239227614Sluigi 1240260368Sluigi cur = nm_ring_next(ring, cur); 1241227614Sluigi } 1242260368Sluigi ring->head = ring->cur = cur; 1243227614Sluigi 1244227614Sluigi return (rx); 1245227614Sluigi} 1246227614Sluigi 1247227614Sluigistatic void * 1248227614Sluigireceiver_body(void *data) 1249227614Sluigi{ 1250227614Sluigi struct targ *targ = (struct targ *) data; 1251261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1252270063Sluigi struct netmap_if *nifp; 1253227614Sluigi struct netmap_ring *rxring; 1254246896Sluigi int i; 1255246896Sluigi uint64_t received = 0; 1256227614Sluigi 1257227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1258227614Sluigi goto quit; 1259227614Sluigi 1260270063Sluigi D("reading from %s fd %d main_fd %d", 1261270063Sluigi targ->g->ifname, targ->fd, targ->g->main_fd); 1262227614Sluigi /* unbounded wait for the first packet. */ 1263270063Sluigi for (;!targ->cancel;) { 1264261909Sluigi i = poll(&pfd, 1, 1000); 1265261909Sluigi if (i > 0 && !(pfd.revents & POLLERR)) 1266227614Sluigi break; 1267261909Sluigi RD(1, "waiting for initial packets, poll returns %d %d", 1268261909Sluigi i, pfd.revents); 1269227614Sluigi } 1270227614Sluigi /* main loop, exit after 1s silence */ 1271251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1272260700Sluigi if (targ->g->dev_type == DEV_TAP) { 1273246896Sluigi while (!targ->cancel) { 1274270063Sluigi char buf[MAX_BODYSIZE]; 1275246896Sluigi /* XXX should we poll ? */ 1276246896Sluigi if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 1277246896Sluigi targ->count++; 1278246896Sluigi } 1279260700Sluigi#ifndef NO_PCAP 1280260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1281260700Sluigi while (!targ->cancel) { 1282260700Sluigi /* XXX should we poll ? */ 1283270063Sluigi pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, 1284270063Sluigi (u_char *)&targ->count); 1285260700Sluigi } 1286260700Sluigi#endif /* !NO_PCAP */ 1287227614Sluigi } else { 1288251426Sluigi int dump = targ->g->options & OPT_DUMP; 1289270063Sluigi 1290270063Sluigi nifp = targ->nmd->nifp; 1291238165Semaste while (!targ->cancel) { 1292227614Sluigi /* Once we started to receive packets, wait at most 1 seconds 1293227614Sluigi before quitting. */ 1294261909Sluigi if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 1295251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1296228975Suqs targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 1297261909Sluigi goto out; 1298227614Sluigi } 1299227614Sluigi 1300261909Sluigi if (pfd.revents & POLLERR) { 1301260368Sluigi D("poll err"); 1302260368Sluigi goto quit; 1303260368Sluigi } 1304260368Sluigi 1305261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 1306227614Sluigi int m; 1307227614Sluigi 1308227614Sluigi rxring = NETMAP_RXRING(nifp, i); 1309260368Sluigi if (nm_ring_empty(rxring)) 1310227614Sluigi continue; 1311227614Sluigi 1312251426Sluigi m = receive_packets(rxring, targ->g->burst, dump); 1313227614Sluigi received += m; 1314227614Sluigi } 1315246896Sluigi targ->count = received; 1316227614Sluigi } 1317227614Sluigi } 1318227614Sluigi 1319261909Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1320261909Sluigi 1321261909Sluigiout: 1322227614Sluigi targ->completed = 1; 1323227614Sluigi targ->count = received; 1324227614Sluigi 1325227614Sluigiquit: 1326227614Sluigi /* reset the ``used`` flag. */ 1327227614Sluigi targ->used = 0; 1328227614Sluigi 1329227614Sluigi return (NULL); 1330227614Sluigi} 1331227614Sluigi 1332246896Sluigi/* very crude code to print a number in normalized form. 1333246896Sluigi * Caller has to make sure that the buffer is large enough. 1334246896Sluigi */ 1335246896Sluigistatic const char * 1336246896Sluiginorm(char *buf, double val) 1337238170Semaste{ 1338261909Sluigi char *units[] = { "", "K", "M", "G", "T" }; 1339246896Sluigi u_int i; 1340238170Semaste 1341261909Sluigi for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++) 1342238170Semaste val /= 1000; 1343246896Sluigi sprintf(buf, "%.2f %s", val, units[i]); 1344246896Sluigi return buf; 1345238170Semaste} 1346238170Semaste 1347227614Sluigistatic void 1348227614Sluigitx_output(uint64_t sent, int size, double delta) 1349227614Sluigi{ 1350246896Sluigi double bw, raw_bw, pps; 1351246896Sluigi char b1[40], b2[80], b3[80]; 1352227614Sluigi 1353261909Sluigi printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", 1354261909Sluigi (unsigned long long)sent, size, delta); 1355246896Sluigi if (delta == 0) 1356246896Sluigi delta = 1e-6; 1357246896Sluigi if (size < 60) /* correct for min packet size */ 1358246896Sluigi size = 60; 1359246896Sluigi pps = sent / delta; 1360246896Sluigi bw = (8.0 * size * sent) / delta; 1361246896Sluigi /* raw packets have4 bytes crc + 20 bytes framing */ 1362246896Sluigi raw_bw = (8.0 * (size + 24) * sent) / delta; 1363238170Semaste 1364246896Sluigi printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1365246896Sluigi norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 1366227614Sluigi} 1367227614Sluigi 1368227614Sluigi 1369227614Sluigistatic void 1370227614Sluigirx_output(uint64_t received, double delta) 1371227614Sluigi{ 1372246896Sluigi double pps; 1373246896Sluigi char b1[40]; 1374227614Sluigi 1375261909Sluigi printf("Received %llu packets, in %.2f seconds.\n", 1376261909Sluigi (unsigned long long) received, delta); 1377227614Sluigi 1378246896Sluigi if (delta == 0) 1379246896Sluigi delta = 1e-6; 1380246896Sluigi pps = received / delta; 1381246896Sluigi printf("Speed: %spps\n", norm(b1, pps)); 1382227614Sluigi} 1383227614Sluigi 1384227614Sluigistatic void 1385227614Sluigiusage(void) 1386227614Sluigi{ 1387227614Sluigi const char *cmd = "pkt-gen"; 1388227614Sluigi fprintf(stderr, 1389227614Sluigi "Usage:\n" 1390227614Sluigi "%s arguments\n" 1391227614Sluigi "\t-i interface interface name\n" 1392246896Sluigi "\t-f function tx rx ping pong\n" 1393246896Sluigi "\t-n count number of iterations (can be 0)\n" 1394246896Sluigi "\t-t pkts_to_send also forces tx mode\n" 1395246896Sluigi "\t-r pkts_to_receive also forces rx mode\n" 1396257529Sluigi "\t-l pkt_size in bytes excluding CRC\n" 1397257529Sluigi "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1398257529Sluigi "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1399257529Sluigi "\t-D dst-mac\n" 1400257529Sluigi "\t-S src-mac\n" 1401246896Sluigi "\t-a cpu_id use setaffinity\n" 1402227614Sluigi "\t-b burst size testing, mostly\n" 1403227614Sluigi "\t-c cores cores to use\n" 1404227614Sluigi "\t-p threads processes/threads to use\n" 1405227614Sluigi "\t-T report_ms milliseconds between reports\n" 1406257529Sluigi "\t-P use libpcap instead of netmap\n" 1407227614Sluigi "\t-w wait_for_link_time in seconds\n" 1408257529Sluigi "\t-R rate in packets per second\n" 1409257529Sluigi "\t-X dump payload\n" 1410260368Sluigi "\t-H len add empty virtio-net-header with size 'len'\n" 1411281746Sadrian "\t-P file load packet from pcap file\n" 1412281746Sadrian "\t-z use random IPv4 src address/port\n" 1413281746Sadrian "\t-Z use random IPv4 dst address/port\n" 1414227614Sluigi "", 1415227614Sluigi cmd); 1416227614Sluigi 1417227614Sluigi exit(0); 1418227614Sluigi} 1419227614Sluigi 1420246896Sluigistatic void 1421246896Sluigistart_threads(struct glob_arg *g) 1422246896Sluigi{ 1423246896Sluigi int i; 1424227614Sluigi 1425246896Sluigi targs = calloc(g->nthreads, sizeof(*targs)); 1426246896Sluigi /* 1427246896Sluigi * Now create the desired number of threads, each one 1428246896Sluigi * using a single descriptor. 1429246896Sluigi */ 1430246896Sluigi for (i = 0; i < g->nthreads; i++) { 1431261909Sluigi struct targ *t = &targs[i]; 1432246896Sluigi 1433261909Sluigi bzero(t, sizeof(*t)); 1434261909Sluigi t->fd = -1; /* default, with pcap */ 1435261909Sluigi t->g = g; 1436261909Sluigi 1437246896Sluigi if (g->dev_type == DEV_NETMAP) { 1438261909Sluigi struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ 1439270063Sluigi uint64_t nmd_flags = 0; 1440270063Sluigi nmd.self = &nmd; 1441246896Sluigi 1442261909Sluigi if (g->nthreads > 1) { 1443261909Sluigi if (nmd.req.nr_flags != NR_REG_ALL_NIC) { 1444261909Sluigi D("invalid nthreads mode %d", nmd.req.nr_flags); 1445261909Sluigi continue; 1446261909Sluigi } 1447261909Sluigi nmd.req.nr_flags = NR_REG_ONE_NIC; 1448261909Sluigi nmd.req.nr_ringid = i; 1449246896Sluigi } 1450261909Sluigi /* Only touch one of the rings (rx is already ok) */ 1451261909Sluigi if (g->td_body == receiver_body) 1452270063Sluigi nmd_flags |= NETMAP_NO_TX_POLL; 1453246896Sluigi 1454261909Sluigi /* register interface. Override ifname and ringid etc. */ 1455270063Sluigi if (g->options & OPT_MONITOR_TX) 1456270063Sluigi nmd.req.nr_flags |= NR_MONITOR_TX; 1457270063Sluigi if (g->options & OPT_MONITOR_RX) 1458270063Sluigi nmd.req.nr_flags |= NR_MONITOR_RX; 1459246896Sluigi 1460270063Sluigi t->nmd = nm_open(t->g->ifname, NULL, nmd_flags | 1461270063Sluigi NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); 1462261909Sluigi if (t->nmd == NULL) { 1463261909Sluigi D("Unable to open %s: %s", 1464261909Sluigi t->g->ifname, strerror(errno)); 1465261909Sluigi continue; 1466246896Sluigi } 1467261909Sluigi t->fd = t->nmd->fd; 1468270063Sluigi set_vnet_hdr_len(t); 1469246896Sluigi 1470246896Sluigi } else { 1471246896Sluigi targs[i].fd = g->main_fd; 1472246896Sluigi } 1473261909Sluigi t->used = 1; 1474261909Sluigi t->me = i; 1475246896Sluigi if (g->affinity >= 0) { 1476246896Sluigi if (g->affinity < g->cpus) 1477261909Sluigi t->affinity = g->affinity; 1478246896Sluigi else 1479261909Sluigi t->affinity = i % g->cpus; 1480261909Sluigi } else { 1481261909Sluigi t->affinity = -1; 1482261909Sluigi } 1483246896Sluigi /* default, init packets */ 1484261909Sluigi initialize_packet(t); 1485246896Sluigi 1486261909Sluigi if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { 1487260368Sluigi D("Unable to create thread %d: %s", i, strerror(errno)); 1488261909Sluigi t->used = 0; 1489246896Sluigi } 1490246896Sluigi } 1491246896Sluigi} 1492246896Sluigi 1493246896Sluigistatic void 1494246896Sluigimain_thread(struct glob_arg *g) 1495246896Sluigi{ 1496246896Sluigi int i; 1497246896Sluigi 1498246896Sluigi uint64_t prev = 0; 1499246896Sluigi uint64_t count = 0; 1500246896Sluigi double delta_t; 1501246896Sluigi struct timeval tic, toc; 1502246896Sluigi 1503246896Sluigi gettimeofday(&toc, NULL); 1504246896Sluigi for (;;) { 1505246896Sluigi struct timeval now, delta; 1506246896Sluigi uint64_t pps, usec, my_count, npkts; 1507246896Sluigi int done = 0; 1508246896Sluigi 1509246896Sluigi delta.tv_sec = g->report_interval/1000; 1510246896Sluigi delta.tv_usec = (g->report_interval%1000)*1000; 1511246896Sluigi select(0, NULL, NULL, NULL, &delta); 1512246896Sluigi gettimeofday(&now, NULL); 1513246896Sluigi timersub(&now, &toc, &toc); 1514246896Sluigi my_count = 0; 1515246896Sluigi for (i = 0; i < g->nthreads; i++) { 1516246896Sluigi my_count += targs[i].count; 1517246896Sluigi if (targs[i].used == 0) 1518246896Sluigi done++; 1519246896Sluigi } 1520246896Sluigi usec = toc.tv_sec* 1000000 + toc.tv_usec; 1521246896Sluigi if (usec < 10000) 1522246896Sluigi continue; 1523246896Sluigi npkts = my_count - prev; 1524246896Sluigi pps = (npkts*1000000 + usec/2) / usec; 1525261909Sluigi D("%llu pps (%llu pkts in %llu usec)", 1526261909Sluigi (unsigned long long)pps, 1527261909Sluigi (unsigned long long)npkts, 1528261909Sluigi (unsigned long long)usec); 1529246896Sluigi prev = my_count; 1530246896Sluigi toc = now; 1531246896Sluigi if (done == g->nthreads) 1532246896Sluigi break; 1533246896Sluigi } 1534246896Sluigi 1535246896Sluigi timerclear(&tic); 1536246896Sluigi timerclear(&toc); 1537246896Sluigi for (i = 0; i < g->nthreads; i++) { 1538251132Sluigi struct timespec t_tic, t_toc; 1539246896Sluigi /* 1540246896Sluigi * Join active threads, unregister interfaces and close 1541246896Sluigi * file descriptors. 1542246896Sluigi */ 1543251132Sluigi if (targs[i].used) 1544251132Sluigi pthread_join(targs[i].thread, NULL); 1545246896Sluigi close(targs[i].fd); 1546246896Sluigi 1547246896Sluigi if (targs[i].completed == 0) 1548246896Sluigi D("ouch, thread %d exited with error", i); 1549246896Sluigi 1550246896Sluigi /* 1551246896Sluigi * Collect threads output and extract information about 1552246896Sluigi * how long it took to send all the packets. 1553246896Sluigi */ 1554246896Sluigi count += targs[i].count; 1555251132Sluigi t_tic = timeval2spec(&tic); 1556251132Sluigi t_toc = timeval2spec(&toc); 1557251132Sluigi if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 1558251132Sluigi tic = timespec2val(&targs[i].tic); 1559251132Sluigi if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 1560251132Sluigi toc = timespec2val(&targs[i].toc); 1561246896Sluigi } 1562246896Sluigi 1563246896Sluigi /* print output. */ 1564246896Sluigi timersub(&toc, &tic, &toc); 1565246896Sluigi delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1566246896Sluigi if (g->td_body == sender_body) 1567246896Sluigi tx_output(count, g->pkt_size, delta_t); 1568246896Sluigi else 1569246896Sluigi rx_output(count, delta_t); 1570246896Sluigi 1571246896Sluigi if (g->dev_type == DEV_NETMAP) { 1572261909Sluigi munmap(g->nmd->mem, g->nmd->req.nr_memsize); 1573246896Sluigi close(g->main_fd); 1574246896Sluigi } 1575246896Sluigi} 1576246896Sluigi 1577246896Sluigi 1578246896Sluigistruct sf { 1579246896Sluigi char *key; 1580246896Sluigi void *f; 1581246896Sluigi}; 1582246896Sluigi 1583246896Sluigistatic struct sf func[] = { 1584246896Sluigi { "tx", sender_body }, 1585246896Sluigi { "rx", receiver_body }, 1586246896Sluigi { "ping", pinger_body }, 1587246896Sluigi { "pong", ponger_body }, 1588246896Sluigi { NULL, NULL } 1589246896Sluigi}; 1590246896Sluigi 1591246896Sluigistatic int 1592246896Sluigitap_alloc(char *dev) 1593246896Sluigi{ 1594246896Sluigi struct ifreq ifr; 1595246896Sluigi int fd, err; 1596246896Sluigi char *clonedev = TAP_CLONEDEV; 1597246896Sluigi 1598246896Sluigi (void)err; 1599246896Sluigi (void)dev; 1600246896Sluigi /* Arguments taken by the function: 1601246896Sluigi * 1602246896Sluigi * char *dev: the name of an interface (or '\0'). MUST have enough 1603246896Sluigi * space to hold the interface name if '\0' is passed 1604246896Sluigi * int flags: interface flags (eg, IFF_TUN etc.) 1605246896Sluigi */ 1606246896Sluigi 1607246896Sluigi#ifdef __FreeBSD__ 1608246896Sluigi if (dev[3]) { /* tapSomething */ 1609246896Sluigi static char buf[128]; 1610246896Sluigi snprintf(buf, sizeof(buf), "/dev/%s", dev); 1611246896Sluigi clonedev = buf; 1612246896Sluigi } 1613246896Sluigi#endif 1614246896Sluigi /* open the device */ 1615246896Sluigi if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1616246896Sluigi return fd; 1617246896Sluigi } 1618246896Sluigi D("%s open successful", clonedev); 1619246896Sluigi 1620246896Sluigi /* preparation of the struct ifr, of type "struct ifreq" */ 1621246896Sluigi memset(&ifr, 0, sizeof(ifr)); 1622246896Sluigi 1623246896Sluigi#ifdef linux 1624246896Sluigi ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1625246896Sluigi 1626246896Sluigi if (*dev) { 1627246896Sluigi /* if a device name was specified, put it in the structure; otherwise, 1628246896Sluigi * the kernel will try to allocate the "next" device of the 1629246896Sluigi * specified type */ 1630246896Sluigi strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1631246896Sluigi } 1632246896Sluigi 1633246896Sluigi /* try to create the device */ 1634246896Sluigi if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 1635260368Sluigi D("failed to to a TUNSETIFF: %s", strerror(errno)); 1636246896Sluigi close(fd); 1637246896Sluigi return err; 1638246896Sluigi } 1639246896Sluigi 1640246896Sluigi /* if the operation was successful, write back the name of the 1641246896Sluigi * interface to the variable "dev", so the caller can know 1642246896Sluigi * it. Note that the caller MUST reserve space in *dev (see calling 1643246896Sluigi * code below) */ 1644246896Sluigi strcpy(dev, ifr.ifr_name); 1645246896Sluigi D("new name is %s", dev); 1646246896Sluigi#endif /* linux */ 1647246896Sluigi 1648246896Sluigi /* this is the special file descriptor that the caller will use to talk 1649246896Sluigi * with the virtual interface */ 1650246896Sluigi return fd; 1651246896Sluigi} 1652246896Sluigi 1653227614Sluigiint 1654227614Sluigimain(int arc, char **argv) 1655227614Sluigi{ 1656246896Sluigi int i; 1657227614Sluigi 1658227614Sluigi struct glob_arg g; 1659227614Sluigi 1660227614Sluigi int ch; 1661227614Sluigi int wait_link = 2; 1662227614Sluigi int devqueues = 1; /* how many device queues */ 1663227614Sluigi 1664227614Sluigi bzero(&g, sizeof(g)); 1665227614Sluigi 1666246896Sluigi g.main_fd = -1; 1667246896Sluigi g.td_body = receiver_body; 1668246896Sluigi g.report_interval = 1000; /* report interval */ 1669246896Sluigi g.affinity = -1; 1670246896Sluigi /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1671246896Sluigi g.src_ip.name = "10.0.0.1"; 1672246896Sluigi g.dst_ip.name = "10.1.0.1"; 1673246896Sluigi g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1674246896Sluigi g.src_mac.name = NULL; 1675227614Sluigi g.pkt_size = 60; 1676227614Sluigi g.burst = 512; // default 1677227614Sluigi g.nthreads = 1; 1678227614Sluigi g.cpus = 1; 1679251426Sluigi g.forever = 1; 1680251132Sluigi g.tx_rate = 0; 1681257529Sluigi g.frags = 1; 1682257529Sluigi g.nmr_config = ""; 1683260368Sluigi g.virt_header = 0; 1684227614Sluigi 1685227614Sluigi while ( (ch = getopt(arc, argv, 1686281746Sadrian "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:m:P:zZ")) != -1) { 1687246896Sluigi struct sf *fn; 1688246896Sluigi 1689227614Sluigi switch(ch) { 1690227614Sluigi default: 1691227614Sluigi D("bad option %c %s", ch, optarg); 1692227614Sluigi usage(); 1693227614Sluigi break; 1694246896Sluigi 1695246896Sluigi case 'n': 1696246896Sluigi g.npackets = atoi(optarg); 1697246896Sluigi break; 1698246896Sluigi 1699257529Sluigi case 'F': 1700257529Sluigi i = atoi(optarg); 1701257529Sluigi if (i < 1 || i > 63) { 1702257529Sluigi D("invalid frags %d [1..63], ignore", i); 1703257529Sluigi break; 1704257529Sluigi } 1705257529Sluigi g.frags = i; 1706257529Sluigi break; 1707257529Sluigi 1708246896Sluigi case 'f': 1709246896Sluigi for (fn = func; fn->key; fn++) { 1710246896Sluigi if (!strcmp(fn->key, optarg)) 1711246896Sluigi break; 1712246896Sluigi } 1713246896Sluigi if (fn->key) 1714246896Sluigi g.td_body = fn->f; 1715246896Sluigi else 1716246896Sluigi D("unrecognised function %s", optarg); 1717246896Sluigi break; 1718246896Sluigi 1719246896Sluigi case 'o': /* data generation options */ 1720234956Sluigi g.options = atoi(optarg); 1721234956Sluigi break; 1722246896Sluigi 1723246896Sluigi case 'a': /* force affinity */ 1724246896Sluigi g.affinity = atoi(optarg); 1725246896Sluigi break; 1726246896Sluigi 1727227614Sluigi case 'i': /* interface */ 1728260700Sluigi /* a prefix of tap: netmap: or pcap: forces the mode. 1729260700Sluigi * otherwise we guess 1730260700Sluigi */ 1731260700Sluigi D("interface is %s", optarg); 1732261909Sluigi if (strlen(optarg) > MAX_IFNAMELEN - 8) { 1733261909Sluigi D("ifname too long %s", optarg); 1734261909Sluigi break; 1735261909Sluigi } 1736261909Sluigi strcpy(g.ifname, optarg); 1737260700Sluigi if (!strcmp(optarg, "null")) { 1738260700Sluigi g.dev_type = DEV_NETMAP; 1739260700Sluigi g.dummy_send = 1; 1740260700Sluigi } else if (!strncmp(optarg, "tap:", 4)) { 1741246896Sluigi g.dev_type = DEV_TAP; 1742261909Sluigi strcpy(g.ifname, optarg + 4); 1743260700Sluigi } else if (!strncmp(optarg, "pcap:", 5)) { 1744260700Sluigi g.dev_type = DEV_PCAP; 1745261909Sluigi strcpy(g.ifname, optarg + 5); 1746261909Sluigi } else if (!strncmp(optarg, "netmap:", 7) || 1747261909Sluigi !strncmp(optarg, "vale", 4)) { 1748246896Sluigi g.dev_type = DEV_NETMAP; 1749260700Sluigi } else if (!strncmp(optarg, "tap", 3)) { 1750260700Sluigi g.dev_type = DEV_TAP; 1751261909Sluigi } else { /* prepend netmap: */ 1752260700Sluigi g.dev_type = DEV_NETMAP; 1753261909Sluigi sprintf(g.ifname, "netmap:%s", optarg); 1754260700Sluigi } 1755227614Sluigi break; 1756246896Sluigi 1757251426Sluigi case 'I': 1758251426Sluigi g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1759251426Sluigi break; 1760251426Sluigi 1761227614Sluigi case 'l': /* pkt_size */ 1762227614Sluigi g.pkt_size = atoi(optarg); 1763227614Sluigi break; 1764246896Sluigi 1765227614Sluigi case 'd': 1766246896Sluigi g.dst_ip.name = optarg; 1767227614Sluigi break; 1768246896Sluigi 1769227614Sluigi case 's': 1770246896Sluigi g.src_ip.name = optarg; 1771227614Sluigi break; 1772246896Sluigi 1773227614Sluigi case 'T': /* report interval */ 1774246896Sluigi g.report_interval = atoi(optarg); 1775227614Sluigi break; 1776246896Sluigi 1777227614Sluigi case 'w': 1778227614Sluigi wait_link = atoi(optarg); 1779227614Sluigi break; 1780246896Sluigi 1781251426Sluigi case 'W': /* XXX changed default */ 1782251426Sluigi g.forever = 0; /* do not exit rx even with no traffic */ 1783246896Sluigi break; 1784246896Sluigi 1785227614Sluigi case 'b': /* burst */ 1786227614Sluigi g.burst = atoi(optarg); 1787227614Sluigi break; 1788227614Sluigi case 'c': 1789227614Sluigi g.cpus = atoi(optarg); 1790227614Sluigi break; 1791227614Sluigi case 'p': 1792227614Sluigi g.nthreads = atoi(optarg); 1793227614Sluigi break; 1794227614Sluigi 1795227614Sluigi case 'D': /* destination mac */ 1796246896Sluigi g.dst_mac.name = optarg; 1797227614Sluigi break; 1798246896Sluigi 1799227614Sluigi case 'S': /* source mac */ 1800246896Sluigi g.src_mac.name = optarg; 1801227614Sluigi break; 1802227614Sluigi case 'v': 1803227614Sluigi verbose++; 1804251132Sluigi break; 1805251132Sluigi case 'R': 1806251132Sluigi g.tx_rate = atoi(optarg); 1807251132Sluigi break; 1808251426Sluigi case 'X': 1809251426Sluigi g.options |= OPT_DUMP; 1810257529Sluigi break; 1811257529Sluigi case 'C': 1812257529Sluigi g.nmr_config = strdup(optarg); 1813260368Sluigi break; 1814260368Sluigi case 'H': 1815260368Sluigi g.virt_header = atoi(optarg); 1816260700Sluigi break; 1817261909Sluigi case 'e': /* extra bufs */ 1818261909Sluigi g.extra_bufs = atoi(optarg); 1819260700Sluigi break; 1820270063Sluigi case 'm': 1821270063Sluigi if (strcmp(optarg, "tx") == 0) { 1822270063Sluigi g.options |= OPT_MONITOR_TX; 1823270063Sluigi } else if (strcmp(optarg, "rx") == 0) { 1824270063Sluigi g.options |= OPT_MONITOR_RX; 1825270063Sluigi } else { 1826270063Sluigi D("unrecognized monitor mode %s", optarg); 1827270063Sluigi } 1828270063Sluigi break; 1829272962Sgnn case 'P': 1830272962Sgnn g.packet_file = strdup(optarg); 1831272962Sgnn break; 1832281746Sadrian case 'z': 1833281746Sadrian g.options |= OPT_RANDOM_SRC; 1834281746Sadrian break; 1835281746Sadrian case 'Z': 1836281746Sadrian g.options |= OPT_RANDOM_DST; 1837281746Sadrian break; 1838227614Sluigi } 1839227614Sluigi } 1840227614Sluigi 1841278641Sgnn if (strlen(g.ifname) <=0 ) { 1842227614Sluigi D("missing ifname"); 1843227614Sluigi usage(); 1844227614Sluigi } 1845246896Sluigi 1846246896Sluigi i = system_ncpus(); 1847246896Sluigi if (g.cpus < 0 || g.cpus > i) { 1848246896Sluigi D("%d cpus is too high, have only %d cpus", g.cpus, i); 1849246896Sluigi usage(); 1850227614Sluigi } 1851246896Sluigi if (g.cpus == 0) 1852246896Sluigi g.cpus = i; 1853246896Sluigi 1854270063Sluigi if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { 1855270063Sluigi D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); 1856227614Sluigi usage(); 1857227614Sluigi } 1858227614Sluigi 1859246896Sluigi if (g.src_mac.name == NULL) { 1860246896Sluigi static char mybuf[20] = "00:00:00:00:00:00"; 1861234956Sluigi /* retrieve source mac address. */ 1862246896Sluigi if (source_hwaddr(g.ifname, mybuf) == -1) { 1863234956Sluigi D("Unable to retrieve source mac"); 1864234956Sluigi // continue, fail later 1865234956Sluigi } 1866246896Sluigi g.src_mac.name = mybuf; 1867234956Sluigi } 1868246896Sluigi /* extract address ranges */ 1869246896Sluigi extract_ip_range(&g.src_ip); 1870246896Sluigi extract_ip_range(&g.dst_ip); 1871246896Sluigi extract_mac_range(&g.src_mac); 1872246896Sluigi extract_mac_range(&g.dst_mac); 1873234956Sluigi 1874260700Sluigi if (g.src_ip.start != g.src_ip.end || 1875260700Sluigi g.src_ip.port0 != g.src_ip.port1 || 1876260700Sluigi g.dst_ip.start != g.dst_ip.end || 1877260700Sluigi g.dst_ip.port0 != g.dst_ip.port1) 1878260700Sluigi g.options |= OPT_COPY; 1879260700Sluigi 1880260368Sluigi if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 1881260368Sluigi && g.virt_header != VIRT_HDR_2) { 1882260368Sluigi D("bad virtio-net-header length"); 1883260368Sluigi usage(); 1884260368Sluigi } 1885260368Sluigi 1886246896Sluigi if (g.dev_type == DEV_TAP) { 1887246896Sluigi D("want to use tap %s", g.ifname); 1888246896Sluigi g.main_fd = tap_alloc(g.ifname); 1889246896Sluigi if (g.main_fd < 0) { 1890246896Sluigi D("cannot open tap %s", g.ifname); 1891246896Sluigi usage(); 1892246896Sluigi } 1893260700Sluigi#ifndef NO_PCAP 1894260700Sluigi } else if (g.dev_type == DEV_PCAP) { 1895246896Sluigi char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1896246896Sluigi 1897246896Sluigi pcap_errbuf[0] = '\0'; // init the buffer 1898270063Sluigi g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); 1899234956Sluigi if (g.p == NULL) { 1900246896Sluigi D("cannot open pcap on %s", g.ifname); 1901234956Sluigi usage(); 1902234956Sluigi } 1903270063Sluigi g.main_fd = pcap_fileno(g.p); 1904270063Sluigi D("using pcap on %s fileno %d", g.ifname, g.main_fd); 1905260700Sluigi#endif /* !NO_PCAP */ 1906260700Sluigi } else if (g.dummy_send) { /* but DEV_NETMAP */ 1907257529Sluigi D("using a dummy send routine"); 1908234956Sluigi } else { 1909270063Sluigi struct nmreq base_nmd; 1910261909Sluigi 1911261909Sluigi bzero(&base_nmd, sizeof(base_nmd)); 1912261909Sluigi 1913270063Sluigi parse_nmr_config(g.nmr_config, &base_nmd); 1914261909Sluigi if (g.extra_bufs) { 1915270063Sluigi base_nmd.nr_arg3 = g.extra_bufs; 1916261909Sluigi } 1917261909Sluigi 1918227614Sluigi /* 1919261909Sluigi * Open the netmap device using nm_open(). 1920227614Sluigi * 1921227614Sluigi * protocol stack and may cause a reset of the card, 1922227614Sluigi * which in turn may take some time for the PHY to 1923261909Sluigi * reconfigure. We do the open here to have time to reset. 1924227614Sluigi */ 1925270063Sluigi g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL); 1926261909Sluigi if (g.nmd == NULL) { 1927261909Sluigi D("Unable to open %s: %s", g.ifname, strerror(errno)); 1928261909Sluigi goto out; 1929227614Sluigi } 1930261909Sluigi g.main_fd = g.nmd->fd; 1931261909Sluigi D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); 1932227614Sluigi 1933270063Sluigi /* get num of queues in tx or rx */ 1934270063Sluigi if (g.td_body == sender_body) 1935270063Sluigi devqueues = g.nmd->req.nr_tx_rings; 1936270063Sluigi else 1937270063Sluigi devqueues = g.nmd->req.nr_rx_rings; 1938261909Sluigi 1939227614Sluigi /* validate provided nthreads. */ 1940227614Sluigi if (g.nthreads < 1 || g.nthreads > devqueues) { 1941227614Sluigi D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 1942227614Sluigi // continue, fail later 1943227614Sluigi } 1944227614Sluigi 1945260700Sluigi if (verbose) { 1946261909Sluigi struct netmap_if *nifp = g.nmd->nifp; 1947261909Sluigi struct nmreq *req = &g.nmd->req; 1948227614Sluigi 1949261909Sluigi D("nifp at offset %d, %d tx %d rx region %d", 1950261909Sluigi req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, 1951261909Sluigi req->nr_arg2); 1952261909Sluigi for (i = 0; i <= req->nr_tx_rings; i++) { 1953270063Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, i); 1954270063Sluigi D(" TX%d at 0x%lx slots %d", i, 1955270063Sluigi (char *)ring - (char *)nifp, ring->num_slots); 1956260700Sluigi } 1957261909Sluigi for (i = 0; i <= req->nr_rx_rings; i++) { 1958270063Sluigi struct netmap_ring *ring = NETMAP_RXRING(nifp, i); 1959270063Sluigi D(" RX%d at 0x%lx slots %d", i, 1960270063Sluigi (char *)ring - (char *)nifp, ring->num_slots); 1961260700Sluigi } 1962260700Sluigi } 1963227614Sluigi 1964227614Sluigi /* Print some debug information. */ 1965227614Sluigi fprintf(stdout, 1966227614Sluigi "%s %s: %d queues, %d threads and %d cpus.\n", 1967246896Sluigi (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1968246896Sluigi g.ifname, 1969227614Sluigi devqueues, 1970227614Sluigi g.nthreads, 1971227614Sluigi g.cpus); 1972246896Sluigi if (g.td_body == sender_body) { 1973227614Sluigi fprintf(stdout, "%s -> %s (%s -> %s)\n", 1974246896Sluigi g.src_ip.name, g.dst_ip.name, 1975246896Sluigi g.src_mac.name, g.dst_mac.name); 1976227614Sluigi } 1977261909Sluigi 1978261909Sluigiout: 1979227614Sluigi /* Exit if something went wrong. */ 1980246896Sluigi if (g.main_fd < 0) { 1981227614Sluigi D("aborting"); 1982227614Sluigi usage(); 1983227614Sluigi } 1984234956Sluigi } 1985227614Sluigi 1986261909Sluigi 1987234956Sluigi if (g.options) { 1988251426Sluigi D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 1989234956Sluigi g.options & OPT_PREFETCH ? " prefetch" : "", 1990234956Sluigi g.options & OPT_ACCESS ? " access" : "", 1991234956Sluigi g.options & OPT_MEMCPY ? " memcpy" : "", 1992251426Sluigi g.options & OPT_INDIRECT ? " indirect" : "", 1993234956Sluigi g.options & OPT_COPY ? " copy" : ""); 1994234956Sluigi } 1995257529Sluigi 1996257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 1997257529Sluigi if (g.tx_rate > 0) { 1998257529Sluigi /* try to have at least something every second, 1999260368Sluigi * reducing the burst size to some 0.01s worth of data 2000257529Sluigi * (but no less than one full set of fragments) 2001257529Sluigi */ 2002260368Sluigi uint64_t x; 2003260368Sluigi int lim = (g.tx_rate)/300; 2004260368Sluigi if (g.burst > lim) 2005260368Sluigi g.burst = lim; 2006257529Sluigi if (g.burst < g.frags) 2007257529Sluigi g.burst = g.frags; 2008260368Sluigi x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 2009260368Sluigi g.tx_period.tv_nsec = x; 2010257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 2011257529Sluigi g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 2012251132Sluigi } 2013257529Sluigi if (g.td_body == sender_body) 2014257529Sluigi D("Sending %d packets every %ld.%09ld s", 2015257529Sluigi g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 2016227614Sluigi /* Wait for PHY reset. */ 2017227614Sluigi D("Wait %d secs for phy reset", wait_link); 2018227614Sluigi sleep(wait_link); 2019227614Sluigi D("Ready..."); 2020227614Sluigi 2021227614Sluigi /* Install ^C handler. */ 2022227614Sluigi global_nthreads = g.nthreads; 2023227614Sluigi signal(SIGINT, sigint_h); 2024227614Sluigi 2025246896Sluigi start_threads(&g); 2026246896Sluigi main_thread(&g); 2027246896Sluigi return 0; 2028246896Sluigi} 2029227614Sluigi 2030227614Sluigi/* end of file */ 2031