1227614Sluigi/* 2260368Sluigi * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. 3341457Svmaffione * Copyright (C) 2013-2015 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: stable/11/tools/tools/netmap/pkt-gen.c 344918 2019-03-08 08:27:33Z vmaffione $ 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 40261909Sluigi#define _GNU_SOURCE /* for CPU_SET() */ 41261909Sluigi#include <stdio.h> 42261909Sluigi#define NETMAP_WITH_LIBS 43261909Sluigi#include <net/netmap_user.h> 44246896Sluigi 45261909Sluigi 46251426Sluigi#include <ctype.h> // isprint() 47261909Sluigi#include <unistd.h> // sysconf() 48261909Sluigi#include <sys/poll.h> 49261909Sluigi#include <arpa/inet.h> /* ntohs */ 50341457Svmaffione#ifndef _WIN32 51261909Sluigi#include <sys/sysctl.h> /* sysctl */ 52341457Svmaffione#endif 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> 58341457Svmaffione#include <netinet/ip6.h> 59341457Svmaffione#ifdef linux 60341457Svmaffione#define IPV6_VERSION 0x60 61341457Svmaffione#define IPV6_DEFHLIM 64 62341457Svmaffione#endif 63341457Svmaffione#include <assert.h> 64341457Svmaffione#include <math.h> 65251426Sluigi 66261909Sluigi#include <pthread.h> 67261909Sluigi 68260700Sluigi#ifndef NO_PCAP 69260700Sluigi#include <pcap/pcap.h> 70260700Sluigi#endif 71261909Sluigi 72341457Svmaffione#include "ctrs.h" 73341457Svmaffione 74341457Svmaffionestatic void usage(int); 75341457Svmaffione 76341457Svmaffione#ifdef _WIN32 77341457Svmaffione#define cpuset_t DWORD_PTR //uint64_t 78341457Svmaffionestatic inline void CPU_ZERO(cpuset_t *p) 79341457Svmaffione{ 80341457Svmaffione *p = 0; 81341457Svmaffione} 82341457Svmaffione 83341457Svmaffionestatic inline void CPU_SET(uint32_t i, cpuset_t *p) 84341457Svmaffione{ 85341457Svmaffione *p |= 1<< (i & 0x3f); 86341457Svmaffione} 87341457Svmaffione 88341457Svmaffione#define pthread_setaffinity_np(a, b, c) !SetThreadAffinityMask(a, *c) //((void)a, 0) 89341457Svmaffione#define TAP_CLONEDEV "/dev/tap" 90341457Svmaffione#define AF_LINK 18 //defined in winsocks.h 91341457Svmaffione#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 92341457Svmaffione#include <net/if_dl.h> 93341457Svmaffione 94341457Svmaffione/* 95341457Svmaffione * Convert an ASCII representation of an ethernet address to 96341457Svmaffione * binary form. 97341457Svmaffione */ 98341457Svmaffionestruct ether_addr * 99341457Svmaffioneether_aton(const char *a) 100341457Svmaffione{ 101341457Svmaffione int i; 102341457Svmaffione static struct ether_addr o; 103341457Svmaffione unsigned int o0, o1, o2, o3, o4, o5; 104341457Svmaffione 105341457Svmaffione i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); 106341457Svmaffione 107341457Svmaffione if (i != 6) 108341457Svmaffione return (NULL); 109341457Svmaffione 110341457Svmaffione o.octet[0]=o0; 111341457Svmaffione o.octet[1]=o1; 112341457Svmaffione o.octet[2]=o2; 113341457Svmaffione o.octet[3]=o3; 114341457Svmaffione o.octet[4]=o4; 115341457Svmaffione o.octet[5]=o5; 116341457Svmaffione 117341457Svmaffione return ((struct ether_addr *)&o); 118341457Svmaffione} 119341457Svmaffione 120341457Svmaffione/* 121341457Svmaffione * Convert a binary representation of an ethernet address to 122341457Svmaffione * an ASCII string. 123341457Svmaffione */ 124341457Svmaffionechar * 125341457Svmaffioneether_ntoa(const struct ether_addr *n) 126341457Svmaffione{ 127341457Svmaffione int i; 128341457Svmaffione static char a[18]; 129341457Svmaffione 130341457Svmaffione i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x", 131341457Svmaffione n->octet[0], n->octet[1], n->octet[2], 132341457Svmaffione n->octet[3], n->octet[4], n->octet[5]); 133341457Svmaffione return (i < 17 ? NULL : (char *)&a); 134341457Svmaffione} 135341457Svmaffione#endif /* _WIN32 */ 136341457Svmaffione 137261909Sluigi#ifdef linux 138261909Sluigi 139261909Sluigi#define cpuset_t cpu_set_t 140261909Sluigi 141261909Sluigi#define ifr_flagshigh ifr_flags /* only the low 16 bits here */ 142261909Sluigi#define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */ 143261909Sluigi#include <linux/ethtool.h> 144261909Sluigi#include <linux/sockios.h> 145261909Sluigi 146261909Sluigi#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 147261909Sluigi#include <netinet/ether.h> /* ether_aton */ 148261909Sluigi#include <linux/if_packet.h> /* sockaddr_ll */ 149261909Sluigi#endif /* linux */ 150261909Sluigi 151261909Sluigi#ifdef __FreeBSD__ 152261909Sluigi#include <sys/endian.h> /* le64toh */ 153261909Sluigi#include <machine/param.h> 154261909Sluigi 155261909Sluigi#include <pthread_np.h> /* pthread w/ affinity */ 156261909Sluigi#include <sys/cpuset.h> /* cpu_set */ 157261909Sluigi#include <net/if_dl.h> /* LLADDR */ 158261909Sluigi#endif /* __FreeBSD__ */ 159261909Sluigi 160261909Sluigi#ifdef __APPLE__ 161261909Sluigi 162261909Sluigi#define cpuset_t uint64_t // XXX 163261909Sluigistatic inline void CPU_ZERO(cpuset_t *p) 164261909Sluigi{ 165341457Svmaffione *p = 0; 166261909Sluigi} 167261909Sluigi 168261909Sluigistatic inline void CPU_SET(uint32_t i, cpuset_t *p) 169261909Sluigi{ 170341457Svmaffione *p |= 1<< (i & 0x3f); 171261909Sluigi} 172261909Sluigi 173261909Sluigi#define pthread_setaffinity_np(a, b, c) ((void)a, 0) 174261909Sluigi 175261909Sluigi#define ifr_flagshigh ifr_flags // XXX 176261909Sluigi#define IFF_PPROMISC IFF_PROMISC 177261909Sluigi#include <net/if_dl.h> /* LLADDR */ 178261909Sluigi#define clock_gettime(a,b) \ 179341457Svmaffione do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) 180261909Sluigi#endif /* __APPLE__ */ 181261909Sluigi 182257529Sluigiconst char *default_payload="netmap pkt-gen DIRECT payload\n" 183227614Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 184227614Sluigi 185257529Sluigiconst char *indirect_payload="netmap pkt-gen indirect payload\n" 186257529Sluigi "http://info.iet.unipi.it/~luigi/netmap/ "; 187257529Sluigi 188227614Sluigiint verbose = 0; 189341457Svmaffioneint normalize = 1; 190227614Sluigi 191260368Sluigi#define VIRT_HDR_1 10 /* length of a base vnet-hdr */ 192260368Sluigi#define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ 193260368Sluigi#define VIRT_HDR_MAX VIRT_HDR_2 194260368Sluigistruct virt_header { 195260368Sluigi uint8_t fields[VIRT_HDR_MAX]; 196260368Sluigi}; 197260368Sluigi 198342035Svmaffione#define MAX_BODYSIZE 65536 199270063Sluigi 200227614Sluigistruct pkt { 201260368Sluigi struct virt_header vh; 202227614Sluigi struct ether_header eh; 203341457Svmaffione union { 204341457Svmaffione struct { 205341457Svmaffione struct ip ip; 206341457Svmaffione struct udphdr udp; 207341457Svmaffione uint8_t body[MAX_BODYSIZE]; /* hardwired */ 208341457Svmaffione } ipv4; 209341457Svmaffione struct { 210341457Svmaffione struct ip6_hdr ip; 211341457Svmaffione struct udphdr udp; 212341457Svmaffione uint8_t body[MAX_BODYSIZE]; /* hardwired */ 213341457Svmaffione } ipv6; 214341457Svmaffione }; 215227614Sluigi} __attribute__((__packed__)); 216227614Sluigi 217341457Svmaffione#define PKT(p, f, af) \ 218341457Svmaffione ((af) == AF_INET ? (p)->ipv4.f: (p)->ipv6.f) 219341457Svmaffione 220246896Sluigistruct ip_range { 221246896Sluigi char *name; 222341457Svmaffione union { 223341457Svmaffione struct { 224341457Svmaffione uint32_t start, end; /* same as struct in_addr */ 225341457Svmaffione } ipv4; 226341457Svmaffione struct { 227341457Svmaffione struct in6_addr start, end; 228341457Svmaffione uint8_t sgroup, egroup; 229341457Svmaffione } ipv6; 230341457Svmaffione }; 231257529Sluigi uint16_t port0, port1; 232246896Sluigi}; 233246896Sluigi 234246896Sluigistruct mac_range { 235246896Sluigi char *name; 236246896Sluigi struct ether_addr start, end; 237246896Sluigi}; 238246896Sluigi 239261909Sluigi/* ifname can be netmap:foo-xxxx */ 240261909Sluigi#define MAX_IFNAMELEN 64 /* our buffer for ifname */ 241270063Sluigi#define MAX_PKTSIZE MAX_BODYSIZE /* XXX: + IP_HDR + ETH_HDR */ 242270063Sluigi 243270063Sluigi/* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */ 244270063Sluigistruct tstamp { 245270063Sluigi uint32_t sec; 246270063Sluigi uint32_t nsec; 247270063Sluigi}; 248270063Sluigi 249227614Sluigi/* 250227614Sluigi * global arguments for all threads 251227614Sluigi */ 252246896Sluigi 253227614Sluigistruct glob_arg { 254341457Svmaffione int af; /* address family AF_INET/AF_INET6 */ 255246896Sluigi struct ip_range src_ip; 256246896Sluigi struct ip_range dst_ip; 257246896Sluigi struct mac_range dst_mac; 258246896Sluigi struct mac_range src_mac; 259227614Sluigi int pkt_size; 260341457Svmaffione int pkt_min_size; 261227614Sluigi int burst; 262246896Sluigi int forever; 263341457Svmaffione uint64_t npackets; /* total packets to send */ 264341457Svmaffione int frags; /* fragments per packet */ 265342035Svmaffione u_int frag_size; /* size of each fragment */ 266227614Sluigi int nthreads; 267341457Svmaffione int cpus; /* cpus used for running */ 268341457Svmaffione int system_cpus; /* cpus on the system */ 269341457Svmaffione 270234956Sluigi int options; /* testing */ 271234956Sluigi#define OPT_PREFETCH 1 272234956Sluigi#define OPT_ACCESS 2 273234956Sluigi#define OPT_COPY 4 274234956Sluigi#define OPT_MEMCPY 8 275246896Sluigi#define OPT_TS 16 /* add a timestamp */ 276251426Sluigi#define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 277251426Sluigi#define OPT_DUMP 64 /* dump rx/tx traffic */ 278341457Svmaffione#define OPT_RUBBISH 256 /* send wathever the buffers contain */ 279281746Sadrian#define OPT_RANDOM_SRC 512 280281746Sadrian#define OPT_RANDOM_DST 1024 281341457Svmaffione#define OPT_PPS_STATS 2048 282246896Sluigi int dev_type; 283260700Sluigi#ifndef NO_PCAP 284227614Sluigi pcap_t *p; 285260700Sluigi#endif 286227614Sluigi 287251132Sluigi int tx_rate; 288251132Sluigi struct timespec tx_period; 289251132Sluigi 290246896Sluigi int affinity; 291246896Sluigi int main_fd; 292261909Sluigi struct nm_desc *nmd; 293260700Sluigi int report_interval; /* milliseconds between prints */ 294246896Sluigi void *(*td_body)(void *); 295341457Svmaffione int td_type; 296246896Sluigi void *mmap_addr; 297261909Sluigi char ifname[MAX_IFNAMELEN]; 298257529Sluigi char *nmr_config; 299257529Sluigi int dummy_send; 300260368Sluigi int virt_header; /* send also the virt_header */ 301272962Sgnn char *packet_file; /* -P option */ 302341457Svmaffione#define STATS_WIN 15 303341457Svmaffione int win_idx; 304341457Svmaffione int64_t win[STATS_WIN]; 305341457Svmaffione int wait_link; 306341457Svmaffione int framing; /* #bits of framing (for bw output) */ 307227614Sluigi}; 308246896Sluigienum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 309227614Sluigi 310342035Svmaffioneenum { 311342035Svmaffione TD_TYPE_SENDER = 1, 312342035Svmaffione TD_TYPE_RECEIVER, 313342035Svmaffione TD_TYPE_OTHER, 314342035Svmaffione}; 315246896Sluigi 316227614Sluigi/* 317227614Sluigi * Arguments for a new thread. The same structure is used by 318227614Sluigi * the source and the sink 319227614Sluigi */ 320227614Sluigistruct targ { 321227614Sluigi struct glob_arg *g; 322227614Sluigi int used; 323227614Sluigi int completed; 324238165Semaste int cancel; 325227614Sluigi int fd; 326261909Sluigi struct nm_desc *nmd; 327341457Svmaffione /* these ought to be volatile, but they are 328341457Svmaffione * only sampled and errors should not accumulate 329341457Svmaffione */ 330341457Svmaffione struct my_ctrs ctr; 331341457Svmaffione 332251132Sluigi struct timespec tic, toc; 333227614Sluigi int me; 334227614Sluigi pthread_t thread; 335227614Sluigi int affinity; 336227614Sluigi 337227614Sluigi struct pkt pkt; 338272962Sgnn void *frame; 339341457Svmaffione uint16_t seed[3]; 340341457Svmaffione u_int frags; 341341457Svmaffione u_int frag_size; 342227614Sluigi}; 343227614Sluigi 344341457Svmaffionestatic __inline uint16_t 345341457Svmaffionecksum_add(uint16_t sum, uint16_t a) 346341457Svmaffione{ 347341457Svmaffione uint16_t res; 348227614Sluigi 349341457Svmaffione res = sum + a; 350341457Svmaffione return (res + (res < a)); 351341457Svmaffione} 352341457Svmaffione 353341457Svmaffionestatic void 354341457Svmaffioneextract_ipv4_addr(char *name, uint32_t *addr, uint16_t *port) 355341457Svmaffione{ 356341457Svmaffione struct in_addr a; 357341457Svmaffione char *pp; 358341457Svmaffione 359341457Svmaffione pp = strchr(name, ':'); 360341457Svmaffione if (pp != NULL) { /* do we have ports ? */ 361341457Svmaffione *pp++ = '\0'; 362341457Svmaffione *port = (uint16_t)strtol(pp, NULL, 0); 363341457Svmaffione } 364341457Svmaffione 365341457Svmaffione inet_pton(AF_INET, name, &a); 366341457Svmaffione *addr = ntohl(a.s_addr); 367341457Svmaffione} 368341457Svmaffione 369341457Svmaffionestatic void 370341457Svmaffioneextract_ipv6_addr(char *name, struct in6_addr *addr, uint16_t *port, 371341457Svmaffione uint8_t *group) 372341457Svmaffione{ 373341457Svmaffione char *pp; 374341457Svmaffione 375341457Svmaffione /* 376341457Svmaffione * We accept IPv6 address in the following form: 377341457Svmaffione * group@[2001:DB8::1001]:port (w/ brackets and port) 378341457Svmaffione * group@[2001:DB8::1] (w/ brackets and w/o port) 379341457Svmaffione * group@2001:DB8::1234 (w/o brackets and w/o port) 380341457Svmaffione */ 381341457Svmaffione pp = strchr(name, '@'); 382341457Svmaffione if (pp != NULL) { 383341457Svmaffione *pp++ = '\0'; 384341457Svmaffione *group = (uint8_t)strtol(name, NULL, 0); 385341457Svmaffione if (*group > 7) 386341457Svmaffione *group = 7; 387341457Svmaffione name = pp; 388341457Svmaffione } 389341457Svmaffione if (name[0] == '[') 390341457Svmaffione name++; 391341457Svmaffione pp = strchr(name, ']'); 392341457Svmaffione if (pp != NULL) 393341457Svmaffione *pp++ = '\0'; 394341457Svmaffione if (pp != NULL && *pp != ':') 395341457Svmaffione pp = NULL; 396341457Svmaffione if (pp != NULL) { /* do we have ports ? */ 397341457Svmaffione *pp++ = '\0'; 398341457Svmaffione *port = (uint16_t)strtol(pp, NULL, 0); 399341457Svmaffione } 400341457Svmaffione inet_pton(AF_INET6, name, addr); 401341457Svmaffione} 402246896Sluigi/* 403246896Sluigi * extract the extremes from a range of ipv4 addresses. 404246896Sluigi * addr_lo[-addr_hi][:port_lo[-port_hi]] 405246896Sluigi */ 406341457Svmaffionestatic int 407341457Svmaffioneextract_ip_range(struct ip_range *r, int af) 408246896Sluigi{ 409341457Svmaffione char *name, *ap, start[INET6_ADDRSTRLEN]; 410341457Svmaffione char end[INET6_ADDRSTRLEN]; 411257529Sluigi struct in_addr a; 412341457Svmaffione uint32_t tmp; 413246896Sluigi 414260368Sluigi if (verbose) 415260368Sluigi D("extract IP range from %s", r->name); 416257529Sluigi 417341457Svmaffione name = strdup(r->name); 418341457Svmaffione if (name == NULL) { 419341457Svmaffione D("strdup failed"); 420341457Svmaffione usage(-1); 421341457Svmaffione } 422257529Sluigi /* the first - splits start/end of range */ 423341457Svmaffione ap = strchr(name, '-'); 424341457Svmaffione if (ap != NULL) 425257529Sluigi *ap++ = '\0'; 426341457Svmaffione r->port0 = 1234; /* default port */ 427341457Svmaffione if (af == AF_INET6) { 428341457Svmaffione r->ipv6.sgroup = 7; /* default group */ 429341457Svmaffione extract_ipv6_addr(name, &r->ipv6.start, &r->port0, 430341457Svmaffione &r->ipv6.sgroup); 431341457Svmaffione } else 432341457Svmaffione extract_ipv4_addr(name, &r->ipv4.start, &r->port0); 433341457Svmaffione 434341457Svmaffione r->port1 = r->port0; 435341457Svmaffione if (af == AF_INET6) { 436341457Svmaffione if (ap != NULL) { 437341457Svmaffione r->ipv6.egroup = r->ipv6.sgroup; 438341457Svmaffione extract_ipv6_addr(ap, &r->ipv6.end, &r->port1, 439341457Svmaffione &r->ipv6.egroup); 440341457Svmaffione } else { 441341457Svmaffione r->ipv6.end = r->ipv6.start; 442341457Svmaffione r->ipv6.egroup = r->ipv6.sgroup; 443246896Sluigi } 444341457Svmaffione } else { 445341457Svmaffione if (ap != NULL) { 446341457Svmaffione extract_ipv4_addr(ap, &r->ipv4.end, &r->port1); 447341457Svmaffione if (r->ipv4.start > r->ipv4.end) { 448341457Svmaffione tmp = r->ipv4.end; 449341457Svmaffione r->ipv4.end = r->ipv4.start; 450341457Svmaffione r->ipv4.start = tmp; 451341457Svmaffione } 452341457Svmaffione } else 453341457Svmaffione r->ipv4.end = r->ipv4.start; 454246896Sluigi } 455341457Svmaffione 456257529Sluigi if (r->port0 > r->port1) { 457341457Svmaffione tmp = r->port0; 458257529Sluigi r->port0 = r->port1; 459257529Sluigi r->port1 = tmp; 460257529Sluigi } 461341457Svmaffione if (af == AF_INET) { 462341457Svmaffione a.s_addr = htonl(r->ipv4.start); 463341457Svmaffione inet_ntop(af, &a, start, sizeof(start)); 464341457Svmaffione a.s_addr = htonl(r->ipv4.end); 465341457Svmaffione inet_ntop(af, &a, end, sizeof(end)); 466341457Svmaffione } else { 467341457Svmaffione inet_ntop(af, &r->ipv6.start, start, sizeof(start)); 468341457Svmaffione inet_ntop(af, &r->ipv6.end, end, sizeof(end)); 469246896Sluigi } 470341457Svmaffione if (af == AF_INET) 471341457Svmaffione D("range is %s:%d to %s:%d", start, r->port0, end, r->port1); 472341457Svmaffione else 473341457Svmaffione D("range is %d@[%s]:%d to %d@[%s]:%d", r->ipv6.sgroup, 474341457Svmaffione start, r->port0, r->ipv6.egroup, end, r->port1); 475257529Sluigi 476341457Svmaffione free(name); 477341457Svmaffione if (r->port0 != r->port1 || 478341457Svmaffione (af == AF_INET && r->ipv4.start != r->ipv4.end) || 479341457Svmaffione (af == AF_INET6 && 480341457Svmaffione !IN6_ARE_ADDR_EQUAL(&r->ipv6.start, &r->ipv6.end))) 481341457Svmaffione return (OPT_COPY); 482341457Svmaffione return (0); 483246896Sluigi} 484246896Sluigi 485341457Svmaffionestatic int 486246896Sluigiextract_mac_range(struct mac_range *r) 487246896Sluigi{ 488341457Svmaffione struct ether_addr *e; 489260368Sluigi if (verbose) 490260368Sluigi D("extract MAC range from %s", r->name); 491341457Svmaffione 492341457Svmaffione e = ether_aton(r->name); 493341457Svmaffione if (e == NULL) { 494341457Svmaffione D("invalid MAC address '%s'", r->name); 495341457Svmaffione return 1; 496341457Svmaffione } 497341457Svmaffione bcopy(e, &r->start, 6); 498341457Svmaffione bcopy(e, &r->end, 6); 499246896Sluigi#if 0 500246896Sluigi bcopy(targ->src_mac, eh->ether_shost, 6); 501246896Sluigi p = index(targ->g->src_mac, '-'); 502246896Sluigi if (p) 503246896Sluigi targ->src_mac_range = atoi(p+1); 504246896Sluigi 505246896Sluigi bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 506246896Sluigi bcopy(targ->dst_mac, eh->ether_dhost, 6); 507246896Sluigi p = index(targ->g->dst_mac, '-'); 508246896Sluigi if (p) 509246896Sluigi targ->dst_mac_range = atoi(p+1); 510246896Sluigi#endif 511260368Sluigi if (verbose) 512260368Sluigi D("%s starts at %s", r->name, ether_ntoa(&r->start)); 513341457Svmaffione return 0; 514246896Sluigi} 515246896Sluigi 516342035Svmaffionestatic int 517342035Svmaffioneget_if_mtu(const struct glob_arg *g) 518342035Svmaffione{ 519342035Svmaffione char ifname[IFNAMSIZ]; 520342035Svmaffione struct ifreq ifreq; 521342035Svmaffione int s, ret; 522342035Svmaffione 523342035Svmaffione if (!strncmp(g->ifname, "netmap:", 7) && !strchr(g->ifname, '{') 524342035Svmaffione && !strchr(g->ifname, '}')) { 525342035Svmaffione /* Parse the interface name and ask the kernel for the 526342035Svmaffione * MTU value. */ 527342035Svmaffione strncpy(ifname, g->ifname+7, IFNAMSIZ-1); 528342035Svmaffione ifname[strcspn(ifname, "-*^{}/@")] = '\0'; 529342035Svmaffione 530342035Svmaffione s = socket(AF_INET, SOCK_DGRAM, 0); 531342035Svmaffione if (s < 0) { 532342035Svmaffione D("socket() failed: %s", strerror(errno)); 533342035Svmaffione return s; 534342035Svmaffione } 535342035Svmaffione 536342035Svmaffione memset(&ifreq, 0, sizeof(ifreq)); 537342035Svmaffione strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); 538342035Svmaffione 539342035Svmaffione ret = ioctl(s, SIOCGIFMTU, &ifreq); 540342035Svmaffione if (ret) { 541342035Svmaffione D("ioctl(SIOCGIFMTU) failed: %s", strerror(errno)); 542342035Svmaffione } 543342035Svmaffione 544342035Svmaffione return ifreq.ifr_mtu; 545342035Svmaffione } 546342035Svmaffione 547342035Svmaffione /* This is a pipe or a VALE port, where the MTU is very large, 548342035Svmaffione * so we use some practical limit. */ 549342035Svmaffione return 65536; 550342035Svmaffione} 551342035Svmaffione 552227614Sluigistatic struct targ *targs; 553227614Sluigistatic int global_nthreads; 554227614Sluigi 555227614Sluigi/* control-C handler */ 556227614Sluigistatic void 557246896Sluigisigint_h(int sig) 558227614Sluigi{ 559246896Sluigi int i; 560246896Sluigi 561246896Sluigi (void)sig; /* UNUSED */ 562341457Svmaffione D("received control-C on thread %p", (void *)pthread_self()); 563246896Sluigi for (i = 0; i < global_nthreads; i++) { 564238165Semaste targs[i].cancel = 1; 565246896Sluigi } 566227614Sluigi} 567227614Sluigi 568227614Sluigi/* sysctl wrapper to return the number of active CPUs */ 569227614Sluigistatic int 570227614Sluigisystem_ncpus(void) 571227614Sluigi{ 572261909Sluigi int ncpus; 573261909Sluigi#if defined (__FreeBSD__) 574261909Sluigi int mib[2] = { CTL_HW, HW_NCPU }; 575261909Sluigi size_t len = sizeof(mib); 576227614Sluigi sysctl(mib, 2, &ncpus, &len, NULL, 0); 577261909Sluigi#elif defined(linux) 578261909Sluigi ncpus = sysconf(_SC_NPROCESSORS_ONLN); 579341457Svmaffione#elif defined(_WIN32) 580341457Svmaffione { 581341457Svmaffione SYSTEM_INFO sysinfo; 582341457Svmaffione GetSystemInfo(&sysinfo); 583341457Svmaffione ncpus = sysinfo.dwNumberOfProcessors; 584341457Svmaffione } 585261909Sluigi#else /* others */ 586261909Sluigi ncpus = 1; 587261909Sluigi#endif /* others */ 588227614Sluigi return (ncpus); 589227614Sluigi} 590227614Sluigi 591246896Sluigi#ifdef __linux__ 592246896Sluigi#define sockaddr_dl sockaddr_ll 593246896Sluigi#define sdl_family sll_family 594246896Sluigi#define AF_LINK AF_PACKET 595246896Sluigi#define LLADDR(s) s->sll_addr; 596246896Sluigi#include <linux/if_tun.h> 597246896Sluigi#define TAP_CLONEDEV "/dev/net/tun" 598246896Sluigi#endif /* __linux__ */ 599246896Sluigi 600246896Sluigi#ifdef __FreeBSD__ 601246896Sluigi#include <net/if_tun.h> 602246896Sluigi#define TAP_CLONEDEV "/dev/tap" 603246896Sluigi#endif /* __FreeBSD */ 604246896Sluigi 605246896Sluigi#ifdef __APPLE__ 606246896Sluigi// #warning TAP not supported on apple ? 607246896Sluigi#include <net/if_utun.h> 608246896Sluigi#define TAP_CLONEDEV "/dev/tap" 609246896Sluigi#endif /* __APPLE__ */ 610246896Sluigi 611246896Sluigi 612227614Sluigi/* 613257529Sluigi * parse the vale configuration in conf and put it in nmr. 614261909Sluigi * Return the flag set if necessary. 615341457Svmaffione * The configuration may consist of 1 to 4 numbers separated 616257906Shiren * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 617257529Sluigi * Missing numbers or zeroes stand for default values. 618257529Sluigi * As an additional convenience, if exactly one number 619257906Shiren * is specified, then this is assigned to both #tx-slots and #rx-slots. 620261909Sluigi * If there is no 4th number, then the 3rd is assigned to both #tx-rings 621257529Sluigi * and #rx-rings. 622257529Sluigi */ 623261909Sluigiint 624261909Sluigiparse_nmr_config(const char* conf, struct nmreq *nmr) 625257529Sluigi{ 626257529Sluigi char *w, *tok; 627257529Sluigi int i, v; 628257529Sluigi 629344918Svmaffione if (conf == NULL || ! *conf) 630344918Svmaffione return 0; 631257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 632257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 633257529Sluigi w = strdup(conf); 634257529Sluigi for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 635257529Sluigi v = atoi(tok); 636257529Sluigi switch (i) { 637257529Sluigi case 0: 638257529Sluigi nmr->nr_tx_slots = nmr->nr_rx_slots = v; 639257529Sluigi break; 640257529Sluigi case 1: 641257529Sluigi nmr->nr_rx_slots = v; 642257529Sluigi break; 643257529Sluigi case 2: 644257529Sluigi nmr->nr_tx_rings = nmr->nr_rx_rings = v; 645257529Sluigi break; 646257529Sluigi case 3: 647257529Sluigi nmr->nr_rx_rings = v; 648257529Sluigi break; 649257529Sluigi default: 650257529Sluigi D("ignored config: %s", tok); 651257529Sluigi break; 652257529Sluigi } 653257529Sluigi } 654257529Sluigi D("txr %d txd %d rxr %d rxd %d", 655257529Sluigi nmr->nr_tx_rings, nmr->nr_tx_slots, 656257529Sluigi nmr->nr_rx_rings, nmr->nr_rx_slots); 657257529Sluigi free(w); 658261909Sluigi return (nmr->nr_tx_rings || nmr->nr_tx_slots || 659341457Svmaffione nmr->nr_rx_rings || nmr->nr_rx_slots) ? 660261909Sluigi NM_OPEN_RING_CFG : 0; 661257529Sluigi} 662257529Sluigi 663257529Sluigi 664257529Sluigi/* 665227614Sluigi * locate the src mac address for our interface, put it 666227614Sluigi * into the user-supplied buffer. return 0 if ok, -1 on error. 667227614Sluigi */ 668227614Sluigistatic int 669227614Sluigisource_hwaddr(const char *ifname, char *buf) 670227614Sluigi{ 671227614Sluigi struct ifaddrs *ifaphead, *ifap; 672227614Sluigi 673227614Sluigi if (getifaddrs(&ifaphead) != 0) { 674227614Sluigi D("getifaddrs %s failed", ifname); 675227614Sluigi return (-1); 676227614Sluigi } 677227614Sluigi 678227614Sluigi for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 679227614Sluigi struct sockaddr_dl *sdl = 680227614Sluigi (struct sockaddr_dl *)ifap->ifa_addr; 681227614Sluigi uint8_t *mac; 682227614Sluigi 683227614Sluigi if (!sdl || sdl->sdl_family != AF_LINK) 684227614Sluigi continue; 685341457Svmaffione if (strncmp(ifap->ifa_name, ifname, IFNAMSIZ) != 0) 686227614Sluigi continue; 687227614Sluigi mac = (uint8_t *)LLADDR(sdl); 688227614Sluigi sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 689227614Sluigi mac[0], mac[1], mac[2], 690227614Sluigi mac[3], mac[4], mac[5]); 691227614Sluigi if (verbose) 692227614Sluigi D("source hwaddr %s", buf); 693227614Sluigi break; 694227614Sluigi } 695227614Sluigi freeifaddrs(ifaphead); 696227614Sluigi return ifap ? 0 : 1; 697227614Sluigi} 698227614Sluigi 699227614Sluigi 700227614Sluigi/* set the thread affinity. */ 701227614Sluigistatic int 702227614Sluigisetaffinity(pthread_t me, int i) 703227614Sluigi{ 704227614Sluigi cpuset_t cpumask; 705227614Sluigi 706227614Sluigi if (i == -1) 707227614Sluigi return 0; 708227614Sluigi 709227614Sluigi /* Set thread affinity affinity.*/ 710227614Sluigi CPU_ZERO(&cpumask); 711227614Sluigi CPU_SET(i, &cpumask); 712227614Sluigi 713227614Sluigi if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 714260368Sluigi D("Unable to set affinity: %s", strerror(errno)); 715227614Sluigi return 1; 716227614Sluigi } 717227614Sluigi return 0; 718227614Sluigi} 719227614Sluigi 720341457Svmaffione 721227614Sluigi/* Compute the checksum of the given ip header. */ 722341457Svmaffionestatic uint32_t 723246896Sluigichecksum(const void *data, uint16_t len, uint32_t sum) 724227614Sluigi{ 725341457Svmaffione const uint8_t *addr = data; 726246896Sluigi uint32_t i; 727227614Sluigi 728341457Svmaffione /* Checksum all the pairs of bytes first... */ 729341457Svmaffione for (i = 0; i < (len & ~1U); i += 2) { 730341457Svmaffione sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 731341457Svmaffione if (sum > 0xFFFF) 732341457Svmaffione sum -= 0xFFFF; 733341457Svmaffione } 734246896Sluigi /* 735246896Sluigi * If there's a single byte left over, checksum it, too. 736246896Sluigi * Network byte order is big-endian, so the remaining byte is 737246896Sluigi * the high byte. 738246896Sluigi */ 739246896Sluigi if (i < len) { 740246896Sluigi sum += addr[i] << 8; 741246896Sluigi if (sum > 0xFFFF) 742246896Sluigi sum -= 0xFFFF; 743246896Sluigi } 744246896Sluigi return sum; 745246896Sluigi} 746227614Sluigi 747341457Svmaffionestatic uint16_t 748341457Svmaffionewrapsum(uint32_t sum) 749246896Sluigi{ 750246896Sluigi sum = ~sum & 0xFFFF; 751246896Sluigi return (htons(sum)); 752227614Sluigi} 753227614Sluigi 754251426Sluigi/* Check the payload of the packet for errors (use it for debug). 755251426Sluigi * Look for consecutive ascii representations of the size of the packet. 756251426Sluigi */ 757251426Sluigistatic void 758341457Svmaffionedump_payload(const char *_p, int len, struct netmap_ring *ring, int cur) 759251426Sluigi{ 760251426Sluigi char buf[128]; 761251426Sluigi int i, j, i0; 762341457Svmaffione const unsigned char *p = (const unsigned char *)_p; 763251426Sluigi 764251426Sluigi /* get the length in ASCII of the length of the packet. */ 765261909Sluigi 766257529Sluigi printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 767257529Sluigi ring, cur, ring->slot[cur].buf_idx, 768257529Sluigi ring->slot[cur].flags, len); 769251426Sluigi /* hexdump routine */ 770251426Sluigi for (i = 0; i < len; ) { 771341457Svmaffione memset(buf, ' ', sizeof(buf)); 772251426Sluigi sprintf(buf, "%5d: ", i); 773251426Sluigi i0 = i; 774251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 775251426Sluigi sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 776251426Sluigi i = i0; 777251426Sluigi for (j=0; j < 16 && i < len; i++, j++) 778251426Sluigi sprintf(buf+7+j + 48, "%c", 779251426Sluigi isprint(p[i]) ? p[i] : '.'); 780251426Sluigi printf("%s\n", buf); 781251426Sluigi } 782251426Sluigi} 783251426Sluigi 784227614Sluigi/* 785227614Sluigi * Fill a packet with some payload. 786246896Sluigi * We create a UDP packet so the payload starts at 787246896Sluigi * 14+20+8 = 42 bytes. 788227614Sluigi */ 789246896Sluigi#ifdef __linux__ 790246896Sluigi#define uh_sport source 791246896Sluigi#define uh_dport dest 792246896Sluigi#define uh_ulen len 793246896Sluigi#define uh_sum check 794246896Sluigi#endif /* linux */ 795251426Sluigi 796227614Sluigistatic void 797341457Svmaffioneupdate_ip(struct pkt *pkt, struct targ *t) 798257529Sluigi{ 799341457Svmaffione struct glob_arg *g = t->g; 800341457Svmaffione struct ip ip; 801341457Svmaffione struct udphdr udp; 802341457Svmaffione uint32_t oaddr, naddr; 803341457Svmaffione uint16_t oport, nport; 804341457Svmaffione uint16_t ip_sum, udp_sum; 805257529Sluigi 806341457Svmaffione memcpy(&ip, &pkt->ipv4.ip, sizeof(ip)); 807341457Svmaffione memcpy(&udp, &pkt->ipv4.udp, sizeof(udp)); 808341457Svmaffione do { 809341457Svmaffione ip_sum = udp_sum = 0; 810341457Svmaffione naddr = oaddr = ntohl(ip.ip_src.s_addr); 811341457Svmaffione nport = oport = ntohs(udp.uh_sport); 812341457Svmaffione if (g->options & OPT_RANDOM_SRC) { 813341457Svmaffione ip.ip_src.s_addr = nrand48(t->seed); 814341457Svmaffione udp.uh_sport = nrand48(t->seed); 815341457Svmaffione naddr = ntohl(ip.ip_src.s_addr); 816341457Svmaffione nport = ntohs(udp.uh_sport); 817281746Sadrian break; 818281746Sadrian } 819341457Svmaffione if (oport < g->src_ip.port1) { 820341457Svmaffione nport = oport + 1; 821341457Svmaffione udp.uh_sport = htons(nport); 822281746Sadrian break; 823281746Sadrian } 824341457Svmaffione nport = g->src_ip.port0; 825341457Svmaffione udp.uh_sport = htons(nport); 826341457Svmaffione if (oaddr < g->src_ip.ipv4.end) { 827341457Svmaffione naddr = oaddr + 1; 828341457Svmaffione ip.ip_src.s_addr = htonl(naddr); 829341457Svmaffione break; 830341457Svmaffione } 831341457Svmaffione naddr = g->src_ip.ipv4.start; 832341457Svmaffione ip.ip_src.s_addr = htonl(naddr); 833341457Svmaffione } while (0); 834341457Svmaffione /* update checksums if needed */ 835341457Svmaffione if (oaddr != naddr) { 836341457Svmaffione ip_sum = cksum_add(ip_sum, ~oaddr >> 16); 837341457Svmaffione ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff); 838341457Svmaffione ip_sum = cksum_add(ip_sum, naddr >> 16); 839341457Svmaffione ip_sum = cksum_add(ip_sum, naddr & 0xffff); 840257529Sluigi } 841341457Svmaffione if (oport != nport) { 842341457Svmaffione udp_sum = cksum_add(udp_sum, ~oport); 843341457Svmaffione udp_sum = cksum_add(udp_sum, nport); 844341457Svmaffione } 845341457Svmaffione do { 846341457Svmaffione naddr = oaddr = ntohl(ip.ip_dst.s_addr); 847341457Svmaffione nport = oport = ntohs(udp.uh_dport); 848341457Svmaffione if (g->options & OPT_RANDOM_DST) { 849341457Svmaffione ip.ip_dst.s_addr = nrand48(t->seed); 850341457Svmaffione udp.uh_dport = nrand48(t->seed); 851341457Svmaffione naddr = ntohl(ip.ip_dst.s_addr); 852341457Svmaffione nport = ntohs(udp.uh_dport); 853281746Sadrian break; 854281746Sadrian } 855341457Svmaffione if (oport < g->dst_ip.port1) { 856341457Svmaffione nport = oport + 1; 857341457Svmaffione udp.uh_dport = htons(nport); 858281746Sadrian break; 859281746Sadrian } 860341457Svmaffione nport = g->dst_ip.port0; 861341457Svmaffione udp.uh_dport = htons(nport); 862341457Svmaffione if (oaddr < g->dst_ip.ipv4.end) { 863341457Svmaffione naddr = oaddr + 1; 864341457Svmaffione ip.ip_dst.s_addr = htonl(naddr); 865341457Svmaffione break; 866341457Svmaffione } 867341457Svmaffione naddr = g->dst_ip.ipv4.start; 868341457Svmaffione ip.ip_dst.s_addr = htonl(naddr); 869341457Svmaffione } while (0); 870341457Svmaffione /* update checksums */ 871341457Svmaffione if (oaddr != naddr) { 872341457Svmaffione ip_sum = cksum_add(ip_sum, ~oaddr >> 16); 873341457Svmaffione ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff); 874341457Svmaffione ip_sum = cksum_add(ip_sum, naddr >> 16); 875341457Svmaffione ip_sum = cksum_add(ip_sum, naddr & 0xffff); 876257529Sluigi } 877341457Svmaffione if (oport != nport) { 878341457Svmaffione udp_sum = cksum_add(udp_sum, ~oport); 879341457Svmaffione udp_sum = cksum_add(udp_sum, nport); 880341457Svmaffione } 881341457Svmaffione if (udp_sum != 0) 882341457Svmaffione udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(udp_sum)); 883341457Svmaffione if (ip_sum != 0) { 884341457Svmaffione ip.ip_sum = ~cksum_add(~ip.ip_sum, htons(ip_sum)); 885341457Svmaffione udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(ip_sum)); 886341457Svmaffione } 887341457Svmaffione memcpy(&pkt->ipv4.ip, &ip, sizeof(ip)); 888341457Svmaffione memcpy(&pkt->ipv4.udp, &udp, sizeof(udp)); 889257529Sluigi} 890257529Sluigi 891341457Svmaffione#ifndef s6_addr16 892341457Svmaffione#define s6_addr16 __u6_addr.__u6_addr16 893341457Svmaffione#endif 894341457Svmaffionestatic void 895341457Svmaffioneupdate_ip6(struct pkt *pkt, struct targ *t) 896341457Svmaffione{ 897341457Svmaffione struct glob_arg *g = t->g; 898341457Svmaffione struct ip6_hdr ip6; 899341457Svmaffione struct udphdr udp; 900341457Svmaffione uint16_t udp_sum; 901341457Svmaffione uint16_t oaddr, naddr; 902341457Svmaffione uint16_t oport, nport; 903341457Svmaffione uint8_t group; 904341457Svmaffione 905341457Svmaffione memcpy(&ip6, &pkt->ipv6.ip, sizeof(ip6)); 906341457Svmaffione memcpy(&udp, &pkt->ipv6.udp, sizeof(udp)); 907341457Svmaffione do { 908341457Svmaffione udp_sum = 0; 909341457Svmaffione group = g->src_ip.ipv6.sgroup; 910341457Svmaffione naddr = oaddr = ntohs(ip6.ip6_src.s6_addr16[group]); 911341457Svmaffione nport = oport = ntohs(udp.uh_sport); 912341457Svmaffione if (g->options & OPT_RANDOM_SRC) { 913341457Svmaffione ip6.ip6_src.s6_addr16[group] = nrand48(t->seed); 914341457Svmaffione udp.uh_sport = nrand48(t->seed); 915341457Svmaffione naddr = ntohs(ip6.ip6_src.s6_addr16[group]); 916341457Svmaffione nport = ntohs(udp.uh_sport); 917341457Svmaffione break; 918341457Svmaffione } 919341457Svmaffione if (oport < g->src_ip.port1) { 920341457Svmaffione nport = oport + 1; 921341457Svmaffione udp.uh_sport = htons(nport); 922341457Svmaffione break; 923341457Svmaffione } 924341457Svmaffione nport = g->src_ip.port0; 925341457Svmaffione udp.uh_sport = htons(nport); 926341457Svmaffione if (oaddr < ntohs(g->src_ip.ipv6.end.s6_addr16[group])) { 927341457Svmaffione naddr = oaddr + 1; 928341457Svmaffione ip6.ip6_src.s6_addr16[group] = htons(naddr); 929341457Svmaffione break; 930341457Svmaffione } 931341457Svmaffione naddr = ntohs(g->src_ip.ipv6.start.s6_addr16[group]); 932341457Svmaffione ip6.ip6_src.s6_addr16[group] = htons(naddr); 933341457Svmaffione } while (0); 934341457Svmaffione /* update checksums if needed */ 935341457Svmaffione if (oaddr != naddr) 936341457Svmaffione udp_sum = cksum_add(~oaddr, naddr); 937341457Svmaffione if (oport != nport) 938341457Svmaffione udp_sum = cksum_add(udp_sum, 939341457Svmaffione cksum_add(~oport, nport)); 940341457Svmaffione do { 941341457Svmaffione group = g->dst_ip.ipv6.egroup; 942341457Svmaffione naddr = oaddr = ntohs(ip6.ip6_dst.s6_addr16[group]); 943341457Svmaffione nport = oport = ntohs(udp.uh_dport); 944341457Svmaffione if (g->options & OPT_RANDOM_DST) { 945341457Svmaffione ip6.ip6_dst.s6_addr16[group] = nrand48(t->seed); 946341457Svmaffione udp.uh_dport = nrand48(t->seed); 947341457Svmaffione naddr = ntohs(ip6.ip6_dst.s6_addr16[group]); 948341457Svmaffione nport = ntohs(udp.uh_dport); 949341457Svmaffione break; 950341457Svmaffione } 951341457Svmaffione if (oport < g->dst_ip.port1) { 952341457Svmaffione nport = oport + 1; 953341457Svmaffione udp.uh_dport = htons(nport); 954341457Svmaffione break; 955341457Svmaffione } 956341457Svmaffione nport = g->dst_ip.port0; 957341457Svmaffione udp.uh_dport = htons(nport); 958341457Svmaffione if (oaddr < ntohs(g->dst_ip.ipv6.end.s6_addr16[group])) { 959341457Svmaffione naddr = oaddr + 1; 960341457Svmaffione ip6.ip6_dst.s6_addr16[group] = htons(naddr); 961341457Svmaffione break; 962341457Svmaffione } 963341457Svmaffione naddr = ntohs(g->dst_ip.ipv6.start.s6_addr16[group]); 964341457Svmaffione ip6.ip6_dst.s6_addr16[group] = htons(naddr); 965341457Svmaffione } while (0); 966341457Svmaffione /* update checksums */ 967341457Svmaffione if (oaddr != naddr) 968341457Svmaffione udp_sum = cksum_add(udp_sum, 969341457Svmaffione cksum_add(~oaddr, naddr)); 970341457Svmaffione if (oport != nport) 971341457Svmaffione udp_sum = cksum_add(udp_sum, 972341457Svmaffione cksum_add(~oport, nport)); 973341457Svmaffione if (udp_sum != 0) 974341457Svmaffione udp.uh_sum = ~cksum_add(~udp.uh_sum, udp_sum); 975341457Svmaffione memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6)); 976341457Svmaffione memcpy(&pkt->ipv6.udp, &udp, sizeof(udp)); 977341457Svmaffione} 978341457Svmaffione 979341457Svmaffionestatic void 980341457Svmaffioneupdate_addresses(struct pkt *pkt, struct targ *t) 981341457Svmaffione{ 982341457Svmaffione 983341457Svmaffione if (t->g->af == AF_INET) 984341457Svmaffione update_ip(pkt, t); 985341457Svmaffione else 986341457Svmaffione update_ip6(pkt, t); 987341457Svmaffione} 988257529Sluigi/* 989257529Sluigi * initialize one packet and prepare for the next one. 990257529Sluigi * The copy could be done better instead of repeating it each time. 991257529Sluigi */ 992257529Sluigistatic void 993227614Sluigiinitialize_packet(struct targ *targ) 994227614Sluigi{ 995227614Sluigi struct pkt *pkt = &targ->pkt; 996227614Sluigi struct ether_header *eh; 997341457Svmaffione struct ip6_hdr ip6; 998341457Svmaffione struct ip ip; 999341457Svmaffione struct udphdr udp; 1000341457Svmaffione void *udp_ptr; 1001341457Svmaffione uint16_t paylen; 1002341457Svmaffione uint32_t csum = 0; 1003251426Sluigi const char *payload = targ->g->options & OPT_INDIRECT ? 1004257529Sluigi indirect_payload : default_payload; 1005260700Sluigi int i, l0 = strlen(payload); 1006227614Sluigi 1007341457Svmaffione#ifndef NO_PCAP 1008272962Sgnn char errbuf[PCAP_ERRBUF_SIZE]; 1009272962Sgnn pcap_t *file; 1010272962Sgnn struct pcap_pkthdr *header; 1011272962Sgnn const unsigned char *packet; 1012341457Svmaffione 1013272962Sgnn /* Read a packet from a PCAP file if asked. */ 1014272962Sgnn if (targ->g->packet_file != NULL) { 1015272962Sgnn if ((file = pcap_open_offline(targ->g->packet_file, 1016272962Sgnn errbuf)) == NULL) 1017272962Sgnn D("failed to open pcap file %s", 1018272962Sgnn targ->g->packet_file); 1019272962Sgnn if (pcap_next_ex(file, &header, &packet) < 0) 1020272962Sgnn D("failed to read packet from %s", 1021272962Sgnn targ->g->packet_file); 1022272962Sgnn if ((targ->frame = malloc(header->caplen)) == NULL) 1023272962Sgnn D("out of memory"); 1024272962Sgnn bcopy(packet, (unsigned char *)targ->frame, header->caplen); 1025272962Sgnn targ->g->pkt_size = header->caplen; 1026272962Sgnn pcap_close(file); 1027272962Sgnn return; 1028272962Sgnn } 1029341457Svmaffione#endif 1030272962Sgnn 1031341457Svmaffione paylen = targ->g->pkt_size - sizeof(*eh) - 1032341457Svmaffione (targ->g->af == AF_INET ? sizeof(ip): sizeof(ip6)); 1033341457Svmaffione 1034257529Sluigi /* create a nice NUL-terminated string */ 1035260700Sluigi for (i = 0; i < paylen; i += l0) { 1036260700Sluigi if (l0 > paylen - i) 1037260700Sluigi l0 = paylen - i; // last round 1038341457Svmaffione bcopy(payload, PKT(pkt, body, targ->g->af) + i, l0); 1039227614Sluigi } 1040341457Svmaffione PKT(pkt, body, targ->g->af)[i - 1] = '\0'; 1041227614Sluigi 1042257529Sluigi /* prepare the headers */ 1043227614Sluigi eh = &pkt->eh; 1044246896Sluigi bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 1045246896Sluigi bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 1046260368Sluigi 1047341457Svmaffione if (targ->g->af == AF_INET) { 1048341457Svmaffione eh->ether_type = htons(ETHERTYPE_IP); 1049341457Svmaffione memcpy(&ip, &pkt->ipv4.ip, sizeof(ip)); 1050341457Svmaffione udp_ptr = &pkt->ipv4.udp; 1051341457Svmaffione ip.ip_v = IPVERSION; 1052341457Svmaffione ip.ip_hl = sizeof(ip) >> 2; 1053341457Svmaffione ip.ip_id = 0; 1054341457Svmaffione ip.ip_tos = IPTOS_LOWDELAY; 1055341457Svmaffione ip.ip_len = htons(targ->g->pkt_size - sizeof(*eh)); 1056341457Svmaffione ip.ip_id = 0; 1057341457Svmaffione ip.ip_off = htons(IP_DF); /* Don't fragment */ 1058341457Svmaffione ip.ip_ttl = IPDEFTTL; 1059341457Svmaffione ip.ip_p = IPPROTO_UDP; 1060341457Svmaffione ip.ip_dst.s_addr = htonl(targ->g->dst_ip.ipv4.start); 1061341457Svmaffione ip.ip_src.s_addr = htonl(targ->g->src_ip.ipv4.start); 1062341457Svmaffione ip.ip_sum = wrapsum(checksum(&ip, sizeof(ip), 0)); 1063341457Svmaffione memcpy(&pkt->ipv4.ip, &ip, sizeof(ip)); 1064341457Svmaffione } else { 1065341457Svmaffione eh->ether_type = htons(ETHERTYPE_IPV6); 1066341457Svmaffione memcpy(&ip6, &pkt->ipv4.ip, sizeof(ip6)); 1067341457Svmaffione udp_ptr = &pkt->ipv6.udp; 1068341457Svmaffione ip6.ip6_flow = 0; 1069341457Svmaffione ip6.ip6_plen = htons(paylen); 1070341457Svmaffione ip6.ip6_vfc = IPV6_VERSION; 1071341457Svmaffione ip6.ip6_nxt = IPPROTO_UDP; 1072341457Svmaffione ip6.ip6_hlim = IPV6_DEFHLIM; 1073341457Svmaffione ip6.ip6_src = targ->g->src_ip.ipv6.start; 1074341457Svmaffione ip6.ip6_dst = targ->g->dst_ip.ipv6.start; 1075341457Svmaffione } 1076341457Svmaffione memcpy(&udp, udp_ptr, sizeof(udp)); 1077341457Svmaffione 1078341457Svmaffione udp.uh_sport = htons(targ->g->src_ip.port0); 1079341457Svmaffione udp.uh_dport = htons(targ->g->dst_ip.port0); 1080341457Svmaffione udp.uh_ulen = htons(paylen); 1081341457Svmaffione if (targ->g->af == AF_INET) { 1082341457Svmaffione /* Magic: taken from sbin/dhclient/packet.c */ 1083341457Svmaffione udp.uh_sum = wrapsum( 1084341457Svmaffione checksum(&udp, sizeof(udp), /* udp header */ 1085341457Svmaffione checksum(pkt->ipv4.body, /* udp payload */ 1086341457Svmaffione paylen - sizeof(udp), 1087341457Svmaffione checksum(&pkt->ipv4.ip.ip_src, /* pseudo header */ 1088341457Svmaffione 2 * sizeof(pkt->ipv4.ip.ip_src), 1089341457Svmaffione IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen))))); 1090341457Svmaffione memcpy(&pkt->ipv4.ip, &ip, sizeof(ip)); 1091341457Svmaffione } else { 1092341457Svmaffione /* Save part of pseudo header checksum into csum */ 1093341457Svmaffione csum = IPPROTO_UDP << 24; 1094341457Svmaffione csum = checksum(&csum, sizeof(csum), paylen); 1095341457Svmaffione udp.uh_sum = wrapsum( 1096341457Svmaffione checksum(udp_ptr, sizeof(udp), /* udp header */ 1097341457Svmaffione checksum(pkt->ipv6.body, /* udp payload */ 1098341457Svmaffione paylen - sizeof(udp), 1099341457Svmaffione checksum(&pkt->ipv6.ip.ip6_src, /* pseudo header */ 1100341457Svmaffione 2 * sizeof(pkt->ipv6.ip.ip6_src), csum)))); 1101341457Svmaffione memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6)); 1102341457Svmaffione } 1103341457Svmaffione memcpy(udp_ptr, &udp, sizeof(udp)); 1104341457Svmaffione 1105260368Sluigi bzero(&pkt->vh, sizeof(pkt->vh)); 1106251426Sluigi // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 1107227614Sluigi} 1108227614Sluigi 1109270063Sluigistatic void 1110341457Svmaffioneget_vnet_hdr_len(struct glob_arg *g) 1111270063Sluigi{ 1112270063Sluigi struct nmreq req; 1113341457Svmaffione int err; 1114227614Sluigi 1115341457Svmaffione memset(&req, 0, sizeof(req)); 1116341457Svmaffione bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 1117341457Svmaffione req.nr_version = NETMAP_API; 1118341457Svmaffione req.nr_cmd = NETMAP_VNET_HDR_GET; 1119341457Svmaffione err = ioctl(g->main_fd, NIOCREGIF, &req); 1120341457Svmaffione if (err) { 1121341457Svmaffione D("Unable to get virtio-net header length"); 1122341457Svmaffione return; 1123341457Svmaffione } 1124341457Svmaffione 1125341457Svmaffione g->virt_header = req.nr_arg1; 1126341457Svmaffione if (g->virt_header) { 1127341457Svmaffione D("Port requires virtio-net header, length = %d", 1128341457Svmaffione g->virt_header); 1129341457Svmaffione } 1130341457Svmaffione} 1131341457Svmaffione 1132341457Svmaffionestatic void 1133341457Svmaffioneset_vnet_hdr_len(struct glob_arg *g) 1134341457Svmaffione{ 1135341457Svmaffione int err, l = g->virt_header; 1136341457Svmaffione struct nmreq req; 1137341457Svmaffione 1138270063Sluigi if (l == 0) 1139270063Sluigi return; 1140227614Sluigi 1141270063Sluigi memset(&req, 0, sizeof(req)); 1142341457Svmaffione bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 1143270063Sluigi req.nr_version = NETMAP_API; 1144270063Sluigi req.nr_cmd = NETMAP_BDG_VNET_HDR; 1145270063Sluigi req.nr_arg1 = l; 1146341457Svmaffione err = ioctl(g->main_fd, NIOCREGIF, &req); 1147270063Sluigi if (err) { 1148341457Svmaffione D("Unable to set virtio-net header length %d", l); 1149270063Sluigi } 1150270063Sluigi} 1151270063Sluigi 1152227614Sluigi/* 1153227614Sluigi * create and enqueue a batch of packets on a ring. 1154227614Sluigi * On the last one set NS_REPORT to tell the driver to generate 1155227614Sluigi * an interrupt when done. 1156227614Sluigi */ 1157227614Sluigistatic int 1158260368Sluigisend_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 1159341457Svmaffione int size, struct targ *t, u_int count, int options) 1160227614Sluigi{ 1161344918Svmaffione u_int n, sent, head = ring->head; 1162341457Svmaffione u_int frags = t->frags; 1163341457Svmaffione u_int frag_size = t->frag_size; 1164344918Svmaffione struct netmap_slot *slot = &ring->slot[head]; 1165227614Sluigi 1166260368Sluigi n = nm_ring_space(ring); 1167234956Sluigi#if 0 1168234956Sluigi if (options & (OPT_COPY | OPT_PREFETCH) ) { 1169234956Sluigi for (sent = 0; sent < count; sent++) { 1170344918Svmaffione struct netmap_slot *slot = &ring->slot[head]; 1171234956Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 1172234956Sluigi 1173260700Sluigi __builtin_prefetch(p); 1174344918Svmaffione head = nm_ring_next(ring, head); 1175234956Sluigi } 1176344918Svmaffione head = ring->head; 1177234956Sluigi } 1178234956Sluigi#endif 1179341457Svmaffione for (sent = 0; sent < count && n >= frags; sent++, n--) { 1180341457Svmaffione char *p; 1181341457Svmaffione int buf_changed; 1182341457Svmaffione u_int tosend = size; 1183227614Sluigi 1184344918Svmaffione slot = &ring->slot[head]; 1185341457Svmaffione p = NETMAP_BUF(ring, slot->buf_idx); 1186341457Svmaffione buf_changed = slot->flags & NS_BUF_CHANGED; 1187341457Svmaffione 1188251426Sluigi slot->flags = 0; 1189341457Svmaffione if (options & OPT_RUBBISH) { 1190341457Svmaffione /* do nothing */ 1191341457Svmaffione } else if (options & OPT_INDIRECT) { 1192251426Sluigi slot->flags |= NS_INDIRECT; 1193341457Svmaffione slot->ptr = (uint64_t)((uintptr_t)frame); 1194341457Svmaffione } else if (frags > 1) { 1195341457Svmaffione u_int i; 1196341457Svmaffione const char *f = frame; 1197341457Svmaffione char *fp = p; 1198341457Svmaffione for (i = 0; i < frags - 1; i++) { 1199341457Svmaffione memcpy(fp, f, frag_size); 1200341457Svmaffione slot->len = frag_size; 1201341457Svmaffione slot->flags = NS_MOREFRAG; 1202341457Svmaffione if (options & OPT_DUMP) 1203344918Svmaffione dump_payload(fp, frag_size, ring, head); 1204341457Svmaffione tosend -= frag_size; 1205341457Svmaffione f += frag_size; 1206344918Svmaffione head = nm_ring_next(ring, head); 1207344918Svmaffione slot = &ring->slot[head]; 1208341457Svmaffione fp = NETMAP_BUF(ring, slot->buf_idx); 1209341457Svmaffione } 1210341457Svmaffione n -= (frags - 1); 1211341457Svmaffione p = fp; 1212341457Svmaffione slot->flags = 0; 1213341457Svmaffione memcpy(p, f, tosend); 1214341457Svmaffione update_addresses(pkt, t); 1215341457Svmaffione } else if ((options & (OPT_COPY | OPT_MEMCPY)) || buf_changed) { 1216341457Svmaffione if (options & OPT_COPY) 1217341457Svmaffione nm_pkt_copy(frame, p, size); 1218341457Svmaffione else 1219341457Svmaffione memcpy(p, frame, size); 1220341457Svmaffione update_addresses(pkt, t); 1221257529Sluigi } else if (options & OPT_PREFETCH) { 1222260700Sluigi __builtin_prefetch(p); 1223257529Sluigi } 1224341457Svmaffione slot->len = tosend; 1225257529Sluigi if (options & OPT_DUMP) 1226344918Svmaffione dump_payload(p, tosend, ring, head); 1227344918Svmaffione head = nm_ring_next(ring, head); 1228227614Sluigi } 1229341457Svmaffione if (sent) { 1230341457Svmaffione slot->flags |= NS_REPORT; 1231344918Svmaffione ring->head = ring->cur = head; 1232341457Svmaffione } 1233341457Svmaffione if (sent < count) { 1234341457Svmaffione /* tell netmap that we need more slots */ 1235341457Svmaffione ring->cur = ring->tail; 1236341457Svmaffione } 1237227614Sluigi 1238227614Sluigi return (sent); 1239227614Sluigi} 1240227614Sluigi 1241246896Sluigi/* 1242341457Svmaffione * Index of the highest bit set 1243341457Svmaffione */ 1244341457Svmaffioneuint32_t 1245341457Svmaffionemsb64(uint64_t x) 1246341457Svmaffione{ 1247341457Svmaffione uint64_t m = 1ULL << 63; 1248341457Svmaffione int i; 1249341457Svmaffione 1250341457Svmaffione for (i = 63; i >= 0; i--, m >>=1) 1251341457Svmaffione if (m & x) 1252341457Svmaffione return i; 1253341457Svmaffione return 0; 1254341457Svmaffione} 1255341457Svmaffione 1256341457Svmaffione/* 1257341457Svmaffione * wait until ts, either busy or sleeping if more than 1ms. 1258341457Svmaffione * Return wakeup time. 1259341457Svmaffione */ 1260341457Svmaffionestatic struct timespec 1261341457Svmaffionewait_time(struct timespec ts) 1262341457Svmaffione{ 1263341457Svmaffione for (;;) { 1264341457Svmaffione struct timespec w, cur; 1265341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 1266341457Svmaffione w = timespec_sub(ts, cur); 1267341457Svmaffione if (w.tv_sec < 0) 1268341457Svmaffione return cur; 1269341457Svmaffione else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 1270341457Svmaffione poll(NULL, 0, 1); 1271341457Svmaffione } 1272341457Svmaffione} 1273341457Svmaffione 1274341457Svmaffione/* 1275246896Sluigi * Send a packet, and wait for a response. 1276246896Sluigi * The payload (after UDP header, ofs 42) has a 4-byte sequence 1277246896Sluigi * followed by a struct timeval (or bintime?) 1278246896Sluigi */ 1279246896Sluigi 1280227614Sluigistatic void * 1281341457Svmaffioneping_body(void *data) 1282246896Sluigi{ 1283246896Sluigi struct targ *targ = (struct targ *) data; 1284261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1285261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 1286341457Svmaffione int i, m, rx = 0; 1287260368Sluigi void *frame; 1288260368Sluigi int size; 1289261909Sluigi struct timespec ts, now, last_print; 1290341457Svmaffione struct timespec nexttime = {0, 0}; /* silence compiler */ 1291341457Svmaffione uint64_t sent = 0, n = targ->g->npackets; 1292341457Svmaffione uint64_t count = 0, t_cur, t_min = ~0, av = 0; 1293341457Svmaffione uint64_t g_min = ~0, g_av = 0; 1294341457Svmaffione uint64_t buckets[64]; /* bins for delays, ns */ 1295341457Svmaffione int rate_limit = targ->g->tx_rate, tosend = 0; 1296246896Sluigi 1297341457Svmaffione frame = (char*)&targ->pkt + sizeof(targ->pkt.vh) - targ->g->virt_header; 1298260368Sluigi size = targ->g->pkt_size + targ->g->virt_header; 1299260368Sluigi 1300341457Svmaffione 1301246896Sluigi if (targ->g->nthreads > 1) { 1302246896Sluigi D("can only ping with 1 thread"); 1303246896Sluigi return NULL; 1304246896Sluigi } 1305246896Sluigi 1306341457Svmaffione bzero(&buckets, sizeof(buckets)); 1307246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 1308260368Sluigi now = last_print; 1309341457Svmaffione if (rate_limit) { 1310341457Svmaffione targ->tic = timespec_add(now, (struct timespec){2,0}); 1311341457Svmaffione targ->tic.tv_nsec = 0; 1312341457Svmaffione wait_time(targ->tic); 1313341457Svmaffione nexttime = targ->tic; 1314341457Svmaffione } 1315341457Svmaffione while (!targ->cancel && (n == 0 || sent < n)) { 1316341457Svmaffione struct netmap_ring *ring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring); 1317246896Sluigi struct netmap_slot *slot; 1318246896Sluigi char *p; 1319341457Svmaffione int rv; 1320341457Svmaffione uint64_t limit, event = 0; 1321246896Sluigi 1322341457Svmaffione if (rate_limit && tosend <= 0) { 1323341457Svmaffione tosend = targ->g->burst; 1324341457Svmaffione nexttime = timespec_add(nexttime, targ->g->tx_period); 1325341457Svmaffione wait_time(nexttime); 1326246896Sluigi } 1327341457Svmaffione 1328341457Svmaffione limit = rate_limit ? tosend : targ->g->burst; 1329341457Svmaffione if (n > 0 && n - sent < limit) 1330341457Svmaffione limit = n - sent; 1331341457Svmaffione for (m = 0; (unsigned)m < limit; m++) { 1332344918Svmaffione slot = &ring->slot[ring->head]; 1333341457Svmaffione slot->len = size; 1334341457Svmaffione p = NETMAP_BUF(ring, slot->buf_idx); 1335341457Svmaffione 1336341457Svmaffione if (nm_ring_empty(ring)) { 1337341457Svmaffione D("-- ouch, cannot send"); 1338341457Svmaffione break; 1339341457Svmaffione } else { 1340341457Svmaffione struct tstamp *tp; 1341341457Svmaffione nm_pkt_copy(frame, p, size); 1342341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 1343341457Svmaffione bcopy(&sent, p+42, sizeof(sent)); 1344341457Svmaffione tp = (struct tstamp *)(p+46); 1345341457Svmaffione tp->sec = (uint32_t)ts.tv_sec; 1346341457Svmaffione tp->nsec = (uint32_t)ts.tv_nsec; 1347341457Svmaffione sent++; 1348344918Svmaffione ring->head = ring->cur = nm_ring_next(ring, ring->head); 1349341457Svmaffione } 1350341457Svmaffione } 1351341457Svmaffione if (m > 0) 1352341457Svmaffione event++; 1353341457Svmaffione targ->ctr.pkts = sent; 1354341457Svmaffione targ->ctr.bytes = sent*size; 1355341457Svmaffione targ->ctr.events = event; 1356341457Svmaffione if (rate_limit) 1357341457Svmaffione tosend -= m; 1358341457Svmaffione#ifdef BUSYWAIT 1359341457Svmaffione rv = ioctl(pfd.fd, NIOCTXSYNC, NULL); 1360341457Svmaffione if (rv < 0) { 1361341457Svmaffione D("TXSYNC error on queue %d: %s", targ->me, 1362341457Svmaffione strerror(errno)); 1363341457Svmaffione } 1364341457Svmaffione again: 1365341457Svmaffione ioctl(pfd.fd, NIOCRXSYNC, NULL); 1366341457Svmaffione#else 1367246896Sluigi /* should use a parameter to decide how often to send */ 1368341457Svmaffione if ( (rv = poll(&pfd, 1, 3000)) <= 0) { 1369341457Svmaffione D("poll error on queue %d: %s", targ->me, 1370341457Svmaffione (rv ? strerror(errno) : "timeout")); 1371246896Sluigi continue; 1372246896Sluigi } 1373341457Svmaffione#endif /* BUSYWAIT */ 1374246896Sluigi /* see what we got back */ 1375341457Svmaffione rx = 0; 1376341457Svmaffione for (i = targ->nmd->first_rx_ring; 1377341457Svmaffione i <= targ->nmd->last_rx_ring; i++) { 1378246896Sluigi ring = NETMAP_RXRING(nifp, i); 1379260368Sluigi while (!nm_ring_empty(ring)) { 1380246896Sluigi uint32_t seq; 1381270063Sluigi struct tstamp *tp; 1382341457Svmaffione int pos; 1383341457Svmaffione 1384344918Svmaffione slot = &ring->slot[ring->head]; 1385246896Sluigi p = NETMAP_BUF(ring, slot->buf_idx); 1386246896Sluigi 1387246896Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &now); 1388246896Sluigi bcopy(p+42, &seq, sizeof(seq)); 1389270063Sluigi tp = (struct tstamp *)(p+46); 1390270063Sluigi ts.tv_sec = (time_t)tp->sec; 1391270063Sluigi ts.tv_nsec = (long)tp->nsec; 1392246896Sluigi ts.tv_sec = now.tv_sec - ts.tv_sec; 1393246896Sluigi ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 1394246896Sluigi if (ts.tv_nsec < 0) { 1395246896Sluigi ts.tv_nsec += 1000000000; 1396246896Sluigi ts.tv_sec--; 1397246896Sluigi } 1398341457Svmaffione if (0) D("seq %d/%llu delta %d.%09d", seq, 1399341457Svmaffione (unsigned long long)sent, 1400246896Sluigi (int)ts.tv_sec, (int)ts.tv_nsec); 1401341457Svmaffione t_cur = ts.tv_sec * 1000000000UL + ts.tv_nsec; 1402341457Svmaffione if (t_cur < t_min) 1403341457Svmaffione t_min = t_cur; 1404246896Sluigi count ++; 1405341457Svmaffione av += t_cur; 1406341457Svmaffione pos = msb64(t_cur); 1407341457Svmaffione buckets[pos]++; 1408341457Svmaffione /* now store it in a bucket */ 1409344918Svmaffione ring->head = ring->cur = nm_ring_next(ring, ring->head); 1410246896Sluigi rx++; 1411246896Sluigi } 1412246896Sluigi } 1413246896Sluigi //D("tx %d rx %d", sent, rx); 1414246896Sluigi //usleep(100000); 1415246896Sluigi ts.tv_sec = now.tv_sec - last_print.tv_sec; 1416246896Sluigi ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 1417246896Sluigi if (ts.tv_nsec < 0) { 1418246896Sluigi ts.tv_nsec += 1000000000; 1419246896Sluigi ts.tv_sec--; 1420246896Sluigi } 1421246896Sluigi if (ts.tv_sec >= 1) { 1422341457Svmaffione D("count %d RTT: min %d av %d ns", 1423341457Svmaffione (int)count, (int)t_min, (int)(av/count)); 1424341457Svmaffione int k, j, kmin, off; 1425341457Svmaffione char buf[512]; 1426341457Svmaffione 1427341457Svmaffione for (kmin = 0; kmin < 64; kmin ++) 1428341457Svmaffione if (buckets[kmin]) 1429341457Svmaffione break; 1430341457Svmaffione for (k = 63; k >= kmin; k--) 1431341457Svmaffione if (buckets[k]) 1432341457Svmaffione break; 1433341457Svmaffione buf[0] = '\0'; 1434341457Svmaffione off = 0; 1435341457Svmaffione for (j = kmin; j <= k; j++) { 1436341457Svmaffione off += sprintf(buf + off, " %5d", (int)buckets[j]); 1437341457Svmaffione } 1438341457Svmaffione D("k: %d .. %d\n\t%s", 1<<kmin, 1<<k, buf); 1439341457Svmaffione bzero(&buckets, sizeof(buckets)); 1440246896Sluigi count = 0; 1441341457Svmaffione g_av += av; 1442246896Sluigi av = 0; 1443341457Svmaffione if (t_min < g_min) 1444341457Svmaffione g_min = t_min; 1445341457Svmaffione t_min = ~0; 1446246896Sluigi last_print = now; 1447246896Sluigi } 1448341457Svmaffione#ifdef BUSYWAIT 1449341457Svmaffione if (rx < m && ts.tv_sec <= 3 && !targ->cancel) 1450341457Svmaffione goto again; 1451341457Svmaffione#endif /* BUSYWAIT */ 1452246896Sluigi } 1453341457Svmaffione 1454341457Svmaffione if (sent > 0) { 1455341457Svmaffione D("RTT over %llu packets: min %d av %d ns", 1456341457Svmaffione (long long unsigned)sent, (int)g_min, 1457341457Svmaffione (int)((double)g_av/sent)); 1458341457Svmaffione } 1459341457Svmaffione targ->completed = 1; 1460341457Svmaffione 1461341457Svmaffione /* reset the ``used`` flag. */ 1462341457Svmaffione targ->used = 0; 1463341457Svmaffione 1464246896Sluigi return NULL; 1465246896Sluigi} 1466246896Sluigi 1467246896Sluigi 1468246896Sluigi/* 1469246896Sluigi * reply to ping requests 1470246896Sluigi */ 1471246896Sluigistatic void * 1472341457Svmaffionepong_body(void *data) 1473246896Sluigi{ 1474246896Sluigi struct targ *targ = (struct targ *) data; 1475261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1476261909Sluigi struct netmap_if *nifp = targ->nmd->nifp; 1477246896Sluigi struct netmap_ring *txring, *rxring; 1478341457Svmaffione int i, rx = 0; 1479341457Svmaffione uint64_t sent = 0, n = targ->g->npackets; 1480246896Sluigi 1481246896Sluigi if (targ->g->nthreads > 1) { 1482246896Sluigi D("can only reply ping with 1 thread"); 1483246896Sluigi return NULL; 1484246896Sluigi } 1485341457Svmaffione if (n > 0) 1486341457Svmaffione D("understood ponger %llu but don't know how to do it", 1487341457Svmaffione (unsigned long long)n); 1488341457Svmaffione while (!targ->cancel && (n == 0 || sent < n)) { 1489344918Svmaffione uint32_t txhead, txavail; 1490246896Sluigi//#define BUSYWAIT 1491246896Sluigi#ifdef BUSYWAIT 1492261909Sluigi ioctl(pfd.fd, NIOCRXSYNC, NULL); 1493246896Sluigi#else 1494341457Svmaffione int rv; 1495341457Svmaffione if ( (rv = poll(&pfd, 1, 1000)) <= 0) { 1496341457Svmaffione D("poll error on queue %d: %s", targ->me, 1497341457Svmaffione rv ? strerror(errno) : "timeout"); 1498246896Sluigi continue; 1499246896Sluigi } 1500246896Sluigi#endif 1501341457Svmaffione txring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring); 1502344918Svmaffione txhead = txring->head; 1503260368Sluigi txavail = nm_ring_space(txring); 1504246896Sluigi /* see what we got back */ 1505261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 1506246896Sluigi rxring = NETMAP_RXRING(nifp, i); 1507260368Sluigi while (!nm_ring_empty(rxring)) { 1508246896Sluigi uint16_t *spkt, *dpkt; 1509344918Svmaffione uint32_t head = rxring->head; 1510344918Svmaffione struct netmap_slot *slot = &rxring->slot[head]; 1511246896Sluigi char *src, *dst; 1512246896Sluigi src = NETMAP_BUF(rxring, slot->buf_idx); 1513246896Sluigi //D("got pkt %p of size %d", src, slot->len); 1514344918Svmaffione rxring->head = rxring->cur = nm_ring_next(rxring, head); 1515246896Sluigi rx++; 1516246896Sluigi if (txavail == 0) 1517246896Sluigi continue; 1518246896Sluigi dst = NETMAP_BUF(txring, 1519344918Svmaffione txring->slot[txhead].buf_idx); 1520246896Sluigi /* copy... */ 1521246896Sluigi dpkt = (uint16_t *)dst; 1522246896Sluigi spkt = (uint16_t *)src; 1523261909Sluigi nm_pkt_copy(src, dst, slot->len); 1524341457Svmaffione /* swap source and destination MAC */ 1525246896Sluigi dpkt[0] = spkt[3]; 1526246896Sluigi dpkt[1] = spkt[4]; 1527246896Sluigi dpkt[2] = spkt[5]; 1528246896Sluigi dpkt[3] = spkt[0]; 1529246896Sluigi dpkt[4] = spkt[1]; 1530246896Sluigi dpkt[5] = spkt[2]; 1531344918Svmaffione txring->slot[txhead].len = slot->len; 1532344918Svmaffione txhead = nm_ring_next(txring, txhead); 1533246896Sluigi txavail--; 1534246896Sluigi sent++; 1535246896Sluigi } 1536246896Sluigi } 1537344918Svmaffione txring->head = txring->cur = txhead; 1538341457Svmaffione targ->ctr.pkts = sent; 1539246896Sluigi#ifdef BUSYWAIT 1540261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1541246896Sluigi#endif 1542246896Sluigi //D("tx %d rx %d", sent, rx); 1543246896Sluigi } 1544246896Sluigi 1545341457Svmaffione targ->completed = 1; 1546246896Sluigi 1547341457Svmaffione /* reset the ``used`` flag. */ 1548341457Svmaffione targ->used = 0; 1549251132Sluigi 1550341457Svmaffione return NULL; 1551251132Sluigi} 1552251132Sluigi 1553251132Sluigi 1554246896Sluigistatic void * 1555227614Sluigisender_body(void *data) 1556227614Sluigi{ 1557227614Sluigi struct targ *targ = (struct targ *) data; 1558261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 1559270063Sluigi struct netmap_if *nifp; 1560341457Svmaffione struct netmap_ring *txring = NULL; 1561341457Svmaffione int i; 1562341457Svmaffione uint64_t n = targ->g->npackets / targ->g->nthreads; 1563341457Svmaffione uint64_t sent = 0; 1564341457Svmaffione uint64_t event = 0; 1565234956Sluigi int options = targ->g->options | OPT_COPY; 1566260368Sluigi struct timespec nexttime = { 0, 0}; // XXX silence compiler 1567251132Sluigi int rate_limit = targ->g->tx_rate; 1568260368Sluigi struct pkt *pkt = &targ->pkt; 1569260368Sluigi void *frame; 1570260368Sluigi int size; 1571251426Sluigi 1572272962Sgnn if (targ->frame == NULL) { 1573341457Svmaffione frame = (char *)pkt + sizeof(pkt->vh) - targ->g->virt_header; 1574272962Sgnn size = targ->g->pkt_size + targ->g->virt_header; 1575272962Sgnn } else { 1576272962Sgnn frame = targ->frame; 1577272962Sgnn size = targ->g->pkt_size; 1578272962Sgnn } 1579341457Svmaffione 1580270063Sluigi D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 1581227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1582227614Sluigi goto quit; 1583227614Sluigi 1584227614Sluigi /* main loop.*/ 1585251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1586251132Sluigi if (rate_limit) { 1587260368Sluigi targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 1588251132Sluigi targ->tic.tv_nsec = 0; 1589260368Sluigi wait_time(targ->tic); 1590251132Sluigi nexttime = targ->tic; 1591251132Sluigi } 1592341457Svmaffione if (targ->g->dev_type == DEV_TAP) { 1593260700Sluigi D("writing to file desc %d", targ->g->main_fd); 1594246896Sluigi 1595246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1596260700Sluigi if (write(targ->g->main_fd, frame, size) != -1) 1597234956Sluigi sent++; 1598341457Svmaffione update_addresses(pkt, targ); 1599234956Sluigi if (i > 10000) { 1600341457Svmaffione targ->ctr.pkts = sent; 1601341457Svmaffione targ->ctr.bytes = sent*size; 1602341457Svmaffione targ->ctr.events = sent; 1603234956Sluigi i = 0; 1604234956Sluigi } 1605246896Sluigi } 1606260700Sluigi#ifndef NO_PCAP 1607260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1608260700Sluigi pcap_t *p = targ->g->p; 1609246896Sluigi 1610246896Sluigi for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1611260700Sluigi if (pcap_inject(p, frame, size) != -1) 1612246896Sluigi sent++; 1613341457Svmaffione update_addresses(pkt, targ); 1614246896Sluigi if (i > 10000) { 1615341457Svmaffione targ->ctr.pkts = sent; 1616341457Svmaffione targ->ctr.bytes = sent*size; 1617341457Svmaffione targ->ctr.events = sent; 1618246896Sluigi i = 0; 1619246896Sluigi } 1620246896Sluigi } 1621260700Sluigi#endif /* NO_PCAP */ 1622227614Sluigi } else { 1623251132Sluigi int tosend = 0; 1624342035Svmaffione u_int bufsz, frag_size = targ->g->frag_size; 1625257529Sluigi 1626341457Svmaffione nifp = targ->nmd->nifp; 1627341457Svmaffione txring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring); 1628341457Svmaffione bufsz = txring->nr_buf_size; 1629342035Svmaffione if (bufsz < frag_size) 1630342035Svmaffione frag_size = bufsz; 1631341457Svmaffione targ->frag_size = targ->g->pkt_size / targ->frags; 1632342035Svmaffione if (targ->frag_size > frag_size) { 1633342035Svmaffione targ->frags = targ->g->pkt_size / frag_size; 1634342035Svmaffione targ->frag_size = frag_size; 1635342035Svmaffione if (targ->g->pkt_size % frag_size != 0) 1636341457Svmaffione targ->frags++; 1637341457Svmaffione } 1638341457Svmaffione D("frags %u frag_size %u", targ->frags, targ->frag_size); 1639246896Sluigi while (!targ->cancel && (n == 0 || sent < n)) { 1640341457Svmaffione int rv; 1641227614Sluigi 1642251132Sluigi if (rate_limit && tosend <= 0) { 1643251132Sluigi tosend = targ->g->burst; 1644260368Sluigi nexttime = timespec_add(nexttime, targ->g->tx_period); 1645260368Sluigi wait_time(nexttime); 1646251132Sluigi } 1647251132Sluigi 1648227614Sluigi /* 1649227614Sluigi * wait for available room in the send queue(s) 1650227614Sluigi */ 1651341457Svmaffione#ifdef BUSYWAIT 1652341457Svmaffione (void)rv; 1653341457Svmaffione if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) { 1654341457Svmaffione D("ioctl error on queue %d: %s", targ->me, 1655341457Svmaffione strerror(errno)); 1656341457Svmaffione goto quit; 1657341457Svmaffione } 1658341457Svmaffione#else /* !BUSYWAIT */ 1659341457Svmaffione if ( (rv = poll(&pfd, 1, 2000)) <= 0) { 1660238165Semaste if (targ->cancel) 1661238165Semaste break; 1662341457Svmaffione D("poll error on queue %d: %s", targ->me, 1663341457Svmaffione rv ? strerror(errno) : "timeout"); 1664261909Sluigi // goto quit; 1665227614Sluigi } 1666261909Sluigi if (pfd.revents & POLLERR) { 1667341457Svmaffione D("poll error on %d ring %d-%d", pfd.fd, 1668341457Svmaffione targ->nmd->first_tx_ring, targ->nmd->last_tx_ring); 1669260368Sluigi goto quit; 1670260368Sluigi } 1671341457Svmaffione#endif /* !BUSYWAIT */ 1672227614Sluigi /* 1673227614Sluigi * scan our queues and send on those with room 1674227614Sluigi */ 1675246896Sluigi if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 1676246896Sluigi D("drop copy"); 1677234956Sluigi options &= ~OPT_COPY; 1678246896Sluigi } 1679261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1680341457Svmaffione int m; 1681341457Svmaffione uint64_t limit = rate_limit ? tosend : targ->g->burst; 1682341457Svmaffione 1683341457Svmaffione if (n > 0 && n == sent) 1684341457Svmaffione break; 1685341457Svmaffione 1686246896Sluigi if (n > 0 && n - sent < limit) 1687246896Sluigi limit = n - sent; 1688227614Sluigi txring = NETMAP_TXRING(nifp, i); 1689260368Sluigi if (nm_ring_empty(txring)) 1690227614Sluigi continue; 1691261909Sluigi 1692341457Svmaffione if (targ->g->pkt_min_size > 0) { 1693341457Svmaffione size = nrand48(targ->seed) % 1694341457Svmaffione (targ->g->pkt_size - targ->g->pkt_min_size) + 1695341457Svmaffione targ->g->pkt_min_size; 1696341457Svmaffione } 1697341457Svmaffione m = send_packets(txring, pkt, frame, size, targ, 1698341457Svmaffione limit, options); 1699341457Svmaffione ND("limit %lu tail %d m %d", 1700341457Svmaffione limit, txring->tail, m); 1701227614Sluigi sent += m; 1702341457Svmaffione if (m > 0) //XXX-ste: can m be 0? 1703341457Svmaffione event++; 1704341457Svmaffione targ->ctr.pkts = sent; 1705341457Svmaffione targ->ctr.bytes += m*size; 1706341457Svmaffione targ->ctr.events = event; 1707257529Sluigi if (rate_limit) { 1708257529Sluigi tosend -= m; 1709257529Sluigi if (tosend <= 0) 1710257529Sluigi break; 1711257529Sluigi } 1712227614Sluigi } 1713227614Sluigi } 1714234956Sluigi /* flush any remaining packets */ 1715341457Svmaffione if (txring != NULL) { 1716341457Svmaffione D("flush tail %d head %d on thread %p", 1717341457Svmaffione txring->tail, txring->head, 1718341457Svmaffione (void *)pthread_self()); 1719341457Svmaffione ioctl(pfd.fd, NIOCTXSYNC, NULL); 1720341457Svmaffione } 1721227614Sluigi 1722227614Sluigi /* final part: wait all the TX queues to be empty. */ 1723261909Sluigi for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 1724227614Sluigi txring = NETMAP_TXRING(nifp, i); 1725341457Svmaffione while (!targ->cancel && nm_tx_pending(txring)) { 1726270063Sluigi RD(5, "pending tx tail %d head %d on ring %d", 1727270063Sluigi txring->tail, txring->head, i); 1728261909Sluigi ioctl(pfd.fd, NIOCTXSYNC, NULL); 1729227614Sluigi usleep(1); /* wait 1 tick */ 1730227614Sluigi } 1731227614Sluigi } 1732260700Sluigi } /* end DEV_NETMAP */ 1733227614Sluigi 1734251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1735227614Sluigi targ->completed = 1; 1736341457Svmaffione targ->ctr.pkts = sent; 1737341457Svmaffione targ->ctr.bytes = sent*size; 1738341457Svmaffione targ->ctr.events = event; 1739227614Sluigiquit: 1740227614Sluigi /* reset the ``used`` flag. */ 1741227614Sluigi targ->used = 0; 1742227614Sluigi 1743227614Sluigi return (NULL); 1744227614Sluigi} 1745227614Sluigi 1746227614Sluigi 1747260700Sluigi#ifndef NO_PCAP 1748227614Sluigistatic void 1749246896Sluigireceive_pcap(u_char *user, const struct pcap_pkthdr * h, 1750246896Sluigi const u_char * bytes) 1751227614Sluigi{ 1752341457Svmaffione struct my_ctrs *ctr = (struct my_ctrs *)user; 1753246896Sluigi (void)bytes; /* UNUSED */ 1754341457Svmaffione ctr->bytes += h->len; 1755341457Svmaffione ctr->pkts++; 1756227614Sluigi} 1757260700Sluigi#endif /* !NO_PCAP */ 1758227614Sluigi 1759341457Svmaffione 1760227614Sluigistatic int 1761341457Svmaffionereceive_packets(struct netmap_ring *ring, u_int limit, int dump, uint64_t *bytes) 1762227614Sluigi{ 1763344918Svmaffione u_int head, rx, n; 1764341457Svmaffione uint64_t b = 0; 1765341457Svmaffione u_int complete = 0; 1766227614Sluigi 1767341457Svmaffione if (bytes == NULL) 1768341457Svmaffione bytes = &b; 1769341457Svmaffione 1770344918Svmaffione head = ring->head; 1771260368Sluigi n = nm_ring_space(ring); 1772260368Sluigi if (n < limit) 1773260368Sluigi limit = n; 1774227614Sluigi for (rx = 0; rx < limit; rx++) { 1775344918Svmaffione struct netmap_slot *slot = &ring->slot[head]; 1776227614Sluigi char *p = NETMAP_BUF(ring, slot->buf_idx); 1777227614Sluigi 1778341457Svmaffione *bytes += slot->len; 1779251426Sluigi if (dump) 1780344918Svmaffione dump_payload(p, slot->len, ring, head); 1781341457Svmaffione if (!(slot->flags & NS_MOREFRAG)) 1782341457Svmaffione complete++; 1783227614Sluigi 1784344918Svmaffione head = nm_ring_next(ring, head); 1785227614Sluigi } 1786344918Svmaffione ring->head = ring->cur = head; 1787227614Sluigi 1788341457Svmaffione return (complete); 1789227614Sluigi} 1790227614Sluigi 1791227614Sluigistatic void * 1792227614Sluigireceiver_body(void *data) 1793227614Sluigi{ 1794227614Sluigi struct targ *targ = (struct targ *) data; 1795261909Sluigi struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1796270063Sluigi struct netmap_if *nifp; 1797227614Sluigi struct netmap_ring *rxring; 1798246896Sluigi int i; 1799341457Svmaffione struct my_ctrs cur; 1800227614Sluigi 1801341457Svmaffione memset(&cur, 0, sizeof(cur)); 1802341457Svmaffione 1803227614Sluigi if (setaffinity(targ->thread, targ->affinity)) 1804227614Sluigi goto quit; 1805227614Sluigi 1806270063Sluigi D("reading from %s fd %d main_fd %d", 1807270063Sluigi targ->g->ifname, targ->fd, targ->g->main_fd); 1808227614Sluigi /* unbounded wait for the first packet. */ 1809270063Sluigi for (;!targ->cancel;) { 1810261909Sluigi i = poll(&pfd, 1, 1000); 1811261909Sluigi if (i > 0 && !(pfd.revents & POLLERR)) 1812227614Sluigi break; 1813341457Svmaffione if (i < 0) { 1814341457Svmaffione D("poll() error: %s", strerror(errno)); 1815341457Svmaffione goto quit; 1816341457Svmaffione } 1817341457Svmaffione if (pfd.revents & POLLERR) { 1818341457Svmaffione D("fd error"); 1819341457Svmaffione goto quit; 1820341457Svmaffione } 1821261909Sluigi RD(1, "waiting for initial packets, poll returns %d %d", 1822261909Sluigi i, pfd.revents); 1823227614Sluigi } 1824227614Sluigi /* main loop, exit after 1s silence */ 1825251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1826260700Sluigi if (targ->g->dev_type == DEV_TAP) { 1827246896Sluigi while (!targ->cancel) { 1828270063Sluigi char buf[MAX_BODYSIZE]; 1829246896Sluigi /* XXX should we poll ? */ 1830341457Svmaffione i = read(targ->g->main_fd, buf, sizeof(buf)); 1831341457Svmaffione if (i > 0) { 1832341457Svmaffione targ->ctr.pkts++; 1833341457Svmaffione targ->ctr.bytes += i; 1834341457Svmaffione targ->ctr.events++; 1835341457Svmaffione } 1836246896Sluigi } 1837260700Sluigi#ifndef NO_PCAP 1838260700Sluigi } else if (targ->g->dev_type == DEV_PCAP) { 1839260700Sluigi while (!targ->cancel) { 1840260700Sluigi /* XXX should we poll ? */ 1841270063Sluigi pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, 1842341457Svmaffione (u_char *)&targ->ctr); 1843341457Svmaffione targ->ctr.events++; 1844260700Sluigi } 1845260700Sluigi#endif /* !NO_PCAP */ 1846227614Sluigi } else { 1847251426Sluigi int dump = targ->g->options & OPT_DUMP; 1848270063Sluigi 1849341457Svmaffione nifp = targ->nmd->nifp; 1850238165Semaste while (!targ->cancel) { 1851227614Sluigi /* Once we started to receive packets, wait at most 1 seconds 1852227614Sluigi before quitting. */ 1853341457Svmaffione#ifdef BUSYWAIT 1854341457Svmaffione if (ioctl(pfd.fd, NIOCRXSYNC, NULL) < 0) { 1855341457Svmaffione D("ioctl error on queue %d: %s", targ->me, 1856341457Svmaffione strerror(errno)); 1857341457Svmaffione goto quit; 1858341457Svmaffione } 1859341457Svmaffione#else /* !BUSYWAIT */ 1860261909Sluigi if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 1861251132Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1862228975Suqs targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 1863261909Sluigi goto out; 1864227614Sluigi } 1865227614Sluigi 1866261909Sluigi if (pfd.revents & POLLERR) { 1867260368Sluigi D("poll err"); 1868260368Sluigi goto quit; 1869260368Sluigi } 1870341457Svmaffione#endif /* !BUSYWAIT */ 1871341457Svmaffione uint64_t cur_space = 0; 1872261909Sluigi for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 1873227614Sluigi int m; 1874227614Sluigi 1875227614Sluigi rxring = NETMAP_RXRING(nifp, i); 1876341457Svmaffione /* compute free space in the ring */ 1877341457Svmaffione m = rxring->head + rxring->num_slots - rxring->tail; 1878341457Svmaffione if (m >= (int) rxring->num_slots) 1879341457Svmaffione m -= rxring->num_slots; 1880341457Svmaffione cur_space += m; 1881260368Sluigi if (nm_ring_empty(rxring)) 1882227614Sluigi continue; 1883227614Sluigi 1884341457Svmaffione m = receive_packets(rxring, targ->g->burst, dump, &cur.bytes); 1885341457Svmaffione cur.pkts += m; 1886341457Svmaffione if (m > 0) 1887341457Svmaffione cur.events++; 1888227614Sluigi } 1889341457Svmaffione cur.min_space = targ->ctr.min_space; 1890341457Svmaffione if (cur_space < cur.min_space) 1891341457Svmaffione cur.min_space = cur_space; 1892341457Svmaffione targ->ctr = cur; 1893227614Sluigi } 1894227614Sluigi } 1895227614Sluigi 1896261909Sluigi clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1897261909Sluigi 1898341457Svmaffione#if !defined(BUSYWAIT) 1899261909Sluigiout: 1900341457Svmaffione#endif 1901227614Sluigi targ->completed = 1; 1902341457Svmaffione targ->ctr = cur; 1903227614Sluigi 1904227614Sluigiquit: 1905227614Sluigi /* reset the ``used`` flag. */ 1906227614Sluigi targ->used = 0; 1907227614Sluigi 1908227614Sluigi return (NULL); 1909227614Sluigi} 1910227614Sluigi 1911341457Svmaffionestatic void * 1912341457Svmaffionetxseq_body(void *data) 1913238170Semaste{ 1914341457Svmaffione struct targ *targ = (struct targ *) data; 1915341457Svmaffione struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 1916341457Svmaffione struct netmap_ring *ring; 1917341457Svmaffione int64_t sent = 0; 1918341457Svmaffione uint64_t event = 0; 1919341457Svmaffione int options = targ->g->options | OPT_COPY; 1920341457Svmaffione struct timespec nexttime = {0, 0}; 1921341457Svmaffione int rate_limit = targ->g->tx_rate; 1922341457Svmaffione struct pkt *pkt = &targ->pkt; 1923341457Svmaffione int frags = targ->g->frags; 1924341457Svmaffione uint32_t sequence = 0; 1925341457Svmaffione int budget = 0; 1926341457Svmaffione void *frame; 1927341457Svmaffione int size; 1928238170Semaste 1929341457Svmaffione if (targ->g->nthreads > 1) { 1930341457Svmaffione D("can only txseq ping with 1 thread"); 1931341457Svmaffione return NULL; 1932341457Svmaffione } 1933341457Svmaffione 1934341457Svmaffione if (targ->g->npackets > 0) { 1935341457Svmaffione D("Ignoring -n argument"); 1936341457Svmaffione } 1937341457Svmaffione 1938341457Svmaffione frame = (char *)pkt + sizeof(pkt->vh) - targ->g->virt_header; 1939341457Svmaffione size = targ->g->pkt_size + targ->g->virt_header; 1940341457Svmaffione 1941341457Svmaffione D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 1942341457Svmaffione if (setaffinity(targ->thread, targ->affinity)) 1943341457Svmaffione goto quit; 1944341457Svmaffione 1945341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1946341457Svmaffione if (rate_limit) { 1947341457Svmaffione targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 1948341457Svmaffione targ->tic.tv_nsec = 0; 1949341457Svmaffione wait_time(targ->tic); 1950341457Svmaffione nexttime = targ->tic; 1951341457Svmaffione } 1952341457Svmaffione 1953341457Svmaffione /* Only use the first queue. */ 1954341457Svmaffione ring = NETMAP_TXRING(targ->nmd->nifp, targ->nmd->first_tx_ring); 1955341457Svmaffione 1956341457Svmaffione while (!targ->cancel) { 1957341457Svmaffione int64_t limit; 1958341457Svmaffione unsigned int space; 1959341457Svmaffione unsigned int head; 1960341457Svmaffione int fcnt; 1961341457Svmaffione uint16_t sum = 0; 1962341457Svmaffione int rv; 1963341457Svmaffione 1964341457Svmaffione if (!rate_limit) { 1965341457Svmaffione budget = targ->g->burst; 1966341457Svmaffione 1967341457Svmaffione } else if (budget <= 0) { 1968341457Svmaffione budget = targ->g->burst; 1969341457Svmaffione nexttime = timespec_add(nexttime, targ->g->tx_period); 1970341457Svmaffione wait_time(nexttime); 1971341457Svmaffione } 1972341457Svmaffione 1973341457Svmaffione /* wait for available room in the send queue */ 1974341457Svmaffione#ifdef BUSYWAIT 1975341457Svmaffione (void)rv; 1976341457Svmaffione if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) { 1977341457Svmaffione D("ioctl error on queue %d: %s", targ->me, 1978341457Svmaffione strerror(errno)); 1979341457Svmaffione goto quit; 1980341457Svmaffione } 1981341457Svmaffione#else /* !BUSYWAIT */ 1982341457Svmaffione if ( (rv = poll(&pfd, 1, 2000)) <= 0) { 1983341457Svmaffione if (targ->cancel) 1984341457Svmaffione break; 1985341457Svmaffione D("poll error on queue %d: %s", targ->me, 1986341457Svmaffione rv ? strerror(errno) : "timeout"); 1987341457Svmaffione // goto quit; 1988341457Svmaffione } 1989341457Svmaffione if (pfd.revents & POLLERR) { 1990341457Svmaffione D("poll error on %d ring %d-%d", pfd.fd, 1991341457Svmaffione targ->nmd->first_tx_ring, targ->nmd->last_tx_ring); 1992341457Svmaffione goto quit; 1993341457Svmaffione } 1994341457Svmaffione#endif /* !BUSYWAIT */ 1995341457Svmaffione 1996341457Svmaffione /* If no room poll() again. */ 1997341457Svmaffione space = nm_ring_space(ring); 1998341457Svmaffione if (!space) { 1999341457Svmaffione continue; 2000341457Svmaffione } 2001341457Svmaffione 2002341457Svmaffione limit = budget; 2003341457Svmaffione 2004341457Svmaffione if (space < limit) { 2005341457Svmaffione limit = space; 2006341457Svmaffione } 2007341457Svmaffione 2008341457Svmaffione /* Cut off ``limit`` to make sure is multiple of ``frags``. */ 2009341457Svmaffione if (frags > 1) { 2010341457Svmaffione limit = (limit / frags) * frags; 2011341457Svmaffione } 2012341457Svmaffione 2013341457Svmaffione limit = sent + limit; /* Convert to absolute. */ 2014341457Svmaffione 2015341457Svmaffione for (fcnt = frags, head = ring->head; 2016341457Svmaffione sent < limit; sent++, sequence++) { 2017341457Svmaffione struct netmap_slot *slot = &ring->slot[head]; 2018341457Svmaffione char *p = NETMAP_BUF(ring, slot->buf_idx); 2019341457Svmaffione uint16_t *w = (uint16_t *)PKT(pkt, body, targ->g->af), t; 2020341457Svmaffione 2021341457Svmaffione memcpy(&sum, targ->g->af == AF_INET ? &pkt->ipv4.udp.uh_sum : &pkt->ipv6.udp.uh_sum, sizeof(sum)); 2022341457Svmaffione 2023341457Svmaffione slot->flags = 0; 2024341457Svmaffione t = *w; 2025341457Svmaffione PKT(pkt, body, targ->g->af)[0] = sequence >> 24; 2026341457Svmaffione PKT(pkt, body, targ->g->af)[1] = (sequence >> 16) & 0xff; 2027341457Svmaffione sum = ~cksum_add(~sum, cksum_add(~t, *w)); 2028341457Svmaffione t = *++w; 2029341457Svmaffione PKT(pkt, body, targ->g->af)[2] = (sequence >> 8) & 0xff; 2030341457Svmaffione PKT(pkt, body, targ->g->af)[3] = sequence & 0xff; 2031341457Svmaffione sum = ~cksum_add(~sum, cksum_add(~t, *w)); 2032341457Svmaffione memcpy(targ->g->af == AF_INET ? &pkt->ipv4.udp.uh_sum : &pkt->ipv6.udp.uh_sum, &sum, sizeof(sum)); 2033341457Svmaffione nm_pkt_copy(frame, p, size); 2034341457Svmaffione if (fcnt == frags) { 2035341457Svmaffione update_addresses(pkt, targ); 2036341457Svmaffione } 2037341457Svmaffione 2038341457Svmaffione if (options & OPT_DUMP) { 2039341457Svmaffione dump_payload(p, size, ring, head); 2040341457Svmaffione } 2041341457Svmaffione 2042341457Svmaffione slot->len = size; 2043341457Svmaffione 2044341457Svmaffione if (--fcnt > 0) { 2045341457Svmaffione slot->flags |= NS_MOREFRAG; 2046341457Svmaffione } else { 2047341457Svmaffione fcnt = frags; 2048341457Svmaffione } 2049341457Svmaffione 2050341457Svmaffione if (sent == limit - 1) { 2051341457Svmaffione /* Make sure we don't push an incomplete 2052341457Svmaffione * packet. */ 2053341457Svmaffione assert(!(slot->flags & NS_MOREFRAG)); 2054341457Svmaffione slot->flags |= NS_REPORT; 2055341457Svmaffione } 2056341457Svmaffione 2057341457Svmaffione head = nm_ring_next(ring, head); 2058341457Svmaffione if (rate_limit) { 2059341457Svmaffione budget--; 2060341457Svmaffione } 2061341457Svmaffione } 2062341457Svmaffione 2063341457Svmaffione ring->cur = ring->head = head; 2064341457Svmaffione 2065341457Svmaffione event ++; 2066341457Svmaffione targ->ctr.pkts = sent; 2067341457Svmaffione targ->ctr.bytes = sent * size; 2068341457Svmaffione targ->ctr.events = event; 2069341457Svmaffione } 2070341457Svmaffione 2071341457Svmaffione /* flush any remaining packets */ 2072341457Svmaffione D("flush tail %d head %d on thread %p", 2073341457Svmaffione ring->tail, ring->head, 2074341457Svmaffione (void *)pthread_self()); 2075341457Svmaffione ioctl(pfd.fd, NIOCTXSYNC, NULL); 2076341457Svmaffione 2077341457Svmaffione /* final part: wait the TX queues to become empty. */ 2078341457Svmaffione while (!targ->cancel && nm_tx_pending(ring)) { 2079341457Svmaffione RD(5, "pending tx tail %d head %d on ring %d", 2080341457Svmaffione ring->tail, ring->head, targ->nmd->first_tx_ring); 2081341457Svmaffione ioctl(pfd.fd, NIOCTXSYNC, NULL); 2082341457Svmaffione usleep(1); /* wait 1 tick */ 2083341457Svmaffione } 2084341457Svmaffione 2085341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 2086341457Svmaffione targ->completed = 1; 2087341457Svmaffione targ->ctr.pkts = sent; 2088341457Svmaffione targ->ctr.bytes = sent * size; 2089341457Svmaffione targ->ctr.events = event; 2090341457Svmaffionequit: 2091341457Svmaffione /* reset the ``used`` flag. */ 2092341457Svmaffione targ->used = 0; 2093341457Svmaffione 2094341457Svmaffione return (NULL); 2095238170Semaste} 2096238170Semaste 2097341457Svmaffione 2098341457Svmaffionestatic char * 2099341457Svmaffionemulti_slot_to_string(struct netmap_ring *ring, unsigned int head, 2100341457Svmaffione unsigned int nfrags, char *strbuf, size_t strbuflen) 2101227614Sluigi{ 2102341457Svmaffione unsigned int f; 2103341457Svmaffione char *ret = strbuf; 2104227614Sluigi 2105341457Svmaffione for (f = 0; f < nfrags; f++) { 2106341457Svmaffione struct netmap_slot *slot = &ring->slot[head]; 2107341457Svmaffione int m = snprintf(strbuf, strbuflen, "|%u,%x|", slot->len, 2108341457Svmaffione slot->flags); 2109341457Svmaffione if (m >= (int)strbuflen) { 2110341457Svmaffione break; 2111341457Svmaffione } 2112341457Svmaffione strbuf += m; 2113341457Svmaffione strbuflen -= m; 2114238170Semaste 2115341457Svmaffione head = nm_ring_next(ring, head); 2116341457Svmaffione } 2117341457Svmaffione 2118341457Svmaffione return ret; 2119227614Sluigi} 2120227614Sluigi 2121341457Svmaffionestatic void * 2122341457Svmaffionerxseq_body(void *data) 2123341457Svmaffione{ 2124341457Svmaffione struct targ *targ = (struct targ *) data; 2125341457Svmaffione struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 2126341457Svmaffione int dump = targ->g->options & OPT_DUMP; 2127341457Svmaffione struct netmap_ring *ring; 2128341457Svmaffione unsigned int frags_exp = 1; 2129341457Svmaffione struct my_ctrs cur; 2130341457Svmaffione unsigned int frags = 0; 2131341457Svmaffione int first_packet = 1; 2132341457Svmaffione int first_slot = 1; 2133341457Svmaffione int i, j, af, nrings; 2134341457Svmaffione uint32_t seq, *seq_exp = NULL; 2135227614Sluigi 2136341457Svmaffione memset(&cur, 0, sizeof(cur)); 2137341457Svmaffione 2138341457Svmaffione if (setaffinity(targ->thread, targ->affinity)) 2139341457Svmaffione goto quit; 2140341457Svmaffione 2141341457Svmaffione nrings = targ->nmd->last_rx_ring - targ->nmd->first_rx_ring + 1; 2142341457Svmaffione seq_exp = calloc(nrings, sizeof(uint32_t)); 2143341457Svmaffione if (seq_exp == NULL) { 2144341457Svmaffione D("failed to allocate seq array"); 2145341457Svmaffione goto quit; 2146341457Svmaffione } 2147341457Svmaffione 2148341457Svmaffione D("reading from %s fd %d main_fd %d", 2149341457Svmaffione targ->g->ifname, targ->fd, targ->g->main_fd); 2150341457Svmaffione /* unbounded wait for the first packet. */ 2151341457Svmaffione for (;!targ->cancel;) { 2152341457Svmaffione i = poll(&pfd, 1, 1000); 2153341457Svmaffione if (i > 0 && !(pfd.revents & POLLERR)) 2154341457Svmaffione break; 2155341457Svmaffione RD(1, "waiting for initial packets, poll returns %d %d", 2156341457Svmaffione i, pfd.revents); 2157341457Svmaffione } 2158341457Svmaffione 2159341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 2160341457Svmaffione 2161341457Svmaffione 2162341457Svmaffione while (!targ->cancel) { 2163341457Svmaffione unsigned int head; 2164341457Svmaffione int limit; 2165341457Svmaffione 2166341457Svmaffione#ifdef BUSYWAIT 2167341457Svmaffione if (ioctl(pfd.fd, NIOCRXSYNC, NULL) < 0) { 2168341457Svmaffione D("ioctl error on queue %d: %s", targ->me, 2169341457Svmaffione strerror(errno)); 2170341457Svmaffione goto quit; 2171341457Svmaffione } 2172341457Svmaffione#else /* !BUSYWAIT */ 2173341457Svmaffione if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 2174341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 2175341457Svmaffione targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 2176341457Svmaffione goto out; 2177341457Svmaffione } 2178341457Svmaffione 2179341457Svmaffione if (pfd.revents & POLLERR) { 2180341457Svmaffione D("poll err"); 2181341457Svmaffione goto quit; 2182341457Svmaffione } 2183341457Svmaffione#endif /* !BUSYWAIT */ 2184341457Svmaffione 2185341457Svmaffione for (j = targ->nmd->first_rx_ring; j <= targ->nmd->last_rx_ring; j++) { 2186341457Svmaffione ring = NETMAP_RXRING(targ->nmd->nifp, j); 2187341457Svmaffione if (nm_ring_empty(ring)) 2188341457Svmaffione continue; 2189341457Svmaffione 2190341457Svmaffione limit = nm_ring_space(ring); 2191341457Svmaffione if (limit > targ->g->burst) 2192341457Svmaffione limit = targ->g->burst; 2193341457Svmaffione 2194341457Svmaffione#if 0 2195341457Svmaffione /* Enable this if 2196341457Svmaffione * 1) we remove the early-return optimization from 2197341457Svmaffione * the netmap poll implementation, or 2198341457Svmaffione * 2) pipes get NS_MOREFRAG support. 2199341457Svmaffione * With the current netmap implementation, an experiment like 2200341457Svmaffione * pkt-gen -i vale:1{1 -f txseq -F 9 2201341457Svmaffione * pkt-gen -i vale:1}1 -f rxseq 2202341457Svmaffione * would get stuck as soon as we find nm_ring_space(ring) < 9, 2203341457Svmaffione * since here limit is rounded to 0 and 2204341457Svmaffione * pipe rxsync is not called anymore by the poll() of this loop. 2205341457Svmaffione */ 2206341457Svmaffione if (frags_exp > 1) { 2207341457Svmaffione int o = limit; 2208341457Svmaffione /* Cut off to the closest smaller multiple. */ 2209341457Svmaffione limit = (limit / frags_exp) * frags_exp; 2210341457Svmaffione RD(2, "LIMIT %d --> %d", o, limit); 2211341457Svmaffione } 2212341457Svmaffione#endif 2213341457Svmaffione 2214341457Svmaffione for (head = ring->head, i = 0; i < limit; i++) { 2215341457Svmaffione struct netmap_slot *slot = &ring->slot[head]; 2216341457Svmaffione char *p = NETMAP_BUF(ring, slot->buf_idx); 2217341457Svmaffione int len = slot->len; 2218341457Svmaffione struct pkt *pkt; 2219341457Svmaffione 2220341457Svmaffione if (dump) { 2221341457Svmaffione dump_payload(p, slot->len, ring, head); 2222341457Svmaffione } 2223341457Svmaffione 2224341457Svmaffione frags++; 2225341457Svmaffione if (!(slot->flags & NS_MOREFRAG)) { 2226341457Svmaffione if (first_packet) { 2227341457Svmaffione first_packet = 0; 2228341457Svmaffione } else if (frags != frags_exp) { 2229341457Svmaffione char prbuf[512]; 2230341457Svmaffione RD(1, "Received packets with %u frags, " 2231341457Svmaffione "expected %u, '%s'", frags, frags_exp, 2232341457Svmaffione multi_slot_to_string(ring, head-frags+1, 2233341457Svmaffione frags, 2234341457Svmaffione prbuf, sizeof(prbuf))); 2235341457Svmaffione } 2236341457Svmaffione first_packet = 0; 2237341457Svmaffione frags_exp = frags; 2238341457Svmaffione frags = 0; 2239341457Svmaffione } 2240341457Svmaffione 2241341457Svmaffione p -= sizeof(pkt->vh) - targ->g->virt_header; 2242341457Svmaffione len += sizeof(pkt->vh) - targ->g->virt_header; 2243341457Svmaffione pkt = (struct pkt *)p; 2244341457Svmaffione if (ntohs(pkt->eh.ether_type) == ETHERTYPE_IP) 2245341457Svmaffione af = AF_INET; 2246341457Svmaffione else 2247341457Svmaffione af = AF_INET6; 2248341457Svmaffione 2249341457Svmaffione if ((char *)pkt + len < ((char *)PKT(pkt, body, af)) + 2250341457Svmaffione sizeof(seq)) { 2251341457Svmaffione RD(1, "%s: packet too small (len=%u)", __func__, 2252341457Svmaffione slot->len); 2253341457Svmaffione } else { 2254341457Svmaffione seq = (PKT(pkt, body, af)[0] << 24) | 2255341457Svmaffione (PKT(pkt, body, af)[1] << 16) | 2256341457Svmaffione (PKT(pkt, body, af)[2] << 8) | 2257341457Svmaffione PKT(pkt, body, af)[3]; 2258341457Svmaffione if (first_slot) { 2259341457Svmaffione /* Grab the first one, whatever it 2260341457Svmaffione is. */ 2261341457Svmaffione seq_exp[j] = seq; 2262341457Svmaffione first_slot = 0; 2263341457Svmaffione } else if (seq != seq_exp[j]) { 2264341457Svmaffione uint32_t delta = seq - seq_exp[j]; 2265341457Svmaffione 2266341457Svmaffione if (delta < (0xFFFFFFFF >> 1)) { 2267341457Svmaffione RD(2, "Sequence GAP: exp %u found %u", 2268341457Svmaffione seq_exp[j], seq); 2269341457Svmaffione } else { 2270341457Svmaffione RD(2, "Sequence OUT OF ORDER: " 2271341457Svmaffione "exp %u found %u", seq_exp[j], seq); 2272341457Svmaffione } 2273341457Svmaffione seq_exp[j] = seq; 2274341457Svmaffione } 2275341457Svmaffione seq_exp[j]++; 2276341457Svmaffione } 2277341457Svmaffione 2278341457Svmaffione cur.bytes += slot->len; 2279341457Svmaffione head = nm_ring_next(ring, head); 2280341457Svmaffione cur.pkts++; 2281341457Svmaffione } 2282341457Svmaffione 2283341457Svmaffione ring->cur = ring->head = head; 2284341457Svmaffione 2285341457Svmaffione cur.events++; 2286341457Svmaffione targ->ctr = cur; 2287341457Svmaffione } 2288341457Svmaffione } 2289341457Svmaffione clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 2290341457Svmaffione 2291341457Svmaffione#ifndef BUSYWAIT 2292341457Svmaffioneout: 2293341457Svmaffione#endif /* !BUSYWAIT */ 2294341457Svmaffione targ->completed = 1; 2295341457Svmaffione targ->ctr = cur; 2296341457Svmaffione 2297341457Svmaffionequit: 2298341457Svmaffione if (seq_exp != NULL) 2299341457Svmaffione free(seq_exp); 2300341457Svmaffione /* reset the ``used`` flag. */ 2301341457Svmaffione targ->used = 0; 2302341457Svmaffione 2303341457Svmaffione return (NULL); 2304341457Svmaffione} 2305341457Svmaffione 2306341457Svmaffione 2307227614Sluigistatic void 2308341457Svmaffionetx_output(struct glob_arg *g, struct my_ctrs *cur, double delta, const char *msg) 2309227614Sluigi{ 2310341457Svmaffione double bw, raw_bw, pps, abs; 2311341457Svmaffione char b1[40], b2[80], b3[80]; 2312341457Svmaffione int size; 2313227614Sluigi 2314341457Svmaffione if (cur->pkts == 0) { 2315341457Svmaffione printf("%s nothing.\n", msg); 2316341457Svmaffione return; 2317341457Svmaffione } 2318227614Sluigi 2319341457Svmaffione size = (int)(cur->bytes / cur->pkts); 2320341457Svmaffione 2321341457Svmaffione printf("%s %llu packets %llu bytes %llu events %d bytes each in %.2f seconds.\n", 2322341457Svmaffione msg, 2323341457Svmaffione (unsigned long long)cur->pkts, 2324341457Svmaffione (unsigned long long)cur->bytes, 2325341457Svmaffione (unsigned long long)cur->events, size, delta); 2326246896Sluigi if (delta == 0) 2327246896Sluigi delta = 1e-6; 2328341457Svmaffione if (size < 60) /* correct for min packet size */ 2329341457Svmaffione size = 60; 2330341457Svmaffione pps = cur->pkts / delta; 2331341457Svmaffione bw = (8.0 * cur->bytes) / delta; 2332341457Svmaffione raw_bw = (8.0 * cur->bytes + cur->pkts * g->framing) / delta; 2333341457Svmaffione abs = cur->pkts / (double)(cur->events); 2334341457Svmaffione 2335341457Svmaffione printf("Speed: %spps Bandwidth: %sbps (raw %sbps). Average batch: %.2f pkts\n", 2336341457Svmaffione norm(b1, pps, normalize), norm(b2, bw, normalize), norm(b3, raw_bw, normalize), abs); 2337227614Sluigi} 2338227614Sluigi 2339227614Sluigistatic void 2340341457Svmaffioneusage(int errcode) 2341227614Sluigi{ 2342341457Svmaffione/* This usage is generated from the pkt-gen man page: 2343341457Svmaffione * $ man pkt-gen > x 2344341457Svmaffione * and pasted here adding the string terminators and endlines with simple 2345341457Svmaffione * regular expressions. */ 2346227614Sluigi const char *cmd = "pkt-gen"; 2347227614Sluigi fprintf(stderr, 2348227614Sluigi "Usage:\n" 2349227614Sluigi "%s arguments\n" 2350341457Svmaffione" -h Show program usage and exit.\n" 2351341457Svmaffione"\n" 2352341457Svmaffione" -i interface\n" 2353341457Svmaffione" Name of the network interface that pkt-gen operates on. It can be a system network interface\n" 2354341457Svmaffione" (e.g., em0), the name of a vale(4) port (e.g., valeSSS:PPP), the name of a netmap pipe or\n" 2355341457Svmaffione" monitor, or any valid netmap port name accepted by the nm_open library function, as docu-\n" 2356341457Svmaffione" mented in netmap(4) (NIOCREGIF section).\n" 2357341457Svmaffione"\n" 2358341457Svmaffione" -f function\n" 2359341457Svmaffione" The function to be executed by pkt-gen. Specify tx for transmission, rx for reception, ping\n" 2360341457Svmaffione" for client-side ping-pong operation, and pong for server-side ping-pong operation.\n" 2361341457Svmaffione"\n" 2362341457Svmaffione" -n count\n" 2363341457Svmaffione" Number of iterations of the pkt-gen function, with 0 meaning infinite). In case of tx or rx,\n" 2364341457Svmaffione" count is the number of packets to receive or transmit. In case of ping or pong, count is the\n" 2365341457Svmaffione" number of ping-pong transactions.\n" 2366341457Svmaffione"\n" 2367341457Svmaffione" -l pkt_size\n" 2368341457Svmaffione" Packet size in bytes excluding CRC. If passed a second time, use random sizes larger or\n" 2369341457Svmaffione" equal than the second one and lower than the first one.\n" 2370341457Svmaffione"\n" 2371341457Svmaffione" -b burst_size\n" 2372341457Svmaffione" Transmit or receive up to burst_size packets at a time.\n" 2373341457Svmaffione"\n" 2374341457Svmaffione" -4 Use IPv4 addresses.\n" 2375341457Svmaffione"\n" 2376341457Svmaffione" -6 Use IPv6 addresses.\n" 2377341457Svmaffione"\n" 2378341457Svmaffione" -d dst_ip[:port[-dst_ip:port]]\n" 2379341457Svmaffione" Destination IPv4/IPv6 address and port, single or range.\n" 2380341457Svmaffione"\n" 2381341457Svmaffione" -s src_ip[:port[-src_ip:port]]\n" 2382341457Svmaffione" Source IPv4/IPv6 address and port, single or range.\n" 2383341457Svmaffione"\n" 2384341457Svmaffione" -D dst_mac\n" 2385341457Svmaffione" Destination MAC address in colon notation (e.g., aa:bb:cc:dd:ee:00).\n" 2386341457Svmaffione"\n" 2387341457Svmaffione" -S src_mac\n" 2388341457Svmaffione" Source MAC address in colon notation.\n" 2389341457Svmaffione"\n" 2390341457Svmaffione" -a cpu_id\n" 2391341457Svmaffione" Pin the first thread of pkt-gen to a particular CPU using pthread_setaffinity_np(3). If more\n" 2392341457Svmaffione" threads are used, they are pinned to the subsequent CPUs, one per thread.\n" 2393341457Svmaffione"\n" 2394341457Svmaffione" -c cpus\n" 2395341457Svmaffione" Maximum number of CPUs to use (0 means to use all the available ones).\n" 2396341457Svmaffione"\n" 2397341457Svmaffione" -p threads\n" 2398341457Svmaffione" Number of threads to use. By default, only a single thread is used to handle all the netmap\n" 2399341457Svmaffione" rings. If threads is larger than one, each thread handles a single TX ring (in tx mode), a\n" 2400341457Svmaffione" single RX ring (in rx mode), or a TX/RX ring couple. The number of threads must be less or\n" 2401341457Svmaffione" equal than the number of TX (or RX) ring available in the device specified by interface.\n" 2402341457Svmaffione"\n" 2403341457Svmaffione" -T report_ms\n" 2404341457Svmaffione" Number of milliseconds between reports.\n" 2405341457Svmaffione"\n" 2406341457Svmaffione" -w wait_for_link_time\n" 2407341457Svmaffione" Number of seconds to wait before starting the pkt-gen function, useuful to make sure that the\n" 2408341457Svmaffione" network link is up. A network device driver may take some time to enter netmap mode, or to\n" 2409341457Svmaffione" create a new transmit/receive ring pair when netmap(4) requests one.\n" 2410341457Svmaffione"\n" 2411341457Svmaffione" -R rate\n" 2412341457Svmaffione" Packet transmission rate. Not setting the packet transmission rate tells pkt-gen to transmit\n" 2413341457Svmaffione" packets as quickly as possible. On servers from 2010 on-wards netmap(4) is able to com-\n" 2414341457Svmaffione" pletely use all of the bandwidth of a 10 or 40Gbps link, so this option should be used unless\n" 2415341457Svmaffione" your intention is to saturate the link.\n" 2416341457Svmaffione"\n" 2417341457Svmaffione" -X Dump payload of each packet transmitted or received.\n" 2418341457Svmaffione"\n" 2419341457Svmaffione" -H len Add empty virtio-net-header with size 'len'. Valid sizes are 0, 10 and 12. This option is\n" 2420341457Svmaffione" only used with Virtual Machine technologies that use virtio as a network interface.\n" 2421341457Svmaffione"\n" 2422341457Svmaffione" -P file\n" 2423341457Svmaffione" Load the packet to be transmitted from a pcap file rather than constructing it within\n" 2424341457Svmaffione" pkt-gen.\n" 2425341457Svmaffione"\n" 2426341457Svmaffione" -z Use random IPv4/IPv6 src address/port.\n" 2427341457Svmaffione"\n" 2428341457Svmaffione" -Z Use random IPv4/IPv6 dst address/port.\n" 2429341457Svmaffione"\n" 2430341457Svmaffione" -N Do not normalize units (i.e., use bps, pps instead of Mbps, Kpps, etc.).\n" 2431341457Svmaffione"\n" 2432341457Svmaffione" -F num_frags\n" 2433341457Svmaffione" Send multi-slot packets, each one with num_frags fragments. A multi-slot packet is repre-\n" 2434341457Svmaffione" sented by two or more consecutive netmap slots with the NS_MOREFRAG flag set (except for the\n" 2435341457Svmaffione" last slot). This is useful to transmit or receive packets larger than the netmap buffer\n" 2436341457Svmaffione" size.\n" 2437341457Svmaffione"\n" 2438341457Svmaffione" -M frag_size\n" 2439341457Svmaffione" In multi-slot mode, frag_size specifies the size of each fragment, if smaller than the packet\n" 2440341457Svmaffione" length divided by num_frags.\n" 2441341457Svmaffione"\n" 2442341457Svmaffione" -I Use indirect buffers. It is only valid for transmitting on VALE ports, and it is implemented\n" 2443341457Svmaffione" by setting the NS_INDIRECT flag in the netmap slots.\n" 2444341457Svmaffione"\n" 2445341457Svmaffione" -W Exit immediately if all the RX rings are empty the first time they are examined.\n" 2446341457Svmaffione"\n" 2447341457Svmaffione" -v Increase the verbosity level.\n" 2448341457Svmaffione"\n" 2449341457Svmaffione" -r In tx mode, do not initialize packets, but send whatever the content of the uninitialized\n" 2450341457Svmaffione" netmap buffers is (rubbish mode).\n" 2451341457Svmaffione"\n" 2452341457Svmaffione" -A Compute mean and standard deviation (over a sliding window) for the transmit or receive rate.\n" 2453341457Svmaffione"\n" 2454341457Svmaffione" -B Take Ethernet framing and CRC into account when computing the average bps. This adds 4 bytes\n" 2455341457Svmaffione" of CRC and 20 bytes of framing to each packet.\n" 2456341457Svmaffione"\n" 2457341457Svmaffione" -C tx_slots[,rx_slots[,tx_rings[,rx_rings]]]\n" 2458341457Svmaffione" Configuration in terms of number of rings and slots to be used when opening the netmap port.\n" 2459341457Svmaffione" Such configuration has effect on software ports created on the fly, such as VALE ports and\n" 2460341457Svmaffione" netmap pipes. The configuration may consist of 1 to 4 numbers separated by commas: tx_slots,\n" 2461341457Svmaffione" rx_slots, tx_rings, rx_rings. Missing numbers or zeroes stand for default values. As an\n" 2462341457Svmaffione" additional convenience, if exactly one number is specified, then this is assigned to both\n" 2463341457Svmaffione" tx_slots and rx_slots. If there is no fourth number, then the third one is assigned to both\n" 2464341457Svmaffione" tx_rings and rx_rings.\n" 2465341457Svmaffione"\n" 2466341457Svmaffione" -o options data generation options (parsed using atoi)\n" 2467341457Svmaffione" OPT_PREFETCH 1\n" 2468341457Svmaffione" OPT_ACCESS 2\n" 2469341457Svmaffione" OPT_COPY 4\n" 2470341457Svmaffione" OPT_MEMCPY 8\n" 2471341457Svmaffione" OPT_TS 16 (add a timestamp)\n" 2472341457Svmaffione" OPT_INDIRECT 32 (use indirect buffers)\n" 2473341457Svmaffione" OPT_DUMP 64 (dump rx/tx traffic)\n" 2474341457Svmaffione" OPT_RUBBISH 256\n" 2475341457Svmaffione" (send wathever the buffers contain)\n" 2476341457Svmaffione" OPT_RANDOM_SRC 512\n" 2477341457Svmaffione" OPT_RANDOM_DST 1024\n" 2478341457Svmaffione" OPT_PPS_STATS 2048\n" 2479341457Svmaffione "", 2480227614Sluigi cmd); 2481341457Svmaffione exit(errcode); 2482227614Sluigi} 2483227614Sluigi 2484246896Sluigistatic void 2485341457Svmaffionestart_threads(struct glob_arg *g) { 2486246896Sluigi int i; 2487227614Sluigi 2488246896Sluigi targs = calloc(g->nthreads, sizeof(*targs)); 2489341457Svmaffione struct targ *t; 2490246896Sluigi /* 2491246896Sluigi * Now create the desired number of threads, each one 2492246896Sluigi * using a single descriptor. 2493341457Svmaffione */ 2494246896Sluigi for (i = 0; i < g->nthreads; i++) { 2495341457Svmaffione uint64_t seed = time(0) | (time(0) << 32); 2496341457Svmaffione t = &targs[i]; 2497246896Sluigi 2498261909Sluigi bzero(t, sizeof(*t)); 2499261909Sluigi t->fd = -1; /* default, with pcap */ 2500261909Sluigi t->g = g; 2501341457Svmaffione memcpy(t->seed, &seed, sizeof(t->seed)); 2502261909Sluigi 2503341457Svmaffione if (g->dev_type == DEV_NETMAP) { 2504341457Svmaffione struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ 2505341457Svmaffione uint64_t nmd_flags = 0; 2506341457Svmaffione nmd.self = &nmd; 2507246896Sluigi 2508341457Svmaffione if (i > 0) { 2509341457Svmaffione /* the first thread uses the fd opened by the main 2510341457Svmaffione * thread, the other threads re-open /dev/netmap 2511341457Svmaffione */ 2512341457Svmaffione if (g->nthreads > 1) { 2513341457Svmaffione nmd.req.nr_flags = 2514341457Svmaffione g->nmd->req.nr_flags & ~NR_REG_MASK; 2515341457Svmaffione nmd.req.nr_flags |= NR_REG_ONE_NIC; 2516341457Svmaffione nmd.req.nr_ringid = i; 2517341457Svmaffione } 2518341457Svmaffione /* Only touch one of the rings (rx is already ok) */ 2519341457Svmaffione if (g->td_type == TD_TYPE_RECEIVER) 2520341457Svmaffione nmd_flags |= NETMAP_NO_TX_POLL; 2521341457Svmaffione 2522341457Svmaffione /* register interface. Override ifname and ringid etc. */ 2523341457Svmaffione t->nmd = nm_open(t->g->ifname, NULL, nmd_flags | 2524341457Svmaffione NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); 2525341457Svmaffione if (t->nmd == NULL) { 2526341457Svmaffione D("Unable to open %s: %s", 2527341457Svmaffione t->g->ifname, strerror(errno)); 2528341457Svmaffione continue; 2529341457Svmaffione } 2530341457Svmaffione } else { 2531341457Svmaffione t->nmd = g->nmd; 2532261909Sluigi } 2533341457Svmaffione t->fd = t->nmd->fd; 2534341457Svmaffione t->frags = g->frags; 2535341457Svmaffione } else { 2536341457Svmaffione targs[i].fd = g->main_fd; 2537246896Sluigi } 2538261909Sluigi t->used = 1; 2539261909Sluigi t->me = i; 2540246896Sluigi if (g->affinity >= 0) { 2541341457Svmaffione t->affinity = (g->affinity + i) % g->cpus; 2542261909Sluigi } else { 2543261909Sluigi t->affinity = -1; 2544261909Sluigi } 2545246896Sluigi /* default, init packets */ 2546261909Sluigi initialize_packet(t); 2547341457Svmaffione } 2548341457Svmaffione /* Wait for PHY reset. */ 2549341457Svmaffione D("Wait %d secs for phy reset", g->wait_link); 2550341457Svmaffione sleep(g->wait_link); 2551341457Svmaffione D("Ready..."); 2552246896Sluigi 2553341457Svmaffione for (i = 0; i < g->nthreads; i++) { 2554341457Svmaffione t = &targs[i]; 2555261909Sluigi if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { 2556260368Sluigi D("Unable to create thread %d: %s", i, strerror(errno)); 2557261909Sluigi t->used = 0; 2558246896Sluigi } 2559246896Sluigi } 2560246896Sluigi} 2561246896Sluigi 2562246896Sluigistatic void 2563246896Sluigimain_thread(struct glob_arg *g) 2564246896Sluigi{ 2565246896Sluigi int i; 2566246896Sluigi 2567341457Svmaffione struct my_ctrs prev, cur; 2568246896Sluigi double delta_t; 2569246896Sluigi struct timeval tic, toc; 2570246896Sluigi 2571341457Svmaffione prev.pkts = prev.bytes = prev.events = 0; 2572341457Svmaffione gettimeofday(&prev.t, NULL); 2573246896Sluigi for (;;) { 2574341457Svmaffione char b1[40], b2[40], b3[40], b4[100]; 2575341457Svmaffione uint64_t pps, usec; 2576341457Svmaffione struct my_ctrs x; 2577341457Svmaffione double abs; 2578246896Sluigi int done = 0; 2579246896Sluigi 2580341457Svmaffione usec = wait_for_next_report(&prev.t, &cur.t, 2581341457Svmaffione g->report_interval); 2582341457Svmaffione 2583341457Svmaffione cur.pkts = cur.bytes = cur.events = 0; 2584341457Svmaffione cur.min_space = 0; 2585341457Svmaffione if (usec < 10000) /* too short to be meaningful */ 2586341457Svmaffione continue; 2587341457Svmaffione /* accumulate counts for all threads */ 2588246896Sluigi for (i = 0; i < g->nthreads; i++) { 2589341457Svmaffione cur.pkts += targs[i].ctr.pkts; 2590341457Svmaffione cur.bytes += targs[i].ctr.bytes; 2591341457Svmaffione cur.events += targs[i].ctr.events; 2592341457Svmaffione cur.min_space += targs[i].ctr.min_space; 2593341457Svmaffione targs[i].ctr.min_space = 99999; 2594246896Sluigi if (targs[i].used == 0) 2595246896Sluigi done++; 2596246896Sluigi } 2597341457Svmaffione x.pkts = cur.pkts - prev.pkts; 2598341457Svmaffione x.bytes = cur.bytes - prev.bytes; 2599341457Svmaffione x.events = cur.events - prev.events; 2600341457Svmaffione pps = (x.pkts*1000000 + usec/2) / usec; 2601341457Svmaffione abs = (x.events > 0) ? (x.pkts / (double) x.events) : 0; 2602341457Svmaffione 2603341457Svmaffione if (!(g->options & OPT_PPS_STATS)) { 2604341457Svmaffione strcpy(b4, ""); 2605341457Svmaffione } else { 2606341457Svmaffione /* Compute some pps stats using a sliding window. */ 2607341457Svmaffione double ppsavg = 0.0, ppsdev = 0.0; 2608341457Svmaffione int nsamples = 0; 2609341457Svmaffione 2610341457Svmaffione g->win[g->win_idx] = pps; 2611341457Svmaffione g->win_idx = (g->win_idx + 1) % STATS_WIN; 2612341457Svmaffione 2613341457Svmaffione for (i = 0; i < STATS_WIN; i++) { 2614341457Svmaffione ppsavg += g->win[i]; 2615341457Svmaffione if (g->win[i]) { 2616341457Svmaffione nsamples ++; 2617341457Svmaffione } 2618341457Svmaffione } 2619341457Svmaffione ppsavg /= nsamples; 2620341457Svmaffione 2621341457Svmaffione for (i = 0; i < STATS_WIN; i++) { 2622341457Svmaffione if (g->win[i] == 0) { 2623341457Svmaffione continue; 2624341457Svmaffione } 2625341457Svmaffione ppsdev += (g->win[i] - ppsavg) * (g->win[i] - ppsavg); 2626341457Svmaffione } 2627341457Svmaffione ppsdev /= nsamples; 2628341457Svmaffione ppsdev = sqrt(ppsdev); 2629341457Svmaffione 2630341457Svmaffione snprintf(b4, sizeof(b4), "[avg/std %s/%s pps]", 2631341457Svmaffione norm(b1, ppsavg, normalize), norm(b2, ppsdev, normalize)); 2632341457Svmaffione } 2633341457Svmaffione 2634341457Svmaffione D("%spps %s(%spkts %sbps in %llu usec) %.2f avg_batch %d min_space", 2635341457Svmaffione norm(b1, pps, normalize), b4, 2636341457Svmaffione norm(b2, (double)x.pkts, normalize), 2637341457Svmaffione norm(b3, (double)x.bytes*8+(double)x.pkts*g->framing, normalize), 2638341457Svmaffione (unsigned long long)usec, 2639341457Svmaffione abs, (int)cur.min_space); 2640341457Svmaffione prev = cur; 2641341457Svmaffione 2642246896Sluigi if (done == g->nthreads) 2643246896Sluigi break; 2644246896Sluigi } 2645246896Sluigi 2646246896Sluigi timerclear(&tic); 2647246896Sluigi timerclear(&toc); 2648341457Svmaffione cur.pkts = cur.bytes = cur.events = 0; 2649341457Svmaffione /* final round */ 2650246896Sluigi for (i = 0; i < g->nthreads; i++) { 2651251132Sluigi struct timespec t_tic, t_toc; 2652246896Sluigi /* 2653246896Sluigi * Join active threads, unregister interfaces and close 2654246896Sluigi * file descriptors. 2655246896Sluigi */ 2656251132Sluigi if (targs[i].used) 2657341457Svmaffione pthread_join(targs[i].thread, NULL); /* blocking */ 2658341457Svmaffione if (g->dev_type == DEV_NETMAP) { 2659341457Svmaffione nm_close(targs[i].nmd); 2660341457Svmaffione targs[i].nmd = NULL; 2661341457Svmaffione } else { 2662341457Svmaffione close(targs[i].fd); 2663341457Svmaffione } 2664246896Sluigi 2665246896Sluigi if (targs[i].completed == 0) 2666246896Sluigi D("ouch, thread %d exited with error", i); 2667246896Sluigi 2668246896Sluigi /* 2669246896Sluigi * Collect threads output and extract information about 2670246896Sluigi * how long it took to send all the packets. 2671246896Sluigi */ 2672341457Svmaffione cur.pkts += targs[i].ctr.pkts; 2673341457Svmaffione cur.bytes += targs[i].ctr.bytes; 2674341457Svmaffione cur.events += targs[i].ctr.events; 2675341457Svmaffione /* collect the largest start (tic) and end (toc) times, 2676341457Svmaffione * XXX maybe we should do the earliest tic, or do a weighted 2677341457Svmaffione * average ? 2678341457Svmaffione */ 2679251132Sluigi t_tic = timeval2spec(&tic); 2680251132Sluigi t_toc = timeval2spec(&toc); 2681251132Sluigi if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 2682251132Sluigi tic = timespec2val(&targs[i].tic); 2683251132Sluigi if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 2684251132Sluigi toc = timespec2val(&targs[i].toc); 2685246896Sluigi } 2686246896Sluigi 2687246896Sluigi /* print output. */ 2688246896Sluigi timersub(&toc, &tic, &toc); 2689246896Sluigi delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 2690341457Svmaffione if (g->td_type == TD_TYPE_SENDER) 2691341457Svmaffione tx_output(g, &cur, delta_t, "Sent"); 2692341457Svmaffione else if (g->td_type == TD_TYPE_RECEIVER) 2693341457Svmaffione tx_output(g, &cur, delta_t, "Received"); 2694246896Sluigi} 2695246896Sluigi 2696341457Svmaffionestruct td_desc { 2697341457Svmaffione int ty; 2698246896Sluigi char *key; 2699246896Sluigi void *f; 2700341457Svmaffione int default_burst; 2701246896Sluigi}; 2702246896Sluigi 2703341457Svmaffionestatic struct td_desc func[] = { 2704341457Svmaffione { TD_TYPE_RECEIVER, "rx", receiver_body, 512}, /* default */ 2705341457Svmaffione { TD_TYPE_SENDER, "tx", sender_body, 512 }, 2706341457Svmaffione { TD_TYPE_OTHER, "ping", ping_body, 1 }, 2707341457Svmaffione { TD_TYPE_OTHER, "pong", pong_body, 1 }, 2708341457Svmaffione { TD_TYPE_SENDER, "txseq", txseq_body, 512 }, 2709341457Svmaffione { TD_TYPE_RECEIVER, "rxseq", rxseq_body, 512 }, 2710341457Svmaffione { 0, NULL, NULL, 0 } 2711246896Sluigi}; 2712246896Sluigi 2713246896Sluigistatic int 2714246896Sluigitap_alloc(char *dev) 2715246896Sluigi{ 2716246896Sluigi struct ifreq ifr; 2717246896Sluigi int fd, err; 2718246896Sluigi char *clonedev = TAP_CLONEDEV; 2719246896Sluigi 2720246896Sluigi (void)err; 2721246896Sluigi (void)dev; 2722246896Sluigi /* Arguments taken by the function: 2723246896Sluigi * 2724246896Sluigi * char *dev: the name of an interface (or '\0'). MUST have enough 2725246896Sluigi * space to hold the interface name if '\0' is passed 2726246896Sluigi * int flags: interface flags (eg, IFF_TUN etc.) 2727246896Sluigi */ 2728246896Sluigi 2729246896Sluigi#ifdef __FreeBSD__ 2730246896Sluigi if (dev[3]) { /* tapSomething */ 2731246896Sluigi static char buf[128]; 2732246896Sluigi snprintf(buf, sizeof(buf), "/dev/%s", dev); 2733246896Sluigi clonedev = buf; 2734246896Sluigi } 2735246896Sluigi#endif 2736246896Sluigi /* open the device */ 2737246896Sluigi if( (fd = open(clonedev, O_RDWR)) < 0 ) { 2738246896Sluigi return fd; 2739246896Sluigi } 2740246896Sluigi D("%s open successful", clonedev); 2741246896Sluigi 2742246896Sluigi /* preparation of the struct ifr, of type "struct ifreq" */ 2743246896Sluigi memset(&ifr, 0, sizeof(ifr)); 2744246896Sluigi 2745246896Sluigi#ifdef linux 2746246896Sluigi ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 2747246896Sluigi 2748246896Sluigi if (*dev) { 2749246896Sluigi /* if a device name was specified, put it in the structure; otherwise, 2750246896Sluigi * the kernel will try to allocate the "next" device of the 2751246896Sluigi * specified type */ 2752341457Svmaffione size_t len = strlen(dev); 2753341457Svmaffione if (len > IFNAMSIZ) { 2754341457Svmaffione D("%s too long", dev); 2755341457Svmaffione return -1; 2756341457Svmaffione } 2757341457Svmaffione memcpy(ifr.ifr_name, dev, len); 2758246896Sluigi } 2759246896Sluigi 2760246896Sluigi /* try to create the device */ 2761246896Sluigi if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 2762260368Sluigi D("failed to to a TUNSETIFF: %s", strerror(errno)); 2763246896Sluigi close(fd); 2764246896Sluigi return err; 2765246896Sluigi } 2766246896Sluigi 2767246896Sluigi /* if the operation was successful, write back the name of the 2768246896Sluigi * interface to the variable "dev", so the caller can know 2769246896Sluigi * it. Note that the caller MUST reserve space in *dev (see calling 2770246896Sluigi * code below) */ 2771246896Sluigi strcpy(dev, ifr.ifr_name); 2772246896Sluigi D("new name is %s", dev); 2773246896Sluigi#endif /* linux */ 2774246896Sluigi 2775341457Svmaffione /* this is the special file descriptor that the caller will use to talk 2776341457Svmaffione * with the virtual interface */ 2777341457Svmaffione return fd; 2778246896Sluigi} 2779246896Sluigi 2780227614Sluigiint 2781227614Sluigimain(int arc, char **argv) 2782227614Sluigi{ 2783246896Sluigi int i; 2784341457Svmaffione struct sigaction sa; 2785341457Svmaffione sigset_t ss; 2786227614Sluigi 2787227614Sluigi struct glob_arg g; 2788227614Sluigi 2789227614Sluigi int ch; 2790227614Sluigi int devqueues = 1; /* how many device queues */ 2791341457Svmaffione int wait_link_arg = 0; 2792227614Sluigi 2793341457Svmaffione int pkt_size_done = 0; 2794341457Svmaffione 2795341457Svmaffione struct td_desc *fn = func; 2796341457Svmaffione 2797227614Sluigi bzero(&g, sizeof(g)); 2798227614Sluigi 2799246896Sluigi g.main_fd = -1; 2800341457Svmaffione g.td_body = fn->f; 2801341457Svmaffione g.td_type = fn->ty; 2802246896Sluigi g.report_interval = 1000; /* report interval */ 2803246896Sluigi g.affinity = -1; 2804246896Sluigi /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 2805341457Svmaffione g.af = AF_INET; /* default */ 2806246896Sluigi g.src_ip.name = "10.0.0.1"; 2807246896Sluigi g.dst_ip.name = "10.1.0.1"; 2808246896Sluigi g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 2809246896Sluigi g.src_mac.name = NULL; 2810227614Sluigi g.pkt_size = 60; 2811341457Svmaffione g.pkt_min_size = 0; 2812227614Sluigi g.nthreads = 1; 2813341457Svmaffione g.cpus = 1; /* default */ 2814251426Sluigi g.forever = 1; 2815251132Sluigi g.tx_rate = 0; 2816342035Svmaffione g.frags = 1; 2817342035Svmaffione g.frag_size = (u_int)-1; /* use the netmap buffer size by default */ 2818257529Sluigi g.nmr_config = ""; 2819260368Sluigi g.virt_header = 0; 2820341457Svmaffione g.wait_link = 2; /* wait 2 seconds for physical ports */ 2821227614Sluigi 2822341457Svmaffione while ((ch = getopt(arc, argv, "46a:f:F:Nn:i:Il:d:s:D:S:b:c:o:p:" 2823341457Svmaffione "T:w:WvR:XC:H:rP:zZAhBM:")) != -1) { 2824246896Sluigi 2825227614Sluigi switch(ch) { 2826227614Sluigi default: 2827227614Sluigi D("bad option %c %s", ch, optarg); 2828341457Svmaffione usage(-1); 2829227614Sluigi break; 2830246896Sluigi 2831341457Svmaffione case 'h': 2832341457Svmaffione usage(0); 2833341457Svmaffione break; 2834341457Svmaffione 2835341457Svmaffione case '4': 2836341457Svmaffione g.af = AF_INET; 2837341457Svmaffione break; 2838341457Svmaffione 2839341457Svmaffione case '6': 2840341457Svmaffione g.af = AF_INET6; 2841341457Svmaffione break; 2842341457Svmaffione 2843341457Svmaffione case 'N': 2844341457Svmaffione normalize = 0; 2845341457Svmaffione break; 2846341457Svmaffione 2847246896Sluigi case 'n': 2848341457Svmaffione g.npackets = strtoull(optarg, NULL, 10); 2849246896Sluigi break; 2850246896Sluigi 2851257529Sluigi case 'F': 2852257529Sluigi i = atoi(optarg); 2853257529Sluigi if (i < 1 || i > 63) { 2854257529Sluigi D("invalid frags %d [1..63], ignore", i); 2855257529Sluigi break; 2856257529Sluigi } 2857257529Sluigi g.frags = i; 2858257529Sluigi break; 2859257529Sluigi 2860341457Svmaffione case 'M': 2861342035Svmaffione g.frag_size = atoi(optarg); 2862341457Svmaffione break; 2863341457Svmaffione 2864246896Sluigi case 'f': 2865246896Sluigi for (fn = func; fn->key; fn++) { 2866246896Sluigi if (!strcmp(fn->key, optarg)) 2867246896Sluigi break; 2868246896Sluigi } 2869341457Svmaffione if (fn->key) { 2870246896Sluigi g.td_body = fn->f; 2871341457Svmaffione g.td_type = fn->ty; 2872341457Svmaffione } else { 2873246896Sluigi D("unrecognised function %s", optarg); 2874341457Svmaffione } 2875246896Sluigi break; 2876246896Sluigi 2877246896Sluigi case 'o': /* data generation options */ 2878341457Svmaffione g.options |= atoi(optarg); 2879234956Sluigi break; 2880246896Sluigi 2881246896Sluigi case 'a': /* force affinity */ 2882246896Sluigi g.affinity = atoi(optarg); 2883246896Sluigi break; 2884246896Sluigi 2885227614Sluigi case 'i': /* interface */ 2886260700Sluigi /* a prefix of tap: netmap: or pcap: forces the mode. 2887260700Sluigi * otherwise we guess 2888260700Sluigi */ 2889260700Sluigi D("interface is %s", optarg); 2890261909Sluigi if (strlen(optarg) > MAX_IFNAMELEN - 8) { 2891261909Sluigi D("ifname too long %s", optarg); 2892261909Sluigi break; 2893261909Sluigi } 2894261909Sluigi strcpy(g.ifname, optarg); 2895260700Sluigi if (!strcmp(optarg, "null")) { 2896260700Sluigi g.dev_type = DEV_NETMAP; 2897260700Sluigi g.dummy_send = 1; 2898260700Sluigi } else if (!strncmp(optarg, "tap:", 4)) { 2899246896Sluigi g.dev_type = DEV_TAP; 2900261909Sluigi strcpy(g.ifname, optarg + 4); 2901260700Sluigi } else if (!strncmp(optarg, "pcap:", 5)) { 2902260700Sluigi g.dev_type = DEV_PCAP; 2903261909Sluigi strcpy(g.ifname, optarg + 5); 2904261909Sluigi } else if (!strncmp(optarg, "netmap:", 7) || 2905261909Sluigi !strncmp(optarg, "vale", 4)) { 2906246896Sluigi g.dev_type = DEV_NETMAP; 2907260700Sluigi } else if (!strncmp(optarg, "tap", 3)) { 2908260700Sluigi g.dev_type = DEV_TAP; 2909261909Sluigi } else { /* prepend netmap: */ 2910260700Sluigi g.dev_type = DEV_NETMAP; 2911261909Sluigi sprintf(g.ifname, "netmap:%s", optarg); 2912260700Sluigi } 2913227614Sluigi break; 2914246896Sluigi 2915251426Sluigi case 'I': 2916341457Svmaffione g.options |= OPT_INDIRECT; /* use indirect buffers */ 2917251426Sluigi break; 2918251426Sluigi 2919227614Sluigi case 'l': /* pkt_size */ 2920341457Svmaffione if (pkt_size_done) { 2921341457Svmaffione g.pkt_min_size = atoi(optarg); 2922341457Svmaffione } else { 2923341457Svmaffione g.pkt_size = atoi(optarg); 2924341457Svmaffione pkt_size_done = 1; 2925341457Svmaffione } 2926227614Sluigi break; 2927246896Sluigi 2928227614Sluigi case 'd': 2929246896Sluigi g.dst_ip.name = optarg; 2930227614Sluigi break; 2931246896Sluigi 2932227614Sluigi case 's': 2933246896Sluigi g.src_ip.name = optarg; 2934227614Sluigi break; 2935246896Sluigi 2936227614Sluigi case 'T': /* report interval */ 2937246896Sluigi g.report_interval = atoi(optarg); 2938227614Sluigi break; 2939246896Sluigi 2940227614Sluigi case 'w': 2941341457Svmaffione g.wait_link = atoi(optarg); 2942341457Svmaffione wait_link_arg = 1; 2943227614Sluigi break; 2944246896Sluigi 2945341457Svmaffione case 'W': 2946341457Svmaffione g.forever = 0; /* exit RX with no traffic */ 2947246896Sluigi break; 2948246896Sluigi 2949227614Sluigi case 'b': /* burst */ 2950227614Sluigi g.burst = atoi(optarg); 2951227614Sluigi break; 2952227614Sluigi case 'c': 2953227614Sluigi g.cpus = atoi(optarg); 2954227614Sluigi break; 2955227614Sluigi case 'p': 2956227614Sluigi g.nthreads = atoi(optarg); 2957227614Sluigi break; 2958227614Sluigi 2959227614Sluigi case 'D': /* destination mac */ 2960246896Sluigi g.dst_mac.name = optarg; 2961227614Sluigi break; 2962246896Sluigi 2963227614Sluigi case 'S': /* source mac */ 2964246896Sluigi g.src_mac.name = optarg; 2965227614Sluigi break; 2966227614Sluigi case 'v': 2967227614Sluigi verbose++; 2968251132Sluigi break; 2969251132Sluigi case 'R': 2970251132Sluigi g.tx_rate = atoi(optarg); 2971251132Sluigi break; 2972251426Sluigi case 'X': 2973251426Sluigi g.options |= OPT_DUMP; 2974257529Sluigi break; 2975257529Sluigi case 'C': 2976257529Sluigi g.nmr_config = strdup(optarg); 2977260368Sluigi break; 2978260368Sluigi case 'H': 2979260368Sluigi g.virt_header = atoi(optarg); 2980260700Sluigi break; 2981272962Sgnn case 'P': 2982272962Sgnn g.packet_file = strdup(optarg); 2983272962Sgnn break; 2984341457Svmaffione case 'r': 2985341457Svmaffione g.options |= OPT_RUBBISH; 2986341457Svmaffione break; 2987281746Sadrian case 'z': 2988281746Sadrian g.options |= OPT_RANDOM_SRC; 2989281746Sadrian break; 2990281746Sadrian case 'Z': 2991281746Sadrian g.options |= OPT_RANDOM_DST; 2992281746Sadrian break; 2993341457Svmaffione case 'A': 2994341457Svmaffione g.options |= OPT_PPS_STATS; 2995341457Svmaffione break; 2996341457Svmaffione case 'B': 2997341457Svmaffione /* raw packets have4 bytes crc + 20 bytes framing */ 2998341457Svmaffione // XXX maybe add an option to pass the IFG 2999341457Svmaffione g.framing = 24 * 8; 3000341457Svmaffione break; 3001227614Sluigi } 3002227614Sluigi } 3003227614Sluigi 3004278641Sgnn if (strlen(g.ifname) <=0 ) { 3005227614Sluigi D("missing ifname"); 3006341457Svmaffione usage(-1); 3007227614Sluigi } 3008246896Sluigi 3009341457Svmaffione if (g.burst == 0) { 3010341457Svmaffione g.burst = fn->default_burst; 3011341457Svmaffione D("using default burst size: %d", g.burst); 3012341457Svmaffione } 3013341457Svmaffione 3014341457Svmaffione g.system_cpus = i = system_ncpus(); 3015246896Sluigi if (g.cpus < 0 || g.cpus > i) { 3016246896Sluigi D("%d cpus is too high, have only %d cpus", g.cpus, i); 3017341457Svmaffione usage(-1); 3018227614Sluigi } 3019341457Svmaffione D("running on %d cpus (have %d)", g.cpus, i); 3020246896Sluigi if (g.cpus == 0) 3021246896Sluigi g.cpus = i; 3022246896Sluigi 3023341457Svmaffione if (!wait_link_arg && !strncmp(g.ifname, "vale", 4)) { 3024341457Svmaffione g.wait_link = 0; 3025341457Svmaffione } 3026341457Svmaffione 3027270063Sluigi if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { 3028270063Sluigi D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); 3029341457Svmaffione usage(-1); 3030227614Sluigi } 3031227614Sluigi 3032341457Svmaffione if (g.pkt_min_size > 0 && (g.pkt_min_size < 16 || g.pkt_min_size > g.pkt_size)) { 3033341457Svmaffione D("bad pktminsize %d [16..%d]\n", g.pkt_min_size, g.pkt_size); 3034341457Svmaffione usage(-1); 3035341457Svmaffione } 3036341457Svmaffione 3037246896Sluigi if (g.src_mac.name == NULL) { 3038246896Sluigi static char mybuf[20] = "00:00:00:00:00:00"; 3039234956Sluigi /* retrieve source mac address. */ 3040246896Sluigi if (source_hwaddr(g.ifname, mybuf) == -1) { 3041234956Sluigi D("Unable to retrieve source mac"); 3042234956Sluigi // continue, fail later 3043234956Sluigi } 3044246896Sluigi g.src_mac.name = mybuf; 3045234956Sluigi } 3046246896Sluigi /* extract address ranges */ 3047341457Svmaffione if (extract_mac_range(&g.src_mac) || extract_mac_range(&g.dst_mac)) 3048341457Svmaffione usage(-1); 3049341457Svmaffione g.options |= extract_ip_range(&g.src_ip, g.af); 3050341457Svmaffione g.options |= extract_ip_range(&g.dst_ip, g.af); 3051234956Sluigi 3052260368Sluigi if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 3053260368Sluigi && g.virt_header != VIRT_HDR_2) { 3054260368Sluigi D("bad virtio-net-header length"); 3055341457Svmaffione usage(-1); 3056260368Sluigi } 3057260368Sluigi 3058246896Sluigi if (g.dev_type == DEV_TAP) { 3059246896Sluigi D("want to use tap %s", g.ifname); 3060246896Sluigi g.main_fd = tap_alloc(g.ifname); 3061246896Sluigi if (g.main_fd < 0) { 3062246896Sluigi D("cannot open tap %s", g.ifname); 3063341457Svmaffione usage(-1); 3064246896Sluigi } 3065260700Sluigi#ifndef NO_PCAP 3066260700Sluigi } else if (g.dev_type == DEV_PCAP) { 3067246896Sluigi char pcap_errbuf[PCAP_ERRBUF_SIZE]; 3068246896Sluigi 3069246896Sluigi pcap_errbuf[0] = '\0'; // init the buffer 3070270063Sluigi g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); 3071234956Sluigi if (g.p == NULL) { 3072246896Sluigi D("cannot open pcap on %s", g.ifname); 3073341457Svmaffione usage(-1); 3074234956Sluigi } 3075270063Sluigi g.main_fd = pcap_fileno(g.p); 3076270063Sluigi D("using pcap on %s fileno %d", g.ifname, g.main_fd); 3077260700Sluigi#endif /* !NO_PCAP */ 3078260700Sluigi } else if (g.dummy_send) { /* but DEV_NETMAP */ 3079257529Sluigi D("using a dummy send routine"); 3080234956Sluigi } else { 3081341457Svmaffione struct nm_desc base_nmd; 3082341457Svmaffione char errmsg[MAXERRMSG]; 3083341457Svmaffione u_int flags; 3084261909Sluigi 3085261909Sluigi bzero(&base_nmd, sizeof(base_nmd)); 3086261909Sluigi 3087341457Svmaffione parse_nmr_config(g.nmr_config, &base_nmd.req); 3088341457Svmaffione 3089341457Svmaffione base_nmd.req.nr_flags |= NR_ACCEPT_VNET_HDR; 3090341457Svmaffione 3091341457Svmaffione if (nm_parse(g.ifname, &base_nmd, errmsg) < 0) { 3092341457Svmaffione D("Invalid name '%s': %s", g.ifname, errmsg); 3093341457Svmaffione goto out; 3094261909Sluigi } 3095261909Sluigi 3096227614Sluigi /* 3097261909Sluigi * Open the netmap device using nm_open(). 3098227614Sluigi * 3099227614Sluigi * protocol stack and may cause a reset of the card, 3100227614Sluigi * which in turn may take some time for the PHY to 3101261909Sluigi * reconfigure. We do the open here to have time to reset. 3102227614Sluigi */ 3103341457Svmaffione flags = NM_OPEN_IFNAME | NM_OPEN_ARG1 | NM_OPEN_ARG2 | 3104341457Svmaffione NM_OPEN_ARG3 | NM_OPEN_RING_CFG; 3105341457Svmaffione if (g.nthreads > 1) { 3106341457Svmaffione base_nmd.req.nr_flags &= ~NR_REG_MASK; 3107341457Svmaffione base_nmd.req.nr_flags |= NR_REG_ONE_NIC; 3108341457Svmaffione base_nmd.req.nr_ringid = 0; 3109341457Svmaffione } 3110341457Svmaffione g.nmd = nm_open(g.ifname, NULL, flags, &base_nmd); 3111261909Sluigi if (g.nmd == NULL) { 3112261909Sluigi D("Unable to open %s: %s", g.ifname, strerror(errno)); 3113261909Sluigi goto out; 3114227614Sluigi } 3115261909Sluigi g.main_fd = g.nmd->fd; 3116341457Svmaffione D("mapped %luKB at %p", (unsigned long)(g.nmd->req.nr_memsize>>10), 3117341457Svmaffione g.nmd->mem); 3118227614Sluigi 3119341457Svmaffione if (g.virt_header) { 3120341457Svmaffione /* Set the virtio-net header length, since the user asked 3121341457Svmaffione * for it explicitely. */ 3122341457Svmaffione set_vnet_hdr_len(&g); 3123341457Svmaffione } else { 3124341457Svmaffione /* Check whether the netmap port we opened requires us to send 3125341457Svmaffione * and receive frames with virtio-net header. */ 3126341457Svmaffione get_vnet_hdr_len(&g); 3127341457Svmaffione } 3128341457Svmaffione 3129341457Svmaffione /* get num of queues in tx or rx */ 3130341457Svmaffione if (g.td_type == TD_TYPE_SENDER) 3131270063Sluigi devqueues = g.nmd->req.nr_tx_rings; 3132341457Svmaffione else 3133270063Sluigi devqueues = g.nmd->req.nr_rx_rings; 3134261909Sluigi 3135227614Sluigi /* validate provided nthreads. */ 3136227614Sluigi if (g.nthreads < 1 || g.nthreads > devqueues) { 3137227614Sluigi D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 3138227614Sluigi // continue, fail later 3139227614Sluigi } 3140227614Sluigi 3141342035Svmaffione if (g.td_type == TD_TYPE_SENDER) { 3142342035Svmaffione int mtu = get_if_mtu(&g); 3143342035Svmaffione 3144342035Svmaffione if (mtu > 0 && g.pkt_size > mtu) { 3145342035Svmaffione D("pkt_size (%d) must be <= mtu (%d)", 3146342035Svmaffione g.pkt_size, mtu); 3147342035Svmaffione return -1; 3148342035Svmaffione } 3149342035Svmaffione } 3150342035Svmaffione 3151260700Sluigi if (verbose) { 3152261909Sluigi struct netmap_if *nifp = g.nmd->nifp; 3153261909Sluigi struct nmreq *req = &g.nmd->req; 3154227614Sluigi 3155261909Sluigi D("nifp at offset %d, %d tx %d rx region %d", 3156261909Sluigi req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, 3157261909Sluigi req->nr_arg2); 3158261909Sluigi for (i = 0; i <= req->nr_tx_rings; i++) { 3159270063Sluigi struct netmap_ring *ring = NETMAP_TXRING(nifp, i); 3160341457Svmaffione D(" TX%d at 0x%p slots %d", i, 3161341457Svmaffione (void *)((char *)ring - (char *)nifp), ring->num_slots); 3162260700Sluigi } 3163261909Sluigi for (i = 0; i <= req->nr_rx_rings; i++) { 3164270063Sluigi struct netmap_ring *ring = NETMAP_RXRING(nifp, i); 3165341457Svmaffione D(" RX%d at 0x%p slots %d", i, 3166341457Svmaffione (void *)((char *)ring - (char *)nifp), ring->num_slots); 3167260700Sluigi } 3168260700Sluigi } 3169227614Sluigi 3170227614Sluigi /* Print some debug information. */ 3171227614Sluigi fprintf(stdout, 3172227614Sluigi "%s %s: %d queues, %d threads and %d cpus.\n", 3173341457Svmaffione (g.td_type == TD_TYPE_SENDER) ? "Sending on" : 3174341457Svmaffione ((g.td_type == TD_TYPE_RECEIVER) ? "Receiving from" : 3175341457Svmaffione "Working on"), 3176246896Sluigi g.ifname, 3177227614Sluigi devqueues, 3178227614Sluigi g.nthreads, 3179227614Sluigi g.cpus); 3180341457Svmaffione if (g.td_type == TD_TYPE_SENDER) { 3181227614Sluigi fprintf(stdout, "%s -> %s (%s -> %s)\n", 3182246896Sluigi g.src_ip.name, g.dst_ip.name, 3183246896Sluigi g.src_mac.name, g.dst_mac.name); 3184227614Sluigi } 3185261909Sluigi 3186261909Sluigiout: 3187227614Sluigi /* Exit if something went wrong. */ 3188246896Sluigi if (g.main_fd < 0) { 3189227614Sluigi D("aborting"); 3190341457Svmaffione usage(-1); 3191227614Sluigi } 3192234956Sluigi } 3193227614Sluigi 3194261909Sluigi 3195234956Sluigi if (g.options) { 3196341457Svmaffione D("--- SPECIAL OPTIONS:%s%s%s%s%s%s\n", 3197234956Sluigi g.options & OPT_PREFETCH ? " prefetch" : "", 3198234956Sluigi g.options & OPT_ACCESS ? " access" : "", 3199234956Sluigi g.options & OPT_MEMCPY ? " memcpy" : "", 3200251426Sluigi g.options & OPT_INDIRECT ? " indirect" : "", 3201341457Svmaffione g.options & OPT_COPY ? " copy" : "", 3202341457Svmaffione g.options & OPT_RUBBISH ? " rubbish " : ""); 3203234956Sluigi } 3204257529Sluigi 3205257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 3206257529Sluigi if (g.tx_rate > 0) { 3207257529Sluigi /* try to have at least something every second, 3208260368Sluigi * reducing the burst size to some 0.01s worth of data 3209257529Sluigi * (but no less than one full set of fragments) 3210257529Sluigi */ 3211260368Sluigi uint64_t x; 3212260368Sluigi int lim = (g.tx_rate)/300; 3213260368Sluigi if (g.burst > lim) 3214260368Sluigi g.burst = lim; 3215341457Svmaffione if (g.burst == 0) 3216341457Svmaffione g.burst = 1; 3217260368Sluigi x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 3218260368Sluigi g.tx_period.tv_nsec = x; 3219257529Sluigi g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 3220257529Sluigi g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 3221251132Sluigi } 3222341457Svmaffione if (g.td_type == TD_TYPE_SENDER) 3223257529Sluigi D("Sending %d packets every %ld.%09ld s", 3224257529Sluigi g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 3225227614Sluigi /* Install ^C handler. */ 3226227614Sluigi global_nthreads = g.nthreads; 3227341457Svmaffione sigemptyset(&ss); 3228341457Svmaffione sigaddset(&ss, SIGINT); 3229341457Svmaffione /* block SIGINT now, so that all created threads will inherit the mask */ 3230341457Svmaffione if (pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) { 3231341457Svmaffione D("failed to block SIGINT: %s", strerror(errno)); 3232341457Svmaffione } 3233341457Svmaffione start_threads(&g); 3234341457Svmaffione /* Install the handler and re-enable SIGINT for the main thread */ 3235341457Svmaffione memset(&sa, 0, sizeof(sa)); 3236341457Svmaffione sa.sa_handler = sigint_h; 3237341457Svmaffione if (sigaction(SIGINT, &sa, NULL) < 0) { 3238341457Svmaffione D("failed to install ^C handler: %s", strerror(errno)); 3239341457Svmaffione } 3240227614Sluigi 3241341457Svmaffione if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL) < 0) { 3242341457Svmaffione D("failed to re-enable SIGINT: %s", strerror(errno)); 3243341457Svmaffione } 3244246896Sluigi main_thread(&g); 3245341457Svmaffione free(targs); 3246246896Sluigi return 0; 3247246896Sluigi} 3248227614Sluigi 3249227614Sluigi/* end of file */ 3250