pkt-gen.c revision 262151
1167465Smp/* 259243Sobrien * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. 359243Sobrien * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved. 459243Sobrien * 559243Sobrien * Redistribution and use in source and binary forms, with or without 659243Sobrien * modification, are permitted provided that the following conditions 759243Sobrien * are met: 859243Sobrien * 1. Redistributions of source code must retain the above copyright 959243Sobrien * notice, this list of conditions and the following disclaimer. 1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1159243Sobrien * notice, this list of conditions and the following disclaimer in the 1259243Sobrien * documentation and/or other materials provided with the distribution. 1359243Sobrien * 1459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17100616Smp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2459243Sobrien * SUCH DAMAGE. 2559243Sobrien */ 2659243Sobrien 2759243Sobrien/* 2859243Sobrien * $FreeBSD: stable/10/tools/tools/netmap/pkt-gen.c 262151 2014-02-18 05:01:04Z luigi $ 2959243Sobrien * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $ 3059243Sobrien * 3159243Sobrien * Example program to show how to build a multithreaded packet 3259243Sobrien * source/sink using the netmap device. 3359243Sobrien * 3459243Sobrien * In this example we create a programmable number of threads 3559243Sobrien * to take care of all the queues of the interface used to 3659243Sobrien * send or receive traffic. 3759243Sobrien * 38145479Smp */ 39167465Smp 40145479Smp#define _GNU_SOURCE /* for CPU_SET() */ 41145479Smp#include <stdio.h> 42145479Smp#define NETMAP_WITH_LIBS 43100616Smp#include <net/netmap_user.h> 44145479Smp 45145479Smp 46145479Smp#include <ctype.h> // isprint() 47100616Smp#include <unistd.h> // sysconf() 48100616Smp#include <sys/poll.h> 49145479Smp#include <arpa/inet.h> /* ntohs */ 50145479Smp#include <sys/sysctl.h> /* sysctl */ 51145479Smp#include <ifaddrs.h> /* getifaddrs */ 52145479Smp#include <net/ethernet.h> 53145479Smp#include <netinet/in.h> 54145479Smp#include <netinet/ip.h> 55145479Smp#include <netinet/udp.h> 56145479Smp 5759243Sobrien#include <pthread.h> 5859243Sobrien 5959243Sobrien#ifndef NO_PCAP 6069408Sache#include <pcap/pcap.h> 6159243Sobrien#endif 6259243Sobrien 6369408Sache#ifdef linux 6459243Sobrien 6559243Sobrien#define cpuset_t cpu_set_t 6659243Sobrien 6759243Sobrien#define ifr_flagshigh ifr_flags /* only the low 16 bits here */ 6859243Sobrien#define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */ 6959243Sobrien#include <linux/ethtool.h> 7059243Sobrien#include <linux/sockios.h> 71145479Smp 7259243Sobrien#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 7369408Sache#include <netinet/ether.h> /* ether_aton */ 7459243Sobrien#include <linux/if_packet.h> /* sockaddr_ll */ 7559243Sobrien#endif /* linux */ 76167465Smp 7769408Sache#ifdef __FreeBSD__ 7859243Sobrien#include <sys/endian.h> /* le64toh */ 7959243Sobrien#include <machine/param.h> 8059243Sobrien 8159243Sobrien#include <pthread_np.h> /* pthread w/ affinity */ 8259243Sobrien#include <sys/cpuset.h> /* cpu_set */ 8359243Sobrien#include <net/if_dl.h> /* LLADDR */ 8459243Sobrien#endif /* __FreeBSD__ */ 8559243Sobrien 8659243Sobrien#ifdef __APPLE__ 8759243Sobrien 8859243Sobrien#define cpuset_t uint64_t // XXX 8959243Sobrienstatic inline void CPU_ZERO(cpuset_t *p) 90167465Smp{ 91145479Smp *p = 0; 92145479Smp} 93145479Smp 94145479Smpstatic inline void CPU_SET(uint32_t i, cpuset_t *p) 95145479Smp{ 96145479Smp *p |= 1<< (i & 0x3f); 97145479Smp} 98145479Smp 9959243Sobrien#define pthread_setaffinity_np(a, b, c) ((void)a, 0) 10059243Sobrien 101145479Smp#define ifr_flagshigh ifr_flags // XXX 102145479Smp#define IFF_PPROMISC IFF_PROMISC 103145479Smp#include <net/if_dl.h> /* LLADDR */ 104145479Smp#define clock_gettime(a,b) \ 105145479Smp do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) 10659243Sobrien#endif /* __APPLE__ */ 10759243Sobrien 10859243Sobrienconst char *default_payload="netmap pkt-gen DIRECT payload\n" 10959243Sobrien "http://info.iet.unipi.it/~luigi/netmap/ "; 110145479Smp 111145479Smpconst char *indirect_payload="netmap pkt-gen indirect payload\n" 112145479Smp "http://info.iet.unipi.it/~luigi/netmap/ "; 113145479Smp 11459243Sobrienint verbose = 0; 115145479Smp 11659243Sobrien#define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */ 11759243Sobrien 11859243Sobrien 11959243Sobrien#define VIRT_HDR_1 10 /* length of a base vnet-hdr */ 12059243Sobrien#define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ 12159243Sobrien#define VIRT_HDR_MAX VIRT_HDR_2 12259243Sobrienstruct virt_header { 123131962Smp uint8_t fields[VIRT_HDR_MAX]; 124100616Smp}; 125100616Smp 126131962Smpstruct pkt { 127131962Smp struct virt_header vh; 128131962Smp struct ether_header eh; 129131962Smp struct ip ip; 130100616Smp struct udphdr udp; 13159243Sobrien uint8_t body[2048]; // XXX hardwired 132100616Smp} __attribute__((__packed__)); 13359243Sobrien 13459243Sobrienstruct ip_range { 13559243Sobrien char *name; 13659243Sobrien uint32_t start, end; /* same as struct in_addr */ 13759243Sobrien uint16_t port0, port1; 13859243Sobrien}; 13959243Sobrien 14059243Sobrienstruct mac_range { 14161524Sobrien char *name; 14259243Sobrien struct ether_addr start, end; 14361524Sobrien}; 14459243Sobrien 14559243Sobrien/* ifname can be netmap:foo-xxxx */ 14661524Sobrien#define MAX_IFNAMELEN 64 /* our buffer for ifname */ 14759243Sobrien/* 14859243Sobrien * global arguments for all threads 14959243Sobrien */ 15059243Sobrien 15159243Sobrienstruct glob_arg { 15259243Sobrien struct ip_range src_ip; 15359243Sobrien struct ip_range dst_ip; 15459243Sobrien struct mac_range dst_mac; 15559243Sobrien struct mac_range src_mac; 15659243Sobrien int pkt_size; 15759243Sobrien int burst; 15859243Sobrien int forever; 15959243Sobrien int npackets; /* total packets to send */ 16059243Sobrien int frags; /* fragments per packet */ 16159243Sobrien int nthreads; 16259243Sobrien int cpus; 16359243Sobrien int options; /* testing */ 16459243Sobrien#define OPT_PREFETCH 1 16559243Sobrien#define OPT_ACCESS 2 16659243Sobrien#define OPT_COPY 4 16759243Sobrien#define OPT_MEMCPY 8 16859243Sobrien#define OPT_TS 16 /* add a timestamp */ 16959243Sobrien#define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 17059243Sobrien#define OPT_DUMP 64 /* dump rx/tx traffic */ 17159243Sobrien int dev_type; 17259243Sobrien#ifndef NO_PCAP 17359243Sobrien pcap_t *p; 17459243Sobrien#endif 17559243Sobrien 17659243Sobrien int tx_rate; 17759243Sobrien struct timespec tx_period; 178145479Smp 17959243Sobrien int affinity; 18059243Sobrien int main_fd; 18159243Sobrien struct nm_desc *nmd; 18259243Sobrien uint64_t nmd_flags; 18359243Sobrien int report_interval; /* milliseconds between prints */ 18459243Sobrien void *(*td_body)(void *); 18559243Sobrien void *mmap_addr; 18659243Sobrien char ifname[MAX_IFNAMELEN]; 18759243Sobrien char *nmr_config; 18859243Sobrien int dummy_send; 18959243Sobrien int virt_header; /* send also the virt_header */ 19059243Sobrien int extra_bufs; /* goes in nr_arg3 */ 19159243Sobrien}; 19259243Sobrienenum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 19359243Sobrien 19459243Sobrien 19559243Sobrien/* 19659243Sobrien * Arguments for a new thread. The same structure is used by 19759243Sobrien * the source and the sink 19859243Sobrien */ 19959243Sobrienstruct targ { 20059243Sobrien struct glob_arg *g; 20159243Sobrien int used; 20259243Sobrien int completed; 20369408Sache int cancel; 20459243Sobrien int fd; 20559243Sobrien struct nm_desc *nmd; 20659243Sobrien volatile uint64_t count; 20769408Sache struct timespec tic, toc; 20859243Sobrien int me; 20959243Sobrien pthread_t thread; 21083098Smp int affinity; 21159243Sobrien 21259243Sobrien struct pkt pkt; 21359243Sobrien}; 21459243Sobrien 21583098Smp 21659243Sobrien/* 21759243Sobrien * extract the extremes from a range of ipv4 addresses. 21859243Sobrien * addr_lo[-addr_hi][:port_lo[-port_hi]] 21959243Sobrien */ 22059243Sobrienstatic void 22159243Sobrienextract_ip_range(struct ip_range *r) 22259243Sobrien{ 22359243Sobrien char *ap, *pp; 22459243Sobrien struct in_addr a; 22559243Sobrien 22659243Sobrien if (verbose) 22759243Sobrien D("extract IP range from %s", r->name); 22869408Sache r->port0 = r->port1 = 0; 22959243Sobrien r->start = r->end = 0; 23069408Sache 23159243Sobrien /* the first - splits start/end of range */ 23259243Sobrien ap = index(r->name, '-'); /* do we have ports ? */ 23359243Sobrien if (ap) { 23459243Sobrien *ap++ = '\0'; 23559243Sobrien } 23659243Sobrien /* grab the initial values (mandatory) */ 23769408Sache pp = index(r->name, ':'); 23859243Sobrien if (pp) { 23969408Sache *pp++ = '\0'; 24059243Sobrien r->port0 = r->port1 = strtol(pp, NULL, 0); 24159243Sobrien }; 24259243Sobrien inet_aton(r->name, &a); 24359243Sobrien r->start = r->end = ntohl(a.s_addr); 24469408Sache if (ap) { 24559243Sobrien pp = index(ap, ':'); 24659243Sobrien if (pp) { 24759243Sobrien *pp++ = '\0'; 24859243Sobrien if (*pp) 24959243Sobrien r->port1 = strtol(pp, NULL, 0); 25059243Sobrien } 25159243Sobrien if (*ap) { 25259243Sobrien inet_aton(ap, &a); 25359243Sobrien r->end = ntohl(a.s_addr); 25459243Sobrien } 25559243Sobrien } 25659243Sobrien if (r->port0 > r->port1) { 25769408Sache uint16_t tmp = r->port0; 25859243Sobrien r->port0 = r->port1; 25959243Sobrien r->port1 = tmp; 26059243Sobrien } 26159243Sobrien if (r->start > r->end) { 26259243Sobrien uint32_t tmp = r->start; 26359243Sobrien r->start = r->end; 26459243Sobrien r->end = tmp; 26559243Sobrien } 26659243Sobrien { 26759243Sobrien struct in_addr a; 26859243Sobrien char buf1[16]; // one ip address 26959243Sobrien 27059243Sobrien a.s_addr = htonl(r->end); 27159243Sobrien strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 27259243Sobrien a.s_addr = htonl(r->start); 27359243Sobrien if (1) 27459243Sobrien D("range is %s:%d to %s:%d", 27559243Sobrien inet_ntoa(a), r->port0, buf1, r->port1); 27659243Sobrien } 27769408Sache} 27859243Sobrien 27959243Sobrienstatic void 28059243Sobrienextract_mac_range(struct mac_range *r) 28159243Sobrien{ 28259243Sobrien if (verbose) 28369408Sache D("extract MAC range from %s", r->name); 28459243Sobrien bcopy(ether_aton(r->name), &r->start, 6); 28559243Sobrien bcopy(ether_aton(r->name), &r->end, 6); 28659243Sobrien#if 0 28759243Sobrien bcopy(targ->src_mac, eh->ether_shost, 6); 28859243Sobrien p = index(targ->g->src_mac, '-'); 28959243Sobrien if (p) 29059243Sobrien targ->src_mac_range = atoi(p+1); 291145479Smp 29259243Sobrien bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 29359243Sobrien bcopy(targ->dst_mac, eh->ether_dhost, 6); 294145479Smp p = index(targ->g->dst_mac, '-'); 29559243Sobrien if (p) 29659243Sobrien targ->dst_mac_range = atoi(p+1); 29759243Sobrien#endif 29859243Sobrien if (verbose) 29959243Sobrien D("%s starts at %s", r->name, ether_ntoa(&r->start)); 30059243Sobrien} 30159243Sobrien 30259243Sobrienstatic struct targ *targs; 30359243Sobrienstatic int global_nthreads; 30459243Sobrien 305145479Smp/* control-C handler */ 30669408Sachestatic void 307167465Smpsigint_h(int sig) 30859243Sobrien{ 309145479Smp int i; 31059243Sobrien 31159243Sobrien (void)sig; /* UNUSED */ 31259243Sobrien for (i = 0; i < global_nthreads; i++) { 31359243Sobrien targs[i].cancel = 1; 31459243Sobrien } 31559243Sobrien signal(SIGINT, SIG_DFL); 31659243Sobrien} 31759243Sobrien 31859243Sobrien/* sysctl wrapper to return the number of active CPUs */ 31959243Sobrienstatic int 32059243Sobriensystem_ncpus(void) 32159243Sobrien{ 322167465Smp int ncpus; 32359243Sobrien#if defined (__FreeBSD__) 32459243Sobrien int mib[2] = { CTL_HW, HW_NCPU }; 32559243Sobrien size_t len = sizeof(mib); 32659243Sobrien sysctl(mib, 2, &ncpus, &len, NULL, 0); 32759243Sobrien#elif defined(linux) 32859243Sobrien ncpus = sysconf(_SC_NPROCESSORS_ONLN); 32959243Sobrien#else /* others */ 33059243Sobrien ncpus = 1; 33159243Sobrien#endif /* others */ 33269408Sache return (ncpus); 33359243Sobrien} 33459243Sobrien 33559243Sobrien#ifdef __linux__ 336167465Smp#define sockaddr_dl sockaddr_ll 33759243Sobrien#define sdl_family sll_family 338167465Smp#define AF_LINK AF_PACKET 33959243Sobrien#define LLADDR(s) s->sll_addr; 34059243Sobrien#include <linux/if_tun.h> 34159243Sobrien#define TAP_CLONEDEV "/dev/net/tun" 34259243Sobrien#endif /* __linux__ */ 343131962Smp 344131962Smp#ifdef __FreeBSD__ 345131962Smp#include <net/if_tun.h> 34659243Sobrien#define TAP_CLONEDEV "/dev/tap" 34759243Sobrien#endif /* __FreeBSD */ 34859243Sobrien 34959243Sobrien#ifdef __APPLE__ 35059243Sobrien// #warning TAP not supported on apple ? 351167465Smp#include <net/if_utun.h> 35259243Sobrien#define TAP_CLONEDEV "/dev/tap" 353145479Smp#endif /* __APPLE__ */ 35459243Sobrien 35559243Sobrien 356145479Smp/* 35759243Sobrien * parse the vale configuration in conf and put it in nmr. 35859243Sobrien * Return the flag set if necessary. 35959243Sobrien * The configuration may consist of 0 to 4 numbers separated 36059243Sobrien * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 36159243Sobrien * Missing numbers or zeroes stand for default values. 362145479Smp * As an additional convenience, if exactly one number 363145479Smp * is specified, then this is assigned to both #tx-slots and #rx-slots. 364145479Smp * If there is no 4th number, then the 3rd is assigned to both #tx-rings 365145479Smp * and #rx-rings. 366167465Smp */ 36759243Sobrienint 368167465Smpparse_nmr_config(const char* conf, struct nmreq *nmr) 36969408Sache{ 37059243Sobrien char *w, *tok; 37159243Sobrien int i, v; 37269408Sache 373145479Smp nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 37459243Sobrien nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 375145479Smp if (conf == NULL || ! *conf) 376145479Smp return 0; 37759243Sobrien w = strdup(conf); 378145479Smp for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 37959243Sobrien v = atoi(tok); 38059243Sobrien switch (i) { 38159243Sobrien case 0: 38259243Sobrien nmr->nr_tx_slots = nmr->nr_rx_slots = v; 38359243Sobrien break; 38459243Sobrien case 1: 38559243Sobrien nmr->nr_rx_slots = v; 38659243Sobrien break; 38759243Sobrien case 2: 38859243Sobrien nmr->nr_tx_rings = nmr->nr_rx_rings = v; 38959243Sobrien break; 39059243Sobrien case 3: 39159243Sobrien nmr->nr_rx_rings = v; 39259243Sobrien break; 39359243Sobrien default: 39459243Sobrien D("ignored config: %s", tok); 39559243Sobrien break; 39659243Sobrien } 39759243Sobrien } 39859243Sobrien D("txr %d txd %d rxr %d rxd %d", 39959243Sobrien nmr->nr_tx_rings, nmr->nr_tx_slots, 40059243Sobrien nmr->nr_rx_rings, nmr->nr_rx_slots); 40159243Sobrien free(w); 40259243Sobrien return (nmr->nr_tx_rings || nmr->nr_tx_slots || 403145479Smp nmr->nr_rx_rings || nmr->nr_rx_slots) ? 404100616Smp NM_OPEN_RING_CFG : 0; 405100616Smp} 406167465Smp 407167465Smp 408167465Smp/* 409100616Smp * locate the src mac address for our interface, put it 410100616Smp * into the user-supplied buffer. return 0 if ok, -1 on error. 41169408Sache */ 41259243Sobrienstatic int 41359243Sobriensource_hwaddr(const char *ifname, char *buf) 41459243Sobrien{ 41559243Sobrien struct ifaddrs *ifaphead, *ifap; 41659243Sobrien int l = sizeof(ifap->ifa_name); 41759243Sobrien 41859243Sobrien if (getifaddrs(&ifaphead) != 0) { 41959243Sobrien D("getifaddrs %s failed", ifname); 42059243Sobrien return (-1); 42159243Sobrien } 42259243Sobrien 42359243Sobrien for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 42459243Sobrien struct sockaddr_dl *sdl = 42559243Sobrien (struct sockaddr_dl *)ifap->ifa_addr; 42659243Sobrien uint8_t *mac; 42759243Sobrien 42859243Sobrien if (!sdl || sdl->sdl_family != AF_LINK) 42959243Sobrien continue; 43059243Sobrien if (strncmp(ifap->ifa_name, ifname, l) != 0) 43159243Sobrien continue; 43259243Sobrien mac = (uint8_t *)LLADDR(sdl); 43359243Sobrien sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 43469408Sache mac[0], mac[1], mac[2], 43569408Sache mac[3], mac[4], mac[5]); 43669408Sache if (verbose) 43769408Sache D("source hwaddr %s", buf); 43869408Sache break; 43969408Sache } 44069408Sache freeifaddrs(ifaphead); 44169408Sache return ifap ? 0 : 1; 44269408Sache} 44359243Sobrien 44459243Sobrien 445167465Smp/* set the thread affinity. */ 446145479Smpstatic int 447167465Smpsetaffinity(pthread_t me, int i) 448145479Smp{ 449167465Smp cpuset_t cpumask; 450145479Smp 451145479Smp if (i == -1) 45259243Sobrien return 0; 45359243Sobrien 45459243Sobrien /* Set thread affinity affinity.*/ 45559243Sobrien CPU_ZERO(&cpumask); 45659243Sobrien CPU_SET(i, &cpumask); 45759243Sobrien 45859243Sobrien if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 45959243Sobrien D("Unable to set affinity: %s", strerror(errno)); 46059243Sobrien return 1; 46159243Sobrien } 46259243Sobrien return 0; 46359243Sobrien} 46459243Sobrien 46559243Sobrien/* Compute the checksum of the given ip header. */ 46659243Sobrienstatic uint16_t 46759243Sobrienchecksum(const void *data, uint16_t len, uint32_t sum) 46859243Sobrien{ 46959243Sobrien const uint8_t *addr = data; 47059243Sobrien uint32_t i; 471167465Smp 472167465Smp /* Checksum all the pairs of bytes first... */ 473167465Smp for (i = 0; i < (len & ~1U); i += 2) { 474167465Smp sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 475167465Smp if (sum > 0xFFFF) 47659243Sobrien sum -= 0xFFFF; 477167465Smp } 478167465Smp /* 479167465Smp * If there's a single byte left over, checksum it, too. 480167465Smp * Network byte order is big-endian, so the remaining byte is 481167465Smp * the high byte. 48259243Sobrien */ 48359243Sobrien if (i < len) { 48459243Sobrien sum += addr[i] << 8; 48559243Sobrien if (sum > 0xFFFF) 48659243Sobrien sum -= 0xFFFF; 48759243Sobrien } 48859243Sobrien return sum; 48959243Sobrien} 49059243Sobrien 49159243Sobrienstatic u_int16_t 49259243Sobrienwrapsum(u_int32_t sum) 49359243Sobrien{ 49459243Sobrien sum = ~sum & 0xFFFF; 49559243Sobrien return (htons(sum)); 49659243Sobrien} 49759243Sobrien 49859243Sobrien/* Check the payload of the packet for errors (use it for debug). 49959243Sobrien * Look for consecutive ascii representations of the size of the packet. 50059243Sobrien */ 50159243Sobrienstatic void 50259243Sobriendump_payload(char *p, int len, struct netmap_ring *ring, int cur) 50359243Sobrien{ 50459243Sobrien char buf[128]; 50559243Sobrien int i, j, i0; 50659243Sobrien 50759243Sobrien /* get the length in ASCII of the length of the packet. */ 50859243Sobrien 509167465Smp printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 510167465Smp ring, cur, ring->slot[cur].buf_idx, 511167465Smp ring->slot[cur].flags, len); 51259243Sobrien /* hexdump routine */ 51359243Sobrien for (i = 0; i < len; ) { 51483098Smp memset(buf, sizeof(buf), ' '); 51559243Sobrien sprintf(buf, "%5d: ", i); 51659243Sobrien i0 = i; 51759243Sobrien for (j=0; j < 16 && i < len; i++, j++) 51859243Sobrien sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 51959243Sobrien i = i0; 52059243Sobrien for (j=0; j < 16 && i < len; i++, j++) 52159243Sobrien sprintf(buf+7+j + 48, "%c", 52259243Sobrien isprint(p[i]) ? p[i] : '.'); 52359243Sobrien printf("%s\n", buf); 52459243Sobrien } 52559243Sobrien} 52659243Sobrien 52759243Sobrien/* 52859243Sobrien * Fill a packet with some payload. 52959243Sobrien * We create a UDP packet so the payload starts at 530145479Smp * 14+20+8 = 42 bytes. 53159243Sobrien */ 53259243Sobrien#ifdef __linux__ 53359243Sobrien#define uh_sport source 53459243Sobrien#define uh_dport dest 535145479Smp#define uh_ulen len 53659243Sobrien#define uh_sum check 53759243Sobrien#endif /* linux */ 538145479Smp 539145479Smp/* 540145479Smp * increment the addressed in the packet, 541145479Smp * starting from the least significant field. 542145479Smp * DST_IP DST_PORT SRC_IP SRC_PORT 543145479Smp */ 544145479Smpstatic void 545145479Smpupdate_addresses(struct pkt *pkt, struct glob_arg *g) 546145479Smp{ 547145479Smp uint32_t a; 548145479Smp uint16_t p; 549145479Smp struct ip *ip = &pkt->ip; 550145479Smp struct udphdr *udp = &pkt->udp; 551145479Smp 552145479Smp do { 553145479Smp p = ntohs(udp->uh_sport); 554145479Smp if (p < g->src_ip.port1) { /* just inc, no wrap */ 555145479Smp udp->uh_sport = htons(p + 1); 556145479Smp break; 557145479Smp } 558145479Smp udp->uh_sport = htons(g->src_ip.port0); 559145479Smp 56059243Sobrien a = ntohl(ip->ip_src.s_addr); 561145479Smp if (a < g->src_ip.end) { /* just inc, no wrap */ 562167465Smp ip->ip_src.s_addr = htonl(a + 1); 563167465Smp break; 564145479Smp } 565145479Smp ip->ip_src.s_addr = htonl(g->src_ip.start); 566145479Smp 56759243Sobrien udp->uh_sport = htons(g->src_ip.port0); 56859243Sobrien p = ntohs(udp->uh_dport); 56959243Sobrien if (p < g->dst_ip.port1) { /* just inc, no wrap */ 57059243Sobrien udp->uh_dport = htons(p + 1); 57159243Sobrien break; 57259243Sobrien } 57359243Sobrien udp->uh_dport = htons(g->dst_ip.port0); 574145479Smp 57559243Sobrien a = ntohl(ip->ip_dst.s_addr); 57659243Sobrien if (a < g->dst_ip.end) { /* just inc, no wrap */ 577145479Smp ip->ip_dst.s_addr = htonl(a + 1); 57859243Sobrien break; 57969408Sache } 58069408Sache ip->ip_dst.s_addr = htonl(g->dst_ip.start); 581167465Smp } while (0); 582167465Smp // update checksum 58359243Sobrien} 58459243Sobrien 58559243Sobrien/* 58659243Sobrien * initialize one packet and prepare for the next one. 58759243Sobrien * The copy could be done better instead of repeating it each time. 58859243Sobrien */ 58959243Sobrienstatic void 59059243Sobrieninitialize_packet(struct targ *targ) 59159243Sobrien{ 59259243Sobrien struct pkt *pkt = &targ->pkt; 59359243Sobrien struct ether_header *eh; 59459243Sobrien struct ip *ip; 59559243Sobrien struct udphdr *udp; 59659243Sobrien uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 59759243Sobrien const char *payload = targ->g->options & OPT_INDIRECT ? 59859243Sobrien indirect_payload : default_payload; 59959243Sobrien int i, l0 = strlen(payload); 600167465Smp 60159243Sobrien /* create a nice NUL-terminated string */ 60259243Sobrien for (i = 0; i < paylen; i += l0) { 60359243Sobrien if (l0 > paylen - i) 60459243Sobrien l0 = paylen - i; // last round 60559243Sobrien bcopy(payload, pkt->body + i, l0); 60659243Sobrien } 60759243Sobrien pkt->body[i-1] = '\0'; 60859243Sobrien ip = &pkt->ip; 60959243Sobrien 61059243Sobrien /* prepare the headers */ 611167465Smp ip->ip_v = IPVERSION; 61259243Sobrien ip->ip_hl = 5; 61359243Sobrien ip->ip_id = 0; 61459243Sobrien ip->ip_tos = IPTOS_LOWDELAY; 61559243Sobrien ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 61659243Sobrien ip->ip_id = 0; 61759243Sobrien ip->ip_off = htons(IP_DF); /* Don't fragment */ 61859243Sobrien ip->ip_ttl = IPDEFTTL; 619145479Smp ip->ip_p = IPPROTO_UDP; 620145479Smp ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 621145479Smp ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 62259243Sobrien ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 62359243Sobrien 62459243Sobrien 62559243Sobrien udp = &pkt->udp; 626167465Smp udp->uh_sport = htons(targ->g->src_ip.port0); 627167465Smp udp->uh_dport = htons(targ->g->dst_ip.port0); 62859243Sobrien udp->uh_ulen = htons(paylen); 62959243Sobrien /* Magic: taken from sbin/dhclient/packet.c */ 63059243Sobrien udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 63159243Sobrien checksum(pkt->body, 63259243Sobrien paylen - sizeof(*udp), 63359243Sobrien checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 63459243Sobrien IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 63559243Sobrien ) 63659243Sobrien ) 63759243Sobrien )); 63859243Sobrien 63959243Sobrien eh = &pkt->eh; 64059243Sobrien bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 64159243Sobrien bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 64259243Sobrien eh->ether_type = htons(ETHERTYPE_IP); 64359243Sobrien 64459243Sobrien bzero(&pkt->vh, sizeof(pkt->vh)); 64559243Sobrien // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 64659243Sobrien} 64759243Sobrien 64859243Sobrien 64959243Sobrien 65059243Sobrien/* 65159243Sobrien * create and enqueue a batch of packets on a ring. 65259243Sobrien * On the last one set NS_REPORT to tell the driver to generate 65359243Sobrien * an interrupt when done. 65459243Sobrien */ 65559243Sobrienstatic int 65659243Sobriensend_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 65759243Sobrien int size, struct glob_arg *g, u_int count, int options, 65859243Sobrien u_int nfrags) 659167465Smp{ 660167465Smp u_int n, sent, cur = ring->cur; 661167465Smp u_int fcnt; 662167465Smp 66359243Sobrien n = nm_ring_space(ring); 66459243Sobrien if (n < count) 665167465Smp count = n; 666167465Smp if (count < nfrags) { 667167465Smp D("truncating packet, no room for frags %d %d", 66859243Sobrien count, nfrags); 669167465Smp } 670167465Smp#if 0 67159243Sobrien if (options & (OPT_COPY | OPT_PREFETCH) ) { 672167465Smp for (sent = 0; sent < count; sent++) { 67359243Sobrien struct netmap_slot *slot = &ring->slot[cur]; 67459243Sobrien char *p = NETMAP_BUF(ring, slot->buf_idx); 67559243Sobrien 67659243Sobrien __builtin_prefetch(p); 67759243Sobrien cur = nm_ring_next(ring, cur); 678167465Smp } 679167465Smp cur = ring->cur; 68059243Sobrien } 68159243Sobrien#endif 68259243Sobrien for (fcnt = nfrags, sent = 0; sent < count; sent++) { 68359243Sobrien struct netmap_slot *slot = &ring->slot[cur]; 68459243Sobrien char *p = NETMAP_BUF(ring, slot->buf_idx); 68559243Sobrien 68659243Sobrien slot->flags = 0; 68759243Sobrien if (options & OPT_INDIRECT) { 68859243Sobrien slot->flags |= NS_INDIRECT; 68959243Sobrien slot->ptr = (uint64_t)frame; 690145479Smp } else if (options & OPT_COPY) { 691145479Smp nm_pkt_copy(frame, p, size); 692145479Smp if (fcnt == nfrags) 693145479Smp update_addresses(pkt, g); 694145479Smp } else if (options & OPT_MEMCPY) { 695145479Smp memcpy(p, frame, size); 696145479Smp if (fcnt == nfrags) 697145479Smp update_addresses(pkt, g); 698145479Smp } else if (options & OPT_PREFETCH) { 699145479Smp __builtin_prefetch(p); 700145479Smp } 701145479Smp if (options & OPT_DUMP) 70259243Sobrien dump_payload(p, size, ring, cur); 703145479Smp slot->len = size; 70459243Sobrien if (--fcnt > 0) 70559243Sobrien slot->flags |= NS_MOREFRAG; 70659243Sobrien else 70759243Sobrien fcnt = nfrags; 70859243Sobrien if (sent == count - 1) { 709145479Smp slot->flags &= ~NS_MOREFRAG; 71059243Sobrien slot->flags |= NS_REPORT; 71159243Sobrien } 71259243Sobrien cur = nm_ring_next(ring, cur); 71359243Sobrien } 71459243Sobrien ring->head = ring->cur = cur; 71559243Sobrien 71659243Sobrien return (sent); 71759243Sobrien} 71859243Sobrien 719145479Smp/* 72059243Sobrien * Send a packet, and wait for a response. 721167465Smp * The payload (after UDP header, ofs 42) has a 4-byte sequence 722145479Smp * followed by a struct timeval (or bintime?) 72359243Sobrien */ 72459243Sobrien#define PAY_OFS 42 /* where in the pkt... */ 72559243Sobrien 72659243Sobrienstatic void * 72759243Sobrienpinger_body(void *data) 72859243Sobrien{ 72959243Sobrien struct targ *targ = (struct targ *) data; 73059243Sobrien struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 73159243Sobrien struct netmap_if *nifp = targ->nmd->nifp; 732167465Smp int i, rx = 0, n = targ->g->npackets; 733167465Smp void *frame; 734167465Smp int size; 735167465Smp uint32_t sent = 0; 736167465Smp struct timespec ts, now, last_print; 737167465Smp uint32_t count = 0, min = 1000000000, av = 0; 738167465Smp 73959243Sobrien frame = &targ->pkt; 74059243Sobrien frame += sizeof(targ->pkt.vh) - targ->g->virt_header; 741145479Smp size = targ->g->pkt_size + targ->g->virt_header; 74259243Sobrien 74359243Sobrien 74459243Sobrien if (targ->g->nthreads > 1) { 74559243Sobrien D("can only ping with 1 thread"); 746145479Smp return NULL; 747145479Smp } 748145479Smp 749145479Smp clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 75059243Sobrien now = last_print; 75159243Sobrien while (n == 0 || (int)sent < n) { 75259243Sobrien struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 75359243Sobrien struct netmap_slot *slot; 75459243Sobrien char *p; 75559243Sobrien for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ 75659243Sobrien slot = &ring->slot[ring->cur]; 75769408Sache slot->len = size; 75869408Sache p = NETMAP_BUF(ring, slot->buf_idx); 75969408Sache 76069408Sache if (nm_ring_empty(ring)) { 76159243Sobrien D("-- ouch, cannot send"); 762167465Smp } else { 76359243Sobrien nm_pkt_copy(frame, p, size); 76459243Sobrien clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 76559243Sobrien bcopy(&sent, p+42, sizeof(sent)); 76659243Sobrien bcopy(&ts, p+46, sizeof(ts)); 76759243Sobrien sent++; 76859243Sobrien ring->head = ring->cur = nm_ring_next(ring, ring->cur); 76959243Sobrien } 77059243Sobrien } 77159243Sobrien /* should use a parameter to decide how often to send */ 77259243Sobrien if (poll(&pfd, 1, 3000) <= 0) { 77359243Sobrien D("poll error/timeout on queue %d: %s", targ->me, 77459243Sobrien strerror(errno)); 77559243Sobrien continue; 77659243Sobrien } 77759243Sobrien /* see what we got back */ 77859243Sobrien for (i = targ->nmd->first_tx_ring; 77959243Sobrien i <= targ->nmd->last_tx_ring; i++) { 780145479Smp ring = NETMAP_RXRING(nifp, i); 78159243Sobrien while (!nm_ring_empty(ring)) { 78259243Sobrien uint32_t seq; 78359243Sobrien slot = &ring->slot[ring->cur]; 78459243Sobrien p = NETMAP_BUF(ring, slot->buf_idx); 78559243Sobrien 78659243Sobrien clock_gettime(CLOCK_REALTIME_PRECISE, &now); 78759243Sobrien bcopy(p+42, &seq, sizeof(seq)); 78859243Sobrien bcopy(p+46, &ts, sizeof(ts)); 789145479Smp ts.tv_sec = now.tv_sec - ts.tv_sec; 79059243Sobrien ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 79159243Sobrien if (ts.tv_nsec < 0) { 79259243Sobrien ts.tv_nsec += 1000000000; 79359243Sobrien ts.tv_sec--; 79459243Sobrien } 79559243Sobrien if (1) D("seq %d/%d delta %d.%09d", seq, sent, 79659243Sobrien (int)ts.tv_sec, (int)ts.tv_nsec); 79759243Sobrien if (ts.tv_nsec < (int)min) 79859243Sobrien min = ts.tv_nsec; 79959243Sobrien count ++; 80059243Sobrien av += ts.tv_nsec; 80159243Sobrien ring->head = ring->cur = nm_ring_next(ring, ring->cur); 80259243Sobrien rx++; 80359243Sobrien } 80459243Sobrien } 80559243Sobrien //D("tx %d rx %d", sent, rx); 80659243Sobrien //usleep(100000); 80759243Sobrien ts.tv_sec = now.tv_sec - last_print.tv_sec; 80859243Sobrien ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 80959243Sobrien if (ts.tv_nsec < 0) { 81059243Sobrien ts.tv_nsec += 1000000000; 81159243Sobrien ts.tv_sec--; 81259243Sobrien } 81359243Sobrien if (ts.tv_sec >= 1) { 81459243Sobrien D("count %d min %d av %d", 81559243Sobrien count, min, av/count); 81659243Sobrien count = 0; 81759243Sobrien av = 0; 81859243Sobrien min = 100000000; 81959243Sobrien last_print = now; 82059243Sobrien } 821167465Smp } 822167465Smp return NULL; 82359243Sobrien} 82459243Sobrien 82559243Sobrien 82659243Sobrien/* 82759243Sobrien * reply to ping requests 82859243Sobrien */ 82959243Sobrienstatic void * 83059243Sobrienponger_body(void *data) 83159243Sobrien{ 83259243Sobrien struct targ *targ = (struct targ *) data; 83359243Sobrien struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 83459243Sobrien struct netmap_if *nifp = targ->nmd->nifp; 83559243Sobrien struct netmap_ring *txring, *rxring; 83659243Sobrien int i, rx = 0, sent = 0, n = targ->g->npackets; 83759243Sobrien 83859243Sobrien if (targ->g->nthreads > 1) { 83959243Sobrien D("can only reply ping with 1 thread"); 84059243Sobrien return NULL; 84159243Sobrien } 84259243Sobrien D("understood ponger %d but don't know how to do it", n); 84359243Sobrien while (n == 0 || sent < n) { 84459243Sobrien uint32_t txcur, txavail; 84559243Sobrien//#define BUSYWAIT 84659243Sobrien#ifdef BUSYWAIT 84759243Sobrien ioctl(pfd.fd, NIOCRXSYNC, NULL); 84859243Sobrien#else 84959243Sobrien if (poll(&pfd, 1, 1000) <= 0) { 85059243Sobrien D("poll error/timeout on queue %d: %s", targ->me, 85159243Sobrien strerror(errno)); 85259243Sobrien continue; 85359243Sobrien } 85459243Sobrien#endif 85559243Sobrien txring = NETMAP_TXRING(nifp, 0); 85659243Sobrien txcur = txring->cur; 85759243Sobrien txavail = nm_ring_space(txring); 85859243Sobrien /* see what we got back */ 85959243Sobrien for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 86059243Sobrien rxring = NETMAP_RXRING(nifp, i); 86159243Sobrien while (!nm_ring_empty(rxring)) { 86259243Sobrien uint16_t *spkt, *dpkt; 86359243Sobrien uint32_t cur = rxring->cur; 86459243Sobrien struct netmap_slot *slot = &rxring->slot[cur]; 86559243Sobrien char *src, *dst; 86659243Sobrien src = NETMAP_BUF(rxring, slot->buf_idx); 86759243Sobrien //D("got pkt %p of size %d", src, slot->len); 86859243Sobrien rxring->head = rxring->cur = nm_ring_next(rxring, cur); 86959243Sobrien rx++; 87059243Sobrien if (txavail == 0) 87159243Sobrien continue; 87259243Sobrien dst = NETMAP_BUF(txring, 87359243Sobrien txring->slot[txcur].buf_idx); 87459243Sobrien /* copy... */ 87559243Sobrien dpkt = (uint16_t *)dst; 87659243Sobrien spkt = (uint16_t *)src; 87759243Sobrien nm_pkt_copy(src, dst, slot->len); 87859243Sobrien dpkt[0] = spkt[3]; 87959243Sobrien dpkt[1] = spkt[4]; 88059243Sobrien dpkt[2] = spkt[5]; 88159243Sobrien dpkt[3] = spkt[0]; 88259243Sobrien dpkt[4] = spkt[1]; 88359243Sobrien dpkt[5] = spkt[2]; 88459243Sobrien txring->slot[txcur].len = slot->len; 88559243Sobrien /* XXX swap src dst mac */ 88659243Sobrien txcur = nm_ring_next(txring, txcur); 88759243Sobrien txavail--; 88859243Sobrien sent++; 88959243Sobrien } 89059243Sobrien } 89159243Sobrien txring->head = txring->cur = txcur; 89259243Sobrien targ->count = sent; 89359243Sobrien#ifdef BUSYWAIT 89459243Sobrien ioctl(pfd.fd, NIOCTXSYNC, NULL); 89559243Sobrien#endif 89659243Sobrien //D("tx %d rx %d", sent, rx); 89759243Sobrien } 89859243Sobrien return NULL; 89959243Sobrien} 90059243Sobrien 90159243Sobrienstatic __inline int 90259243Sobrientimespec_ge(const struct timespec *a, const struct timespec *b) 90359243Sobrien{ 90459243Sobrien 90559243Sobrien if (a->tv_sec > b->tv_sec) 90659243Sobrien return (1); 90759243Sobrien if (a->tv_sec < b->tv_sec) 90859243Sobrien return (0); 90959243Sobrien if (a->tv_nsec >= b->tv_nsec) 91059243Sobrien return (1); 91159243Sobrien return (0); 91259243Sobrien} 91359243Sobrien 91459243Sobrienstatic __inline struct timespec 91559243Sobrientimeval2spec(const struct timeval *a) 91659243Sobrien{ 91759243Sobrien struct timespec ts = { 918167465Smp .tv_sec = a->tv_sec, 91959243Sobrien .tv_nsec = a->tv_usec * 1000 92059243Sobrien }; 921167465Smp return ts; 922145479Smp} 92359243Sobrien 92459243Sobrienstatic __inline struct timeval 92559243Sobrientimespec2val(const struct timespec *a) 92659243Sobrien{ 92769408Sache struct timeval tv = { 92859243Sobrien .tv_sec = a->tv_sec, 92959243Sobrien .tv_usec = a->tv_nsec / 1000 93069408Sache }; 931145479Smp return tv; 93259243Sobrien} 93359243Sobrien 934145479Smp 935145479Smpstatic __inline struct timespec 936145479Smptimespec_add(struct timespec a, struct timespec b) 93759243Sobrien{ 93859243Sobrien struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; 93959243Sobrien if (ret.tv_nsec >= 1000000000) { 94059243Sobrien ret.tv_sec++; 94159243Sobrien ret.tv_nsec -= 1000000000; 94259243Sobrien } 94359243Sobrien return ret; 94459243Sobrien} 94559243Sobrien 94659243Sobrienstatic __inline struct timespec 94759243Sobrientimespec_sub(struct timespec a, struct timespec b) 94859243Sobrien{ 94959243Sobrien struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; 95059243Sobrien if (ret.tv_nsec < 0) { 95159243Sobrien ret.tv_sec--; 95259243Sobrien ret.tv_nsec += 1000000000; 95359243Sobrien } 95459243Sobrien return ret; 95559243Sobrien} 95659243Sobrien 95759243Sobrien 95859243Sobrien/* 95959243Sobrien * wait until ts, either busy or sleeping if more than 1ms. 96059243Sobrien * Return wakeup time. 96159243Sobrien */ 96259243Sobrienstatic struct timespec 96359243Sobrienwait_time(struct timespec ts) 96459243Sobrien{ 96559243Sobrien for (;;) { 96659243Sobrien struct timespec w, cur; 96759243Sobrien clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 96859243Sobrien w = timespec_sub(ts, cur); 96959243Sobrien if (w.tv_sec < 0) 97059243Sobrien return cur; 97159243Sobrien else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 97259243Sobrien poll(NULL, 0, 1); 97359243Sobrien } 97459243Sobrien} 97559243Sobrien 97659243Sobrienstatic void * 97759243Sobriensender_body(void *data) 97859243Sobrien{ 97959243Sobrien struct targ *targ = (struct targ *) data; 98059243Sobrien struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 98159243Sobrien struct netmap_if *nifp = targ->nmd->nifp; 98259243Sobrien struct netmap_ring *txring; 98359243Sobrien int i, n = targ->g->npackets / targ->g->nthreads; 98459243Sobrien int64_t sent = 0; 98559243Sobrien int options = targ->g->options | OPT_COPY; 98659243Sobrien struct timespec nexttime = { 0, 0}; // XXX silence compiler 98759243Sobrien int rate_limit = targ->g->tx_rate; 98859243Sobrien struct pkt *pkt = &targ->pkt; 98959243Sobrien void *frame; 99059243Sobrien int size; 991131962Smp 992131962Smp frame = pkt; 993131962Smp frame += sizeof(pkt->vh) - targ->g->virt_header; 994131962Smp size = targ->g->pkt_size + targ->g->virt_header; 99559243Sobrien 996167465Smp D("start"); 997167465Smp if (setaffinity(targ->thread, targ->affinity)) 998167465Smp goto quit; 999167465Smp 1000167465Smp /* main loop.*/ 100159243Sobrien clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 100259243Sobrien if (rate_limit) { 100359243Sobrien targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 100459243Sobrien targ->tic.tv_nsec = 0; 100559243Sobrien wait_time(targ->tic); 100659243Sobrien nexttime = targ->tic; 100759243Sobrien } 1008145479Smp if (targ->g->dev_type == DEV_TAP) { 1009145479Smp D("writing to file desc %d", targ->g->main_fd); 1010145479Smp 1011145479Smp for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 101259243Sobrien if (write(targ->g->main_fd, frame, size) != -1) 101359243Sobrien sent++; 101459243Sobrien update_addresses(pkt, targ->g); 101559243Sobrien if (i > 10000) { 101659243Sobrien targ->count = sent; 101759243Sobrien i = 0; 101859243Sobrien } 101959243Sobrien } 102059243Sobrien#ifndef NO_PCAP 102159243Sobrien } else if (targ->g->dev_type == DEV_PCAP) { 102259243Sobrien pcap_t *p = targ->g->p; 102359243Sobrien 102459243Sobrien for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 102559243Sobrien if (pcap_inject(p, frame, size) != -1) 102659243Sobrien sent++; 102759243Sobrien update_addresses(pkt, targ->g); 102859243Sobrien if (i > 10000) { 102959243Sobrien targ->count = sent; 103059243Sobrien i = 0; 103159243Sobrien } 103259243Sobrien } 103359243Sobrien#endif /* NO_PCAP */ 103459243Sobrien } else { 103559243Sobrien int tosend = 0; 103659243Sobrien int frags = targ->g->frags; 103759243Sobrien 103859243Sobrien while (!targ->cancel && (n == 0 || sent < n)) { 103959243Sobrien 104059243Sobrien if (rate_limit && tosend <= 0) { 104159243Sobrien tosend = targ->g->burst; 104259243Sobrien nexttime = timespec_add(nexttime, targ->g->tx_period); 104359243Sobrien wait_time(nexttime); 104459243Sobrien } 104559243Sobrien 104659243Sobrien /* 104759243Sobrien * wait for available room in the send queue(s) 1048145479Smp */ 1049145479Smp if (poll(&pfd, 1, 2000) <= 0) { 1050145479Smp if (targ->cancel) 1051145479Smp break; 1052145479Smp D("poll error/timeout on queue %d: %s", targ->me, 1053145479Smp strerror(errno)); 105459243Sobrien // goto quit; 105559243Sobrien } 105659243Sobrien if (pfd.revents & POLLERR) { 105759243Sobrien D("poll error"); 105859243Sobrien goto quit; 105959243Sobrien } 106059243Sobrien /* 106159243Sobrien * scan our queues and send on those with room 106259243Sobrien */ 106359243Sobrien if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 106459243Sobrien D("drop copy"); 106559243Sobrien options &= ~OPT_COPY; 106659243Sobrien } 106759243Sobrien for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 106859243Sobrien int m, limit = rate_limit ? tosend : targ->g->burst; 106959243Sobrien if (n > 0 && n - sent < limit) 107059243Sobrien limit = n - sent; 1071131962Smp txring = NETMAP_TXRING(nifp, i); 107259243Sobrien if (nm_ring_empty(txring)) 107359243Sobrien continue; 1074167465Smp if (frags > 1) 107559243Sobrien limit = ((limit + frags - 1) / frags) * frags; 107659243Sobrien 107759243Sobrien m = send_packets(txring, pkt, frame, size, targ->g, 107859243Sobrien limit, options, frags); 107959243Sobrien ND("limit %d tail %d frags %d m %d", 108059243Sobrien limit, txring->tail, frags, m); 108159243Sobrien sent += m; 1082167465Smp targ->count = sent; 108359243Sobrien if (rate_limit) { 1084145479Smp tosend -= m; 1085145479Smp if (tosend <= 0) 1086145479Smp break; 1087145479Smp } 1088145479Smp } 1089145479Smp } 1090145479Smp /* flush any remaining packets */ 1091145479Smp ioctl(pfd.fd, NIOCTXSYNC, NULL); 1092145479Smp 1093145479Smp /* final part: wait all the TX queues to be empty. */ 1094145479Smp for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 109559243Sobrien txring = NETMAP_TXRING(nifp, i); 109659243Sobrien while (nm_tx_pending(txring)) { 109759243Sobrien ioctl(pfd.fd, NIOCTXSYNC, NULL); 109859243Sobrien usleep(1); /* wait 1 tick */ 109959243Sobrien } 110059243Sobrien } 110159243Sobrien } /* end DEV_NETMAP */ 110259243Sobrien 110359243Sobrien clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1104145479Smp targ->completed = 1; 1105131962Smp targ->count = sent; 110659243Sobrien 110759243Sobrienquit: 1108167465Smp /* reset the ``used`` flag. */ 110959243Sobrien targ->used = 0; 111059243Sobrien 111159243Sobrien return (NULL); 111259243Sobrien} 111359243Sobrien 111459243Sobrien 111559243Sobrien#ifndef NO_PCAP 111659243Sobrienstatic void 1117145479Smpreceive_pcap(u_char *user, const struct pcap_pkthdr * h, 111859243Sobrien const u_char * bytes) 111959243Sobrien{ 112059243Sobrien int *count = (int *)user; 112159243Sobrien (void)h; /* UNUSED */ 112259243Sobrien (void)bytes; /* UNUSED */ 112359243Sobrien (*count)++; 112459243Sobrien} 112559243Sobrien#endif /* !NO_PCAP */ 112659243Sobrien 112759243Sobrienstatic int 112859243Sobrienreceive_packets(struct netmap_ring *ring, u_int limit, int dump) 112959243Sobrien{ 113059243Sobrien u_int cur, rx, n; 113159243Sobrien 1132145479Smp cur = ring->cur; 1133145479Smp n = nm_ring_space(ring); 1134145479Smp if (n < limit) 113559243Sobrien limit = n; 113659243Sobrien for (rx = 0; rx < limit; rx++) { 113759243Sobrien struct netmap_slot *slot = &ring->slot[cur]; 113859243Sobrien char *p = NETMAP_BUF(ring, slot->buf_idx); 113959243Sobrien 114059243Sobrien if (dump) 114159243Sobrien dump_payload(p, slot->len, ring, cur); 114259243Sobrien 114359243Sobrien cur = nm_ring_next(ring, cur); 114459243Sobrien } 114559243Sobrien ring->head = ring->cur = cur; 114659243Sobrien 114759243Sobrien return (rx); 114859243Sobrien} 114959243Sobrien 115059243Sobrienstatic void * 115159243Sobrienreceiver_body(void *data) 115259243Sobrien{ 115359243Sobrien struct targ *targ = (struct targ *) data; 115459243Sobrien struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 115559243Sobrien struct netmap_if *nifp = targ->nmd->nifp; 115659243Sobrien struct netmap_ring *rxring; 115759243Sobrien int i; 1158145479Smp uint64_t received = 0; 1159145479Smp 1160145479Smp if (setaffinity(targ->thread, targ->affinity)) 1161145479Smp goto quit; 1162145479Smp 1163145479Smp /* unbounded wait for the first packet. */ 1164145479Smp for (;;) { 1165145479Smp i = poll(&pfd, 1, 1000); 1166145479Smp if (i > 0 && !(pfd.revents & POLLERR)) 1167145479Smp break; 1168145479Smp RD(1, "waiting for initial packets, poll returns %d %d", 1169145479Smp i, pfd.revents); 1170145479Smp } 1171145479Smp 1172167465Smp /* main loop, exit after 1s silence */ 1173167465Smp clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1174167465Smp if (targ->g->dev_type == DEV_TAP) { 117559243Sobrien D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd); 117659243Sobrien while (!targ->cancel) { 117759243Sobrien char buf[2048]; 117869408Sache /* XXX should we poll ? */ 117959243Sobrien if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 1180145479Smp targ->count++; 118159243Sobrien } 118259243Sobrien#ifndef NO_PCAP 118359243Sobrien } else if (targ->g->dev_type == DEV_PCAP) { 118459243Sobrien while (!targ->cancel) { 118559243Sobrien /* XXX should we poll ? */ 118659243Sobrien pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 118759243Sobrien } 118859243Sobrien#endif /* !NO_PCAP */ 118959243Sobrien } else { 119059243Sobrien int dump = targ->g->options & OPT_DUMP; 119159243Sobrien while (!targ->cancel) { 119259243Sobrien /* Once we started to receive packets, wait at most 1 seconds 1193167465Smp before quitting. */ 1194167465Smp if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 1195167465Smp clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 119659243Sobrien targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 119759243Sobrien goto out; 119859243Sobrien } 119959243Sobrien 120059243Sobrien if (pfd.revents & POLLERR) { 120159243Sobrien D("poll err"); 120259243Sobrien goto quit; 120359243Sobrien } 120459243Sobrien 120559243Sobrien for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 120659243Sobrien int m; 120759243Sobrien 120859243Sobrien rxring = NETMAP_RXRING(nifp, i); 1209167465Smp if (nm_ring_empty(rxring)) 1210145479Smp continue; 1211145479Smp 1212167465Smp m = receive_packets(rxring, targ->g->burst, dump); 1213145479Smp received += m; 121459243Sobrien } 121559243Sobrien targ->count = received; 121659243Sobrien } 121759243Sobrien } 121859243Sobrien 121969408Sache clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 122059243Sobrien 122159243Sobrienout: 122269408Sache targ->completed = 1; 122359243Sobrien targ->count = received; 1224131962Smp 1225145479Smpquit: 1226131962Smp /* reset the ``used`` flag. */ 1227131962Smp targ->used = 0; 1228145479Smp 122959243Sobrien return (NULL); 123059243Sobrien} 123159243Sobrien 123259243Sobrien/* very crude code to print a number in normalized form. 123359243Sobrien * Caller has to make sure that the buffer is large enough. 123459243Sobrien */ 123559243Sobrienstatic const char * 1236145479Smpnorm(char *buf, double val) 1237145479Smp{ 1238145479Smp char *units[] = { "", "K", "M", "G", "T" }; 1239145479Smp u_int i; 1240145479Smp 1241145479Smp for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++) 1242145479Smp val /= 1000; 1243145479Smp sprintf(buf, "%.2f %s", val, units[i]); 1244145479Smp return buf; 1245145479Smp} 1246145479Smp 1247145479Smpstatic void 1248145479Smptx_output(uint64_t sent, int size, double delta) 1249145479Smp{ 1250145479Smp double bw, raw_bw, pps; 1251145479Smp char b1[40], b2[80], b3[80]; 1252145479Smp 1253145479Smp printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", 1254145479Smp (unsigned long long)sent, size, delta); 1255145479Smp if (delta == 0) 1256145479Smp delta = 1e-6; 1257145479Smp if (size < 60) /* correct for min packet size */ 1258145479Smp size = 60; 1259145479Smp pps = sent / delta; 1260145479Smp bw = (8.0 * size * sent) / delta; 1261145479Smp /* raw packets have4 bytes crc + 20 bytes framing */ 1262145479Smp raw_bw = (8.0 * (size + 24) * sent) / delta; 1263145479Smp 126459243Sobrien printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1265 norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 1266} 1267 1268 1269static void 1270rx_output(uint64_t received, double delta) 1271{ 1272 double pps; 1273 char b1[40]; 1274 1275 printf("Received %llu packets, in %.2f seconds.\n", 1276 (unsigned long long) received, delta); 1277 1278 if (delta == 0) 1279 delta = 1e-6; 1280 pps = received / delta; 1281 printf("Speed: %spps\n", norm(b1, pps)); 1282} 1283 1284static void 1285usage(void) 1286{ 1287 const char *cmd = "pkt-gen"; 1288 fprintf(stderr, 1289 "Usage:\n" 1290 "%s arguments\n" 1291 "\t-i interface interface name\n" 1292 "\t-f function tx rx ping pong\n" 1293 "\t-n count number of iterations (can be 0)\n" 1294 "\t-t pkts_to_send also forces tx mode\n" 1295 "\t-r pkts_to_receive also forces rx mode\n" 1296 "\t-l pkt_size in bytes excluding CRC\n" 1297 "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1298 "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1299 "\t-D dst-mac\n" 1300 "\t-S src-mac\n" 1301 "\t-a cpu_id use setaffinity\n" 1302 "\t-b burst size testing, mostly\n" 1303 "\t-c cores cores to use\n" 1304 "\t-p threads processes/threads to use\n" 1305 "\t-T report_ms milliseconds between reports\n" 1306 "\t-P use libpcap instead of netmap\n" 1307 "\t-w wait_for_link_time in seconds\n" 1308 "\t-R rate in packets per second\n" 1309 "\t-X dump payload\n" 1310 "\t-H len add empty virtio-net-header with size 'len'\n" 1311 "", 1312 cmd); 1313 1314 exit(0); 1315} 1316 1317static void 1318start_threads(struct glob_arg *g) 1319{ 1320 int i; 1321 1322 targs = calloc(g->nthreads, sizeof(*targs)); 1323 /* 1324 * Now create the desired number of threads, each one 1325 * using a single descriptor. 1326 */ 1327 for (i = 0; i < g->nthreads; i++) { 1328 struct targ *t = &targs[i]; 1329 1330 bzero(t, sizeof(*t)); 1331 t->fd = -1; /* default, with pcap */ 1332 t->g = g; 1333 1334 if (g->dev_type == DEV_NETMAP) { 1335 struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ 1336 1337 if (g->nthreads > 1) { 1338 if (nmd.req.nr_flags != NR_REG_ALL_NIC) { 1339 D("invalid nthreads mode %d", nmd.req.nr_flags); 1340 continue; 1341 } 1342 nmd.req.nr_flags = NR_REG_ONE_NIC; 1343 nmd.req.nr_ringid = i; 1344 } 1345 /* Only touch one of the rings (rx is already ok) */ 1346 if (g->td_body == receiver_body) 1347 nmd.req.nr_ringid |= NETMAP_NO_TX_POLL; 1348 1349 /* register interface. Override ifname and ringid etc. */ 1350 1351 t->nmd = nm_open(t->g->ifname, NULL, g->nmd_flags | 1352 NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, g->nmd); 1353 if (t->nmd == NULL) { 1354 D("Unable to open %s: %s", 1355 t->g->ifname, strerror(errno)); 1356 continue; 1357 } 1358 t->fd = t->nmd->fd; 1359 1360 } else { 1361 targs[i].fd = g->main_fd; 1362 } 1363 t->used = 1; 1364 t->me = i; 1365 if (g->affinity >= 0) { 1366 if (g->affinity < g->cpus) 1367 t->affinity = g->affinity; 1368 else 1369 t->affinity = i % g->cpus; 1370 } else { 1371 t->affinity = -1; 1372 } 1373 /* default, init packets */ 1374 initialize_packet(t); 1375 1376 if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { 1377 D("Unable to create thread %d: %s", i, strerror(errno)); 1378 t->used = 0; 1379 } 1380 } 1381} 1382 1383static void 1384main_thread(struct glob_arg *g) 1385{ 1386 int i; 1387 1388 uint64_t prev = 0; 1389 uint64_t count = 0; 1390 double delta_t; 1391 struct timeval tic, toc; 1392 1393 gettimeofday(&toc, NULL); 1394 for (;;) { 1395 struct timeval now, delta; 1396 uint64_t pps, usec, my_count, npkts; 1397 int done = 0; 1398 1399 delta.tv_sec = g->report_interval/1000; 1400 delta.tv_usec = (g->report_interval%1000)*1000; 1401 select(0, NULL, NULL, NULL, &delta); 1402 gettimeofday(&now, NULL); 1403 timersub(&now, &toc, &toc); 1404 my_count = 0; 1405 for (i = 0; i < g->nthreads; i++) { 1406 my_count += targs[i].count; 1407 if (targs[i].used == 0) 1408 done++; 1409 } 1410 usec = toc.tv_sec* 1000000 + toc.tv_usec; 1411 if (usec < 10000) 1412 continue; 1413 npkts = my_count - prev; 1414 pps = (npkts*1000000 + usec/2) / usec; 1415 D("%llu pps (%llu pkts in %llu usec)", 1416 (unsigned long long)pps, 1417 (unsigned long long)npkts, 1418 (unsigned long long)usec); 1419 prev = my_count; 1420 toc = now; 1421 if (done == g->nthreads) 1422 break; 1423 } 1424 1425 timerclear(&tic); 1426 timerclear(&toc); 1427 for (i = 0; i < g->nthreads; i++) { 1428 struct timespec t_tic, t_toc; 1429 /* 1430 * Join active threads, unregister interfaces and close 1431 * file descriptors. 1432 */ 1433 if (targs[i].used) 1434 pthread_join(targs[i].thread, NULL); 1435 close(targs[i].fd); 1436 1437 if (targs[i].completed == 0) 1438 D("ouch, thread %d exited with error", i); 1439 1440 /* 1441 * Collect threads output and extract information about 1442 * how long it took to send all the packets. 1443 */ 1444 count += targs[i].count; 1445 t_tic = timeval2spec(&tic); 1446 t_toc = timeval2spec(&toc); 1447 if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 1448 tic = timespec2val(&targs[i].tic); 1449 if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 1450 toc = timespec2val(&targs[i].toc); 1451 } 1452 1453 /* print output. */ 1454 timersub(&toc, &tic, &toc); 1455 delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1456 if (g->td_body == sender_body) 1457 tx_output(count, g->pkt_size, delta_t); 1458 else 1459 rx_output(count, delta_t); 1460 1461 if (g->dev_type == DEV_NETMAP) { 1462 munmap(g->nmd->mem, g->nmd->req.nr_memsize); 1463 close(g->main_fd); 1464 } 1465} 1466 1467 1468struct sf { 1469 char *key; 1470 void *f; 1471}; 1472 1473static struct sf func[] = { 1474 { "tx", sender_body }, 1475 { "rx", receiver_body }, 1476 { "ping", pinger_body }, 1477 { "pong", ponger_body }, 1478 { NULL, NULL } 1479}; 1480 1481static int 1482tap_alloc(char *dev) 1483{ 1484 struct ifreq ifr; 1485 int fd, err; 1486 char *clonedev = TAP_CLONEDEV; 1487 1488 (void)err; 1489 (void)dev; 1490 /* Arguments taken by the function: 1491 * 1492 * char *dev: the name of an interface (or '\0'). MUST have enough 1493 * space to hold the interface name if '\0' is passed 1494 * int flags: interface flags (eg, IFF_TUN etc.) 1495 */ 1496 1497#ifdef __FreeBSD__ 1498 if (dev[3]) { /* tapSomething */ 1499 static char buf[128]; 1500 snprintf(buf, sizeof(buf), "/dev/%s", dev); 1501 clonedev = buf; 1502 } 1503#endif 1504 /* open the device */ 1505 if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1506 return fd; 1507 } 1508 D("%s open successful", clonedev); 1509 1510 /* preparation of the struct ifr, of type "struct ifreq" */ 1511 memset(&ifr, 0, sizeof(ifr)); 1512 1513#ifdef linux 1514 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1515 1516 if (*dev) { 1517 /* if a device name was specified, put it in the structure; otherwise, 1518 * the kernel will try to allocate the "next" device of the 1519 * specified type */ 1520 strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1521 } 1522 1523 /* try to create the device */ 1524 if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 1525 D("failed to to a TUNSETIFF: %s", strerror(errno)); 1526 close(fd); 1527 return err; 1528 } 1529 1530 /* if the operation was successful, write back the name of the 1531 * interface to the variable "dev", so the caller can know 1532 * it. Note that the caller MUST reserve space in *dev (see calling 1533 * code below) */ 1534 strcpy(dev, ifr.ifr_name); 1535 D("new name is %s", dev); 1536#endif /* linux */ 1537 1538 /* this is the special file descriptor that the caller will use to talk 1539 * with the virtual interface */ 1540 return fd; 1541} 1542 1543int 1544main(int arc, char **argv) 1545{ 1546 int i; 1547 1548 struct glob_arg g; 1549 1550 int ch; 1551 int wait_link = 2; 1552 int devqueues = 1; /* how many device queues */ 1553 1554 bzero(&g, sizeof(g)); 1555 1556 g.main_fd = -1; 1557 g.td_body = receiver_body; 1558 g.report_interval = 1000; /* report interval */ 1559 g.affinity = -1; 1560 /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1561 g.src_ip.name = "10.0.0.1"; 1562 g.dst_ip.name = "10.1.0.1"; 1563 g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1564 g.src_mac.name = NULL; 1565 g.pkt_size = 60; 1566 g.burst = 512; // default 1567 g.nthreads = 1; 1568 g.cpus = 1; 1569 g.forever = 1; 1570 g.tx_rate = 0; 1571 g.frags = 1; 1572 g.nmr_config = ""; 1573 g.virt_header = 0; 1574 1575 while ( (ch = getopt(arc, argv, 1576 "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:")) != -1) { 1577 struct sf *fn; 1578 1579 switch(ch) { 1580 default: 1581 D("bad option %c %s", ch, optarg); 1582 usage(); 1583 break; 1584 1585 case 'n': 1586 g.npackets = atoi(optarg); 1587 break; 1588 1589 case 'F': 1590 i = atoi(optarg); 1591 if (i < 1 || i > 63) { 1592 D("invalid frags %d [1..63], ignore", i); 1593 break; 1594 } 1595 g.frags = i; 1596 break; 1597 1598 case 'f': 1599 for (fn = func; fn->key; fn++) { 1600 if (!strcmp(fn->key, optarg)) 1601 break; 1602 } 1603 if (fn->key) 1604 g.td_body = fn->f; 1605 else 1606 D("unrecognised function %s", optarg); 1607 break; 1608 1609 case 'o': /* data generation options */ 1610 g.options = atoi(optarg); 1611 break; 1612 1613 case 'a': /* force affinity */ 1614 g.affinity = atoi(optarg); 1615 break; 1616 1617 case 'i': /* interface */ 1618 /* a prefix of tap: netmap: or pcap: forces the mode. 1619 * otherwise we guess 1620 */ 1621 D("interface is %s", optarg); 1622 if (strlen(optarg) > MAX_IFNAMELEN - 8) { 1623 D("ifname too long %s", optarg); 1624 break; 1625 } 1626 strcpy(g.ifname, optarg); 1627 if (!strcmp(optarg, "null")) { 1628 g.dev_type = DEV_NETMAP; 1629 g.dummy_send = 1; 1630 } else if (!strncmp(optarg, "tap:", 4)) { 1631 g.dev_type = DEV_TAP; 1632 strcpy(g.ifname, optarg + 4); 1633 } else if (!strncmp(optarg, "pcap:", 5)) { 1634 g.dev_type = DEV_PCAP; 1635 strcpy(g.ifname, optarg + 5); 1636 } else if (!strncmp(optarg, "netmap:", 7) || 1637 !strncmp(optarg, "vale", 4)) { 1638 g.dev_type = DEV_NETMAP; 1639 } else if (!strncmp(optarg, "tap", 3)) { 1640 g.dev_type = DEV_TAP; 1641 } else { /* prepend netmap: */ 1642 g.dev_type = DEV_NETMAP; 1643 sprintf(g.ifname, "netmap:%s", optarg); 1644 } 1645 break; 1646 1647 case 'I': 1648 g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1649 break; 1650 1651 case 'l': /* pkt_size */ 1652 g.pkt_size = atoi(optarg); 1653 break; 1654 1655 case 'd': 1656 g.dst_ip.name = optarg; 1657 break; 1658 1659 case 's': 1660 g.src_ip.name = optarg; 1661 break; 1662 1663 case 'T': /* report interval */ 1664 g.report_interval = atoi(optarg); 1665 break; 1666 1667 case 'w': 1668 wait_link = atoi(optarg); 1669 break; 1670 1671 case 'W': /* XXX changed default */ 1672 g.forever = 0; /* do not exit rx even with no traffic */ 1673 break; 1674 1675 case 'b': /* burst */ 1676 g.burst = atoi(optarg); 1677 break; 1678 case 'c': 1679 g.cpus = atoi(optarg); 1680 break; 1681 case 'p': 1682 g.nthreads = atoi(optarg); 1683 break; 1684 1685 case 'D': /* destination mac */ 1686 g.dst_mac.name = optarg; 1687 break; 1688 1689 case 'S': /* source mac */ 1690 g.src_mac.name = optarg; 1691 break; 1692 case 'v': 1693 verbose++; 1694 break; 1695 case 'R': 1696 g.tx_rate = atoi(optarg); 1697 break; 1698 case 'X': 1699 g.options |= OPT_DUMP; 1700 break; 1701 case 'C': 1702 g.nmr_config = strdup(optarg); 1703 break; 1704 case 'H': 1705 g.virt_header = atoi(optarg); 1706 break; 1707 case 'e': /* extra bufs */ 1708 g.extra_bufs = atoi(optarg); 1709 break; 1710 } 1711 } 1712 1713 if (g.ifname == NULL) { 1714 D("missing ifname"); 1715 usage(); 1716 } 1717 1718 i = system_ncpus(); 1719 if (g.cpus < 0 || g.cpus > i) { 1720 D("%d cpus is too high, have only %d cpus", g.cpus, i); 1721 usage(); 1722 } 1723 if (g.cpus == 0) 1724 g.cpus = i; 1725 1726 if (g.pkt_size < 16 || g.pkt_size > 1536) { 1727 D("bad pktsize %d\n", g.pkt_size); 1728 usage(); 1729 } 1730 1731 if (g.src_mac.name == NULL) { 1732 static char mybuf[20] = "00:00:00:00:00:00"; 1733 /* retrieve source mac address. */ 1734 if (source_hwaddr(g.ifname, mybuf) == -1) { 1735 D("Unable to retrieve source mac"); 1736 // continue, fail later 1737 } 1738 g.src_mac.name = mybuf; 1739 } 1740 /* extract address ranges */ 1741 extract_ip_range(&g.src_ip); 1742 extract_ip_range(&g.dst_ip); 1743 extract_mac_range(&g.src_mac); 1744 extract_mac_range(&g.dst_mac); 1745 1746 if (g.src_ip.start != g.src_ip.end || 1747 g.src_ip.port0 != g.src_ip.port1 || 1748 g.dst_ip.start != g.dst_ip.end || 1749 g.dst_ip.port0 != g.dst_ip.port1) 1750 g.options |= OPT_COPY; 1751 1752 if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 1753 && g.virt_header != VIRT_HDR_2) { 1754 D("bad virtio-net-header length"); 1755 usage(); 1756 } 1757 1758 if (g.dev_type == DEV_TAP) { 1759 D("want to use tap %s", g.ifname); 1760 g.main_fd = tap_alloc(g.ifname); 1761 if (g.main_fd < 0) { 1762 D("cannot open tap %s", g.ifname); 1763 usage(); 1764 } 1765#ifndef NO_PCAP 1766 } else if (g.dev_type == DEV_PCAP) { 1767 char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1768 1769 D("using pcap on %s", g.ifname); 1770 pcap_errbuf[0] = '\0'; // init the buffer 1771 g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf); 1772 if (g.p == NULL) { 1773 D("cannot open pcap on %s", g.ifname); 1774 usage(); 1775 } 1776#endif /* !NO_PCAP */ 1777 } else if (g.dummy_send) { /* but DEV_NETMAP */ 1778 D("using a dummy send routine"); 1779 } else { 1780 struct nm_desc base_nmd; 1781 1782 bzero(&base_nmd, sizeof(base_nmd)); 1783 1784 g.nmd_flags = 0; 1785 g.nmd_flags |= parse_nmr_config(g.nmr_config, &base_nmd.req); 1786 if (g.extra_bufs) { 1787 base_nmd.req.nr_arg3 = g.extra_bufs; 1788 g.nmd_flags |= NM_OPEN_ARG3; 1789 } 1790 1791 /* 1792 * Open the netmap device using nm_open(). 1793 * 1794 * protocol stack and may cause a reset of the card, 1795 * which in turn may take some time for the PHY to 1796 * reconfigure. We do the open here to have time to reset. 1797 */ 1798 g.nmd = nm_open(g.ifname, NULL, g.nmd_flags, &base_nmd); 1799 if (g.nmd == NULL) { 1800 D("Unable to open %s: %s", g.ifname, strerror(errno)); 1801 goto out; 1802 } 1803 g.main_fd = g.nmd->fd; 1804 D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); 1805 1806 devqueues = g.nmd->req.nr_rx_rings; 1807 1808 /* validate provided nthreads. */ 1809 if (g.nthreads < 1 || g.nthreads > devqueues) { 1810 D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 1811 // continue, fail later 1812 } 1813 1814 if (verbose) { 1815 struct netmap_if *nifp = g.nmd->nifp; 1816 struct nmreq *req = &g.nmd->req; 1817 1818 D("nifp at offset %d, %d tx %d rx region %d", 1819 req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, 1820 req->nr_arg2); 1821 for (i = 0; i <= req->nr_tx_rings; i++) { 1822 D(" TX%d at 0x%lx", i, 1823 (char *)NETMAP_TXRING(nifp, i) - (char *)nifp); 1824 } 1825 for (i = 0; i <= req->nr_rx_rings; i++) { 1826 D(" RX%d at 0x%lx", i, 1827 (char *)NETMAP_RXRING(nifp, i) - (char *)nifp); 1828 } 1829 } 1830 1831 /* Print some debug information. */ 1832 fprintf(stdout, 1833 "%s %s: %d queues, %d threads and %d cpus.\n", 1834 (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1835 g.ifname, 1836 devqueues, 1837 g.nthreads, 1838 g.cpus); 1839 if (g.td_body == sender_body) { 1840 fprintf(stdout, "%s -> %s (%s -> %s)\n", 1841 g.src_ip.name, g.dst_ip.name, 1842 g.src_mac.name, g.dst_mac.name); 1843 } 1844 1845out: 1846 /* Exit if something went wrong. */ 1847 if (g.main_fd < 0) { 1848 D("aborting"); 1849 usage(); 1850 } 1851 } 1852 1853 1854 if (g.options) { 1855 D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 1856 g.options & OPT_PREFETCH ? " prefetch" : "", 1857 g.options & OPT_ACCESS ? " access" : "", 1858 g.options & OPT_MEMCPY ? " memcpy" : "", 1859 g.options & OPT_INDIRECT ? " indirect" : "", 1860 g.options & OPT_COPY ? " copy" : ""); 1861 } 1862 1863 g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 1864 if (g.tx_rate > 0) { 1865 /* try to have at least something every second, 1866 * reducing the burst size to some 0.01s worth of data 1867 * (but no less than one full set of fragments) 1868 */ 1869 uint64_t x; 1870 int lim = (g.tx_rate)/300; 1871 if (g.burst > lim) 1872 g.burst = lim; 1873 if (g.burst < g.frags) 1874 g.burst = g.frags; 1875 x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 1876 g.tx_period.tv_nsec = x; 1877 g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 1878 g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 1879 } 1880 if (g.td_body == sender_body) 1881 D("Sending %d packets every %ld.%09ld s", 1882 g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 1883 /* Wait for PHY reset. */ 1884 D("Wait %d secs for phy reset", wait_link); 1885 sleep(wait_link); 1886 D("Ready..."); 1887 1888 /* Install ^C handler. */ 1889 global_nthreads = g.nthreads; 1890 signal(SIGINT, sigint_h); 1891 1892 start_threads(&g); 1893 main_thread(&g); 1894 return 0; 1895} 1896 1897/* end of file */ 1898