pkt-gen.c revision 272962
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: head/tools/tools/netmap/pkt-gen.c 272962 2014-10-11 21:43:05Z gnn $ 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 186246896Sluigi int dev_type; 187260700Sluigi#ifndef NO_PCAP 188227614Sluigi pcap_t *p; 189260700Sluigi#endif 190227614Sluigi 191251132Sluigi int tx_rate; 192251132Sluigi struct timespec tx_period; 193251132Sluigi 194246896Sluigi int affinity; 195246896Sluigi int main_fd; 196261909Sluigi struct nm_desc *nmd; 197260700Sluigi int report_interval; /* milliseconds between prints */ 198246896Sluigi void *(*td_body)(void *); 199246896Sluigi void *mmap_addr; 200261909Sluigi char ifname[MAX_IFNAMELEN]; 201257529Sluigi char *nmr_config; 202257529Sluigi int dummy_send; 203260368Sluigi int virt_header; /* send also the virt_header */ 204261909Sluigi int extra_bufs; /* goes in nr_arg3 */ 205272962Sgnn char *packet_file; /* -P option */ 206227614Sluigi}; 207246896Sluigienum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 208227614Sluigi 209246896Sluigi 210227614Sluigi/* 211227614Sluigi * Arguments for a new thread. The same structure is used by 212227614Sluigi * the source and the sink 213227614Sluigi */ 214227614Sluigistruct targ { 215227614Sluigi struct glob_arg *g; 216227614Sluigi int used; 217227614Sluigi int completed; 218238165Semaste int cancel; 219227614Sluigi int fd; 220261909Sluigi struct nm_desc *nmd; 221246896Sluigi volatile uint64_t count; 222251132Sluigi struct timespec tic, toc; 223227614Sluigi int me; 224227614Sluigi pthread_t thread; 225227614Sluigi int affinity; 226227614Sluigi 227227614Sluigi struct pkt pkt; 228272962Sgnn void *frame; 229227614Sluigi}; 230227614Sluigi 231227614Sluigi 232246896Sluigi/* 233246896Sluigi * extract the extremes from a range of ipv4 addresses. 234246896Sluigi * addr_lo[-addr_hi][:port_lo[-port_hi]] 235246896Sluigi */ 236246896Sluigistatic void 237246896Sluigiextract_ip_range(struct ip_range *r) 238246896Sluigi{ 239257529Sluigi char *ap, *pp; 240257529Sluigi struct in_addr a; 241246896Sluigi 242260368Sluigi if (verbose) 243260368Sluigi D("extract IP range from %s", r->name); 244257529Sluigi r->port0 = r->port1 = 0; 245257529Sluigi r->start = r->end = 0; 246257529Sluigi 247257529Sluigi /* the first - splits start/end of range */ 248257529Sluigi ap = index(r->name, '-'); /* do we have ports ? */ 249257529Sluigi if (ap) { 250257529Sluigi *ap++ = '\0'; 251257529Sluigi } 252257529Sluigi /* grab the initial values (mandatory) */ 253257529Sluigi pp = index(r->name, ':'); 254257529Sluigi if (pp) { 255257529Sluigi *pp++ = '\0'; 256257529Sluigi r->port0 = r->port1 = strtol(pp, NULL, 0); 257257529Sluigi }; 258257529Sluigi inet_aton(r->name, &a); 259257529Sluigi r->start = r->end = ntohl(a.s_addr); 260257529Sluigi if (ap) { 261257529Sluigi pp = index(ap, ':'); 262257529Sluigi if (pp) { 263257529Sluigi *pp++ = '\0'; 264261909Sluigi if (*pp) 265257529Sluigi r->port1 = strtol(pp, NULL, 0); 266246896Sluigi } 267257529Sluigi if (*ap) { 268257529Sluigi inet_aton(ap, &a); 269257529Sluigi r->end = ntohl(a.s_addr); 270257529Sluigi } 271246896Sluigi } 272257529Sluigi if (r->port0 > r->port1) { 273257529Sluigi uint16_t tmp = r->port0; 274257529Sluigi r->port0 = r->port1; 275257529Sluigi r->port1 = tmp; 276257529Sluigi } 277257529Sluigi if (r->start > r->end) { 278257529Sluigi uint32_t tmp = r->start; 279246896Sluigi r->start = r->end; 280257529Sluigi r->end = tmp; 281246896Sluigi } 282257529Sluigi { 283257529Sluigi struct in_addr a; 284257529Sluigi char buf1[16]; // one ip address 285257529Sluigi 286257529Sluigi a.s_addr = htonl(r->end); 287257529Sluigi strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 288257529Sluigi a.s_addr = htonl(r->start); 289260368Sluigi if (1) 290260368Sluigi D("range is %s:%d to %s:%d", 291257529Sluigi inet_ntoa(a), r->port0, buf1, r->port1); 292257529Sluigi } 293246896Sluigi} 294246896Sluigi 295246896Sluigistatic void 296246896Sluigiextract_mac_range(struct mac_range *r) 297246896Sluigi{ 298260368Sluigi if (verbose) 299260368Sluigi D("extract MAC range from %s", r->name); 300246896Sluigi bcopy(ether_aton(r->name), &r->start, 6); 301246896Sluigi bcopy(ether_aton(r->name), &r->end, 6); 302246896Sluigi#if 0 303246896Sluigi bcopy(targ->src_mac, eh->ether_shost, 6); 304246896Sluigi p = index(targ->g->src_mac, '-'); 305246896Sluigi if (p) 306246896Sluigi targ->src_mac_range = atoi(p+1); 307246896Sluigi 308246896Sluigi bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 309246896Sluigi bcopy(targ->dst_mac, eh->ether_dhost, 6); 310246896Sluigi p = index(targ->g->dst_mac, '-'); 311246896Sluigi if (p) 312246896Sluigi targ->dst_mac_range = atoi(p+1); 313246896Sluigi#endif 314260368Sluigi if (verbose) 315260368Sluigi D("%s starts at %s", r->name, ether_ntoa(&r->start)); 316246896Sluigi} 317246896Sluigi 318227614Sluigistatic struct targ *targs; 319227614Sluigistatic int global_nthreads; 320227614Sluigi 321227614Sluigi/* control-C handler */ 322227614Sluigistatic void 323246896Sluigisigint_h(int sig) 324227614Sluigi{ 325246896Sluigi int i; 326246896Sluigi 327246896Sluigi (void)sig; /* UNUSED */ 328270063Sluigi D("received control-C on thread %p", pthread_self()); 329246896Sluigi for (i = 0; i < global_nthreads; i++) { 330238165Semaste targs[i].cancel = 1; 331246896Sluigi } 332227614Sluigi signal(SIGINT, SIG_DFL); 333227614Sluigi} 334227614Sluigi 335227614Sluigi/* sysctl wrapper to return the number of active CPUs */ 336227614Sluigistatic int 337227614Sluigisystem_ncpus(void) 338227614Sluigi{ 339261909Sluigi int ncpus; 340261909Sluigi#if defined (__FreeBSD__) 341261909Sluigi int mib[2] = { CTL_HW, HW_NCPU }; 342261909Sluigi size_t len = sizeof(mib); 343227614Sluigi sysctl(mib, 2, &ncpus, &len, NULL, 0); 344261909Sluigi#elif defined(linux) 345261909Sluigi ncpus = sysconf(_SC_NPROCESSORS_ONLN); 346261909Sluigi#else /* others */ 347261909Sluigi ncpus = 1; 348261909Sluigi#endif /* others */ 349227614Sluigi return (ncpus); 350227614Sluigi} 351227614Sluigi 352246896Sluigi#ifdef __linux__ 353246896Sluigi#define sockaddr_dl sockaddr_ll 354246896Sluigi#define sdl_family sll_family 355246896Sluigi#define AF_LINK AF_PACKET 356246896Sluigi#define LLADDR(s) s->sll_addr; 357246896Sluigi#include <linux/if_tun.h> 358246896Sluigi#define TAP_CLONEDEV "/dev/net/tun" 359246896Sluigi#endif /* __linux__ */ 360246896Sluigi 361246896Sluigi#ifdef __FreeBSD__ 362246896Sluigi#include <net/if_tun.h> 363246896Sluigi#define TAP_CLONEDEV "/dev/tap" 364246896Sluigi#endif /* __FreeBSD */ 365246896Sluigi 366246896Sluigi#ifdef __APPLE__ 367246896Sluigi// #warning TAP not supported on apple ? 368246896Sluigi#include <net/if_utun.h> 369246896Sluigi#define TAP_CLONEDEV "/dev/tap" 370246896Sluigi#endif /* __APPLE__ */ 371246896Sluigi 372246896Sluigi 373227614Sluigi/* 374257529Sluigi * parse the vale configuration in conf and put it in nmr. 375261909Sluigi * Return the flag set if necessary. 376257529Sluigi * The configuration may consist of 0 to 4 numbers separated 377257906Shiren * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 378257529Sluigi * Missing numbers or zeroes stand for default values. 379257529Sluigi * As an additional convenience, if exactly one number 380257906Shiren * is specified, then this is assigned to both #tx-slots and #rx-slots. 381261909Sluigi * If there is no 4th number, then the 3rd is assigned to both #tx-rings 382257529Sluigi * and #rx-rings. 383257529Sluigi */ 384261909Sluigiint 385261909Sluigiparse_nmr_config(const char* conf, struct nmreq *nmr) 386257529Sluigi{ 387257529Sluigi char *w, *tok; 388257529Sluigi int i, v; 389257529Sluigi 390257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 391257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 392257529Sluigi if (conf == NULL || ! *conf) 393261909Sluigi return 0; 394257529Sluigi w = strdup(conf); 395257529Sluigi for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 396257529Sluigi v = atoi(tok); 397257529Sluigi switch (i) { 398257529Sluigi case 0: 399257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = v; 400257529Sluigi break; 401257529Sluigi case 1: 402257529Sluigi nmr->nr_rx_slots = v; 403257529Sluigi break; 404257529Sluigi case 2: 405257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = v; 406257529Sluigi break; 407257529Sluigi case 3: 408257529Sluigi nmr->nr_rx_rings = v; 409257529Sluigi break; 410257529Sluigi default: 411257529Sluigi D("ignored config: %s", tok); 412257529Sluigi break; 413257529Sluigi } 414257529Sluigi } 415257529Sluigi D("txr %d txd %d rxr %d rxd %d", 416257529Sluigi nmr->nr_tx_rings, nmr->nr_tx_slots, 417257529Sluigi nmr->nr_rx_rings, nmr->nr_rx_slots); 418257529Sluigi free(w); 419261909Sluigi return (nmr->nr_tx_rings || nmr->nr_tx_slots || 420261909Sluigi nmr->nr_rx_rings || nmr->nr_rx_slots) ? 421261909Sluigi NM_OPEN_RING_CFG : 0; 422257529Sluigi} 423257529Sluigi 424257529Sluigi 425257529Sluigi/* 426227614Sluigi * locate the src mac address for our interface, put it 427227614Sluigi * into the user-supplied buffer. return 0 if ok, -1 on error. 428227614Sluigi */ 429227614Sluigistatic int 430227614Sluigisource_hwaddr(const char *ifname, char *buf) 431227614Sluigi{ 432227614Sluigi struct ifaddrs *ifaphead, *ifap; 433227614Sluigi int l = sizeof(ifap->ifa_name); 434227614Sluigi 435227614Sluigi if (getifaddrs(&ifaphead) != 0) { 436227614Sluigi D("getifaddrs %s failed", ifname); 437227614Sluigi return (-1); 438227614Sluigi } 439227614Sluigi 440227614Sluigi for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 441227614Sluigi struct sockaddr_dl *sdl = 442227614Sluigi (struct sockaddr_dl *)ifap->ifa_addr; 443227614Sluigi uint8_t *mac; 444227614Sluigi 445227614Sluigi if (!sdl || sdl->sdl_family != AF_LINK) 446227614Sluigi continue; 447227614Sluigi if (strncmp(ifap->ifa_name, ifname, l) != 0) 448227614Sluigi continue; 449227614Sluigi mac = (uint8_t *)LLADDR(sdl); 450227614Sluigi sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 451227614Sluigi mac[0], mac[1], mac[2], 452227614Sluigi mac[3], mac[4], mac[5]); 453227614Sluigi if (verbose) 454227614Sluigi D("source hwaddr %s", buf); 455227614Sluigi break; 456227614Sluigi } 457227614Sluigi freeifaddrs(ifaphead); 458227614Sluigi return ifap ? 0 : 1; 459227614Sluigi} 460227614Sluigi 461227614Sluigi 462227614Sluigi/* set the thread affinity. */ 463227614Sluigistatic int 464227614Sluigisetaffinity(pthread_t me, int i) 465227614Sluigi{ 466227614Sluigi cpuset_t cpumask; 467227614Sluigi 468227614Sluigi if (i == -1) 469227614Sluigi return 0; 470227614Sluigi 471227614Sluigi /* Set thread affinity affinity.*/ 472227614Sluigi CPU_ZERO(&cpumask); 473227614Sluigi CPU_SET(i, &cpumask); 474227614Sluigi 475227614Sluigi if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 476260368Sluigi D("Unable to set affinity: %s", strerror(errno)); 477227614Sluigi return 1; 478227614Sluigi } 479227614Sluigi return 0; 480227614Sluigi} 481227614Sluigi 482227614Sluigi/* Compute the checksum of the given ip header. */ 483227614Sluigistatic uint16_t 484246896Sluigichecksum(const void *data, uint16_t len, uint32_t sum) 485227614Sluigi{ 486227614Sluigi const uint8_t *addr = data; 487246896Sluigi uint32_t i; 488227614Sluigi 489246896Sluigi /* Checksum all the pairs of bytes first... */ 490246896Sluigi for (i = 0; i < (len & ~1U); i += 2) { 491246896Sluigi sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 492246896Sluigi if (sum > 0xFFFF) 493246896Sluigi sum -= 0xFFFF; 494227614Sluigi } 495246896Sluigi /* 496246896Sluigi * If there's a single byte left over, checksum it, too. 497246896Sluigi * Network byte order is big-endian, so the remaining byte is 498246896Sluigi * the high byte. 499246896Sluigi */ 500246896Sluigi if (i < len) { 501246896Sluigi sum += addr[i] << 8; 502246896Sluigi if (sum > 0xFFFF) 503246896Sluigi sum -= 0xFFFF; 504246896Sluigi } 505246896Sluigi return sum; 506246896Sluigi} 507227614Sluigi 508246896Sluigistatic u_int16_t 509246896Sluigiwrapsum(u_int32_t sum) 510246896Sluigi{ 511246896Sluigi sum = ~sum & 0xFFFF; 512246896Sluigi return (htons(sum)); 513227614Sluigi} 514227614Sluigi 515251426Sluigi/* Check the payload of the packet for errors (use it for debug). 516251426Sluigi * Look for consecutive ascii representations of the size of the packet. 517251426Sluigi */ 518251426Sluigistatic void 519251426Sluigidump_payload(char *p, int len, struct netmap_ring *ring, int cur) 520251426Sluigi{ 521251426Sluigi char buf[128]; 522251426Sluigi int i, j, i0; 523251426Sluigi 524251426Sluigi /* get the length in ASCII of the length of the packet. */ 525261909Sluigi 526257529Sluigi printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 527257529Sluigi ring, cur, ring->slot[cur].buf_idx, 528257529Sluigi ring->slot[cur].flags, len); 529251426Sluigi /* hexdump routine */ 530251426Sluigi for (i = 0; i < len; ) { 531251426Sluigi memset(buf, sizeof(buf), ' '); 532251426Sluigi sprintf(buf, "%5d: ", i); 533251426Sluigi i0 = i; 534251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 535251426Sluigi sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 536251426Sluigi i = i0; 537251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 538251426Sluigi sprintf(buf+7+j + 48, "%c", 539251426Sluigi isprint(p[i]) ? p[i] : '.'); 540251426Sluigi printf("%s\n", buf); 541251426Sluigi } 542251426Sluigi} 543251426Sluigi 544227614Sluigi/* 545227614Sluigi * Fill a packet with some payload. 546246896Sluigi * We create a UDP packet so the payload starts at 547246896Sluigi * 14+20+8 = 42 bytes. 548227614Sluigi */ 549246896Sluigi#ifdef __linux__ 550246896Sluigi#define uh_sport source 551246896Sluigi#define uh_dport dest 552246896Sluigi#define uh_ulen len 553246896Sluigi#define uh_sum check 554246896Sluigi#endif /* linux */ 555251426Sluigi 556257529Sluigi/* 557257529Sluigi * increment the addressed in the packet, 558257529Sluigi * starting from the least significant field. 559257529Sluigi * DST_IP DST_PORT SRC_IP SRC_PORT 560257529Sluigi */ 561227614Sluigistatic void 562257529Sluigiupdate_addresses(struct pkt *pkt, struct glob_arg *g) 563257529Sluigi{ 564257529Sluigi uint32_t a; 565257529Sluigi uint16_t p; 566257529Sluigi struct ip *ip = &pkt->ip; 567257529Sluigi struct udphdr *udp = &pkt->udp; 568257529Sluigi 569260700Sluigi do { 570257529Sluigi p = ntohs(udp->uh_sport); 571257529Sluigi if (p < g->src_ip.port1) { /* just inc, no wrap */ 572257529Sluigi udp->uh_sport = htons(p + 1); 573260700Sluigi break; 574257529Sluigi } 575257529Sluigi udp->uh_sport = htons(g->src_ip.port0); 576257529Sluigi 577257529Sluigi a = ntohl(ip->ip_src.s_addr); 578257529Sluigi if (a < g->src_ip.end) { /* just inc, no wrap */ 579257529Sluigi ip->ip_src.s_addr = htonl(a + 1); 580260700Sluigi break; 581257529Sluigi } 582257529Sluigi ip->ip_src.s_addr = htonl(g->src_ip.start); 583257529Sluigi 584257529Sluigi udp->uh_sport = htons(g->src_ip.port0); 585257529Sluigi p = ntohs(udp->uh_dport); 586257529Sluigi if (p < g->dst_ip.port1) { /* just inc, no wrap */ 587257529Sluigi udp->uh_dport = htons(p + 1); 588260700Sluigi break; 589257529Sluigi } 590257529Sluigi udp->uh_dport = htons(g->dst_ip.port0); 591257529Sluigi 592257529Sluigi a = ntohl(ip->ip_dst.s_addr); 593257529Sluigi if (a < g->dst_ip.end) { /* just inc, no wrap */ 594257529Sluigi ip->ip_dst.s_addr = htonl(a + 1); 595260700Sluigi break; 596257529Sluigi } 597257529Sluigi ip->ip_dst.s_addr = htonl(g->dst_ip.start); 598260700Sluigi } while (0); 599260700Sluigi // update checksum 600257529Sluigi} 601257529Sluigi 602257529Sluigi/* 603257529Sluigi * initialize one packet and prepare for the next one. 604257529Sluigi * The copy could be done better instead of repeating it each time. 605257529Sluigi */ 606257529Sluigistatic void 607227614Sluigiinitialize_packet(struct targ *targ) 608227614Sluigi{ 609227614Sluigi struct pkt *pkt = &targ->pkt; 610227614Sluigi struct ether_header *eh; 611227614Sluigi struct ip *ip; 612227614Sluigi struct udphdr *udp; 613246896Sluigi uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 614251426Sluigi const char *payload = targ->g->options & OPT_INDIRECT ? 615257529Sluigi indirect_payload : default_payload; 616260700Sluigi int i, l0 = strlen(payload); 617227614Sluigi 618272962Sgnn char errbuf[PCAP_ERRBUF_SIZE]; 619272962Sgnn pcap_t *file; 620272962Sgnn struct pcap_pkthdr *header; 621272962Sgnn const unsigned char *packet; 622272962Sgnn 623272962Sgnn /* Read a packet from a PCAP file if asked. */ 624272962Sgnn if (targ->g->packet_file != NULL) { 625272962Sgnn if ((file = pcap_open_offline(targ->g->packet_file, 626272962Sgnn errbuf)) == NULL) 627272962Sgnn D("failed to open pcap file %s", 628272962Sgnn targ->g->packet_file); 629272962Sgnn if (pcap_next_ex(file, &header, &packet) < 0) 630272962Sgnn D("failed to read packet from %s", 631272962Sgnn targ->g->packet_file); 632272962Sgnn if ((targ->frame = malloc(header->caplen)) == NULL) 633272962Sgnn D("out of memory"); 634272962Sgnn bcopy(packet, (unsigned char *)targ->frame, header->caplen); 635272962Sgnn targ->g->pkt_size = header->caplen; 636272962Sgnn pcap_close(file); 637272962Sgnn return; 638272962Sgnn } 639272962Sgnn 640257529Sluigi /* create a nice NUL-terminated string */ 641260700Sluigi for (i = 0; i < paylen; i += l0) { 642260700Sluigi if (l0 > paylen - i) 643260700Sluigi l0 = paylen - i; // last round 644260700Sluigi bcopy(payload, pkt->body + i, l0); 645227614Sluigi } 646227614Sluigi pkt->body[i-1] = '\0'; 647246896Sluigi ip = &pkt->ip; 648227614Sluigi 649257529Sluigi /* prepare the headers */ 650227614Sluigi ip->ip_v = IPVERSION; 651227614Sluigi ip->ip_hl = 5; 652227614Sluigi ip->ip_id = 0; 653227614Sluigi ip->ip_tos = IPTOS_LOWDELAY; 654227614Sluigi ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 655227614Sluigi ip->ip_id = 0; 656227614Sluigi ip->ip_off = htons(IP_DF); /* Don't fragment */ 657227614Sluigi ip->ip_ttl = IPDEFTTL; 658227614Sluigi ip->ip_p = IPPROTO_UDP; 659257529Sluigi ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 660257529Sluigi ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 661246896Sluigi ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 662227614Sluigi 663246896Sluigi 664246896Sluigi udp = &pkt->udp; 665257529Sluigi udp->uh_sport = htons(targ->g->src_ip.port0); 666257529Sluigi udp->uh_dport = htons(targ->g->dst_ip.port0); 667246896Sluigi udp->uh_ulen = htons(paylen); 668246896Sluigi /* Magic: taken from sbin/dhclient/packet.c */ 669246896Sluigi udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 670246896Sluigi checksum(pkt->body, 671246896Sluigi paylen - sizeof(*udp), 672246896Sluigi checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 673246896Sluigi IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 674246896Sluigi ) 675246896Sluigi ) 676246896Sluigi )); 677246896Sluigi 678227614Sluigi eh = &pkt->eh; 679246896Sluigi bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 680246896Sluigi bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 681227614Sluigi eh->ether_type = htons(ETHERTYPE_IP); 682260368Sluigi 683260368Sluigi bzero(&pkt->vh, sizeof(pkt->vh)); 684270063Sluigi#ifdef TRASH_VHOST_HDR 685270063Sluigi /* set bogus content */ 686270063Sluigi pkt->vh.fields[0] = 0xff; 687270063Sluigi pkt->vh.fields[1] = 0xff; 688270063Sluigi pkt->vh.fields[2] = 0xff; 689270063Sluigi pkt->vh.fields[3] = 0xff; 690270063Sluigi pkt->vh.fields[4] = 0xff; 691270063Sluigi pkt->vh.fields[5] = 0xff; 692270063Sluigi#endif /* TRASH_VHOST_HDR */ 693251426Sluigi // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 694227614Sluigi} 695227614Sluigi 696270063Sluigistatic void 697270063Sluigiset_vnet_hdr_len(struct targ *t) 698270063Sluigi{ 699270063Sluigi int err, l = t->g->virt_header; 700270063Sluigi struct nmreq req; 701227614Sluigi 702270063Sluigi if (l == 0) 703270063Sluigi return; 704227614Sluigi 705270063Sluigi memset(&req, 0, sizeof(req)); 706270063Sluigi bcopy(t->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 707270063Sluigi req.nr_version = NETMAP_API; 708270063Sluigi req.nr_cmd = NETMAP_BDG_VNET_HDR; 709270063Sluigi req.nr_arg1 = l; 710270063Sluigi err = ioctl(t->fd, NIOCREGIF, &req); 711270063Sluigi if (err) { 712270063Sluigi D("Unable to set vnet header length %d", l); 713270063Sluigi } 714270063Sluigi} 715270063Sluigi 716270063Sluigi 717227614Sluigi/* 718227614Sluigi * create and enqueue a batch of packets on a ring. 719227614Sluigi * On the last one set NS_REPORT to tell the driver to generate 720227614Sluigi * an interrupt when done. 721227614Sluigi */ 722227614Sluigistatic int 723260368Sluigisend_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 724260368Sluigi int size, struct glob_arg *g, u_int count, int options, 725260368Sluigi u_int nfrags) 726227614Sluigi{ 727260368Sluigi u_int n, sent, cur = ring->cur; 728260700Sluigi u_int fcnt; 729227614Sluigi 730260368Sluigi n = nm_ring_space(ring); 731260368Sluigi if (n < count) 732260368Sluigi count = n; 733257529Sluigi if (count < nfrags) { 734257529Sluigi D("truncating packet, no room for frags %d %d", 735260368Sluigi count, nfrags); 736257529Sluigi } 737234956Sluigi#if 0 738234956Sluigi if (options & (OPT_COPY | OPT_PREFETCH) ) { 739234956Sluigi for (sent = 0; sent < count; sent++) { 740234956Sluigi struct netmap_slot *slot = &ring->slot[cur]; 741234956Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 742234956Sluigi 743260700Sluigi __builtin_prefetch(p); 744260368Sluigi cur = nm_ring_next(ring, cur); 745234956Sluigi } 746234956Sluigi cur = ring->cur; 747234956Sluigi } 748234956Sluigi#endif 749257529Sluigi for (fcnt = nfrags, sent = 0; sent < count; sent++) { 750227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 751227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 752227614Sluigi 753251426Sluigi slot->flags = 0; 754251426Sluigi if (options & OPT_INDIRECT) { 755251426Sluigi slot->flags |= NS_INDIRECT; 756260368Sluigi slot->ptr = (uint64_t)frame; 757257529Sluigi } else if (options & OPT_COPY) { 758261909Sluigi nm_pkt_copy(frame, p, size); 759260700Sluigi if (fcnt == nfrags) 760257529Sluigi update_addresses(pkt, g); 761257529Sluigi } else if (options & OPT_MEMCPY) { 762260368Sluigi memcpy(p, frame, size); 763260700Sluigi if (fcnt == nfrags) 764257529Sluigi update_addresses(pkt, g); 765257529Sluigi } else if (options & OPT_PREFETCH) { 766260700Sluigi __builtin_prefetch(p); 767257529Sluigi } 768257529Sluigi if (options & OPT_DUMP) 769257529Sluigi dump_payload(p, size, ring, cur); 770227614Sluigi slot->len = size; 771257529Sluigi if (--fcnt > 0) 772257529Sluigi slot->flags |= NS_MOREFRAG; 773257529Sluigi else 774257529Sluigi fcnt = nfrags; 775257529Sluigi if (sent == count - 1) { 776257529Sluigi slot->flags &= ~NS_MOREFRAG; 777227614Sluigi slot->flags |= NS_REPORT; 778257529Sluigi } 779260368Sluigi cur = nm_ring_next(ring, cur); 780227614Sluigi } 781260368Sluigi ring->head = ring->cur = cur; 782227614Sluigi 783227614Sluigi return (sent); 784227614Sluigi} 785227614Sluigi 786246896Sluigi/* 787246896Sluigi * Send a packet, and wait for a response. 788246896Sluigi * The payload (after UDP header, ofs 42) has a 4-byte sequence 789246896Sluigi * followed by a struct timeval (or bintime?) 790246896Sluigi */ 791246896Sluigi#define PAY_OFS 42 /* where in the pkt... */ 792246896Sluigi 793227614Sluigistatic void * 794246896Sluigipinger_body(void *data) 795246896Sluigi{ 796246896Sluigi struct targ *targ = (struct targ *) data; 797261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 798261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 799246896Sluigi int i, rx = 0, n = targ->g->npackets; 800260368Sluigi void *frame; 801260368Sluigi int size; 802261909Sluigi uint32_t sent = 0; 803261909Sluigi struct timespec ts, now, last_print; 804261909Sluigi uint32_t count = 0, min = 1000000000, av = 0; 805246896Sluigi 806260368Sluigi frame = &targ->pkt; 807260368Sluigi frame += sizeof(targ->pkt.vh) - targ->g->virt_header; 808260368Sluigi size = targ->g->pkt_size + targ->g->virt_header; 809260368Sluigi 810246896Sluigi if (targ->g->nthreads > 1) { 811246896Sluigi D("can only ping with 1 thread"); 812246896Sluigi return NULL; 813246896Sluigi } 814246896Sluigi 815246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 816260368Sluigi now = last_print; 817246896Sluigi while (n == 0 || (int)sent < n) { 818246896Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 819246896Sluigi struct netmap_slot *slot; 820246896Sluigi char *p; 821260368Sluigi for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ 822246896Sluigi slot = &ring->slot[ring->cur]; 823260368Sluigi slot->len = size; 824246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 825246896Sluigi 826260368Sluigi if (nm_ring_empty(ring)) { 827246896Sluigi D("-- ouch, cannot send"); 828246896Sluigi } else { 829270063Sluigi struct tstamp *tp; 830261909Sluigi nm_pkt_copy(frame, p, size); 831246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 832246896Sluigi bcopy(&sent, p+42, sizeof(sent)); 833270063Sluigi tp = (struct tstamp *)(p+46); 834270063Sluigi tp->sec = (uint32_t)ts.tv_sec; 835270063Sluigi tp->nsec = (uint32_t)ts.tv_nsec; 836246896Sluigi sent++; 837260368Sluigi ring->head = ring->cur = nm_ring_next(ring, ring->cur); 838246896Sluigi } 839246896Sluigi } 840246896Sluigi /* should use a parameter to decide how often to send */ 841261909Sluigi if (poll(&pfd, 1, 3000) <= 0) { 842260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 843260368Sluigi strerror(errno)); 844246896Sluigi continue; 845246896Sluigi } 846246896Sluigi /* see what we got back */ 847261909Sluigi for (i = targ->nmd->first_tx_ring; 848261909Sluigi i <= targ->nmd->last_tx_ring; i++) { 849246896Sluigi ring = NETMAP_RXRING(nifp, i); 850260368Sluigi while (!nm_ring_empty(ring)) { 851246896Sluigi uint32_t seq; 852270063Sluigi struct tstamp *tp; 853246896Sluigi slot = &ring->slot[ring->cur]; 854246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 855246896Sluigi 856246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &now); 857246896Sluigi bcopy(p+42, &seq, sizeof(seq)); 858270063Sluigi tp = (struct tstamp *)(p+46); 859270063Sluigi ts.tv_sec = (time_t)tp->sec; 860270063Sluigi ts.tv_nsec = (long)tp->nsec; 861246896Sluigi ts.tv_sec = now.tv_sec - ts.tv_sec; 862246896Sluigi ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 863246896Sluigi if (ts.tv_nsec < 0) { 864246896Sluigi ts.tv_nsec += 1000000000; 865246896Sluigi ts.tv_sec--; 866246896Sluigi } 867246896Sluigi if (1) D("seq %d/%d delta %d.%09d", seq, sent, 868246896Sluigi (int)ts.tv_sec, (int)ts.tv_nsec); 869246896Sluigi if (ts.tv_nsec < (int)min) 870246896Sluigi min = ts.tv_nsec; 871246896Sluigi count ++; 872246896Sluigi av += ts.tv_nsec; 873260368Sluigi ring->head = ring->cur = nm_ring_next(ring, ring->cur); 874246896Sluigi rx++; 875246896Sluigi } 876246896Sluigi } 877246896Sluigi //D("tx %d rx %d", sent, rx); 878246896Sluigi //usleep(100000); 879246896Sluigi ts.tv_sec = now.tv_sec - last_print.tv_sec; 880246896Sluigi ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 881246896Sluigi if (ts.tv_nsec < 0) { 882246896Sluigi ts.tv_nsec += 1000000000; 883246896Sluigi ts.tv_sec--; 884246896Sluigi } 885246896Sluigi if (ts.tv_sec >= 1) { 886246896Sluigi D("count %d min %d av %d", 887246896Sluigi count, min, av/count); 888246896Sluigi count = 0; 889246896Sluigi av = 0; 890246896Sluigi min = 100000000; 891246896Sluigi last_print = now; 892246896Sluigi } 893246896Sluigi } 894246896Sluigi return NULL; 895246896Sluigi} 896246896Sluigi 897246896Sluigi 898246896Sluigi/* 899246896Sluigi * reply to ping requests 900246896Sluigi */ 901246896Sluigistatic void * 902246896Sluigiponger_body(void *data) 903246896Sluigi{ 904246896Sluigi struct targ *targ = (struct targ *) data; 905261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 906261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 907246896Sluigi struct netmap_ring *txring, *rxring; 908246896Sluigi int i, rx = 0, sent = 0, n = targ->g->npackets; 909246896Sluigi 910246896Sluigi if (targ->g->nthreads > 1) { 911246896Sluigi D("can only reply ping with 1 thread"); 912246896Sluigi return NULL; 913246896Sluigi } 914246896Sluigi D("understood ponger %d but don't know how to do it", n); 915246896Sluigi while (n == 0 || sent < n) { 916246896Sluigi uint32_t txcur, txavail; 917246896Sluigi//#define BUSYWAIT 918246896Sluigi#ifdef BUSYWAIT 919261909Sluigi ioctl(pfd.fd, NIOCRXSYNC, NULL); 920246896Sluigi#else 921261909Sluigi if (poll(&pfd, 1, 1000) <= 0) { 922260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 923260368Sluigi strerror(errno)); 924246896Sluigi continue; 925246896Sluigi } 926246896Sluigi#endif 927246896Sluigi txring = NETMAP_TXRING(nifp, 0); 928246896Sluigi txcur = txring->cur; 929260368Sluigi txavail = nm_ring_space(txring); 930246896Sluigi /* see what we got back */ 931261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 932246896Sluigi rxring = NETMAP_RXRING(nifp, i); 933260368Sluigi while (!nm_ring_empty(rxring)) { 934246896Sluigi uint16_t *spkt, *dpkt; 935246896Sluigi uint32_t cur = rxring->cur; 936246896Sluigi struct netmap_slot *slot = &rxring->slot[cur]; 937246896Sluigi char *src, *dst; 938246896Sluigi src = NETMAP_BUF(rxring, slot->buf_idx); 939246896Sluigi //D("got pkt %p of size %d", src, slot->len); 940260368Sluigi rxring->head = rxring->cur = nm_ring_next(rxring, cur); 941246896Sluigi rx++; 942246896Sluigi if (txavail == 0) 943246896Sluigi continue; 944246896Sluigi dst = NETMAP_BUF(txring, 945246896Sluigi txring->slot[txcur].buf_idx); 946246896Sluigi /* copy... */ 947246896Sluigi dpkt = (uint16_t *)dst; 948246896Sluigi spkt = (uint16_t *)src; 949261909Sluigi nm_pkt_copy(src, dst, slot->len); 950246896Sluigi dpkt[0] = spkt[3]; 951246896Sluigi dpkt[1] = spkt[4]; 952246896Sluigi dpkt[2] = spkt[5]; 953246896Sluigi dpkt[3] = spkt[0]; 954246896Sluigi dpkt[4] = spkt[1]; 955246896Sluigi dpkt[5] = spkt[2]; 956246896Sluigi txring->slot[txcur].len = slot->len; 957246896Sluigi /* XXX swap src dst mac */ 958260368Sluigi txcur = nm_ring_next(txring, txcur); 959246896Sluigi txavail--; 960246896Sluigi sent++; 961246896Sluigi } 962246896Sluigi } 963260368Sluigi txring->head = txring->cur = txcur; 964246896Sluigi targ->count = sent; 965246896Sluigi#ifdef BUSYWAIT 966261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 967246896Sluigi#endif 968246896Sluigi //D("tx %d rx %d", sent, rx); 969246896Sluigi } 970246896Sluigi return NULL; 971246896Sluigi} 972246896Sluigi 973251132Sluigistatic __inline int 974251132Sluigitimespec_ge(const struct timespec *a, const struct timespec *b) 975251132Sluigi{ 976246896Sluigi 977251132Sluigi if (a->tv_sec > b->tv_sec) 978251132Sluigi return (1); 979251132Sluigi if (a->tv_sec < b->tv_sec) 980251132Sluigi return (0); 981251132Sluigi if (a->tv_nsec >= b->tv_nsec) 982251132Sluigi return (1); 983251132Sluigi return (0); 984251132Sluigi} 985251132Sluigi 986251132Sluigistatic __inline struct timespec 987251132Sluigitimeval2spec(const struct timeval *a) 988251132Sluigi{ 989251132Sluigi struct timespec ts = { 990251132Sluigi .tv_sec = a->tv_sec, 991251132Sluigi .tv_nsec = a->tv_usec * 1000 992251132Sluigi }; 993251132Sluigi return ts; 994251132Sluigi} 995251132Sluigi 996251132Sluigistatic __inline struct timeval 997251132Sluigitimespec2val(const struct timespec *a) 998251132Sluigi{ 999251132Sluigi struct timeval tv = { 1000251132Sluigi .tv_sec = a->tv_sec, 1001251132Sluigi .tv_usec = a->tv_nsec / 1000 1002251132Sluigi }; 1003251132Sluigi return tv; 1004251132Sluigi} 1005251132Sluigi 1006251132Sluigi 1007260368Sluigistatic __inline struct timespec 1008260368Sluigitimespec_add(struct timespec a, struct timespec b) 1009251132Sluigi{ 1010260368Sluigi struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; 1011260368Sluigi if (ret.tv_nsec >= 1000000000) { 1012260368Sluigi ret.tv_sec++; 1013260368Sluigi ret.tv_nsec -= 1000000000; 1014260368Sluigi } 1015260368Sluigi return ret; 1016260368Sluigi} 1017251132Sluigi 1018260368Sluigistatic __inline struct timespec 1019260368Sluigitimespec_sub(struct timespec a, struct timespec b) 1020260368Sluigi{ 1021260368Sluigi struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; 1022260368Sluigi if (ret.tv_nsec < 0) { 1023260368Sluigi ret.tv_sec--; 1024260368Sluigi ret.tv_nsec += 1000000000; 1025251132Sluigi } 1026260368Sluigi return ret; 1027251132Sluigi} 1028251132Sluigi 1029260368Sluigi 1030260368Sluigi/* 1031260368Sluigi * wait until ts, either busy or sleeping if more than 1ms. 1032260368Sluigi * Return wakeup time. 1033260368Sluigi */ 1034260368Sluigistatic struct timespec 1035260368Sluigiwait_time(struct timespec ts) 1036251132Sluigi{ 1037260368Sluigi for (;;) { 1038260368Sluigi struct timespec w, cur; 1039260368Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 1040260368Sluigi w = timespec_sub(ts, cur); 1041260368Sluigi if (w.tv_sec < 0) 1042260368Sluigi return cur; 1043260368Sluigi else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 1044260368Sluigi poll(NULL, 0, 1); 1045251132Sluigi } 1046251132Sluigi} 1047251132Sluigi 1048246896Sluigistatic void * 1049227614Sluigisender_body(void *data) 1050227614Sluigi{ 1051227614Sluigi struct targ *targ = (struct targ *) data; 1052261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 1053270063Sluigi struct netmap_if *nifp; 1054227614Sluigi struct netmap_ring *txring; 1055261909Sluigi int i, n = targ->g->npackets / targ->g->nthreads; 1056261909Sluigi int64_t sent = 0; 1057234956Sluigi int options = targ->g->options | OPT_COPY; 1058260368Sluigi struct timespec nexttime = { 0, 0}; // XXX silence compiler 1059251132Sluigi int rate_limit = targ->g->tx_rate; 1060260368Sluigi struct pkt *pkt = &targ->pkt; 1061260368Sluigi void *frame; 1062260368Sluigi int size; 1063251426Sluigi 1064272962Sgnn if (targ->frame == NULL) { 1065272962Sgnn frame = pkt; 1066272962Sgnn frame += sizeof(pkt->vh) - targ->g->virt_header; 1067272962Sgnn size = targ->g->pkt_size + targ->g->virt_header; 1068272962Sgnn } else { 1069272962Sgnn frame = targ->frame; 1070272962Sgnn size = targ->g->pkt_size; 1071272962Sgnn } 1072272962Sgnn 1073270063Sluigi D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 1074227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1075227614Sluigi goto quit; 1076227614Sluigi 1077227614Sluigi /* main loop.*/ 1078251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1079251132Sluigi if (rate_limit) { 1080260368Sluigi targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 1081251132Sluigi targ->tic.tv_nsec = 0; 1082260368Sluigi wait_time(targ->tic); 1083251132Sluigi nexttime = targ->tic; 1084251132Sluigi } 1085261909Sluigi if (targ->g->dev_type == DEV_TAP) { 1086260700Sluigi D("writing to file desc %d", targ->g->main_fd); 1087246896Sluigi 1088246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1089260700Sluigi if (write(targ->g->main_fd, frame, size) != -1) 1090234956Sluigi sent++; 1091257529Sluigi update_addresses(pkt, targ->g); 1092234956Sluigi if (i > 10000) { 1093234956Sluigi targ->count = sent; 1094234956Sluigi i = 0; 1095234956Sluigi } 1096246896Sluigi } 1097260700Sluigi#ifndef NO_PCAP 1098260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1099260700Sluigi pcap_t *p = targ->g->p; 1100246896Sluigi 1101246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1102260700Sluigi if (pcap_inject(p, frame, size) != -1) 1103246896Sluigi sent++; 1104257529Sluigi update_addresses(pkt, targ->g); 1105246896Sluigi if (i > 10000) { 1106246896Sluigi targ->count = sent; 1107246896Sluigi i = 0; 1108246896Sluigi } 1109246896Sluigi } 1110260700Sluigi#endif /* NO_PCAP */ 1111227614Sluigi } else { 1112251132Sluigi int tosend = 0; 1113257529Sluigi int frags = targ->g->frags; 1114257529Sluigi 1115270063Sluigi nifp = targ->nmd->nifp; 1116246896Sluigi while (!targ->cancel && (n == 0 || sent < n)) { 1117227614Sluigi 1118251132Sluigi if (rate_limit && tosend <= 0) { 1119251132Sluigi tosend = targ->g->burst; 1120260368Sluigi nexttime = timespec_add(nexttime, targ->g->tx_period); 1121260368Sluigi wait_time(nexttime); 1122251132Sluigi } 1123251132Sluigi 1124227614Sluigi /* 1125227614Sluigi * wait for available room in the send queue(s) 1126227614Sluigi */ 1127261909Sluigi if (poll(&pfd, 1, 2000) <= 0) { 1128238165Semaste if (targ->cancel) 1129238165Semaste break; 1130260368Sluigi D("poll error/timeout on queue %d: %s", targ->me, 1131260368Sluigi strerror(errno)); 1132261909Sluigi // goto quit; 1133227614Sluigi } 1134261909Sluigi if (pfd.revents & POLLERR) { 1135260368Sluigi D("poll error"); 1136260368Sluigi goto quit; 1137260368Sluigi } 1138227614Sluigi /* 1139227614Sluigi * scan our queues and send on those with room 1140227614Sluigi */ 1141246896Sluigi if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 1142246896Sluigi D("drop copy"); 1143234956Sluigi options &= ~OPT_COPY; 1144246896Sluigi } 1145261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1146251132Sluigi int m, limit = rate_limit ? tosend : targ->g->burst; 1147246896Sluigi if (n > 0 && n - sent < limit) 1148246896Sluigi limit = n - sent; 1149227614Sluigi txring = NETMAP_TXRING(nifp, i); 1150260368Sluigi if (nm_ring_empty(txring)) 1151227614Sluigi continue; 1152257529Sluigi if (frags > 1) 1153257529Sluigi limit = ((limit + frags - 1) / frags) * frags; 1154261909Sluigi 1155260368Sluigi m = send_packets(txring, pkt, frame, size, targ->g, 1156257529Sluigi limit, options, frags); 1157261909Sluigi ND("limit %d tail %d frags %d m %d", 1158260700Sluigi limit, txring->tail, frags, m); 1159227614Sluigi sent += m; 1160227614Sluigi targ->count = sent; 1161257529Sluigi if (rate_limit) { 1162257529Sluigi tosend -= m; 1163257529Sluigi if (tosend <= 0) 1164257529Sluigi break; 1165257529Sluigi } 1166227614Sluigi } 1167227614Sluigi } 1168234956Sluigi /* flush any remaining packets */ 1169270063Sluigi D("flush tail %d head %d on thread %p", 1170270063Sluigi txring->tail, txring->head, 1171270063Sluigi pthread_self()); 1172261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1173227614Sluigi 1174227614Sluigi /* final part: wait all the TX queues to be empty. */ 1175261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1176227614Sluigi txring = NETMAP_TXRING(nifp, i); 1177260368Sluigi while (nm_tx_pending(txring)) { 1178270063Sluigi RD(5, "pending tx tail %d head %d on ring %d", 1179270063Sluigi txring->tail, txring->head, i); 1180261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1181227614Sluigi usleep(1); /* wait 1 tick */ 1182227614Sluigi } 1183227614Sluigi } 1184260700Sluigi } /* end DEV_NETMAP */ 1185227614Sluigi 1186251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1187227614Sluigi targ->completed = 1; 1188227614Sluigi targ->count = sent; 1189227614Sluigi 1190227614Sluigiquit: 1191227614Sluigi /* reset the ``used`` flag. */ 1192227614Sluigi targ->used = 0; 1193227614Sluigi 1194227614Sluigi return (NULL); 1195227614Sluigi} 1196227614Sluigi 1197227614Sluigi 1198260700Sluigi#ifndef NO_PCAP 1199227614Sluigistatic void 1200246896Sluigireceive_pcap(u_char *user, const struct pcap_pkthdr * h, 1201246896Sluigi const u_char * bytes) 1202227614Sluigi{ 1203227614Sluigi int *count = (int *)user; 1204246896Sluigi (void)h; /* UNUSED */ 1205246896Sluigi (void)bytes; /* UNUSED */ 1206227614Sluigi (*count)++; 1207227614Sluigi} 1208260700Sluigi#endif /* !NO_PCAP */ 1209227614Sluigi 1210227614Sluigistatic int 1211251426Sluigireceive_packets(struct netmap_ring *ring, u_int limit, int dump) 1212227614Sluigi{ 1213260368Sluigi u_int cur, rx, n; 1214227614Sluigi 1215227614Sluigi cur = ring->cur; 1216260368Sluigi n = nm_ring_space(ring); 1217260368Sluigi if (n < limit) 1218260368Sluigi limit = n; 1219227614Sluigi for (rx = 0; rx < limit; rx++) { 1220227614Sluigi struct netmap_slot *slot = &ring->slot[cur]; 1221227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 1222227614Sluigi 1223251426Sluigi if (dump) 1224251426Sluigi dump_payload(p, slot->len, ring, cur); 1225227614Sluigi 1226260368Sluigi cur = nm_ring_next(ring, cur); 1227227614Sluigi } 1228260368Sluigi ring->head = ring->cur = cur; 1229227614Sluigi 1230227614Sluigi return (rx); 1231227614Sluigi} 1232227614Sluigi 1233227614Sluigistatic void * 1234227614Sluigireceiver_body(void *data) 1235227614Sluigi{ 1236227614Sluigi struct targ *targ = (struct targ *) data; 1237261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1238270063Sluigi struct netmap_if *nifp; 1239227614Sluigi struct netmap_ring *rxring; 1240246896Sluigi int i; 1241246896Sluigi uint64_t received = 0; 1242227614Sluigi 1243227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1244227614Sluigi goto quit; 1245227614Sluigi 1246270063Sluigi D("reading from %s fd %d main_fd %d", 1247270063Sluigi targ->g->ifname, targ->fd, targ->g->main_fd); 1248227614Sluigi /* unbounded wait for the first packet. */ 1249270063Sluigi for (;!targ->cancel;) { 1250261909Sluigi i = poll(&pfd, 1, 1000); 1251261909Sluigi if (i > 0 && !(pfd.revents & POLLERR)) 1252227614Sluigi break; 1253261909Sluigi RD(1, "waiting for initial packets, poll returns %d %d", 1254261909Sluigi i, pfd.revents); 1255227614Sluigi } 1256227614Sluigi /* main loop, exit after 1s silence */ 1257251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1258260700Sluigi if (targ->g->dev_type == DEV_TAP) { 1259246896Sluigi while (!targ->cancel) { 1260270063Sluigi char buf[MAX_BODYSIZE]; 1261246896Sluigi /* XXX should we poll ? */ 1262246896Sluigi if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 1263246896Sluigi targ->count++; 1264246896Sluigi } 1265260700Sluigi#ifndef NO_PCAP 1266260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1267260700Sluigi while (!targ->cancel) { 1268260700Sluigi /* XXX should we poll ? */ 1269270063Sluigi pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, 1270270063Sluigi (u_char *)&targ->count); 1271260700Sluigi } 1272260700Sluigi#endif /* !NO_PCAP */ 1273227614Sluigi } else { 1274251426Sluigi int dump = targ->g->options & OPT_DUMP; 1275270063Sluigi 1276270063Sluigi nifp = targ->nmd->nifp; 1277238165Semaste while (!targ->cancel) { 1278227614Sluigi /* Once we started to receive packets, wait at most 1 seconds 1279227614Sluigi before quitting. */ 1280261909Sluigi if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 1281251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1282228975Suqs targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 1283261909Sluigi goto out; 1284227614Sluigi } 1285227614Sluigi 1286261909Sluigi if (pfd.revents & POLLERR) { 1287260368Sluigi D("poll err"); 1288260368Sluigi goto quit; 1289260368Sluigi } 1290260368Sluigi 1291261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 1292227614Sluigi int m; 1293227614Sluigi 1294227614Sluigi rxring = NETMAP_RXRING(nifp, i); 1295260368Sluigi if (nm_ring_empty(rxring)) 1296227614Sluigi continue; 1297227614Sluigi 1298251426Sluigi m = receive_packets(rxring, targ->g->burst, dump); 1299227614Sluigi received += m; 1300227614Sluigi } 1301246896Sluigi targ->count = received; 1302227614Sluigi } 1303227614Sluigi } 1304227614Sluigi 1305261909Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1306261909Sluigi 1307261909Sluigiout: 1308227614Sluigi targ->completed = 1; 1309227614Sluigi targ->count = received; 1310227614Sluigi 1311227614Sluigiquit: 1312227614Sluigi /* reset the ``used`` flag. */ 1313227614Sluigi targ->used = 0; 1314227614Sluigi 1315227614Sluigi return (NULL); 1316227614Sluigi} 1317227614Sluigi 1318246896Sluigi/* very crude code to print a number in normalized form. 1319246896Sluigi * Caller has to make sure that the buffer is large enough. 1320246896Sluigi */ 1321246896Sluigistatic const char * 1322246896Sluiginorm(char *buf, double val) 1323238170Semaste{ 1324261909Sluigi char *units[] = { "", "K", "M", "G", "T" }; 1325246896Sluigi u_int i; 1326238170Semaste 1327261909Sluigi for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++) 1328238170Semaste val /= 1000; 1329246896Sluigi sprintf(buf, "%.2f %s", val, units[i]); 1330246896Sluigi return buf; 1331238170Semaste} 1332238170Semaste 1333227614Sluigistatic void 1334227614Sluigitx_output(uint64_t sent, int size, double delta) 1335227614Sluigi{ 1336246896Sluigi double bw, raw_bw, pps; 1337246896Sluigi char b1[40], b2[80], b3[80]; 1338227614Sluigi 1339261909Sluigi printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", 1340261909Sluigi (unsigned long long)sent, size, delta); 1341246896Sluigi if (delta == 0) 1342246896Sluigi delta = 1e-6; 1343246896Sluigi if (size < 60) /* correct for min packet size */ 1344246896Sluigi size = 60; 1345246896Sluigi pps = sent / delta; 1346246896Sluigi bw = (8.0 * size * sent) / delta; 1347246896Sluigi /* raw packets have4 bytes crc + 20 bytes framing */ 1348246896Sluigi raw_bw = (8.0 * (size + 24) * sent) / delta; 1349238170Semaste 1350246896Sluigi printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1351246896Sluigi norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 1352227614Sluigi} 1353227614Sluigi 1354227614Sluigi 1355227614Sluigistatic void 1356227614Sluigirx_output(uint64_t received, double delta) 1357227614Sluigi{ 1358246896Sluigi double pps; 1359246896Sluigi char b1[40]; 1360227614Sluigi 1361261909Sluigi printf("Received %llu packets, in %.2f seconds.\n", 1362261909Sluigi (unsigned long long) received, delta); 1363227614Sluigi 1364246896Sluigi if (delta == 0) 1365246896Sluigi delta = 1e-6; 1366246896Sluigi pps = received / delta; 1367246896Sluigi printf("Speed: %spps\n", norm(b1, pps)); 1368227614Sluigi} 1369227614Sluigi 1370227614Sluigistatic void 1371227614Sluigiusage(void) 1372227614Sluigi{ 1373227614Sluigi const char *cmd = "pkt-gen"; 1374227614Sluigi fprintf(stderr, 1375227614Sluigi "Usage:\n" 1376227614Sluigi "%s arguments\n" 1377227614Sluigi "\t-i interface interface name\n" 1378246896Sluigi "\t-f function tx rx ping pong\n" 1379246896Sluigi "\t-n count number of iterations (can be 0)\n" 1380246896Sluigi "\t-t pkts_to_send also forces tx mode\n" 1381246896Sluigi "\t-r pkts_to_receive also forces rx mode\n" 1382257529Sluigi "\t-l pkt_size in bytes excluding CRC\n" 1383257529Sluigi "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1384257529Sluigi "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1385257529Sluigi "\t-D dst-mac\n" 1386257529Sluigi "\t-S src-mac\n" 1387246896Sluigi "\t-a cpu_id use setaffinity\n" 1388227614Sluigi "\t-b burst size testing, mostly\n" 1389227614Sluigi "\t-c cores cores to use\n" 1390227614Sluigi "\t-p threads processes/threads to use\n" 1391227614Sluigi "\t-T report_ms milliseconds between reports\n" 1392257529Sluigi "\t-P use libpcap instead of netmap\n" 1393227614Sluigi "\t-w wait_for_link_time in seconds\n" 1394257529Sluigi "\t-R rate in packets per second\n" 1395257529Sluigi "\t-X dump payload\n" 1396260368Sluigi "\t-H len add empty virtio-net-header with size 'len'\n" 1397272962Sgnn "\t-P file load packet from pcap file" 1398227614Sluigi "", 1399227614Sluigi cmd); 1400227614Sluigi 1401227614Sluigi exit(0); 1402227614Sluigi} 1403227614Sluigi 1404246896Sluigistatic void 1405246896Sluigistart_threads(struct glob_arg *g) 1406246896Sluigi{ 1407246896Sluigi int i; 1408227614Sluigi 1409246896Sluigi targs = calloc(g->nthreads, sizeof(*targs)); 1410246896Sluigi /* 1411246896Sluigi * Now create the desired number of threads, each one 1412246896Sluigi * using a single descriptor. 1413246896Sluigi */ 1414246896Sluigi for (i = 0; i < g->nthreads; i++) { 1415261909Sluigi struct targ *t = &targs[i]; 1416246896Sluigi 1417261909Sluigi bzero(t, sizeof(*t)); 1418261909Sluigi t->fd = -1; /* default, with pcap */ 1419261909Sluigi t->g = g; 1420261909Sluigi 1421246896Sluigi if (g->dev_type == DEV_NETMAP) { 1422261909Sluigi struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ 1423270063Sluigi uint64_t nmd_flags = 0; 1424270063Sluigi nmd.self = &nmd; 1425246896Sluigi 1426261909Sluigi if (g->nthreads > 1) { 1427261909Sluigi if (nmd.req.nr_flags != NR_REG_ALL_NIC) { 1428261909Sluigi D("invalid nthreads mode %d", nmd.req.nr_flags); 1429261909Sluigi continue; 1430261909Sluigi } 1431261909Sluigi nmd.req.nr_flags = NR_REG_ONE_NIC; 1432261909Sluigi nmd.req.nr_ringid = i; 1433246896Sluigi } 1434261909Sluigi /* Only touch one of the rings (rx is already ok) */ 1435261909Sluigi if (g->td_body == receiver_body) 1436270063Sluigi nmd_flags |= NETMAP_NO_TX_POLL; 1437246896Sluigi 1438261909Sluigi /* register interface. Override ifname and ringid etc. */ 1439270063Sluigi if (g->options & OPT_MONITOR_TX) 1440270063Sluigi nmd.req.nr_flags |= NR_MONITOR_TX; 1441270063Sluigi if (g->options & OPT_MONITOR_RX) 1442270063Sluigi nmd.req.nr_flags |= NR_MONITOR_RX; 1443246896Sluigi 1444270063Sluigi t->nmd = nm_open(t->g->ifname, NULL, nmd_flags | 1445270063Sluigi NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); 1446261909Sluigi if (t->nmd == NULL) { 1447261909Sluigi D("Unable to open %s: %s", 1448261909Sluigi t->g->ifname, strerror(errno)); 1449261909Sluigi continue; 1450246896Sluigi } 1451261909Sluigi t->fd = t->nmd->fd; 1452270063Sluigi set_vnet_hdr_len(t); 1453246896Sluigi 1454246896Sluigi } else { 1455246896Sluigi targs[i].fd = g->main_fd; 1456246896Sluigi } 1457261909Sluigi t->used = 1; 1458261909Sluigi t->me = i; 1459246896Sluigi if (g->affinity >= 0) { 1460246896Sluigi if (g->affinity < g->cpus) 1461261909Sluigi t->affinity = g->affinity; 1462246896Sluigi else 1463261909Sluigi t->affinity = i % g->cpus; 1464261909Sluigi } else { 1465261909Sluigi t->affinity = -1; 1466261909Sluigi } 1467246896Sluigi /* default, init packets */ 1468261909Sluigi initialize_packet(t); 1469246896Sluigi 1470261909Sluigi if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { 1471260368Sluigi D("Unable to create thread %d: %s", i, strerror(errno)); 1472261909Sluigi t->used = 0; 1473246896Sluigi } 1474246896Sluigi } 1475246896Sluigi} 1476246896Sluigi 1477246896Sluigistatic void 1478246896Sluigimain_thread(struct glob_arg *g) 1479246896Sluigi{ 1480246896Sluigi int i; 1481246896Sluigi 1482246896Sluigi uint64_t prev = 0; 1483246896Sluigi uint64_t count = 0; 1484246896Sluigi double delta_t; 1485246896Sluigi struct timeval tic, toc; 1486246896Sluigi 1487246896Sluigi gettimeofday(&toc, NULL); 1488246896Sluigi for (;;) { 1489246896Sluigi struct timeval now, delta; 1490246896Sluigi uint64_t pps, usec, my_count, npkts; 1491246896Sluigi int done = 0; 1492246896Sluigi 1493246896Sluigi delta.tv_sec = g->report_interval/1000; 1494246896Sluigi delta.tv_usec = (g->report_interval%1000)*1000; 1495246896Sluigi select(0, NULL, NULL, NULL, &delta); 1496246896Sluigi gettimeofday(&now, NULL); 1497246896Sluigi timersub(&now, &toc, &toc); 1498246896Sluigi my_count = 0; 1499246896Sluigi for (i = 0; i < g->nthreads; i++) { 1500246896Sluigi my_count += targs[i].count; 1501246896Sluigi if (targs[i].used == 0) 1502246896Sluigi done++; 1503246896Sluigi } 1504246896Sluigi usec = toc.tv_sec* 1000000 + toc.tv_usec; 1505246896Sluigi if (usec < 10000) 1506246896Sluigi continue; 1507246896Sluigi npkts = my_count - prev; 1508246896Sluigi pps = (npkts*1000000 + usec/2) / usec; 1509261909Sluigi D("%llu pps (%llu pkts in %llu usec)", 1510261909Sluigi (unsigned long long)pps, 1511261909Sluigi (unsigned long long)npkts, 1512261909Sluigi (unsigned long long)usec); 1513246896Sluigi prev = my_count; 1514246896Sluigi toc = now; 1515246896Sluigi if (done == g->nthreads) 1516246896Sluigi break; 1517246896Sluigi } 1518246896Sluigi 1519246896Sluigi timerclear(&tic); 1520246896Sluigi timerclear(&toc); 1521246896Sluigi for (i = 0; i < g->nthreads; i++) { 1522251132Sluigi struct timespec t_tic, t_toc; 1523246896Sluigi /* 1524246896Sluigi * Join active threads, unregister interfaces and close 1525246896Sluigi * file descriptors. 1526246896Sluigi */ 1527251132Sluigi if (targs[i].used) 1528251132Sluigi pthread_join(targs[i].thread, NULL); 1529246896Sluigi close(targs[i].fd); 1530246896Sluigi 1531246896Sluigi if (targs[i].completed == 0) 1532246896Sluigi D("ouch, thread %d exited with error", i); 1533246896Sluigi 1534246896Sluigi /* 1535246896Sluigi * Collect threads output and extract information about 1536246896Sluigi * how long it took to send all the packets. 1537246896Sluigi */ 1538246896Sluigi count += targs[i].count; 1539251132Sluigi t_tic = timeval2spec(&tic); 1540251132Sluigi t_toc = timeval2spec(&toc); 1541251132Sluigi if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 1542251132Sluigi tic = timespec2val(&targs[i].tic); 1543251132Sluigi if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 1544251132Sluigi toc = timespec2val(&targs[i].toc); 1545246896Sluigi } 1546246896Sluigi 1547246896Sluigi /* print output. */ 1548246896Sluigi timersub(&toc, &tic, &toc); 1549246896Sluigi delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1550246896Sluigi if (g->td_body == sender_body) 1551246896Sluigi tx_output(count, g->pkt_size, delta_t); 1552246896Sluigi else 1553246896Sluigi rx_output(count, delta_t); 1554246896Sluigi 1555246896Sluigi if (g->dev_type == DEV_NETMAP) { 1556261909Sluigi munmap(g->nmd->mem, g->nmd->req.nr_memsize); 1557246896Sluigi close(g->main_fd); 1558246896Sluigi } 1559246896Sluigi} 1560246896Sluigi 1561246896Sluigi 1562246896Sluigistruct sf { 1563246896Sluigi char *key; 1564246896Sluigi void *f; 1565246896Sluigi}; 1566246896Sluigi 1567246896Sluigistatic struct sf func[] = { 1568246896Sluigi { "tx", sender_body }, 1569246896Sluigi { "rx", receiver_body }, 1570246896Sluigi { "ping", pinger_body }, 1571246896Sluigi { "pong", ponger_body }, 1572246896Sluigi { NULL, NULL } 1573246896Sluigi}; 1574246896Sluigi 1575246896Sluigistatic int 1576246896Sluigitap_alloc(char *dev) 1577246896Sluigi{ 1578246896Sluigi struct ifreq ifr; 1579246896Sluigi int fd, err; 1580246896Sluigi char *clonedev = TAP_CLONEDEV; 1581246896Sluigi 1582246896Sluigi (void)err; 1583246896Sluigi (void)dev; 1584246896Sluigi /* Arguments taken by the function: 1585246896Sluigi * 1586246896Sluigi * char *dev: the name of an interface (or '\0'). MUST have enough 1587246896Sluigi * space to hold the interface name if '\0' is passed 1588246896Sluigi * int flags: interface flags (eg, IFF_TUN etc.) 1589246896Sluigi */ 1590246896Sluigi 1591246896Sluigi#ifdef __FreeBSD__ 1592246896Sluigi if (dev[3]) { /* tapSomething */ 1593246896Sluigi static char buf[128]; 1594246896Sluigi snprintf(buf, sizeof(buf), "/dev/%s", dev); 1595246896Sluigi clonedev = buf; 1596246896Sluigi } 1597246896Sluigi#endif 1598246896Sluigi /* open the device */ 1599246896Sluigi if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1600246896Sluigi return fd; 1601246896Sluigi } 1602246896Sluigi D("%s open successful", clonedev); 1603246896Sluigi 1604246896Sluigi /* preparation of the struct ifr, of type "struct ifreq" */ 1605246896Sluigi memset(&ifr, 0, sizeof(ifr)); 1606246896Sluigi 1607246896Sluigi#ifdef linux 1608246896Sluigi ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1609246896Sluigi 1610246896Sluigi if (*dev) { 1611246896Sluigi /* if a device name was specified, put it in the structure; otherwise, 1612246896Sluigi * the kernel will try to allocate the "next" device of the 1613246896Sluigi * specified type */ 1614246896Sluigi strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1615246896Sluigi } 1616246896Sluigi 1617246896Sluigi /* try to create the device */ 1618246896Sluigi if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 1619260368Sluigi D("failed to to a TUNSETIFF: %s", strerror(errno)); 1620246896Sluigi close(fd); 1621246896Sluigi return err; 1622246896Sluigi } 1623246896Sluigi 1624246896Sluigi /* if the operation was successful, write back the name of the 1625246896Sluigi * interface to the variable "dev", so the caller can know 1626246896Sluigi * it. Note that the caller MUST reserve space in *dev (see calling 1627246896Sluigi * code below) */ 1628246896Sluigi strcpy(dev, ifr.ifr_name); 1629246896Sluigi D("new name is %s", dev); 1630246896Sluigi#endif /* linux */ 1631246896Sluigi 1632246896Sluigi /* this is the special file descriptor that the caller will use to talk 1633246896Sluigi * with the virtual interface */ 1634246896Sluigi return fd; 1635246896Sluigi} 1636246896Sluigi 1637227614Sluigiint 1638227614Sluigimain(int arc, char **argv) 1639227614Sluigi{ 1640246896Sluigi int i; 1641227614Sluigi 1642227614Sluigi struct glob_arg g; 1643227614Sluigi 1644227614Sluigi int ch; 1645227614Sluigi int wait_link = 2; 1646227614Sluigi int devqueues = 1; /* how many device queues */ 1647227614Sluigi 1648227614Sluigi bzero(&g, sizeof(g)); 1649227614Sluigi 1650246896Sluigi g.main_fd = -1; 1651246896Sluigi g.td_body = receiver_body; 1652246896Sluigi g.report_interval = 1000; /* report interval */ 1653246896Sluigi g.affinity = -1; 1654246896Sluigi /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1655246896Sluigi g.src_ip.name = "10.0.0.1"; 1656246896Sluigi g.dst_ip.name = "10.1.0.1"; 1657246896Sluigi g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1658246896Sluigi g.src_mac.name = NULL; 1659227614Sluigi g.pkt_size = 60; 1660227614Sluigi g.burst = 512; // default 1661227614Sluigi g.nthreads = 1; 1662227614Sluigi g.cpus = 1; 1663251426Sluigi g.forever = 1; 1664251132Sluigi g.tx_rate = 0; 1665257529Sluigi g.frags = 1; 1666257529Sluigi g.nmr_config = ""; 1667260368Sluigi g.virt_header = 0; 1668227614Sluigi 1669227614Sluigi while ( (ch = getopt(arc, argv, 1670272962Sgnn "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:m:P:")) != -1) { 1671246896Sluigi struct sf *fn; 1672246896Sluigi 1673227614Sluigi switch(ch) { 1674227614Sluigi default: 1675227614Sluigi D("bad option %c %s", ch, optarg); 1676227614Sluigi usage(); 1677227614Sluigi break; 1678246896Sluigi 1679246896Sluigi case 'n': 1680246896Sluigi g.npackets = atoi(optarg); 1681246896Sluigi break; 1682246896Sluigi 1683257529Sluigi case 'F': 1684257529Sluigi i = atoi(optarg); 1685257529Sluigi if (i < 1 || i > 63) { 1686257529Sluigi D("invalid frags %d [1..63], ignore", i); 1687257529Sluigi break; 1688257529Sluigi } 1689257529Sluigi g.frags = i; 1690257529Sluigi break; 1691257529Sluigi 1692246896Sluigi case 'f': 1693246896Sluigi for (fn = func; fn->key; fn++) { 1694246896Sluigi if (!strcmp(fn->key, optarg)) 1695246896Sluigi break; 1696246896Sluigi } 1697246896Sluigi if (fn->key) 1698246896Sluigi g.td_body = fn->f; 1699246896Sluigi else 1700246896Sluigi D("unrecognised function %s", optarg); 1701246896Sluigi break; 1702246896Sluigi 1703246896Sluigi case 'o': /* data generation options */ 1704234956Sluigi g.options = atoi(optarg); 1705234956Sluigi break; 1706246896Sluigi 1707246896Sluigi case 'a': /* force affinity */ 1708246896Sluigi g.affinity = atoi(optarg); 1709246896Sluigi break; 1710246896Sluigi 1711227614Sluigi case 'i': /* interface */ 1712260700Sluigi /* a prefix of tap: netmap: or pcap: forces the mode. 1713260700Sluigi * otherwise we guess 1714260700Sluigi */ 1715260700Sluigi D("interface is %s", optarg); 1716261909Sluigi if (strlen(optarg) > MAX_IFNAMELEN - 8) { 1717261909Sluigi D("ifname too long %s", optarg); 1718261909Sluigi break; 1719261909Sluigi } 1720261909Sluigi strcpy(g.ifname, optarg); 1721260700Sluigi if (!strcmp(optarg, "null")) { 1722260700Sluigi g.dev_type = DEV_NETMAP; 1723260700Sluigi g.dummy_send = 1; 1724260700Sluigi } else if (!strncmp(optarg, "tap:", 4)) { 1725246896Sluigi g.dev_type = DEV_TAP; 1726261909Sluigi strcpy(g.ifname, optarg + 4); 1727260700Sluigi } else if (!strncmp(optarg, "pcap:", 5)) { 1728260700Sluigi g.dev_type = DEV_PCAP; 1729261909Sluigi strcpy(g.ifname, optarg + 5); 1730261909Sluigi } else if (!strncmp(optarg, "netmap:", 7) || 1731261909Sluigi !strncmp(optarg, "vale", 4)) { 1732246896Sluigi g.dev_type = DEV_NETMAP; 1733260700Sluigi } else if (!strncmp(optarg, "tap", 3)) { 1734260700Sluigi g.dev_type = DEV_TAP; 1735261909Sluigi } else { /* prepend netmap: */ 1736260700Sluigi g.dev_type = DEV_NETMAP; 1737261909Sluigi sprintf(g.ifname, "netmap:%s", optarg); 1738260700Sluigi } 1739227614Sluigi break; 1740246896Sluigi 1741251426Sluigi case 'I': 1742251426Sluigi g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1743251426Sluigi break; 1744251426Sluigi 1745227614Sluigi case 'l': /* pkt_size */ 1746227614Sluigi g.pkt_size = atoi(optarg); 1747227614Sluigi break; 1748246896Sluigi 1749227614Sluigi case 'd': 1750246896Sluigi g.dst_ip.name = optarg; 1751227614Sluigi break; 1752246896Sluigi 1753227614Sluigi case 's': 1754246896Sluigi g.src_ip.name = optarg; 1755227614Sluigi break; 1756246896Sluigi 1757227614Sluigi case 'T': /* report interval */ 1758246896Sluigi g.report_interval = atoi(optarg); 1759227614Sluigi break; 1760246896Sluigi 1761227614Sluigi case 'w': 1762227614Sluigi wait_link = atoi(optarg); 1763227614Sluigi break; 1764246896Sluigi 1765251426Sluigi case 'W': /* XXX changed default */ 1766251426Sluigi g.forever = 0; /* do not exit rx even with no traffic */ 1767246896Sluigi break; 1768246896Sluigi 1769227614Sluigi case 'b': /* burst */ 1770227614Sluigi g.burst = atoi(optarg); 1771227614Sluigi break; 1772227614Sluigi case 'c': 1773227614Sluigi g.cpus = atoi(optarg); 1774227614Sluigi break; 1775227614Sluigi case 'p': 1776227614Sluigi g.nthreads = atoi(optarg); 1777227614Sluigi break; 1778227614Sluigi 1779227614Sluigi case 'D': /* destination mac */ 1780246896Sluigi g.dst_mac.name = optarg; 1781227614Sluigi break; 1782246896Sluigi 1783227614Sluigi case 'S': /* source mac */ 1784246896Sluigi g.src_mac.name = optarg; 1785227614Sluigi break; 1786227614Sluigi case 'v': 1787227614Sluigi verbose++; 1788251132Sluigi break; 1789251132Sluigi case 'R': 1790251132Sluigi g.tx_rate = atoi(optarg); 1791251132Sluigi break; 1792251426Sluigi case 'X': 1793251426Sluigi g.options |= OPT_DUMP; 1794257529Sluigi break; 1795257529Sluigi case 'C': 1796257529Sluigi g.nmr_config = strdup(optarg); 1797260368Sluigi break; 1798260368Sluigi case 'H': 1799260368Sluigi g.virt_header = atoi(optarg); 1800260700Sluigi break; 1801261909Sluigi case 'e': /* extra bufs */ 1802261909Sluigi g.extra_bufs = atoi(optarg); 1803260700Sluigi break; 1804270063Sluigi case 'm': 1805270063Sluigi if (strcmp(optarg, "tx") == 0) { 1806270063Sluigi g.options |= OPT_MONITOR_TX; 1807270063Sluigi } else if (strcmp(optarg, "rx") == 0) { 1808270063Sluigi g.options |= OPT_MONITOR_RX; 1809270063Sluigi } else { 1810270063Sluigi D("unrecognized monitor mode %s", optarg); 1811270063Sluigi } 1812270063Sluigi break; 1813272962Sgnn case 'P': 1814272962Sgnn g.packet_file = strdup(optarg); 1815272962Sgnn break; 1816227614Sluigi } 1817272962Sgnn 1818227614Sluigi } 1819227614Sluigi 1820246896Sluigi if (g.ifname == NULL) { 1821227614Sluigi D("missing ifname"); 1822227614Sluigi usage(); 1823227614Sluigi } 1824246896Sluigi 1825246896Sluigi i = system_ncpus(); 1826246896Sluigi if (g.cpus < 0 || g.cpus > i) { 1827246896Sluigi D("%d cpus is too high, have only %d cpus", g.cpus, i); 1828246896Sluigi usage(); 1829227614Sluigi } 1830246896Sluigi if (g.cpus == 0) 1831246896Sluigi g.cpus = i; 1832246896Sluigi 1833270063Sluigi if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { 1834270063Sluigi D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); 1835227614Sluigi usage(); 1836227614Sluigi } 1837227614Sluigi 1838246896Sluigi if (g.src_mac.name == NULL) { 1839246896Sluigi static char mybuf[20] = "00:00:00:00:00:00"; 1840234956Sluigi /* retrieve source mac address. */ 1841246896Sluigi if (source_hwaddr(g.ifname, mybuf) == -1) { 1842234956Sluigi D("Unable to retrieve source mac"); 1843234956Sluigi // continue, fail later 1844234956Sluigi } 1845246896Sluigi g.src_mac.name = mybuf; 1846234956Sluigi } 1847246896Sluigi /* extract address ranges */ 1848246896Sluigi extract_ip_range(&g.src_ip); 1849246896Sluigi extract_ip_range(&g.dst_ip); 1850246896Sluigi extract_mac_range(&g.src_mac); 1851246896Sluigi extract_mac_range(&g.dst_mac); 1852234956Sluigi 1853260700Sluigi if (g.src_ip.start != g.src_ip.end || 1854260700Sluigi g.src_ip.port0 != g.src_ip.port1 || 1855260700Sluigi g.dst_ip.start != g.dst_ip.end || 1856260700Sluigi g.dst_ip.port0 != g.dst_ip.port1) 1857260700Sluigi g.options |= OPT_COPY; 1858260700Sluigi 1859260368Sluigi if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 1860260368Sluigi && g.virt_header != VIRT_HDR_2) { 1861260368Sluigi D("bad virtio-net-header length"); 1862260368Sluigi usage(); 1863260368Sluigi } 1864260368Sluigi 1865246896Sluigi if (g.dev_type == DEV_TAP) { 1866246896Sluigi D("want to use tap %s", g.ifname); 1867246896Sluigi g.main_fd = tap_alloc(g.ifname); 1868246896Sluigi if (g.main_fd < 0) { 1869246896Sluigi D("cannot open tap %s", g.ifname); 1870246896Sluigi usage(); 1871246896Sluigi } 1872260700Sluigi#ifndef NO_PCAP 1873260700Sluigi } else if (g.dev_type == DEV_PCAP) { 1874246896Sluigi char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1875246896Sluigi 1876246896Sluigi pcap_errbuf[0] = '\0'; // init the buffer 1877270063Sluigi g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); 1878234956Sluigi if (g.p == NULL) { 1879246896Sluigi D("cannot open pcap on %s", g.ifname); 1880234956Sluigi usage(); 1881234956Sluigi } 1882270063Sluigi g.main_fd = pcap_fileno(g.p); 1883270063Sluigi D("using pcap on %s fileno %d", g.ifname, g.main_fd); 1884260700Sluigi#endif /* !NO_PCAP */ 1885260700Sluigi } else if (g.dummy_send) { /* but DEV_NETMAP */ 1886257529Sluigi D("using a dummy send routine"); 1887234956Sluigi } else { 1888270063Sluigi struct nmreq base_nmd; 1889261909Sluigi 1890261909Sluigi bzero(&base_nmd, sizeof(base_nmd)); 1891261909Sluigi 1892270063Sluigi parse_nmr_config(g.nmr_config, &base_nmd); 1893261909Sluigi if (g.extra_bufs) { 1894270063Sluigi base_nmd.nr_arg3 = g.extra_bufs; 1895261909Sluigi } 1896261909Sluigi 1897227614Sluigi /* 1898261909Sluigi * Open the netmap device using nm_open(). 1899227614Sluigi * 1900227614Sluigi * protocol stack and may cause a reset of the card, 1901227614Sluigi * which in turn may take some time for the PHY to 1902261909Sluigi * reconfigure. We do the open here to have time to reset. 1903227614Sluigi */ 1904270063Sluigi g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL); 1905261909Sluigi if (g.nmd == NULL) { 1906261909Sluigi D("Unable to open %s: %s", g.ifname, strerror(errno)); 1907261909Sluigi goto out; 1908227614Sluigi } 1909261909Sluigi g.main_fd = g.nmd->fd; 1910261909Sluigi D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); 1911227614Sluigi 1912270063Sluigi /* get num of queues in tx or rx */ 1913270063Sluigi if (g.td_body == sender_body) 1914270063Sluigi devqueues = g.nmd->req.nr_tx_rings; 1915270063Sluigi else 1916270063Sluigi devqueues = g.nmd->req.nr_rx_rings; 1917261909Sluigi 1918227614Sluigi /* validate provided nthreads. */ 1919227614Sluigi if (g.nthreads < 1 || g.nthreads > devqueues) { 1920227614Sluigi D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 1921227614Sluigi // continue, fail later 1922227614Sluigi } 1923227614Sluigi 1924260700Sluigi if (verbose) { 1925261909Sluigi struct netmap_if *nifp = g.nmd->nifp; 1926261909Sluigi struct nmreq *req = &g.nmd->req; 1927227614Sluigi 1928261909Sluigi D("nifp at offset %d, %d tx %d rx region %d", 1929261909Sluigi req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, 1930261909Sluigi req->nr_arg2); 1931261909Sluigi for (i = 0; i <= req->nr_tx_rings; i++) { 1932270063Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, i); 1933270063Sluigi D(" TX%d at 0x%lx slots %d", i, 1934270063Sluigi (char *)ring - (char *)nifp, ring->num_slots); 1935260700Sluigi } 1936261909Sluigi for (i = 0; i <= req->nr_rx_rings; i++) { 1937270063Sluigi struct netmap_ring *ring = NETMAP_RXRING(nifp, i); 1938270063Sluigi D(" RX%d at 0x%lx slots %d", i, 1939270063Sluigi (char *)ring - (char *)nifp, ring->num_slots); 1940260700Sluigi } 1941260700Sluigi } 1942227614Sluigi 1943227614Sluigi /* Print some debug information. */ 1944227614Sluigi fprintf(stdout, 1945227614Sluigi "%s %s: %d queues, %d threads and %d cpus.\n", 1946246896Sluigi (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1947246896Sluigi g.ifname, 1948227614Sluigi devqueues, 1949227614Sluigi g.nthreads, 1950227614Sluigi g.cpus); 1951246896Sluigi if (g.td_body == sender_body) { 1952227614Sluigi fprintf(stdout, "%s -> %s (%s -> %s)\n", 1953246896Sluigi g.src_ip.name, g.dst_ip.name, 1954246896Sluigi g.src_mac.name, g.dst_mac.name); 1955227614Sluigi } 1956261909Sluigi 1957261909Sluigiout: 1958227614Sluigi /* Exit if something went wrong. */ 1959246896Sluigi if (g.main_fd < 0) { 1960227614Sluigi D("aborting"); 1961227614Sluigi usage(); 1962227614Sluigi } 1963234956Sluigi } 1964227614Sluigi 1965261909Sluigi 1966234956Sluigi if (g.options) { 1967251426Sluigi D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 1968234956Sluigi g.options & OPT_PREFETCH ? " prefetch" : "", 1969234956Sluigi g.options & OPT_ACCESS ? " access" : "", 1970234956Sluigi g.options & OPT_MEMCPY ? " memcpy" : "", 1971251426Sluigi g.options & OPT_INDIRECT ? " indirect" : "", 1972234956Sluigi g.options & OPT_COPY ? " copy" : ""); 1973234956Sluigi } 1974257529Sluigi 1975257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 1976257529Sluigi if (g.tx_rate > 0) { 1977257529Sluigi /* try to have at least something every second, 1978260368Sluigi * reducing the burst size to some 0.01s worth of data 1979257529Sluigi * (but no less than one full set of fragments) 1980257529Sluigi */ 1981260368Sluigi uint64_t x; 1982260368Sluigi int lim = (g.tx_rate)/300; 1983260368Sluigi if (g.burst > lim) 1984260368Sluigi g.burst = lim; 1985257529Sluigi if (g.burst < g.frags) 1986257529Sluigi g.burst = g.frags; 1987260368Sluigi x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 1988260368Sluigi g.tx_period.tv_nsec = x; 1989257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 1990257529Sluigi g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 1991251132Sluigi } 1992257529Sluigi if (g.td_body == sender_body) 1993257529Sluigi D("Sending %d packets every %ld.%09ld s", 1994257529Sluigi g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 1995227614Sluigi /* Wait for PHY reset. */ 1996227614Sluigi D("Wait %d secs for phy reset", wait_link); 1997227614Sluigi sleep(wait_link); 1998227614Sluigi D("Ready..."); 1999227614Sluigi 2000227614Sluigi /* Install ^C handler. */ 2001227614Sluigi global_nthreads = g.nthreads; 2002227614Sluigi signal(SIGINT, sigint_h); 2003227614Sluigi 2004246896Sluigi start_threads(&g); 2005246896Sluigi main_thread(&g); 2006246896Sluigi return 0; 2007246896Sluigi} 2008227614Sluigi 2009227614Sluigi/* end of file */ 2010