168b8534bSLuigi Rizzo /* 217885a7bSLuigi Rizzo * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. 317885a7bSLuigi Rizzo * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved. 468b8534bSLuigi Rizzo * 568b8534bSLuigi Rizzo * Redistribution and use in source and binary forms, with or without 668b8534bSLuigi Rizzo * modification, are permitted provided that the following conditions 768b8534bSLuigi Rizzo * are met: 868b8534bSLuigi Rizzo * 1. Redistributions of source code must retain the above copyright 968b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer. 1068b8534bSLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 1168b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 1268b8534bSLuigi Rizzo * documentation and/or other materials provided with the distribution. 1368b8534bSLuigi Rizzo * 1468b8534bSLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1568b8534bSLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1668b8534bSLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1768b8534bSLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1868b8534bSLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1968b8534bSLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2068b8534bSLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2168b8534bSLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2268b8534bSLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2368b8534bSLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2468b8534bSLuigi Rizzo * SUCH DAMAGE. 2568b8534bSLuigi Rizzo */ 2668b8534bSLuigi Rizzo 2768b8534bSLuigi Rizzo /* 2868b8534bSLuigi Rizzo * $FreeBSD$ 29ce3ee1e7SLuigi Rizzo * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $ 3068b8534bSLuigi Rizzo * 3168b8534bSLuigi Rizzo * Example program to show how to build a multithreaded packet 3268b8534bSLuigi Rizzo * source/sink using the netmap device. 3368b8534bSLuigi Rizzo * 3468b8534bSLuigi Rizzo * In this example we create a programmable number of threads 3568b8534bSLuigi Rizzo * to take care of all the queues of the interface used to 3668b8534bSLuigi Rizzo * send or receive traffic. 3768b8534bSLuigi Rizzo * 3868b8534bSLuigi Rizzo */ 3968b8534bSLuigi Rizzo 40f2637526SLuigi Rizzo #define MY_PCAP 41f8e4e36aSLuigi Rizzo #include "nm_util.h" 42f2637526SLuigi Rizzo // #include <net/netmap_user.h> 43f8e4e36aSLuigi Rizzo 44b303f675SLuigi Rizzo #include <ctype.h> // isprint() 45b303f675SLuigi Rizzo 46f2637526SLuigi Rizzo #ifndef NO_PCAP 47f2637526SLuigi Rizzo #include <pcap/pcap.h> 48f2637526SLuigi Rizzo #endif 49ce3ee1e7SLuigi Rizzo const char *default_payload="netmap pkt-gen DIRECT payload\n" 50ce3ee1e7SLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 51ce3ee1e7SLuigi Rizzo 52ce3ee1e7SLuigi Rizzo const char *indirect_payload="netmap pkt-gen indirect payload\n" 5368b8534bSLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 5468b8534bSLuigi Rizzo 55f8e4e36aSLuigi Rizzo int time_second; // support for RD() debugging macro 5668b8534bSLuigi Rizzo 5768b8534bSLuigi Rizzo int verbose = 0; 5868b8534bSLuigi Rizzo 5968b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */ 6068b8534bSLuigi Rizzo 6117885a7bSLuigi Rizzo 6217885a7bSLuigi Rizzo #define VIRT_HDR_1 10 /* length of a base vnet-hdr */ 6317885a7bSLuigi Rizzo #define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ 6417885a7bSLuigi Rizzo #define VIRT_HDR_MAX VIRT_HDR_2 6517885a7bSLuigi Rizzo struct virt_header { 6617885a7bSLuigi Rizzo uint8_t fields[VIRT_HDR_MAX]; 6717885a7bSLuigi Rizzo }; 6817885a7bSLuigi Rizzo 6968b8534bSLuigi Rizzo struct pkt { 7017885a7bSLuigi Rizzo struct virt_header vh; 7168b8534bSLuigi Rizzo struct ether_header eh; 7268b8534bSLuigi Rizzo struct ip ip; 7368b8534bSLuigi Rizzo struct udphdr udp; 745819da83SLuigi Rizzo uint8_t body[2048]; // XXX hardwired 7568b8534bSLuigi Rizzo } __attribute__((__packed__)); 7668b8534bSLuigi Rizzo 77f8e4e36aSLuigi Rizzo struct ip_range { 78f8e4e36aSLuigi Rizzo char *name; 79ce3ee1e7SLuigi Rizzo uint32_t start, end; /* same as struct in_addr */ 80ce3ee1e7SLuigi Rizzo uint16_t port0, port1; 81f8e4e36aSLuigi Rizzo }; 82f8e4e36aSLuigi Rizzo 83f8e4e36aSLuigi Rizzo struct mac_range { 84f8e4e36aSLuigi Rizzo char *name; 85f8e4e36aSLuigi Rizzo struct ether_addr start, end; 86f8e4e36aSLuigi Rizzo }; 87f8e4e36aSLuigi Rizzo 8868b8534bSLuigi Rizzo /* 8968b8534bSLuigi Rizzo * global arguments for all threads 9068b8534bSLuigi Rizzo */ 91f8e4e36aSLuigi Rizzo 9268b8534bSLuigi Rizzo struct glob_arg { 93f8e4e36aSLuigi Rizzo struct ip_range src_ip; 94f8e4e36aSLuigi Rizzo struct ip_range dst_ip; 95f8e4e36aSLuigi Rizzo struct mac_range dst_mac; 96f8e4e36aSLuigi Rizzo struct mac_range src_mac; 9768b8534bSLuigi Rizzo int pkt_size; 9868b8534bSLuigi Rizzo int burst; 99f8e4e36aSLuigi Rizzo int forever; 10068b8534bSLuigi Rizzo int npackets; /* total packets to send */ 101ce3ee1e7SLuigi Rizzo int frags; /* fragments per packet */ 10268b8534bSLuigi Rizzo int nthreads; 10368b8534bSLuigi Rizzo int cpus; 10499fb123fSLuigi Rizzo int options; /* testing */ 10599fb123fSLuigi Rizzo #define OPT_PREFETCH 1 10699fb123fSLuigi Rizzo #define OPT_ACCESS 2 10799fb123fSLuigi Rizzo #define OPT_COPY 4 10899fb123fSLuigi Rizzo #define OPT_MEMCPY 8 109f8e4e36aSLuigi Rizzo #define OPT_TS 16 /* add a timestamp */ 110b303f675SLuigi Rizzo #define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 111b303f675SLuigi Rizzo #define OPT_DUMP 64 /* dump rx/tx traffic */ 112f8e4e36aSLuigi Rizzo int dev_type; 113f2637526SLuigi Rizzo #ifndef NO_PCAP 11468b8534bSLuigi Rizzo pcap_t *p; 115f2637526SLuigi Rizzo #endif 11668b8534bSLuigi Rizzo 1171cb4c501SLuigi Rizzo int tx_rate; 1181cb4c501SLuigi Rizzo struct timespec tx_period; 1191cb4c501SLuigi Rizzo 120f8e4e36aSLuigi Rizzo int affinity; 121f8e4e36aSLuigi Rizzo int main_fd; 122f2637526SLuigi Rizzo int report_interval; /* milliseconds between prints */ 123f8e4e36aSLuigi Rizzo void *(*td_body)(void *); 124f8e4e36aSLuigi Rizzo void *mmap_addr; 125f8e4e36aSLuigi Rizzo int mmap_size; 126f8e4e36aSLuigi Rizzo char *ifname; 127ce3ee1e7SLuigi Rizzo char *nmr_config; 128ce3ee1e7SLuigi Rizzo int dummy_send; 12917885a7bSLuigi Rizzo int virt_header; /* send also the virt_header */ 13017885a7bSLuigi Rizzo int host_ring; 13168b8534bSLuigi Rizzo }; 132f8e4e36aSLuigi Rizzo enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 133f8e4e36aSLuigi Rizzo 13468b8534bSLuigi Rizzo 13568b8534bSLuigi Rizzo /* 13668b8534bSLuigi Rizzo * Arguments for a new thread. The same structure is used by 13768b8534bSLuigi Rizzo * the source and the sink 13868b8534bSLuigi Rizzo */ 13968b8534bSLuigi Rizzo struct targ { 14068b8534bSLuigi Rizzo struct glob_arg *g; 14168b8534bSLuigi Rizzo int used; 14268b8534bSLuigi Rizzo int completed; 1433fe77e68SEd Maste int cancel; 14468b8534bSLuigi Rizzo int fd; 14568b8534bSLuigi Rizzo struct nmreq nmr; 14668b8534bSLuigi Rizzo struct netmap_if *nifp; 14768b8534bSLuigi Rizzo uint16_t qfirst, qlast; /* range of queues to scan */ 148f8e4e36aSLuigi Rizzo volatile uint64_t count; 1491cb4c501SLuigi Rizzo struct timespec tic, toc; 15068b8534bSLuigi Rizzo int me; 15168b8534bSLuigi Rizzo pthread_t thread; 15268b8534bSLuigi Rizzo int affinity; 15368b8534bSLuigi Rizzo 15468b8534bSLuigi Rizzo struct pkt pkt; 15568b8534bSLuigi Rizzo }; 15668b8534bSLuigi Rizzo 15768b8534bSLuigi Rizzo 158f8e4e36aSLuigi Rizzo /* 159f8e4e36aSLuigi Rizzo * extract the extremes from a range of ipv4 addresses. 160f8e4e36aSLuigi Rizzo * addr_lo[-addr_hi][:port_lo[-port_hi]] 161f8e4e36aSLuigi Rizzo */ 162f8e4e36aSLuigi Rizzo static void 163f8e4e36aSLuigi Rizzo extract_ip_range(struct ip_range *r) 164f8e4e36aSLuigi Rizzo { 165ce3ee1e7SLuigi Rizzo char *ap, *pp; 166ce3ee1e7SLuigi Rizzo struct in_addr a; 167f8e4e36aSLuigi Rizzo 16817885a7bSLuigi Rizzo if (verbose) 169f8e4e36aSLuigi Rizzo D("extract IP range from %s", r->name); 170ce3ee1e7SLuigi Rizzo r->port0 = r->port1 = 0; 171ce3ee1e7SLuigi Rizzo r->start = r->end = 0; 172ce3ee1e7SLuigi Rizzo 173ce3ee1e7SLuigi Rizzo /* the first - splits start/end of range */ 174ce3ee1e7SLuigi Rizzo ap = index(r->name, '-'); /* do we have ports ? */ 175ce3ee1e7SLuigi Rizzo if (ap) { 176ce3ee1e7SLuigi Rizzo *ap++ = '\0'; 177ce3ee1e7SLuigi Rizzo } 178ce3ee1e7SLuigi Rizzo /* grab the initial values (mandatory) */ 179ce3ee1e7SLuigi Rizzo pp = index(r->name, ':'); 180ce3ee1e7SLuigi Rizzo if (pp) { 181ce3ee1e7SLuigi Rizzo *pp++ = '\0'; 182ce3ee1e7SLuigi Rizzo r->port0 = r->port1 = strtol(pp, NULL, 0); 183ce3ee1e7SLuigi Rizzo }; 184ce3ee1e7SLuigi Rizzo inet_aton(r->name, &a); 185ce3ee1e7SLuigi Rizzo r->start = r->end = ntohl(a.s_addr); 186ce3ee1e7SLuigi Rizzo if (ap) { 187ce3ee1e7SLuigi Rizzo pp = index(ap, ':'); 188ce3ee1e7SLuigi Rizzo if (pp) { 189ce3ee1e7SLuigi Rizzo *pp++ = '\0'; 190ce3ee1e7SLuigi Rizzo if (*pp) 191ce3ee1e7SLuigi Rizzo r->port1 = strtol(pp, NULL, 0); 192ce3ee1e7SLuigi Rizzo } 193ce3ee1e7SLuigi Rizzo if (*ap) { 194ce3ee1e7SLuigi Rizzo inet_aton(ap, &a); 195ce3ee1e7SLuigi Rizzo r->end = ntohl(a.s_addr); 196ce3ee1e7SLuigi Rizzo } 197ce3ee1e7SLuigi Rizzo } 198ce3ee1e7SLuigi Rizzo if (r->port0 > r->port1) { 199ce3ee1e7SLuigi Rizzo uint16_t tmp = r->port0; 200f8e4e36aSLuigi Rizzo r->port0 = r->port1; 201ce3ee1e7SLuigi Rizzo r->port1 = tmp; 202f8e4e36aSLuigi Rizzo } 203ce3ee1e7SLuigi Rizzo if (r->start > r->end) { 204ce3ee1e7SLuigi Rizzo uint32_t tmp = r->start; 205f8e4e36aSLuigi Rizzo r->start = r->end; 206ce3ee1e7SLuigi Rizzo r->end = tmp; 207f8e4e36aSLuigi Rizzo } 208ce3ee1e7SLuigi Rizzo { 209ce3ee1e7SLuigi Rizzo struct in_addr a; 210ce3ee1e7SLuigi Rizzo char buf1[16]; // one ip address 211ce3ee1e7SLuigi Rizzo 212ce3ee1e7SLuigi Rizzo a.s_addr = htonl(r->end); 213ce3ee1e7SLuigi Rizzo strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 214ce3ee1e7SLuigi Rizzo a.s_addr = htonl(r->start); 21517885a7bSLuigi Rizzo if (1) 216ce3ee1e7SLuigi Rizzo D("range is %s:%d to %s:%d", 217ce3ee1e7SLuigi Rizzo inet_ntoa(a), r->port0, buf1, r->port1); 218ce3ee1e7SLuigi Rizzo } 219f8e4e36aSLuigi Rizzo } 220f8e4e36aSLuigi Rizzo 221f8e4e36aSLuigi Rizzo static void 222f8e4e36aSLuigi Rizzo extract_mac_range(struct mac_range *r) 223f8e4e36aSLuigi Rizzo { 22417885a7bSLuigi Rizzo if (verbose) 225f8e4e36aSLuigi Rizzo D("extract MAC range from %s", r->name); 226f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->start, 6); 227f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->end, 6); 228f8e4e36aSLuigi Rizzo #if 0 229f8e4e36aSLuigi Rizzo bcopy(targ->src_mac, eh->ether_shost, 6); 230f8e4e36aSLuigi Rizzo p = index(targ->g->src_mac, '-'); 231f8e4e36aSLuigi Rizzo if (p) 232f8e4e36aSLuigi Rizzo targ->src_mac_range = atoi(p+1); 233f8e4e36aSLuigi Rizzo 234f8e4e36aSLuigi Rizzo bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 235f8e4e36aSLuigi Rizzo bcopy(targ->dst_mac, eh->ether_dhost, 6); 236f8e4e36aSLuigi Rizzo p = index(targ->g->dst_mac, '-'); 237f8e4e36aSLuigi Rizzo if (p) 238f8e4e36aSLuigi Rizzo targ->dst_mac_range = atoi(p+1); 239f8e4e36aSLuigi Rizzo #endif 24017885a7bSLuigi Rizzo if (verbose) 241f8e4e36aSLuigi Rizzo D("%s starts at %s", r->name, ether_ntoa(&r->start)); 242f8e4e36aSLuigi Rizzo } 243f8e4e36aSLuigi Rizzo 24468b8534bSLuigi Rizzo static struct targ *targs; 24568b8534bSLuigi Rizzo static int global_nthreads; 24668b8534bSLuigi Rizzo 24768b8534bSLuigi Rizzo /* control-C handler */ 24868b8534bSLuigi Rizzo static void 249f8e4e36aSLuigi Rizzo sigint_h(int sig) 25068b8534bSLuigi Rizzo { 251f8e4e36aSLuigi Rizzo int i; 25268b8534bSLuigi Rizzo 253f8e4e36aSLuigi Rizzo (void)sig; /* UNUSED */ 254f8e4e36aSLuigi Rizzo for (i = 0; i < global_nthreads; i++) { 255f8e4e36aSLuigi Rizzo targs[i].cancel = 1; 256f8e4e36aSLuigi Rizzo } 25768b8534bSLuigi Rizzo signal(SIGINT, SIG_DFL); 25868b8534bSLuigi Rizzo } 25968b8534bSLuigi Rizzo 26068b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */ 26168b8534bSLuigi Rizzo static int 26268b8534bSLuigi Rizzo system_ncpus(void) 26368b8534bSLuigi Rizzo { 264f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 26568b8534bSLuigi Rizzo int mib[2], ncpus; 26668b8534bSLuigi Rizzo size_t len; 26768b8534bSLuigi Rizzo 26868b8534bSLuigi Rizzo mib[0] = CTL_HW; 26968b8534bSLuigi Rizzo mib[1] = HW_NCPU; 27068b8534bSLuigi Rizzo len = sizeof(mib); 27168b8534bSLuigi Rizzo sysctl(mib, 2, &ncpus, &len, NULL, 0); 27268b8534bSLuigi Rizzo 27368b8534bSLuigi Rizzo return (ncpus); 274f8e4e36aSLuigi Rizzo #else 275f8e4e36aSLuigi Rizzo return 1; 276f8e4e36aSLuigi Rizzo #endif /* !__FreeBSD__ */ 27768b8534bSLuigi Rizzo } 27868b8534bSLuigi Rizzo 279f8e4e36aSLuigi Rizzo #ifdef __linux__ 280f8e4e36aSLuigi Rizzo #define sockaddr_dl sockaddr_ll 281f8e4e36aSLuigi Rizzo #define sdl_family sll_family 282f8e4e36aSLuigi Rizzo #define AF_LINK AF_PACKET 283f8e4e36aSLuigi Rizzo #define LLADDR(s) s->sll_addr; 284f8e4e36aSLuigi Rizzo #include <linux/if_tun.h> 285f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/net/tun" 286f8e4e36aSLuigi Rizzo #endif /* __linux__ */ 287f8e4e36aSLuigi Rizzo 288f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 289f8e4e36aSLuigi Rizzo #include <net/if_tun.h> 290f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 291f8e4e36aSLuigi Rizzo #endif /* __FreeBSD */ 292f8e4e36aSLuigi Rizzo 293f8e4e36aSLuigi Rizzo #ifdef __APPLE__ 294f8e4e36aSLuigi Rizzo // #warning TAP not supported on apple ? 295f8e4e36aSLuigi Rizzo #include <net/if_utun.h> 296f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 297f8e4e36aSLuigi Rizzo #endif /* __APPLE__ */ 298f8e4e36aSLuigi Rizzo 299f8e4e36aSLuigi Rizzo 30068b8534bSLuigi Rizzo /* 301ce3ee1e7SLuigi Rizzo * parse the vale configuration in conf and put it in nmr. 302ce3ee1e7SLuigi Rizzo * The configuration may consist of 0 to 4 numbers separated 303fc6eb28bSHiren Panchasara * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 304ce3ee1e7SLuigi Rizzo * Missing numbers or zeroes stand for default values. 305ce3ee1e7SLuigi Rizzo * As an additional convenience, if exactly one number 306fc6eb28bSHiren Panchasara * is specified, then this is assigned to both #tx-slots and #rx-slots. 307fc6eb28bSHiren Panchasara * If there is no 4th number, then the 3rd is assigned to both #tx-rings 308ce3ee1e7SLuigi Rizzo * and #rx-rings. 309ce3ee1e7SLuigi Rizzo */ 310ce3ee1e7SLuigi Rizzo void parse_nmr_config(const char* conf, struct nmreq *nmr) 311ce3ee1e7SLuigi Rizzo { 312ce3ee1e7SLuigi Rizzo char *w, *tok; 313ce3ee1e7SLuigi Rizzo int i, v; 314ce3ee1e7SLuigi Rizzo 315ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 316ce3ee1e7SLuigi Rizzo nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 317ce3ee1e7SLuigi Rizzo if (conf == NULL || ! *conf) 318ce3ee1e7SLuigi Rizzo return; 319ce3ee1e7SLuigi Rizzo w = strdup(conf); 320ce3ee1e7SLuigi Rizzo for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 321ce3ee1e7SLuigi Rizzo v = atoi(tok); 322ce3ee1e7SLuigi Rizzo switch (i) { 323ce3ee1e7SLuigi Rizzo case 0: 324ce3ee1e7SLuigi Rizzo nmr->nr_tx_slots = nmr->nr_rx_slots = v; 325ce3ee1e7SLuigi Rizzo break; 326ce3ee1e7SLuigi Rizzo case 1: 327ce3ee1e7SLuigi Rizzo nmr->nr_rx_slots = v; 328ce3ee1e7SLuigi Rizzo break; 329ce3ee1e7SLuigi Rizzo case 2: 330ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings = nmr->nr_rx_rings = v; 331ce3ee1e7SLuigi Rizzo break; 332ce3ee1e7SLuigi Rizzo case 3: 333ce3ee1e7SLuigi Rizzo nmr->nr_rx_rings = v; 334ce3ee1e7SLuigi Rizzo break; 335ce3ee1e7SLuigi Rizzo default: 336ce3ee1e7SLuigi Rizzo D("ignored config: %s", tok); 337ce3ee1e7SLuigi Rizzo break; 338ce3ee1e7SLuigi Rizzo } 339ce3ee1e7SLuigi Rizzo } 340ce3ee1e7SLuigi Rizzo D("txr %d txd %d rxr %d rxd %d", 341ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings, nmr->nr_tx_slots, 342ce3ee1e7SLuigi Rizzo nmr->nr_rx_rings, nmr->nr_rx_slots); 343ce3ee1e7SLuigi Rizzo free(w); 344ce3ee1e7SLuigi Rizzo } 345ce3ee1e7SLuigi Rizzo 346ce3ee1e7SLuigi Rizzo 347ce3ee1e7SLuigi Rizzo /* 34868b8534bSLuigi Rizzo * locate the src mac address for our interface, put it 34968b8534bSLuigi Rizzo * into the user-supplied buffer. return 0 if ok, -1 on error. 35068b8534bSLuigi Rizzo */ 35168b8534bSLuigi Rizzo static int 35268b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf) 35368b8534bSLuigi Rizzo { 35468b8534bSLuigi Rizzo struct ifaddrs *ifaphead, *ifap; 35568b8534bSLuigi Rizzo int l = sizeof(ifap->ifa_name); 35668b8534bSLuigi Rizzo 35768b8534bSLuigi Rizzo if (getifaddrs(&ifaphead) != 0) { 35868b8534bSLuigi Rizzo D("getifaddrs %s failed", ifname); 35968b8534bSLuigi Rizzo return (-1); 36068b8534bSLuigi Rizzo } 36168b8534bSLuigi Rizzo 36268b8534bSLuigi Rizzo for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 36368b8534bSLuigi Rizzo struct sockaddr_dl *sdl = 36468b8534bSLuigi Rizzo (struct sockaddr_dl *)ifap->ifa_addr; 36568b8534bSLuigi Rizzo uint8_t *mac; 36668b8534bSLuigi Rizzo 36768b8534bSLuigi Rizzo if (!sdl || sdl->sdl_family != AF_LINK) 36868b8534bSLuigi Rizzo continue; 36968b8534bSLuigi Rizzo if (strncmp(ifap->ifa_name, ifname, l) != 0) 37068b8534bSLuigi Rizzo continue; 37168b8534bSLuigi Rizzo mac = (uint8_t *)LLADDR(sdl); 37268b8534bSLuigi Rizzo sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 37368b8534bSLuigi Rizzo mac[0], mac[1], mac[2], 37468b8534bSLuigi Rizzo mac[3], mac[4], mac[5]); 37568b8534bSLuigi Rizzo if (verbose) 37668b8534bSLuigi Rizzo D("source hwaddr %s", buf); 37768b8534bSLuigi Rizzo break; 37868b8534bSLuigi Rizzo } 37968b8534bSLuigi Rizzo freeifaddrs(ifaphead); 38068b8534bSLuigi Rizzo return ifap ? 0 : 1; 38168b8534bSLuigi Rizzo } 38268b8534bSLuigi Rizzo 38368b8534bSLuigi Rizzo 38468b8534bSLuigi Rizzo /* set the thread affinity. */ 38568b8534bSLuigi Rizzo static int 38668b8534bSLuigi Rizzo setaffinity(pthread_t me, int i) 38768b8534bSLuigi Rizzo { 38817885a7bSLuigi Rizzo #if 1 // def __FreeBSD__ 38968b8534bSLuigi Rizzo cpuset_t cpumask; 39068b8534bSLuigi Rizzo 39168b8534bSLuigi Rizzo if (i == -1) 39268b8534bSLuigi Rizzo return 0; 39368b8534bSLuigi Rizzo 39468b8534bSLuigi Rizzo /* Set thread affinity affinity.*/ 39568b8534bSLuigi Rizzo CPU_ZERO(&cpumask); 39668b8534bSLuigi Rizzo CPU_SET(i, &cpumask); 39768b8534bSLuigi Rizzo 39868b8534bSLuigi Rizzo if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 39917885a7bSLuigi Rizzo D("Unable to set affinity: %s", strerror(errno)); 40068b8534bSLuigi Rizzo return 1; 40168b8534bSLuigi Rizzo } 402f8e4e36aSLuigi Rizzo #else 403f8e4e36aSLuigi Rizzo (void)me; /* suppress 'unused' warnings */ 404f8e4e36aSLuigi Rizzo (void)i; 405f8e4e36aSLuigi Rizzo #endif /* __FreeBSD__ */ 40668b8534bSLuigi Rizzo return 0; 40768b8534bSLuigi Rizzo } 40868b8534bSLuigi Rizzo 40968b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */ 41068b8534bSLuigi Rizzo static uint16_t 411f8e4e36aSLuigi Rizzo checksum(const void *data, uint16_t len, uint32_t sum) 41268b8534bSLuigi Rizzo { 41368b8534bSLuigi Rizzo const uint8_t *addr = data; 414f8e4e36aSLuigi Rizzo uint32_t i; 41568b8534bSLuigi Rizzo 416f8e4e36aSLuigi Rizzo /* Checksum all the pairs of bytes first... */ 417f8e4e36aSLuigi Rizzo for (i = 0; i < (len & ~1U); i += 2) { 418f8e4e36aSLuigi Rizzo sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 419f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 420f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 421f8e4e36aSLuigi Rizzo } 422f8e4e36aSLuigi Rizzo /* 423f8e4e36aSLuigi Rizzo * If there's a single byte left over, checksum it, too. 424f8e4e36aSLuigi Rizzo * Network byte order is big-endian, so the remaining byte is 425f8e4e36aSLuigi Rizzo * the high byte. 426f8e4e36aSLuigi Rizzo */ 427f8e4e36aSLuigi Rizzo if (i < len) { 428f8e4e36aSLuigi Rizzo sum += addr[i] << 8; 429f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 430f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 431f8e4e36aSLuigi Rizzo } 432f8e4e36aSLuigi Rizzo return sum; 43368b8534bSLuigi Rizzo } 43468b8534bSLuigi Rizzo 435f8e4e36aSLuigi Rizzo static u_int16_t 436f8e4e36aSLuigi Rizzo wrapsum(u_int32_t sum) 437f8e4e36aSLuigi Rizzo { 438f8e4e36aSLuigi Rizzo sum = ~sum & 0xFFFF; 439f8e4e36aSLuigi Rizzo return (htons(sum)); 44068b8534bSLuigi Rizzo } 44168b8534bSLuigi Rizzo 442b303f675SLuigi Rizzo /* Check the payload of the packet for errors (use it for debug). 443b303f675SLuigi Rizzo * Look for consecutive ascii representations of the size of the packet. 444b303f675SLuigi Rizzo */ 445b303f675SLuigi Rizzo static void 446b303f675SLuigi Rizzo dump_payload(char *p, int len, struct netmap_ring *ring, int cur) 447b303f675SLuigi Rizzo { 448b303f675SLuigi Rizzo char buf[128]; 449b303f675SLuigi Rizzo int i, j, i0; 450b303f675SLuigi Rizzo 451b303f675SLuigi Rizzo /* get the length in ASCII of the length of the packet. */ 452b303f675SLuigi Rizzo 453ce3ee1e7SLuigi Rizzo printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 454ce3ee1e7SLuigi Rizzo ring, cur, ring->slot[cur].buf_idx, 455ce3ee1e7SLuigi Rizzo ring->slot[cur].flags, len); 456b303f675SLuigi Rizzo /* hexdump routine */ 457b303f675SLuigi Rizzo for (i = 0; i < len; ) { 458b303f675SLuigi Rizzo memset(buf, sizeof(buf), ' '); 459b303f675SLuigi Rizzo sprintf(buf, "%5d: ", i); 460b303f675SLuigi Rizzo i0 = i; 461b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 462b303f675SLuigi Rizzo sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 463b303f675SLuigi Rizzo i = i0; 464b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 465b303f675SLuigi Rizzo sprintf(buf+7+j + 48, "%c", 466b303f675SLuigi Rizzo isprint(p[i]) ? p[i] : '.'); 467b303f675SLuigi Rizzo printf("%s\n", buf); 468b303f675SLuigi Rizzo } 469b303f675SLuigi Rizzo } 470b303f675SLuigi Rizzo 47168b8534bSLuigi Rizzo /* 47268b8534bSLuigi Rizzo * Fill a packet with some payload. 473f8e4e36aSLuigi Rizzo * We create a UDP packet so the payload starts at 474f8e4e36aSLuigi Rizzo * 14+20+8 = 42 bytes. 47568b8534bSLuigi Rizzo */ 476f8e4e36aSLuigi Rizzo #ifdef __linux__ 477f8e4e36aSLuigi Rizzo #define uh_sport source 478f8e4e36aSLuigi Rizzo #define uh_dport dest 479f8e4e36aSLuigi Rizzo #define uh_ulen len 480f8e4e36aSLuigi Rizzo #define uh_sum check 481f8e4e36aSLuigi Rizzo #endif /* linux */ 482b303f675SLuigi Rizzo 483ce3ee1e7SLuigi Rizzo /* 484ce3ee1e7SLuigi Rizzo * increment the addressed in the packet, 485ce3ee1e7SLuigi Rizzo * starting from the least significant field. 486ce3ee1e7SLuigi Rizzo * DST_IP DST_PORT SRC_IP SRC_PORT 487ce3ee1e7SLuigi Rizzo */ 488ce3ee1e7SLuigi Rizzo static void 489ce3ee1e7SLuigi Rizzo update_addresses(struct pkt *pkt, struct glob_arg *g) 490ce3ee1e7SLuigi Rizzo { 491ce3ee1e7SLuigi Rizzo uint32_t a; 492ce3ee1e7SLuigi Rizzo uint16_t p; 493ce3ee1e7SLuigi Rizzo struct ip *ip = &pkt->ip; 494ce3ee1e7SLuigi Rizzo struct udphdr *udp = &pkt->udp; 495ce3ee1e7SLuigi Rizzo 496f2637526SLuigi Rizzo do { 497ce3ee1e7SLuigi Rizzo p = ntohs(udp->uh_sport); 498ce3ee1e7SLuigi Rizzo if (p < g->src_ip.port1) { /* just inc, no wrap */ 499ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(p + 1); 500f2637526SLuigi Rizzo break; 501ce3ee1e7SLuigi Rizzo } 502ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(g->src_ip.port0); 503ce3ee1e7SLuigi Rizzo 504ce3ee1e7SLuigi Rizzo a = ntohl(ip->ip_src.s_addr); 505ce3ee1e7SLuigi Rizzo if (a < g->src_ip.end) { /* just inc, no wrap */ 506ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(a + 1); 507f2637526SLuigi Rizzo break; 508ce3ee1e7SLuigi Rizzo } 509ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(g->src_ip.start); 510ce3ee1e7SLuigi Rizzo 511ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(g->src_ip.port0); 512ce3ee1e7SLuigi Rizzo p = ntohs(udp->uh_dport); 513ce3ee1e7SLuigi Rizzo if (p < g->dst_ip.port1) { /* just inc, no wrap */ 514ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(p + 1); 515f2637526SLuigi Rizzo break; 516ce3ee1e7SLuigi Rizzo } 517ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(g->dst_ip.port0); 518ce3ee1e7SLuigi Rizzo 519ce3ee1e7SLuigi Rizzo a = ntohl(ip->ip_dst.s_addr); 520ce3ee1e7SLuigi Rizzo if (a < g->dst_ip.end) { /* just inc, no wrap */ 521ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(a + 1); 522f2637526SLuigi Rizzo break; 523ce3ee1e7SLuigi Rizzo } 524ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(g->dst_ip.start); 525f2637526SLuigi Rizzo } while (0); 526f2637526SLuigi Rizzo // update checksum 527ce3ee1e7SLuigi Rizzo } 528ce3ee1e7SLuigi Rizzo 529ce3ee1e7SLuigi Rizzo /* 530ce3ee1e7SLuigi Rizzo * initialize one packet and prepare for the next one. 531ce3ee1e7SLuigi Rizzo * The copy could be done better instead of repeating it each time. 532ce3ee1e7SLuigi Rizzo */ 53368b8534bSLuigi Rizzo static void 53468b8534bSLuigi Rizzo initialize_packet(struct targ *targ) 53568b8534bSLuigi Rizzo { 53668b8534bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 53768b8534bSLuigi Rizzo struct ether_header *eh; 53868b8534bSLuigi Rizzo struct ip *ip; 53968b8534bSLuigi Rizzo struct udphdr *udp; 540f8e4e36aSLuigi Rizzo uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 541b303f675SLuigi Rizzo const char *payload = targ->g->options & OPT_INDIRECT ? 542ce3ee1e7SLuigi Rizzo indirect_payload : default_payload; 543f2637526SLuigi Rizzo int i, l0 = strlen(payload); 54468b8534bSLuigi Rizzo 545ce3ee1e7SLuigi Rizzo /* create a nice NUL-terminated string */ 546f2637526SLuigi Rizzo for (i = 0; i < paylen; i += l0) { 547f2637526SLuigi Rizzo if (l0 > paylen - i) 548f2637526SLuigi Rizzo l0 = paylen - i; // last round 549f2637526SLuigi Rizzo bcopy(payload, pkt->body + i, l0); 55068b8534bSLuigi Rizzo } 55168b8534bSLuigi Rizzo pkt->body[i-1] = '\0'; 55268b8534bSLuigi Rizzo ip = &pkt->ip; 553f8e4e36aSLuigi Rizzo 554ce3ee1e7SLuigi Rizzo /* prepare the headers */ 55568b8534bSLuigi Rizzo ip->ip_v = IPVERSION; 55668b8534bSLuigi Rizzo ip->ip_hl = 5; 55768b8534bSLuigi Rizzo ip->ip_id = 0; 55868b8534bSLuigi Rizzo ip->ip_tos = IPTOS_LOWDELAY; 55968b8534bSLuigi Rizzo ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 56068b8534bSLuigi Rizzo ip->ip_id = 0; 56168b8534bSLuigi Rizzo ip->ip_off = htons(IP_DF); /* Don't fragment */ 56268b8534bSLuigi Rizzo ip->ip_ttl = IPDEFTTL; 56368b8534bSLuigi Rizzo ip->ip_p = IPPROTO_UDP; 564ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 565ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 566f8e4e36aSLuigi Rizzo ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 567f8e4e36aSLuigi Rizzo 568f8e4e36aSLuigi Rizzo 569f8e4e36aSLuigi Rizzo udp = &pkt->udp; 570ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(targ->g->src_ip.port0); 571ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(targ->g->dst_ip.port0); 572f8e4e36aSLuigi Rizzo udp->uh_ulen = htons(paylen); 573f8e4e36aSLuigi Rizzo /* Magic: taken from sbin/dhclient/packet.c */ 574f8e4e36aSLuigi Rizzo udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 575f8e4e36aSLuigi Rizzo checksum(pkt->body, 576f8e4e36aSLuigi Rizzo paylen - sizeof(*udp), 577f8e4e36aSLuigi Rizzo checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 578f8e4e36aSLuigi Rizzo IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 579f8e4e36aSLuigi Rizzo ) 580f8e4e36aSLuigi Rizzo ) 581f8e4e36aSLuigi Rizzo )); 58268b8534bSLuigi Rizzo 58368b8534bSLuigi Rizzo eh = &pkt->eh; 584f8e4e36aSLuigi Rizzo bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 585f8e4e36aSLuigi Rizzo bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 58668b8534bSLuigi Rizzo eh->ether_type = htons(ETHERTYPE_IP); 58717885a7bSLuigi Rizzo 58817885a7bSLuigi Rizzo bzero(&pkt->vh, sizeof(pkt->vh)); 589b303f675SLuigi Rizzo // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 59068b8534bSLuigi Rizzo } 59168b8534bSLuigi Rizzo 59268b8534bSLuigi Rizzo 59368b8534bSLuigi Rizzo 59468b8534bSLuigi Rizzo /* 59568b8534bSLuigi Rizzo * create and enqueue a batch of packets on a ring. 59668b8534bSLuigi Rizzo * On the last one set NS_REPORT to tell the driver to generate 59768b8534bSLuigi Rizzo * an interrupt when done. 59868b8534bSLuigi Rizzo */ 59968b8534bSLuigi Rizzo static int 60017885a7bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 60117885a7bSLuigi Rizzo int size, struct glob_arg *g, u_int count, int options, 60217885a7bSLuigi Rizzo u_int nfrags) 60368b8534bSLuigi Rizzo { 60417885a7bSLuigi Rizzo u_int n, sent, cur = ring->cur; 605f2637526SLuigi Rizzo u_int fcnt; 60668b8534bSLuigi Rizzo 60717885a7bSLuigi Rizzo n = nm_ring_space(ring); 60817885a7bSLuigi Rizzo if (n < count) 60917885a7bSLuigi Rizzo count = n; 610ce3ee1e7SLuigi Rizzo if (count < nfrags) { 611ce3ee1e7SLuigi Rizzo D("truncating packet, no room for frags %d %d", 612ce3ee1e7SLuigi Rizzo count, nfrags); 613ce3ee1e7SLuigi Rizzo } 61499fb123fSLuigi Rizzo #if 0 61599fb123fSLuigi Rizzo if (options & (OPT_COPY | OPT_PREFETCH) ) { 61668b8534bSLuigi Rizzo for (sent = 0; sent < count; sent++) { 61768b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 61868b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 61968b8534bSLuigi Rizzo 620f2637526SLuigi Rizzo __builtin_prefetch(p); 62117885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 62299fb123fSLuigi Rizzo } 62399fb123fSLuigi Rizzo cur = ring->cur; 62499fb123fSLuigi Rizzo } 62599fb123fSLuigi Rizzo #endif 626ce3ee1e7SLuigi Rizzo for (fcnt = nfrags, sent = 0; sent < count; sent++) { 62799fb123fSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 62899fb123fSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 62999fb123fSLuigi Rizzo 630b303f675SLuigi Rizzo slot->flags = 0; 631b303f675SLuigi Rizzo if (options & OPT_INDIRECT) { 632b303f675SLuigi Rizzo slot->flags |= NS_INDIRECT; 63317885a7bSLuigi Rizzo slot->ptr = (uint64_t)frame; 634ce3ee1e7SLuigi Rizzo } else if (options & OPT_COPY) { 63517885a7bSLuigi Rizzo pkt_copy(frame, p, size); 636f2637526SLuigi Rizzo if (fcnt == nfrags) 637ce3ee1e7SLuigi Rizzo update_addresses(pkt, g); 638ce3ee1e7SLuigi Rizzo } else if (options & OPT_MEMCPY) { 63917885a7bSLuigi Rizzo memcpy(p, frame, size); 640f2637526SLuigi Rizzo if (fcnt == nfrags) 641ce3ee1e7SLuigi Rizzo update_addresses(pkt, g); 642ce3ee1e7SLuigi Rizzo } else if (options & OPT_PREFETCH) { 643f2637526SLuigi Rizzo __builtin_prefetch(p); 644ce3ee1e7SLuigi Rizzo } 645ce3ee1e7SLuigi Rizzo if (options & OPT_DUMP) 646ce3ee1e7SLuigi Rizzo dump_payload(p, size, ring, cur); 64768b8534bSLuigi Rizzo slot->len = size; 648ce3ee1e7SLuigi Rizzo if (--fcnt > 0) 649ce3ee1e7SLuigi Rizzo slot->flags |= NS_MOREFRAG; 650ce3ee1e7SLuigi Rizzo else 651ce3ee1e7SLuigi Rizzo fcnt = nfrags; 652ce3ee1e7SLuigi Rizzo if (sent == count - 1) { 653ce3ee1e7SLuigi Rizzo slot->flags &= ~NS_MOREFRAG; 65468b8534bSLuigi Rizzo slot->flags |= NS_REPORT; 655ce3ee1e7SLuigi Rizzo } 65617885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 65768b8534bSLuigi Rizzo } 65817885a7bSLuigi Rizzo ring->head = ring->cur = cur; 65968b8534bSLuigi Rizzo 66068b8534bSLuigi Rizzo return (sent); 66168b8534bSLuigi Rizzo } 66268b8534bSLuigi Rizzo 663f8e4e36aSLuigi Rizzo /* 664f8e4e36aSLuigi Rizzo * Send a packet, and wait for a response. 665f8e4e36aSLuigi Rizzo * The payload (after UDP header, ofs 42) has a 4-byte sequence 666f8e4e36aSLuigi Rizzo * followed by a struct timeval (or bintime?) 667f8e4e36aSLuigi Rizzo */ 668f8e4e36aSLuigi Rizzo #define PAY_OFS 42 /* where in the pkt... */ 669f8e4e36aSLuigi Rizzo 67068b8534bSLuigi Rizzo static void * 671f8e4e36aSLuigi Rizzo pinger_body(void *data) 67268b8534bSLuigi Rizzo { 67368b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 67468b8534bSLuigi Rizzo struct pollfd fds[1]; 67568b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 676f8e4e36aSLuigi Rizzo int i, rx = 0, n = targ->g->npackets; 67717885a7bSLuigi Rizzo void *frame; 67817885a7bSLuigi Rizzo int size; 67917885a7bSLuigi Rizzo 68017885a7bSLuigi Rizzo frame = &targ->pkt; 68117885a7bSLuigi Rizzo frame += sizeof(targ->pkt.vh) - targ->g->virt_header; 68217885a7bSLuigi Rizzo size = targ->g->pkt_size + targ->g->virt_header; 683e5ecae38SEd Maste 684f8e4e36aSLuigi Rizzo fds[0].fd = targ->fd; 685f8e4e36aSLuigi Rizzo fds[0].events = (POLLIN); 686f8e4e36aSLuigi Rizzo static uint32_t sent; 687f8e4e36aSLuigi Rizzo struct timespec ts, now, last_print; 688f8e4e36aSLuigi Rizzo uint32_t count = 0, min = 1000000000, av = 0; 689f8e4e36aSLuigi Rizzo 690f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 691f8e4e36aSLuigi Rizzo D("can only ping with 1 thread"); 692f8e4e36aSLuigi Rizzo return NULL; 693f95a30bdSEd Maste } 694f8e4e36aSLuigi Rizzo 695f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 69617885a7bSLuigi Rizzo now = last_print; 697f8e4e36aSLuigi Rizzo while (n == 0 || (int)sent < n) { 698f8e4e36aSLuigi Rizzo struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 699f8e4e36aSLuigi Rizzo struct netmap_slot *slot; 700f8e4e36aSLuigi Rizzo char *p; 70117885a7bSLuigi Rizzo for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ 702f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 70317885a7bSLuigi Rizzo slot->len = size; 704f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 705f8e4e36aSLuigi Rizzo 70617885a7bSLuigi Rizzo if (nm_ring_empty(ring)) { 707f8e4e36aSLuigi Rizzo D("-- ouch, cannot send"); 708f8e4e36aSLuigi Rizzo } else { 70917885a7bSLuigi Rizzo pkt_copy(frame, p, size); 710f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 711f8e4e36aSLuigi Rizzo bcopy(&sent, p+42, sizeof(sent)); 712f8e4e36aSLuigi Rizzo bcopy(&ts, p+46, sizeof(ts)); 713f8e4e36aSLuigi Rizzo sent++; 71417885a7bSLuigi Rizzo ring->head = ring->cur = nm_ring_next(ring, ring->cur); 715f8e4e36aSLuigi Rizzo } 716f8e4e36aSLuigi Rizzo } 717f8e4e36aSLuigi Rizzo /* should use a parameter to decide how often to send */ 718f8e4e36aSLuigi Rizzo if (poll(fds, 1, 3000) <= 0) { 71917885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 72017885a7bSLuigi Rizzo strerror(errno)); 721f8e4e36aSLuigi Rizzo continue; 722f8e4e36aSLuigi Rizzo } 723f8e4e36aSLuigi Rizzo /* see what we got back */ 724f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 725f8e4e36aSLuigi Rizzo ring = NETMAP_RXRING(nifp, i); 72617885a7bSLuigi Rizzo while (!nm_ring_empty(ring)) { 727f8e4e36aSLuigi Rizzo uint32_t seq; 728f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 729f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 730f8e4e36aSLuigi Rizzo 731f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &now); 732f8e4e36aSLuigi Rizzo bcopy(p+42, &seq, sizeof(seq)); 733f8e4e36aSLuigi Rizzo bcopy(p+46, &ts, sizeof(ts)); 734f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - ts.tv_sec; 735f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 736f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 737f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 738f8e4e36aSLuigi Rizzo ts.tv_sec--; 739f8e4e36aSLuigi Rizzo } 740f8e4e36aSLuigi Rizzo if (1) D("seq %d/%d delta %d.%09d", seq, sent, 741f8e4e36aSLuigi Rizzo (int)ts.tv_sec, (int)ts.tv_nsec); 742f8e4e36aSLuigi Rizzo if (ts.tv_nsec < (int)min) 743f8e4e36aSLuigi Rizzo min = ts.tv_nsec; 744f8e4e36aSLuigi Rizzo count ++; 745f8e4e36aSLuigi Rizzo av += ts.tv_nsec; 74617885a7bSLuigi Rizzo ring->head = ring->cur = nm_ring_next(ring, ring->cur); 747f8e4e36aSLuigi Rizzo rx++; 748f8e4e36aSLuigi Rizzo } 749f8e4e36aSLuigi Rizzo } 750f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 751f8e4e36aSLuigi Rizzo //usleep(100000); 752f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - last_print.tv_sec; 753f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 754f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 755f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 756f8e4e36aSLuigi Rizzo ts.tv_sec--; 757f8e4e36aSLuigi Rizzo } 758f8e4e36aSLuigi Rizzo if (ts.tv_sec >= 1) { 759f8e4e36aSLuigi Rizzo D("count %d min %d av %d", 760f8e4e36aSLuigi Rizzo count, min, av/count); 761f8e4e36aSLuigi Rizzo count = 0; 762f8e4e36aSLuigi Rizzo av = 0; 763f8e4e36aSLuigi Rizzo min = 100000000; 764f8e4e36aSLuigi Rizzo last_print = now; 765f8e4e36aSLuigi Rizzo } 766f8e4e36aSLuigi Rizzo } 767f8e4e36aSLuigi Rizzo return NULL; 768f8e4e36aSLuigi Rizzo } 769f8e4e36aSLuigi Rizzo 770f8e4e36aSLuigi Rizzo 771f8e4e36aSLuigi Rizzo /* 772f8e4e36aSLuigi Rizzo * reply to ping requests 773f8e4e36aSLuigi Rizzo */ 774f8e4e36aSLuigi Rizzo static void * 775f8e4e36aSLuigi Rizzo ponger_body(void *data) 776f8e4e36aSLuigi Rizzo { 777f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 778f8e4e36aSLuigi Rizzo struct pollfd fds[1]; 779f8e4e36aSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 780f8e4e36aSLuigi Rizzo struct netmap_ring *txring, *rxring; 781f8e4e36aSLuigi Rizzo int i, rx = 0, sent = 0, n = targ->g->npackets; 782f8e4e36aSLuigi Rizzo fds[0].fd = targ->fd; 783f8e4e36aSLuigi Rizzo fds[0].events = (POLLIN); 784f8e4e36aSLuigi Rizzo 785f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 786f8e4e36aSLuigi Rizzo D("can only reply ping with 1 thread"); 787f8e4e36aSLuigi Rizzo return NULL; 788f8e4e36aSLuigi Rizzo } 789f8e4e36aSLuigi Rizzo D("understood ponger %d but don't know how to do it", n); 790f8e4e36aSLuigi Rizzo while (n == 0 || sent < n) { 791f8e4e36aSLuigi Rizzo uint32_t txcur, txavail; 792f8e4e36aSLuigi Rizzo //#define BUSYWAIT 793f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 794f8e4e36aSLuigi Rizzo ioctl(fds[0].fd, NIOCRXSYNC, NULL); 795f8e4e36aSLuigi Rizzo #else 796f8e4e36aSLuigi Rizzo if (poll(fds, 1, 1000) <= 0) { 79717885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 79817885a7bSLuigi Rizzo strerror(errno)); 799f8e4e36aSLuigi Rizzo continue; 800f8e4e36aSLuigi Rizzo } 801f8e4e36aSLuigi Rizzo #endif 802f8e4e36aSLuigi Rizzo txring = NETMAP_TXRING(nifp, 0); 803f8e4e36aSLuigi Rizzo txcur = txring->cur; 80417885a7bSLuigi Rizzo txavail = nm_ring_space(txring); 805f8e4e36aSLuigi Rizzo /* see what we got back */ 806f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 807f8e4e36aSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 80817885a7bSLuigi Rizzo while (!nm_ring_empty(rxring)) { 809f8e4e36aSLuigi Rizzo uint16_t *spkt, *dpkt; 810f8e4e36aSLuigi Rizzo uint32_t cur = rxring->cur; 811f8e4e36aSLuigi Rizzo struct netmap_slot *slot = &rxring->slot[cur]; 812f8e4e36aSLuigi Rizzo char *src, *dst; 813f8e4e36aSLuigi Rizzo src = NETMAP_BUF(rxring, slot->buf_idx); 814f8e4e36aSLuigi Rizzo //D("got pkt %p of size %d", src, slot->len); 81517885a7bSLuigi Rizzo rxring->head = rxring->cur = nm_ring_next(rxring, cur); 816f8e4e36aSLuigi Rizzo rx++; 817f8e4e36aSLuigi Rizzo if (txavail == 0) 818f8e4e36aSLuigi Rizzo continue; 819f8e4e36aSLuigi Rizzo dst = NETMAP_BUF(txring, 820f8e4e36aSLuigi Rizzo txring->slot[txcur].buf_idx); 821f8e4e36aSLuigi Rizzo /* copy... */ 822f8e4e36aSLuigi Rizzo dpkt = (uint16_t *)dst; 823f8e4e36aSLuigi Rizzo spkt = (uint16_t *)src; 824f8e4e36aSLuigi Rizzo pkt_copy(src, dst, slot->len); 825f8e4e36aSLuigi Rizzo dpkt[0] = spkt[3]; 826f8e4e36aSLuigi Rizzo dpkt[1] = spkt[4]; 827f8e4e36aSLuigi Rizzo dpkt[2] = spkt[5]; 828f8e4e36aSLuigi Rizzo dpkt[3] = spkt[0]; 829f8e4e36aSLuigi Rizzo dpkt[4] = spkt[1]; 830f8e4e36aSLuigi Rizzo dpkt[5] = spkt[2]; 831f8e4e36aSLuigi Rizzo txring->slot[txcur].len = slot->len; 832f8e4e36aSLuigi Rizzo /* XXX swap src dst mac */ 83317885a7bSLuigi Rizzo txcur = nm_ring_next(txring, txcur); 834f8e4e36aSLuigi Rizzo txavail--; 835f8e4e36aSLuigi Rizzo sent++; 836f8e4e36aSLuigi Rizzo } 837f8e4e36aSLuigi Rizzo } 83817885a7bSLuigi Rizzo txring->head = txring->cur = txcur; 839f8e4e36aSLuigi Rizzo targ->count = sent; 840f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 841f8e4e36aSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 842f8e4e36aSLuigi Rizzo #endif 843f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 844f8e4e36aSLuigi Rizzo } 845f8e4e36aSLuigi Rizzo return NULL; 846f8e4e36aSLuigi Rizzo } 847f8e4e36aSLuigi Rizzo 8481cb4c501SLuigi Rizzo static __inline int 8491cb4c501SLuigi Rizzo timespec_ge(const struct timespec *a, const struct timespec *b) 8501cb4c501SLuigi Rizzo { 8511cb4c501SLuigi Rizzo 8521cb4c501SLuigi Rizzo if (a->tv_sec > b->tv_sec) 8531cb4c501SLuigi Rizzo return (1); 8541cb4c501SLuigi Rizzo if (a->tv_sec < b->tv_sec) 8551cb4c501SLuigi Rizzo return (0); 8561cb4c501SLuigi Rizzo if (a->tv_nsec >= b->tv_nsec) 8571cb4c501SLuigi Rizzo return (1); 8581cb4c501SLuigi Rizzo return (0); 8591cb4c501SLuigi Rizzo } 8601cb4c501SLuigi Rizzo 8611cb4c501SLuigi Rizzo static __inline struct timespec 8621cb4c501SLuigi Rizzo timeval2spec(const struct timeval *a) 8631cb4c501SLuigi Rizzo { 8641cb4c501SLuigi Rizzo struct timespec ts = { 8651cb4c501SLuigi Rizzo .tv_sec = a->tv_sec, 8661cb4c501SLuigi Rizzo .tv_nsec = a->tv_usec * 1000 8671cb4c501SLuigi Rizzo }; 8681cb4c501SLuigi Rizzo return ts; 8691cb4c501SLuigi Rizzo } 8701cb4c501SLuigi Rizzo 8711cb4c501SLuigi Rizzo static __inline struct timeval 8721cb4c501SLuigi Rizzo timespec2val(const struct timespec *a) 8731cb4c501SLuigi Rizzo { 8741cb4c501SLuigi Rizzo struct timeval tv = { 8751cb4c501SLuigi Rizzo .tv_sec = a->tv_sec, 8761cb4c501SLuigi Rizzo .tv_usec = a->tv_nsec / 1000 8771cb4c501SLuigi Rizzo }; 8781cb4c501SLuigi Rizzo return tv; 8791cb4c501SLuigi Rizzo } 8801cb4c501SLuigi Rizzo 8811cb4c501SLuigi Rizzo 88217885a7bSLuigi Rizzo static __inline struct timespec 88317885a7bSLuigi Rizzo timespec_add(struct timespec a, struct timespec b) 8841cb4c501SLuigi Rizzo { 88517885a7bSLuigi Rizzo struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; 88617885a7bSLuigi Rizzo if (ret.tv_nsec >= 1000000000) { 88717885a7bSLuigi Rizzo ret.tv_sec++; 88817885a7bSLuigi Rizzo ret.tv_nsec -= 1000000000; 8891cb4c501SLuigi Rizzo } 89017885a7bSLuigi Rizzo return ret; 8911cb4c501SLuigi Rizzo } 8921cb4c501SLuigi Rizzo 89317885a7bSLuigi Rizzo static __inline struct timespec 89417885a7bSLuigi Rizzo timespec_sub(struct timespec a, struct timespec b) 8951cb4c501SLuigi Rizzo { 89617885a7bSLuigi Rizzo struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; 89717885a7bSLuigi Rizzo if (ret.tv_nsec < 0) { 89817885a7bSLuigi Rizzo ret.tv_sec--; 89917885a7bSLuigi Rizzo ret.tv_nsec += 1000000000; 9001cb4c501SLuigi Rizzo } 90117885a7bSLuigi Rizzo return ret; 9021cb4c501SLuigi Rizzo } 9031cb4c501SLuigi Rizzo 904f8e4e36aSLuigi Rizzo 90517885a7bSLuigi Rizzo /* 90617885a7bSLuigi Rizzo * wait until ts, either busy or sleeping if more than 1ms. 90717885a7bSLuigi Rizzo * Return wakeup time. 90817885a7bSLuigi Rizzo */ 90917885a7bSLuigi Rizzo static struct timespec 91017885a7bSLuigi Rizzo wait_time(struct timespec ts) 91117885a7bSLuigi Rizzo { 91217885a7bSLuigi Rizzo for (;;) { 91317885a7bSLuigi Rizzo struct timespec w, cur; 91417885a7bSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 91517885a7bSLuigi Rizzo w = timespec_sub(ts, cur); 91617885a7bSLuigi Rizzo if (w.tv_sec < 0) 91717885a7bSLuigi Rizzo return cur; 91817885a7bSLuigi Rizzo else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 91917885a7bSLuigi Rizzo poll(NULL, 0, 1); 92017885a7bSLuigi Rizzo } 92117885a7bSLuigi Rizzo } 92217885a7bSLuigi Rizzo 923f8e4e36aSLuigi Rizzo static void * 924f8e4e36aSLuigi Rizzo sender_body(void *data) 925f8e4e36aSLuigi Rizzo { 926f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 927f8e4e36aSLuigi Rizzo 928f8e4e36aSLuigi Rizzo struct pollfd fds[1]; 929f8e4e36aSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 930f8e4e36aSLuigi Rizzo struct netmap_ring *txring; 931f8e4e36aSLuigi Rizzo int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 932f8e4e36aSLuigi Rizzo int options = targ->g->options | OPT_COPY; 93317885a7bSLuigi Rizzo struct timespec nexttime = { 0, 0}; // XXX silence compiler 9341cb4c501SLuigi Rizzo int rate_limit = targ->g->tx_rate; 93517885a7bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 93617885a7bSLuigi Rizzo void *frame; 93717885a7bSLuigi Rizzo int size; 93817885a7bSLuigi Rizzo 93917885a7bSLuigi Rizzo frame = pkt; 94017885a7bSLuigi Rizzo frame += sizeof(pkt->vh) - targ->g->virt_header; 94117885a7bSLuigi Rizzo size = targ->g->pkt_size + targ->g->virt_header; 942b303f675SLuigi Rizzo 943f8e4e36aSLuigi Rizzo D("start"); 94468b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 94568b8534bSLuigi Rizzo goto quit; 9468ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 94768b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 94868b8534bSLuigi Rizzo fds[0].fd = targ->fd; 94968b8534bSLuigi Rizzo fds[0].events = (POLLOUT); 95068b8534bSLuigi Rizzo 95168b8534bSLuigi Rizzo /* main loop.*/ 9521cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 9531cb4c501SLuigi Rizzo if (rate_limit) { 95417885a7bSLuigi Rizzo targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 9551cb4c501SLuigi Rizzo targ->tic.tv_nsec = 0; 95617885a7bSLuigi Rizzo wait_time(targ->tic); 9571cb4c501SLuigi Rizzo nexttime = targ->tic; 9581cb4c501SLuigi Rizzo } 959f2637526SLuigi Rizzo if (targ->g->dev_type == DEV_TAP) { 960f8e4e36aSLuigi Rizzo D("writing to file desc %d", targ->g->main_fd); 961f8e4e36aSLuigi Rizzo 962f8e4e36aSLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 96317885a7bSLuigi Rizzo if (write(targ->g->main_fd, frame, size) != -1) 964f8e4e36aSLuigi Rizzo sent++; 965ce3ee1e7SLuigi Rizzo update_addresses(pkt, targ->g); 966f8e4e36aSLuigi Rizzo if (i > 10000) { 967f8e4e36aSLuigi Rizzo targ->count = sent; 968f8e4e36aSLuigi Rizzo i = 0; 969f8e4e36aSLuigi Rizzo } 970f8e4e36aSLuigi Rizzo } 971f2637526SLuigi Rizzo #ifndef NO_PCAP 972f2637526SLuigi Rizzo } else if (targ->g->dev_type == DEV_PCAP) { 973f2637526SLuigi Rizzo pcap_t *p = targ->g->p; 974f2637526SLuigi Rizzo 975f2637526SLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 976f2637526SLuigi Rizzo if (pcap_inject(p, frame, size) != -1) 977f2637526SLuigi Rizzo sent++; 978f2637526SLuigi Rizzo update_addresses(pkt, targ->g); 979f2637526SLuigi Rizzo if (i > 10000) { 980f2637526SLuigi Rizzo targ->count = sent; 981f2637526SLuigi Rizzo i = 0; 982f2637526SLuigi Rizzo } 983f2637526SLuigi Rizzo } 984f2637526SLuigi Rizzo #endif /* NO_PCAP */ 98568b8534bSLuigi Rizzo } else { 9861cb4c501SLuigi Rizzo int tosend = 0; 987ce3ee1e7SLuigi Rizzo int frags = targ->g->frags; 988ce3ee1e7SLuigi Rizzo 989f8e4e36aSLuigi Rizzo while (!targ->cancel && (n == 0 || sent < n)) { 99068b8534bSLuigi Rizzo 9911cb4c501SLuigi Rizzo if (rate_limit && tosend <= 0) { 9921cb4c501SLuigi Rizzo tosend = targ->g->burst; 99317885a7bSLuigi Rizzo nexttime = timespec_add(nexttime, targ->g->tx_period); 99417885a7bSLuigi Rizzo wait_time(nexttime); 9951cb4c501SLuigi Rizzo } 9961cb4c501SLuigi Rizzo 99768b8534bSLuigi Rizzo /* 99868b8534bSLuigi Rizzo * wait for available room in the send queue(s) 99968b8534bSLuigi Rizzo */ 1000f8e4e36aSLuigi Rizzo if (poll(fds, 1, 2000) <= 0) { 10013fe77e68SEd Maste if (targ->cancel) 10023fe77e68SEd Maste break; 100317885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 100417885a7bSLuigi Rizzo strerror(errno)); 100517885a7bSLuigi Rizzo goto quit; 100617885a7bSLuigi Rizzo } 100717885a7bSLuigi Rizzo if (fds[0].revents & POLLERR) { 100817885a7bSLuigi Rizzo D("poll error"); 100968b8534bSLuigi Rizzo goto quit; 101068b8534bSLuigi Rizzo } 101168b8534bSLuigi Rizzo /* 101268b8534bSLuigi Rizzo * scan our queues and send on those with room 101368b8534bSLuigi Rizzo */ 1014f8e4e36aSLuigi Rizzo if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 1015f8e4e36aSLuigi Rizzo D("drop copy"); 101699fb123fSLuigi Rizzo options &= ~OPT_COPY; 1017f8e4e36aSLuigi Rizzo } 1018f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 10191cb4c501SLuigi Rizzo int m, limit = rate_limit ? tosend : targ->g->burst; 1020f8e4e36aSLuigi Rizzo if (n > 0 && n - sent < limit) 1021f8e4e36aSLuigi Rizzo limit = n - sent; 102268b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 102317885a7bSLuigi Rizzo if (nm_ring_empty(txring)) 102468b8534bSLuigi Rizzo continue; 1025ce3ee1e7SLuigi Rizzo if (frags > 1) 1026ce3ee1e7SLuigi Rizzo limit = ((limit + frags - 1) / frags) * frags; 1027ce3ee1e7SLuigi Rizzo 102817885a7bSLuigi Rizzo m = send_packets(txring, pkt, frame, size, targ->g, 1029ce3ee1e7SLuigi Rizzo limit, options, frags); 1030f2637526SLuigi Rizzo ND("limit %d tail %d frags %d m %d", 1031f2637526SLuigi Rizzo limit, txring->tail, frags, m); 103268b8534bSLuigi Rizzo sent += m; 103368b8534bSLuigi Rizzo targ->count = sent; 1034ce3ee1e7SLuigi Rizzo if (rate_limit) { 1035ce3ee1e7SLuigi Rizzo tosend -= m; 1036ce3ee1e7SLuigi Rizzo if (tosend <= 0) 1037ce3ee1e7SLuigi Rizzo break; 1038ce3ee1e7SLuigi Rizzo } 103968b8534bSLuigi Rizzo } 104068b8534bSLuigi Rizzo } 104199fb123fSLuigi Rizzo /* flush any remaining packets */ 104268b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 104368b8534bSLuigi Rizzo 104468b8534bSLuigi Rizzo /* final part: wait all the TX queues to be empty. */ 104568b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 104668b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 104717885a7bSLuigi Rizzo while (nm_tx_pending(txring)) { 104868b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 104968b8534bSLuigi Rizzo usleep(1); /* wait 1 tick */ 105068b8534bSLuigi Rizzo } 105168b8534bSLuigi Rizzo } 1052f2637526SLuigi Rizzo } /* end DEV_NETMAP */ 105368b8534bSLuigi Rizzo 10541cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 105568b8534bSLuigi Rizzo targ->completed = 1; 105668b8534bSLuigi Rizzo targ->count = sent; 105768b8534bSLuigi Rizzo 105868b8534bSLuigi Rizzo quit: 105968b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 106068b8534bSLuigi Rizzo targ->used = 0; 106168b8534bSLuigi Rizzo 106268b8534bSLuigi Rizzo return (NULL); 106368b8534bSLuigi Rizzo } 106468b8534bSLuigi Rizzo 106568b8534bSLuigi Rizzo 1066f2637526SLuigi Rizzo #ifndef NO_PCAP 106768b8534bSLuigi Rizzo static void 1068f8e4e36aSLuigi Rizzo receive_pcap(u_char *user, const struct pcap_pkthdr * h, 1069f8e4e36aSLuigi Rizzo const u_char * bytes) 107068b8534bSLuigi Rizzo { 107168b8534bSLuigi Rizzo int *count = (int *)user; 1072f8e4e36aSLuigi Rizzo (void)h; /* UNUSED */ 1073f8e4e36aSLuigi Rizzo (void)bytes; /* UNUSED */ 107468b8534bSLuigi Rizzo (*count)++; 107568b8534bSLuigi Rizzo } 1076f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 107768b8534bSLuigi Rizzo 107868b8534bSLuigi Rizzo static int 1079b303f675SLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int dump) 108068b8534bSLuigi Rizzo { 108117885a7bSLuigi Rizzo u_int cur, rx, n; 108268b8534bSLuigi Rizzo 108368b8534bSLuigi Rizzo cur = ring->cur; 108417885a7bSLuigi Rizzo n = nm_ring_space(ring); 108517885a7bSLuigi Rizzo if (n < limit) 108617885a7bSLuigi Rizzo limit = n; 108768b8534bSLuigi Rizzo for (rx = 0; rx < limit; rx++) { 108868b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 108968b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 109068b8534bSLuigi Rizzo 1091b303f675SLuigi Rizzo if (dump) 1092b303f675SLuigi Rizzo dump_payload(p, slot->len, ring, cur); 109368b8534bSLuigi Rizzo 109417885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 109568b8534bSLuigi Rizzo } 109617885a7bSLuigi Rizzo ring->head = ring->cur = cur; 109768b8534bSLuigi Rizzo 109868b8534bSLuigi Rizzo return (rx); 109968b8534bSLuigi Rizzo } 110068b8534bSLuigi Rizzo 110168b8534bSLuigi Rizzo static void * 110268b8534bSLuigi Rizzo receiver_body(void *data) 110368b8534bSLuigi Rizzo { 110468b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 110568b8534bSLuigi Rizzo struct pollfd fds[1]; 110668b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 110768b8534bSLuigi Rizzo struct netmap_ring *rxring; 1108f8e4e36aSLuigi Rizzo int i; 1109f8e4e36aSLuigi Rizzo uint64_t received = 0; 111068b8534bSLuigi Rizzo 111168b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 111268b8534bSLuigi Rizzo goto quit; 111368b8534bSLuigi Rizzo 11148ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 111568b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 111668b8534bSLuigi Rizzo fds[0].fd = targ->fd; 111768b8534bSLuigi Rizzo fds[0].events = (POLLIN); 111868b8534bSLuigi Rizzo 111968b8534bSLuigi Rizzo /* unbounded wait for the first packet. */ 1120f8e4e36aSLuigi Rizzo for (;;) { 112168b8534bSLuigi Rizzo i = poll(fds, 1, 1000); 112268b8534bSLuigi Rizzo if (i > 0 && !(fds[0].revents & POLLERR)) 112368b8534bSLuigi Rizzo break; 112417885a7bSLuigi Rizzo RD(1, "waiting for initial packets, poll returns %d %d", i, fds[0].revents); 112568b8534bSLuigi Rizzo } 112668b8534bSLuigi Rizzo 112768b8534bSLuigi Rizzo /* main loop, exit after 1s silence */ 11281cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1129f2637526SLuigi Rizzo if (targ->g->dev_type == DEV_TAP) { 1130f8e4e36aSLuigi Rizzo D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd); 1131f8e4e36aSLuigi Rizzo while (!targ->cancel) { 1132f8e4e36aSLuigi Rizzo char buf[2048]; 1133f8e4e36aSLuigi Rizzo /* XXX should we poll ? */ 1134f8e4e36aSLuigi Rizzo if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 1135f8e4e36aSLuigi Rizzo targ->count++; 1136f8e4e36aSLuigi Rizzo } 1137f2637526SLuigi Rizzo #ifndef NO_PCAP 1138f2637526SLuigi Rizzo } else if (targ->g->dev_type == DEV_PCAP) { 1139f2637526SLuigi Rizzo while (!targ->cancel) { 1140f2637526SLuigi Rizzo /* XXX should we poll ? */ 1141f2637526SLuigi Rizzo pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 1142f2637526SLuigi Rizzo } 1143f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 114468b8534bSLuigi Rizzo } else { 1145b303f675SLuigi Rizzo int dump = targ->g->options & OPT_DUMP; 11463fe77e68SEd Maste while (!targ->cancel) { 114768b8534bSLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 114868b8534bSLuigi Rizzo before quitting. */ 11491cb4c501SLuigi Rizzo if (poll(fds, 1, 1 * 1000) <= 0 && !targ->g->forever) { 11501cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 11518ce070c1SUlrich Spörlein targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 115268b8534bSLuigi Rizzo break; 115368b8534bSLuigi Rizzo } 115468b8534bSLuigi Rizzo 115517885a7bSLuigi Rizzo if (fds[0].revents & POLLERR) { 115617885a7bSLuigi Rizzo D("poll err"); 115717885a7bSLuigi Rizzo goto quit; 115817885a7bSLuigi Rizzo } 115917885a7bSLuigi Rizzo 116068b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 116168b8534bSLuigi Rizzo int m; 116268b8534bSLuigi Rizzo 116368b8534bSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 116417885a7bSLuigi Rizzo if (nm_ring_empty(rxring)) 116568b8534bSLuigi Rizzo continue; 116668b8534bSLuigi Rizzo 1167b303f675SLuigi Rizzo m = receive_packets(rxring, targ->g->burst, dump); 116868b8534bSLuigi Rizzo received += m; 116968b8534bSLuigi Rizzo } 1170f8e4e36aSLuigi Rizzo targ->count = received; 117168b8534bSLuigi Rizzo 117268b8534bSLuigi Rizzo // tell the card we have read the data 117368b8534bSLuigi Rizzo //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 117468b8534bSLuigi Rizzo } 117568b8534bSLuigi Rizzo } 117668b8534bSLuigi Rizzo 117768b8534bSLuigi Rizzo targ->completed = 1; 117868b8534bSLuigi Rizzo targ->count = received; 117968b8534bSLuigi Rizzo 118068b8534bSLuigi Rizzo quit: 118168b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 118268b8534bSLuigi Rizzo targ->used = 0; 118368b8534bSLuigi Rizzo 118468b8534bSLuigi Rizzo return (NULL); 118568b8534bSLuigi Rizzo } 118668b8534bSLuigi Rizzo 1187f8e4e36aSLuigi Rizzo /* very crude code to print a number in normalized form. 1188f8e4e36aSLuigi Rizzo * Caller has to make sure that the buffer is large enough. 1189f8e4e36aSLuigi Rizzo */ 1190f8e4e36aSLuigi Rizzo static const char * 1191f8e4e36aSLuigi Rizzo norm(char *buf, double val) 119266a698c9SEd Maste { 1193f8e4e36aSLuigi Rizzo char *units[] = { "", "K", "M", "G" }; 1194f8e4e36aSLuigi Rizzo u_int i; 119566a698c9SEd Maste 1196f8e4e36aSLuigi Rizzo for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++) 119766a698c9SEd Maste val /= 1000; 1198f8e4e36aSLuigi Rizzo sprintf(buf, "%.2f %s", val, units[i]); 1199f8e4e36aSLuigi Rizzo return buf; 120066a698c9SEd Maste } 120166a698c9SEd Maste 120268b8534bSLuigi Rizzo static void 120368b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta) 120468b8534bSLuigi Rizzo { 1205f8e4e36aSLuigi Rizzo double bw, raw_bw, pps; 1206f8e4e36aSLuigi Rizzo char b1[40], b2[80], b3[80]; 120768b8534bSLuigi Rizzo 1208506cc70cSLuigi Rizzo printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n", 120968b8534bSLuigi Rizzo sent, size, delta); 1210f8e4e36aSLuigi Rizzo if (delta == 0) 1211f8e4e36aSLuigi Rizzo delta = 1e-6; 1212f8e4e36aSLuigi Rizzo if (size < 60) /* correct for min packet size */ 1213f8e4e36aSLuigi Rizzo size = 60; 1214f8e4e36aSLuigi Rizzo pps = sent / delta; 1215f8e4e36aSLuigi Rizzo bw = (8.0 * size * sent) / delta; 1216f8e4e36aSLuigi Rizzo /* raw packets have4 bytes crc + 20 bytes framing */ 1217f8e4e36aSLuigi Rizzo raw_bw = (8.0 * (size + 24) * sent) / delta; 121866a698c9SEd Maste 1219f8e4e36aSLuigi Rizzo printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1220f8e4e36aSLuigi Rizzo norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 122168b8534bSLuigi Rizzo } 122268b8534bSLuigi Rizzo 122368b8534bSLuigi Rizzo 122468b8534bSLuigi Rizzo static void 122568b8534bSLuigi Rizzo rx_output(uint64_t received, double delta) 122668b8534bSLuigi Rizzo { 1227f8e4e36aSLuigi Rizzo double pps; 1228f8e4e36aSLuigi Rizzo char b1[40]; 122968b8534bSLuigi Rizzo 1230506cc70cSLuigi Rizzo printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta); 1231f8e4e36aSLuigi Rizzo 1232f8e4e36aSLuigi Rizzo if (delta == 0) 1233f8e4e36aSLuigi Rizzo delta = 1e-6; 1234f8e4e36aSLuigi Rizzo pps = received / delta; 1235f8e4e36aSLuigi Rizzo printf("Speed: %spps\n", norm(b1, pps)); 123668b8534bSLuigi Rizzo } 123768b8534bSLuigi Rizzo 123868b8534bSLuigi Rizzo static void 123968b8534bSLuigi Rizzo usage(void) 124068b8534bSLuigi Rizzo { 124168b8534bSLuigi Rizzo const char *cmd = "pkt-gen"; 124268b8534bSLuigi Rizzo fprintf(stderr, 124368b8534bSLuigi Rizzo "Usage:\n" 124468b8534bSLuigi Rizzo "%s arguments\n" 124568b8534bSLuigi Rizzo "\t-i interface interface name\n" 1246f8e4e36aSLuigi Rizzo "\t-f function tx rx ping pong\n" 1247f8e4e36aSLuigi Rizzo "\t-n count number of iterations (can be 0)\n" 1248f8e4e36aSLuigi Rizzo "\t-t pkts_to_send also forces tx mode\n" 1249f8e4e36aSLuigi Rizzo "\t-r pkts_to_receive also forces rx mode\n" 1250ce3ee1e7SLuigi Rizzo "\t-l pkt_size in bytes excluding CRC\n" 1251ce3ee1e7SLuigi Rizzo "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1252ce3ee1e7SLuigi Rizzo "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1253ce3ee1e7SLuigi Rizzo "\t-D dst-mac\n" 1254ce3ee1e7SLuigi Rizzo "\t-S src-mac\n" 1255f8e4e36aSLuigi Rizzo "\t-a cpu_id use setaffinity\n" 125668b8534bSLuigi Rizzo "\t-b burst size testing, mostly\n" 125768b8534bSLuigi Rizzo "\t-c cores cores to use\n" 125868b8534bSLuigi Rizzo "\t-p threads processes/threads to use\n" 125968b8534bSLuigi Rizzo "\t-T report_ms milliseconds between reports\n" 1260f8e4e36aSLuigi Rizzo "\t-P use libpcap instead of netmap\n" 126168b8534bSLuigi Rizzo "\t-w wait_for_link_time in seconds\n" 1262ce3ee1e7SLuigi Rizzo "\t-R rate in packets per second\n" 1263ce3ee1e7SLuigi Rizzo "\t-X dump payload\n" 126417885a7bSLuigi Rizzo "\t-H len add empty virtio-net-header with size 'len'\n" 126517885a7bSLuigi Rizzo "\t-h use host ring\n" 126668b8534bSLuigi Rizzo "", 126768b8534bSLuigi Rizzo cmd); 126868b8534bSLuigi Rizzo 126968b8534bSLuigi Rizzo exit(0); 127068b8534bSLuigi Rizzo } 127168b8534bSLuigi Rizzo 1272f8e4e36aSLuigi Rizzo static void 1273f8e4e36aSLuigi Rizzo start_threads(struct glob_arg *g) 1274f8e4e36aSLuigi Rizzo { 1275f8e4e36aSLuigi Rizzo int i; 1276f8e4e36aSLuigi Rizzo 1277f8e4e36aSLuigi Rizzo targs = calloc(g->nthreads, sizeof(*targs)); 1278f8e4e36aSLuigi Rizzo /* 1279f8e4e36aSLuigi Rizzo * Now create the desired number of threads, each one 1280f8e4e36aSLuigi Rizzo * using a single descriptor. 1281f8e4e36aSLuigi Rizzo */ 1282f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 1283f8e4e36aSLuigi Rizzo bzero(&targs[i], sizeof(targs[i])); 1284f8e4e36aSLuigi Rizzo targs[i].fd = -1; /* default, with pcap */ 1285f8e4e36aSLuigi Rizzo targs[i].g = g; 1286f8e4e36aSLuigi Rizzo 1287f8e4e36aSLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 1288f8e4e36aSLuigi Rizzo struct nmreq tifreq; 1289f8e4e36aSLuigi Rizzo int tfd; 1290f8e4e36aSLuigi Rizzo 1291f8e4e36aSLuigi Rizzo /* register interface. */ 1292f8e4e36aSLuigi Rizzo tfd = open("/dev/netmap", O_RDWR); 1293f8e4e36aSLuigi Rizzo if (tfd == -1) { 129417885a7bSLuigi Rizzo D("Unable to open /dev/netmap: %s", strerror(errno)); 1295f8e4e36aSLuigi Rizzo continue; 1296f8e4e36aSLuigi Rizzo } 1297f8e4e36aSLuigi Rizzo targs[i].fd = tfd; 1298f8e4e36aSLuigi Rizzo 1299f8e4e36aSLuigi Rizzo bzero(&tifreq, sizeof(tifreq)); 1300f8e4e36aSLuigi Rizzo strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name)); 1301f8e4e36aSLuigi Rizzo tifreq.nr_version = NETMAP_API; 130217885a7bSLuigi Rizzo if (g->host_ring) { 130317885a7bSLuigi Rizzo tifreq.nr_ringid = NETMAP_SW_RING; 130417885a7bSLuigi Rizzo } else { 1305f8e4e36aSLuigi Rizzo tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 130617885a7bSLuigi Rizzo } 1307ce3ee1e7SLuigi Rizzo parse_nmr_config(g->nmr_config, &tifreq); 1308f8e4e36aSLuigi Rizzo 1309f8e4e36aSLuigi Rizzo /* 1310f8e4e36aSLuigi Rizzo * if we are acting as a receiver only, do not touch the transmit ring. 1311f8e4e36aSLuigi Rizzo * This is not the default because many apps may use the interface 1312f8e4e36aSLuigi Rizzo * in both directions, but a pure receiver does not. 1313f8e4e36aSLuigi Rizzo */ 1314f8e4e36aSLuigi Rizzo if (g->td_body == receiver_body) { 1315f8e4e36aSLuigi Rizzo tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 1316f8e4e36aSLuigi Rizzo } 1317f8e4e36aSLuigi Rizzo 1318f8e4e36aSLuigi Rizzo if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 131917885a7bSLuigi Rizzo D("Unable to register %s: %s", g->ifname, strerror(errno)); 1320f8e4e36aSLuigi Rizzo continue; 1321f8e4e36aSLuigi Rizzo } 1322ce3ee1e7SLuigi Rizzo D("memsize is %d MB", tifreq.nr_memsize >> 20); 1323f8e4e36aSLuigi Rizzo targs[i].nmr = tifreq; 1324f8e4e36aSLuigi Rizzo targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset); 1325ce3ee1e7SLuigi Rizzo D("nifp flags 0x%x", targs[i].nifp->ni_flags); 1326f8e4e36aSLuigi Rizzo /* start threads. */ 132717885a7bSLuigi Rizzo if (g->host_ring) { 132817885a7bSLuigi Rizzo targs[i].qfirst = (g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings); 132917885a7bSLuigi Rizzo targs[i].qlast = targs[i].qfirst + 1; 133017885a7bSLuigi Rizzo } else { 1331f8e4e36aSLuigi Rizzo targs[i].qfirst = (g->nthreads > 1) ? i : 0; 1332f8e4e36aSLuigi Rizzo targs[i].qlast = (g->nthreads > 1) ? i+1 : 1333f8e4e36aSLuigi Rizzo (g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings); 133417885a7bSLuigi Rizzo } 1335f8e4e36aSLuigi Rizzo } else { 1336f8e4e36aSLuigi Rizzo targs[i].fd = g->main_fd; 1337f8e4e36aSLuigi Rizzo } 1338f8e4e36aSLuigi Rizzo targs[i].used = 1; 1339f8e4e36aSLuigi Rizzo targs[i].me = i; 1340f8e4e36aSLuigi Rizzo if (g->affinity >= 0) { 1341f8e4e36aSLuigi Rizzo if (g->affinity < g->cpus) 1342f8e4e36aSLuigi Rizzo targs[i].affinity = g->affinity; 1343f8e4e36aSLuigi Rizzo else 1344f8e4e36aSLuigi Rizzo targs[i].affinity = i % g->cpus; 1345f8e4e36aSLuigi Rizzo } else 1346f8e4e36aSLuigi Rizzo targs[i].affinity = -1; 1347f8e4e36aSLuigi Rizzo /* default, init packets */ 1348f8e4e36aSLuigi Rizzo initialize_packet(&targs[i]); 1349f8e4e36aSLuigi Rizzo 1350f8e4e36aSLuigi Rizzo if (pthread_create(&targs[i].thread, NULL, g->td_body, 1351f8e4e36aSLuigi Rizzo &targs[i]) == -1) { 135217885a7bSLuigi Rizzo D("Unable to create thread %d: %s", i, strerror(errno)); 1353f8e4e36aSLuigi Rizzo targs[i].used = 0; 1354f8e4e36aSLuigi Rizzo } 1355f8e4e36aSLuigi Rizzo } 1356f8e4e36aSLuigi Rizzo } 1357f8e4e36aSLuigi Rizzo 1358f8e4e36aSLuigi Rizzo static void 1359f8e4e36aSLuigi Rizzo main_thread(struct glob_arg *g) 1360f8e4e36aSLuigi Rizzo { 1361f8e4e36aSLuigi Rizzo int i; 1362f8e4e36aSLuigi Rizzo 1363f8e4e36aSLuigi Rizzo uint64_t prev = 0; 1364f8e4e36aSLuigi Rizzo uint64_t count = 0; 1365f8e4e36aSLuigi Rizzo double delta_t; 1366f8e4e36aSLuigi Rizzo struct timeval tic, toc; 1367f8e4e36aSLuigi Rizzo 1368f8e4e36aSLuigi Rizzo gettimeofday(&toc, NULL); 1369f8e4e36aSLuigi Rizzo for (;;) { 1370f8e4e36aSLuigi Rizzo struct timeval now, delta; 1371f8e4e36aSLuigi Rizzo uint64_t pps, usec, my_count, npkts; 1372f8e4e36aSLuigi Rizzo int done = 0; 1373f8e4e36aSLuigi Rizzo 1374f8e4e36aSLuigi Rizzo delta.tv_sec = g->report_interval/1000; 1375f8e4e36aSLuigi Rizzo delta.tv_usec = (g->report_interval%1000)*1000; 1376f8e4e36aSLuigi Rizzo select(0, NULL, NULL, NULL, &delta); 1377f8e4e36aSLuigi Rizzo gettimeofday(&now, NULL); 1378f8e4e36aSLuigi Rizzo time_second = now.tv_sec; 1379f8e4e36aSLuigi Rizzo timersub(&now, &toc, &toc); 1380f8e4e36aSLuigi Rizzo my_count = 0; 1381f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 1382f8e4e36aSLuigi Rizzo my_count += targs[i].count; 1383f8e4e36aSLuigi Rizzo if (targs[i].used == 0) 1384f8e4e36aSLuigi Rizzo done++; 1385f8e4e36aSLuigi Rizzo } 1386f8e4e36aSLuigi Rizzo usec = toc.tv_sec* 1000000 + toc.tv_usec; 1387f8e4e36aSLuigi Rizzo if (usec < 10000) 1388f8e4e36aSLuigi Rizzo continue; 1389f8e4e36aSLuigi Rizzo npkts = my_count - prev; 1390f8e4e36aSLuigi Rizzo pps = (npkts*1000000 + usec/2) / usec; 1391f8e4e36aSLuigi Rizzo D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)", 1392f8e4e36aSLuigi Rizzo pps, npkts, usec); 1393f8e4e36aSLuigi Rizzo prev = my_count; 1394f8e4e36aSLuigi Rizzo toc = now; 1395f8e4e36aSLuigi Rizzo if (done == g->nthreads) 1396f8e4e36aSLuigi Rizzo break; 1397f8e4e36aSLuigi Rizzo } 1398f8e4e36aSLuigi Rizzo 1399f8e4e36aSLuigi Rizzo timerclear(&tic); 1400f8e4e36aSLuigi Rizzo timerclear(&toc); 1401f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 14021cb4c501SLuigi Rizzo struct timespec t_tic, t_toc; 1403f8e4e36aSLuigi Rizzo /* 1404f8e4e36aSLuigi Rizzo * Join active threads, unregister interfaces and close 1405f8e4e36aSLuigi Rizzo * file descriptors. 1406f8e4e36aSLuigi Rizzo */ 14071cb4c501SLuigi Rizzo if (targs[i].used) 1408f8e4e36aSLuigi Rizzo pthread_join(targs[i].thread, NULL); 1409f8e4e36aSLuigi Rizzo close(targs[i].fd); 1410f8e4e36aSLuigi Rizzo 1411f8e4e36aSLuigi Rizzo if (targs[i].completed == 0) 1412f8e4e36aSLuigi Rizzo D("ouch, thread %d exited with error", i); 1413f8e4e36aSLuigi Rizzo 1414f8e4e36aSLuigi Rizzo /* 1415f8e4e36aSLuigi Rizzo * Collect threads output and extract information about 1416f8e4e36aSLuigi Rizzo * how long it took to send all the packets. 1417f8e4e36aSLuigi Rizzo */ 1418f8e4e36aSLuigi Rizzo count += targs[i].count; 14191cb4c501SLuigi Rizzo t_tic = timeval2spec(&tic); 14201cb4c501SLuigi Rizzo t_toc = timeval2spec(&toc); 14211cb4c501SLuigi Rizzo if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 14221cb4c501SLuigi Rizzo tic = timespec2val(&targs[i].tic); 14231cb4c501SLuigi Rizzo if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 14241cb4c501SLuigi Rizzo toc = timespec2val(&targs[i].toc); 1425f8e4e36aSLuigi Rizzo } 1426f8e4e36aSLuigi Rizzo 1427f8e4e36aSLuigi Rizzo /* print output. */ 1428f8e4e36aSLuigi Rizzo timersub(&toc, &tic, &toc); 1429f8e4e36aSLuigi Rizzo delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1430f8e4e36aSLuigi Rizzo if (g->td_body == sender_body) 1431f8e4e36aSLuigi Rizzo tx_output(count, g->pkt_size, delta_t); 1432f8e4e36aSLuigi Rizzo else 1433f8e4e36aSLuigi Rizzo rx_output(count, delta_t); 1434f8e4e36aSLuigi Rizzo 1435f8e4e36aSLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 1436f8e4e36aSLuigi Rizzo munmap(g->mmap_addr, g->mmap_size); 1437f8e4e36aSLuigi Rizzo close(g->main_fd); 1438f8e4e36aSLuigi Rizzo } 1439f8e4e36aSLuigi Rizzo } 1440f8e4e36aSLuigi Rizzo 1441f8e4e36aSLuigi Rizzo 1442f8e4e36aSLuigi Rizzo struct sf { 1443f8e4e36aSLuigi Rizzo char *key; 1444f8e4e36aSLuigi Rizzo void *f; 1445f8e4e36aSLuigi Rizzo }; 1446f8e4e36aSLuigi Rizzo 1447f8e4e36aSLuigi Rizzo static struct sf func[] = { 1448f8e4e36aSLuigi Rizzo { "tx", sender_body }, 1449f8e4e36aSLuigi Rizzo { "rx", receiver_body }, 1450f8e4e36aSLuigi Rizzo { "ping", pinger_body }, 1451f8e4e36aSLuigi Rizzo { "pong", ponger_body }, 1452f8e4e36aSLuigi Rizzo { NULL, NULL } 1453f8e4e36aSLuigi Rizzo }; 1454f8e4e36aSLuigi Rizzo 1455f8e4e36aSLuigi Rizzo static int 1456f8e4e36aSLuigi Rizzo tap_alloc(char *dev) 1457f8e4e36aSLuigi Rizzo { 1458f8e4e36aSLuigi Rizzo struct ifreq ifr; 1459f8e4e36aSLuigi Rizzo int fd, err; 1460f8e4e36aSLuigi Rizzo char *clonedev = TAP_CLONEDEV; 1461f8e4e36aSLuigi Rizzo 1462f8e4e36aSLuigi Rizzo (void)err; 1463f8e4e36aSLuigi Rizzo (void)dev; 1464f8e4e36aSLuigi Rizzo /* Arguments taken by the function: 1465f8e4e36aSLuigi Rizzo * 1466f8e4e36aSLuigi Rizzo * char *dev: the name of an interface (or '\0'). MUST have enough 1467f8e4e36aSLuigi Rizzo * space to hold the interface name if '\0' is passed 1468f8e4e36aSLuigi Rizzo * int flags: interface flags (eg, IFF_TUN etc.) 1469f8e4e36aSLuigi Rizzo */ 1470f8e4e36aSLuigi Rizzo 1471f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 1472f8e4e36aSLuigi Rizzo if (dev[3]) { /* tapSomething */ 1473f8e4e36aSLuigi Rizzo static char buf[128]; 1474f8e4e36aSLuigi Rizzo snprintf(buf, sizeof(buf), "/dev/%s", dev); 1475f8e4e36aSLuigi Rizzo clonedev = buf; 1476f8e4e36aSLuigi Rizzo } 1477f8e4e36aSLuigi Rizzo #endif 1478f8e4e36aSLuigi Rizzo /* open the device */ 1479f8e4e36aSLuigi Rizzo if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1480f8e4e36aSLuigi Rizzo return fd; 1481f8e4e36aSLuigi Rizzo } 1482f8e4e36aSLuigi Rizzo D("%s open successful", clonedev); 1483f8e4e36aSLuigi Rizzo 1484f8e4e36aSLuigi Rizzo /* preparation of the struct ifr, of type "struct ifreq" */ 1485f8e4e36aSLuigi Rizzo memset(&ifr, 0, sizeof(ifr)); 1486f8e4e36aSLuigi Rizzo 1487f8e4e36aSLuigi Rizzo #ifdef linux 1488f8e4e36aSLuigi Rizzo ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1489f8e4e36aSLuigi Rizzo 1490f8e4e36aSLuigi Rizzo if (*dev) { 1491f8e4e36aSLuigi Rizzo /* if a device name was specified, put it in the structure; otherwise, 1492f8e4e36aSLuigi Rizzo * the kernel will try to allocate the "next" device of the 1493f8e4e36aSLuigi Rizzo * specified type */ 1494f8e4e36aSLuigi Rizzo strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1495f8e4e36aSLuigi Rizzo } 1496f8e4e36aSLuigi Rizzo 1497f8e4e36aSLuigi Rizzo /* try to create the device */ 1498f8e4e36aSLuigi Rizzo if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 149917885a7bSLuigi Rizzo D("failed to to a TUNSETIFF: %s", strerror(errno)); 1500f8e4e36aSLuigi Rizzo close(fd); 1501f8e4e36aSLuigi Rizzo return err; 1502f8e4e36aSLuigi Rizzo } 1503f8e4e36aSLuigi Rizzo 1504f8e4e36aSLuigi Rizzo /* if the operation was successful, write back the name of the 1505f8e4e36aSLuigi Rizzo * interface to the variable "dev", so the caller can know 1506f8e4e36aSLuigi Rizzo * it. Note that the caller MUST reserve space in *dev (see calling 1507f8e4e36aSLuigi Rizzo * code below) */ 1508f8e4e36aSLuigi Rizzo strcpy(dev, ifr.ifr_name); 1509f8e4e36aSLuigi Rizzo D("new name is %s", dev); 1510f8e4e36aSLuigi Rizzo #endif /* linux */ 1511f8e4e36aSLuigi Rizzo 1512f8e4e36aSLuigi Rizzo /* this is the special file descriptor that the caller will use to talk 1513f8e4e36aSLuigi Rizzo * with the virtual interface */ 1514f8e4e36aSLuigi Rizzo return fd; 1515f8e4e36aSLuigi Rizzo } 151668b8534bSLuigi Rizzo 151768b8534bSLuigi Rizzo int 151868b8534bSLuigi Rizzo main(int arc, char **argv) 151968b8534bSLuigi Rizzo { 1520f8e4e36aSLuigi Rizzo int i; 152168b8534bSLuigi Rizzo 152268b8534bSLuigi Rizzo struct glob_arg g; 152368b8534bSLuigi Rizzo 152468b8534bSLuigi Rizzo struct nmreq nmr; 152568b8534bSLuigi Rizzo int ch; 152668b8534bSLuigi Rizzo int wait_link = 2; 152768b8534bSLuigi Rizzo int devqueues = 1; /* how many device queues */ 152868b8534bSLuigi Rizzo 152968b8534bSLuigi Rizzo bzero(&g, sizeof(g)); 153068b8534bSLuigi Rizzo 1531f8e4e36aSLuigi Rizzo g.main_fd = -1; 1532f8e4e36aSLuigi Rizzo g.td_body = receiver_body; 1533f8e4e36aSLuigi Rizzo g.report_interval = 1000; /* report interval */ 1534f8e4e36aSLuigi Rizzo g.affinity = -1; 1535f8e4e36aSLuigi Rizzo /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1536f8e4e36aSLuigi Rizzo g.src_ip.name = "10.0.0.1"; 1537f8e4e36aSLuigi Rizzo g.dst_ip.name = "10.1.0.1"; 1538f8e4e36aSLuigi Rizzo g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1539f8e4e36aSLuigi Rizzo g.src_mac.name = NULL; 154068b8534bSLuigi Rizzo g.pkt_size = 60; 154168b8534bSLuigi Rizzo g.burst = 512; // default 154268b8534bSLuigi Rizzo g.nthreads = 1; 154368b8534bSLuigi Rizzo g.cpus = 1; 1544b303f675SLuigi Rizzo g.forever = 1; 15451cb4c501SLuigi Rizzo g.tx_rate = 0; 1546ce3ee1e7SLuigi Rizzo g.frags = 1; 1547ce3ee1e7SLuigi Rizzo g.nmr_config = ""; 154817885a7bSLuigi Rizzo g.virt_header = 0; 154968b8534bSLuigi Rizzo 155068b8534bSLuigi Rizzo while ( (ch = getopt(arc, argv, 1551f2637526SLuigi Rizzo "a:f:F:n:i:It:r:l:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:h")) != -1) { 1552f8e4e36aSLuigi Rizzo struct sf *fn; 1553f8e4e36aSLuigi Rizzo 155468b8534bSLuigi Rizzo switch(ch) { 155568b8534bSLuigi Rizzo default: 155668b8534bSLuigi Rizzo D("bad option %c %s", ch, optarg); 155768b8534bSLuigi Rizzo usage(); 155868b8534bSLuigi Rizzo break; 1559f8e4e36aSLuigi Rizzo 1560f8e4e36aSLuigi Rizzo case 'n': 1561f8e4e36aSLuigi Rizzo g.npackets = atoi(optarg); 1562f8e4e36aSLuigi Rizzo break; 1563f8e4e36aSLuigi Rizzo 1564ce3ee1e7SLuigi Rizzo case 'F': 1565ce3ee1e7SLuigi Rizzo i = atoi(optarg); 1566ce3ee1e7SLuigi Rizzo if (i < 1 || i > 63) { 1567ce3ee1e7SLuigi Rizzo D("invalid frags %d [1..63], ignore", i); 1568ce3ee1e7SLuigi Rizzo break; 1569ce3ee1e7SLuigi Rizzo } 1570ce3ee1e7SLuigi Rizzo g.frags = i; 1571ce3ee1e7SLuigi Rizzo break; 1572ce3ee1e7SLuigi Rizzo 1573f8e4e36aSLuigi Rizzo case 'f': 1574f8e4e36aSLuigi Rizzo for (fn = func; fn->key; fn++) { 1575f8e4e36aSLuigi Rizzo if (!strcmp(fn->key, optarg)) 1576f8e4e36aSLuigi Rizzo break; 1577f8e4e36aSLuigi Rizzo } 1578f8e4e36aSLuigi Rizzo if (fn->key) 1579f8e4e36aSLuigi Rizzo g.td_body = fn->f; 1580f8e4e36aSLuigi Rizzo else 1581f8e4e36aSLuigi Rizzo D("unrecognised function %s", optarg); 1582f8e4e36aSLuigi Rizzo break; 1583f8e4e36aSLuigi Rizzo 1584f8e4e36aSLuigi Rizzo case 'o': /* data generation options */ 158599fb123fSLuigi Rizzo g.options = atoi(optarg); 158699fb123fSLuigi Rizzo break; 1587f8e4e36aSLuigi Rizzo 1588f8e4e36aSLuigi Rizzo case 'a': /* force affinity */ 1589f8e4e36aSLuigi Rizzo g.affinity = atoi(optarg); 1590f8e4e36aSLuigi Rizzo break; 1591f8e4e36aSLuigi Rizzo 159268b8534bSLuigi Rizzo case 'i': /* interface */ 1593f2637526SLuigi Rizzo /* a prefix of tap: netmap: or pcap: forces the mode. 1594f2637526SLuigi Rizzo * otherwise we guess 1595f2637526SLuigi Rizzo */ 1596f2637526SLuigi Rizzo D("interface is %s", optarg); 1597f8e4e36aSLuigi Rizzo g.ifname = optarg; 1598f2637526SLuigi Rizzo if (!strcmp(optarg, "null")) { 1599f8e4e36aSLuigi Rizzo g.dev_type = DEV_NETMAP; 1600ce3ee1e7SLuigi Rizzo g.dummy_send = 1; 1601f2637526SLuigi Rizzo } else if (!strncmp(optarg, "tap:", 4)) { 1602f2637526SLuigi Rizzo g.dev_type = DEV_TAP; 1603f2637526SLuigi Rizzo g.ifname = optarg + 4; 1604f2637526SLuigi Rizzo } else if (!strncmp(optarg, "pcap:", 5)) { 1605f2637526SLuigi Rizzo g.dev_type = DEV_PCAP; 1606f2637526SLuigi Rizzo g.ifname = optarg + 5; 1607f2637526SLuigi Rizzo } else if (!strncmp(optarg, "netmap:", 7)) { 1608f2637526SLuigi Rizzo g.dev_type = DEV_NETMAP; 1609f2637526SLuigi Rizzo g.ifname = optarg + 7; 1610f2637526SLuigi Rizzo } else if (!strncmp(optarg, "tap", 3)) { 1611f2637526SLuigi Rizzo g.dev_type = DEV_TAP; 1612f2637526SLuigi Rizzo } else { 1613f2637526SLuigi Rizzo g.dev_type = DEV_NETMAP; 1614f2637526SLuigi Rizzo } 161568b8534bSLuigi Rizzo break; 1616f8e4e36aSLuigi Rizzo 1617b303f675SLuigi Rizzo case 'I': 1618b303f675SLuigi Rizzo g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1619b303f675SLuigi Rizzo break; 1620b303f675SLuigi Rizzo 1621f8e4e36aSLuigi Rizzo case 't': /* send, deprecated */ 1622f8e4e36aSLuigi Rizzo D("-t deprecated, please use -f tx -n %s", optarg); 1623f8e4e36aSLuigi Rizzo g.td_body = sender_body; 162468b8534bSLuigi Rizzo g.npackets = atoi(optarg); 162568b8534bSLuigi Rizzo break; 1626f8e4e36aSLuigi Rizzo 162768b8534bSLuigi Rizzo case 'r': /* receive */ 1628f8e4e36aSLuigi Rizzo D("-r deprecated, please use -f rx -n %s", optarg); 1629f8e4e36aSLuigi Rizzo g.td_body = receiver_body; 163068b8534bSLuigi Rizzo g.npackets = atoi(optarg); 163168b8534bSLuigi Rizzo break; 1632f8e4e36aSLuigi Rizzo 163368b8534bSLuigi Rizzo case 'l': /* pkt_size */ 163468b8534bSLuigi Rizzo g.pkt_size = atoi(optarg); 163568b8534bSLuigi Rizzo break; 1636f8e4e36aSLuigi Rizzo 163768b8534bSLuigi Rizzo case 'd': 1638f8e4e36aSLuigi Rizzo g.dst_ip.name = optarg; 163968b8534bSLuigi Rizzo break; 1640f8e4e36aSLuigi Rizzo 164168b8534bSLuigi Rizzo case 's': 1642f8e4e36aSLuigi Rizzo g.src_ip.name = optarg; 164368b8534bSLuigi Rizzo break; 1644f8e4e36aSLuigi Rizzo 164568b8534bSLuigi Rizzo case 'T': /* report interval */ 1646f8e4e36aSLuigi Rizzo g.report_interval = atoi(optarg); 164768b8534bSLuigi Rizzo break; 1648f8e4e36aSLuigi Rizzo 164968b8534bSLuigi Rizzo case 'w': 165068b8534bSLuigi Rizzo wait_link = atoi(optarg); 165168b8534bSLuigi Rizzo break; 1652f8e4e36aSLuigi Rizzo 1653b303f675SLuigi Rizzo case 'W': /* XXX changed default */ 1654b303f675SLuigi Rizzo g.forever = 0; /* do not exit rx even with no traffic */ 1655f8e4e36aSLuigi Rizzo break; 1656f8e4e36aSLuigi Rizzo 165768b8534bSLuigi Rizzo case 'b': /* burst */ 165868b8534bSLuigi Rizzo g.burst = atoi(optarg); 165968b8534bSLuigi Rizzo break; 166068b8534bSLuigi Rizzo case 'c': 166168b8534bSLuigi Rizzo g.cpus = atoi(optarg); 166268b8534bSLuigi Rizzo break; 166368b8534bSLuigi Rizzo case 'p': 166468b8534bSLuigi Rizzo g.nthreads = atoi(optarg); 166568b8534bSLuigi Rizzo break; 166668b8534bSLuigi Rizzo 166768b8534bSLuigi Rizzo case 'D': /* destination mac */ 1668f8e4e36aSLuigi Rizzo g.dst_mac.name = optarg; 166968b8534bSLuigi Rizzo break; 1670f8e4e36aSLuigi Rizzo 167168b8534bSLuigi Rizzo case 'S': /* source mac */ 1672f8e4e36aSLuigi Rizzo g.src_mac.name = optarg; 167368b8534bSLuigi Rizzo break; 167468b8534bSLuigi Rizzo case 'v': 167568b8534bSLuigi Rizzo verbose++; 16761cb4c501SLuigi Rizzo break; 16771cb4c501SLuigi Rizzo case 'R': 16781cb4c501SLuigi Rizzo g.tx_rate = atoi(optarg); 16791cb4c501SLuigi Rizzo break; 1680b303f675SLuigi Rizzo case 'X': 1681b303f675SLuigi Rizzo g.options |= OPT_DUMP; 1682ce3ee1e7SLuigi Rizzo break; 1683ce3ee1e7SLuigi Rizzo case 'C': 1684ce3ee1e7SLuigi Rizzo g.nmr_config = strdup(optarg); 168517885a7bSLuigi Rizzo break; 168617885a7bSLuigi Rizzo case 'H': 168717885a7bSLuigi Rizzo g.virt_header = atoi(optarg); 1688f2637526SLuigi Rizzo break; 168917885a7bSLuigi Rizzo case 'h': 169017885a7bSLuigi Rizzo g.host_ring = 1; 1691f2637526SLuigi Rizzo break; 169268b8534bSLuigi Rizzo } 169368b8534bSLuigi Rizzo } 169468b8534bSLuigi Rizzo 1695f8e4e36aSLuigi Rizzo if (g.ifname == NULL) { 169668b8534bSLuigi Rizzo D("missing ifname"); 169768b8534bSLuigi Rizzo usage(); 169868b8534bSLuigi Rizzo } 1699f8e4e36aSLuigi Rizzo 1700f8e4e36aSLuigi Rizzo i = system_ncpus(); 1701f8e4e36aSLuigi Rizzo if (g.cpus < 0 || g.cpus > i) { 1702f8e4e36aSLuigi Rizzo D("%d cpus is too high, have only %d cpus", g.cpus, i); 170368b8534bSLuigi Rizzo usage(); 170468b8534bSLuigi Rizzo } 170568b8534bSLuigi Rizzo if (g.cpus == 0) 1706f8e4e36aSLuigi Rizzo g.cpus = i; 1707f8e4e36aSLuigi Rizzo 170868b8534bSLuigi Rizzo if (g.pkt_size < 16 || g.pkt_size > 1536) { 170968b8534bSLuigi Rizzo D("bad pktsize %d\n", g.pkt_size); 171068b8534bSLuigi Rizzo usage(); 171168b8534bSLuigi Rizzo } 171268b8534bSLuigi Rizzo 1713f8e4e36aSLuigi Rizzo if (g.src_mac.name == NULL) { 1714f8e4e36aSLuigi Rizzo static char mybuf[20] = "00:00:00:00:00:00"; 171599fb123fSLuigi Rizzo /* retrieve source mac address. */ 1716f8e4e36aSLuigi Rizzo if (source_hwaddr(g.ifname, mybuf) == -1) { 171799fb123fSLuigi Rizzo D("Unable to retrieve source mac"); 171899fb123fSLuigi Rizzo // continue, fail later 171999fb123fSLuigi Rizzo } 1720f8e4e36aSLuigi Rizzo g.src_mac.name = mybuf; 172199fb123fSLuigi Rizzo } 1722f8e4e36aSLuigi Rizzo /* extract address ranges */ 1723f8e4e36aSLuigi Rizzo extract_ip_range(&g.src_ip); 1724f8e4e36aSLuigi Rizzo extract_ip_range(&g.dst_ip); 1725f8e4e36aSLuigi Rizzo extract_mac_range(&g.src_mac); 1726f8e4e36aSLuigi Rizzo extract_mac_range(&g.dst_mac); 172799fb123fSLuigi Rizzo 1728f2637526SLuigi Rizzo if (g.src_ip.start != g.src_ip.end || 1729f2637526SLuigi Rizzo g.src_ip.port0 != g.src_ip.port1 || 1730f2637526SLuigi Rizzo g.dst_ip.start != g.dst_ip.end || 1731f2637526SLuigi Rizzo g.dst_ip.port0 != g.dst_ip.port1) 1732f2637526SLuigi Rizzo g.options |= OPT_COPY; 1733f2637526SLuigi Rizzo 173417885a7bSLuigi Rizzo if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 173517885a7bSLuigi Rizzo && g.virt_header != VIRT_HDR_2) { 173617885a7bSLuigi Rizzo D("bad virtio-net-header length"); 173717885a7bSLuigi Rizzo usage(); 173817885a7bSLuigi Rizzo } 173917885a7bSLuigi Rizzo 1740f8e4e36aSLuigi Rizzo if (g.dev_type == DEV_TAP) { 1741f8e4e36aSLuigi Rizzo D("want to use tap %s", g.ifname); 1742f8e4e36aSLuigi Rizzo g.main_fd = tap_alloc(g.ifname); 1743f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 1744f8e4e36aSLuigi Rizzo D("cannot open tap %s", g.ifname); 174599fb123fSLuigi Rizzo usage(); 174699fb123fSLuigi Rizzo } 1747f2637526SLuigi Rizzo #ifndef NO_PCAP 1748f2637526SLuigi Rizzo } else if (g.dev_type == DEV_PCAP) { 1749f8e4e36aSLuigi Rizzo char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1750f8e4e36aSLuigi Rizzo 1751f8e4e36aSLuigi Rizzo D("using pcap on %s", g.ifname); 1752f8e4e36aSLuigi Rizzo pcap_errbuf[0] = '\0'; // init the buffer 1753f8e4e36aSLuigi Rizzo g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf); 1754f8e4e36aSLuigi Rizzo if (g.p == NULL) { 1755f8e4e36aSLuigi Rizzo D("cannot open pcap on %s", g.ifname); 1756f8e4e36aSLuigi Rizzo usage(); 1757f8e4e36aSLuigi Rizzo } 1758f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 1759f2637526SLuigi Rizzo } else if (g.dummy_send) { /* but DEV_NETMAP */ 1760ce3ee1e7SLuigi Rizzo D("using a dummy send routine"); 176199fb123fSLuigi Rizzo } else { 176268b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 176364ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 176468b8534bSLuigi Rizzo /* 176568b8534bSLuigi Rizzo * Open the netmap device to fetch the number of queues of our 176668b8534bSLuigi Rizzo * interface. 176768b8534bSLuigi Rizzo * 176868b8534bSLuigi Rizzo * The first NIOCREGIF also detaches the card from the 176968b8534bSLuigi Rizzo * protocol stack and may cause a reset of the card, 177068b8534bSLuigi Rizzo * which in turn may take some time for the PHY to 177168b8534bSLuigi Rizzo * reconfigure. 177268b8534bSLuigi Rizzo */ 1773f8e4e36aSLuigi Rizzo g.main_fd = open("/dev/netmap", O_RDWR); 1774f8e4e36aSLuigi Rizzo if (g.main_fd == -1) { 177517885a7bSLuigi Rizzo D("Unable to open /dev/netmap: %s", strerror(errno)); 1776f8e4e36aSLuigi Rizzo // fail later 177768b8534bSLuigi Rizzo } 1778ce3ee1e7SLuigi Rizzo /* 1779ce3ee1e7SLuigi Rizzo * Register the interface on the netmap device: from now on, 1780ce3ee1e7SLuigi Rizzo * we can operate on the network interface without any 1781ce3ee1e7SLuigi Rizzo * interference from the legacy network stack. 1782ce3ee1e7SLuigi Rizzo * 1783ce3ee1e7SLuigi Rizzo * We decide to put the first interface registration here to 1784ce3ee1e7SLuigi Rizzo * give time to cards that take a long time to reset the PHY. 1785ce3ee1e7SLuigi Rizzo */ 178668b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 178764ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 1788f8e4e36aSLuigi Rizzo strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name)); 1789ce3ee1e7SLuigi Rizzo parse_nmr_config(g.nmr_config, &nmr); 1790ce3ee1e7SLuigi Rizzo if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) { 179117885a7bSLuigi Rizzo D("Unable to register interface %s: %s", g.ifname, strerror(errno)); 1792ce3ee1e7SLuigi Rizzo //continue, fail later 1793ce3ee1e7SLuigi Rizzo } 1794ce3ee1e7SLuigi Rizzo ND("%s: txr %d txd %d rxr %d rxd %d", g.ifname, 1795ce3ee1e7SLuigi Rizzo nmr.nr_tx_rings, nmr.nr_tx_slots, 1796ce3ee1e7SLuigi Rizzo nmr.nr_rx_rings, nmr.nr_rx_slots); 179764ae02c3SLuigi Rizzo devqueues = nmr.nr_rx_rings; 179868b8534bSLuigi Rizzo 179968b8534bSLuigi Rizzo /* validate provided nthreads. */ 180068b8534bSLuigi Rizzo if (g.nthreads < 1 || g.nthreads > devqueues) { 180168b8534bSLuigi Rizzo D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 180268b8534bSLuigi Rizzo // continue, fail later 180368b8534bSLuigi Rizzo } 180468b8534bSLuigi Rizzo 180568b8534bSLuigi Rizzo /* 180668b8534bSLuigi Rizzo * Map the netmap shared memory: instead of issuing mmap() 180768b8534bSLuigi Rizzo * inside the body of the threads, we prefer to keep this 180868b8534bSLuigi Rizzo * operation here to simplify the thread logic. 180968b8534bSLuigi Rizzo */ 1810f8e4e36aSLuigi Rizzo D("mapping %d Kbytes", nmr.nr_memsize>>10); 1811f8e4e36aSLuigi Rizzo g.mmap_size = nmr.nr_memsize; 1812f8e4e36aSLuigi Rizzo g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 181368b8534bSLuigi Rizzo PROT_WRITE | PROT_READ, 1814f8e4e36aSLuigi Rizzo MAP_SHARED, g.main_fd, 0); 1815f8e4e36aSLuigi Rizzo if (g.mmap_addr == MAP_FAILED) { 181617885a7bSLuigi Rizzo D("Unable to mmap %d KB: %s", nmr.nr_memsize >> 10, strerror(errno)); 181768b8534bSLuigi Rizzo // continue, fail later 181868b8534bSLuigi Rizzo } 181968b8534bSLuigi Rizzo 1820f2637526SLuigi Rizzo if (verbose) { 1821f2637526SLuigi Rizzo struct netmap_if *nifp = NETMAP_IF(g.mmap_addr, nmr.nr_offset); 182268b8534bSLuigi Rizzo 1823f2637526SLuigi Rizzo D("nifp at offset %d, %d tx %d rx rings %s", 1824f2637526SLuigi Rizzo nmr.nr_offset, nmr.nr_tx_rings, nmr.nr_rx_rings, 1825f2637526SLuigi Rizzo nmr.nr_ringid & NETMAP_PRIV_MEM ? "PRIVATE" : "common" ); 1826f2637526SLuigi Rizzo for (i = 0; i <= nmr.nr_tx_rings; i++) { 1827f2637526SLuigi Rizzo D(" TX%d at 0x%lx", i, 1828f2637526SLuigi Rizzo (char *)NETMAP_TXRING(nifp, i) - (char *)nifp); 1829f2637526SLuigi Rizzo } 1830f2637526SLuigi Rizzo for (i = 0; i <= nmr.nr_rx_rings; i++) { 1831f2637526SLuigi Rizzo D(" RX%d at 0x%lx", i, 1832f2637526SLuigi Rizzo (char *)NETMAP_RXRING(nifp, i) - (char *)nifp); 1833f2637526SLuigi Rizzo } 1834f2637526SLuigi Rizzo } 183568b8534bSLuigi Rizzo 183668b8534bSLuigi Rizzo /* Print some debug information. */ 183768b8534bSLuigi Rizzo fprintf(stdout, 183868b8534bSLuigi Rizzo "%s %s: %d queues, %d threads and %d cpus.\n", 1839f8e4e36aSLuigi Rizzo (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1840f8e4e36aSLuigi Rizzo g.ifname, 184168b8534bSLuigi Rizzo devqueues, 184268b8534bSLuigi Rizzo g.nthreads, 184368b8534bSLuigi Rizzo g.cpus); 1844f8e4e36aSLuigi Rizzo if (g.td_body == sender_body) { 184568b8534bSLuigi Rizzo fprintf(stdout, "%s -> %s (%s -> %s)\n", 1846f8e4e36aSLuigi Rizzo g.src_ip.name, g.dst_ip.name, 1847f8e4e36aSLuigi Rizzo g.src_mac.name, g.dst_mac.name); 184868b8534bSLuigi Rizzo } 184968b8534bSLuigi Rizzo 185068b8534bSLuigi Rizzo /* Exit if something went wrong. */ 1851f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 185268b8534bSLuigi Rizzo D("aborting"); 185368b8534bSLuigi Rizzo usage(); 185468b8534bSLuigi Rizzo } 185599fb123fSLuigi Rizzo } 185668b8534bSLuigi Rizzo 1857ce3ee1e7SLuigi Rizzo 185899fb123fSLuigi Rizzo if (g.options) { 1859b303f675SLuigi Rizzo D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 186099fb123fSLuigi Rizzo g.options & OPT_PREFETCH ? " prefetch" : "", 186199fb123fSLuigi Rizzo g.options & OPT_ACCESS ? " access" : "", 186299fb123fSLuigi Rizzo g.options & OPT_MEMCPY ? " memcpy" : "", 1863b303f675SLuigi Rizzo g.options & OPT_INDIRECT ? " indirect" : "", 186499fb123fSLuigi Rizzo g.options & OPT_COPY ? " copy" : ""); 186599fb123fSLuigi Rizzo } 18661cb4c501SLuigi Rizzo 1867ce3ee1e7SLuigi Rizzo g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 1868ce3ee1e7SLuigi Rizzo if (g.tx_rate > 0) { 1869ce3ee1e7SLuigi Rizzo /* try to have at least something every second, 187017885a7bSLuigi Rizzo * reducing the burst size to some 0.01s worth of data 1871ce3ee1e7SLuigi Rizzo * (but no less than one full set of fragments) 1872ce3ee1e7SLuigi Rizzo */ 187317885a7bSLuigi Rizzo uint64_t x; 187417885a7bSLuigi Rizzo int lim = (g.tx_rate)/300; 187517885a7bSLuigi Rizzo if (g.burst > lim) 187617885a7bSLuigi Rizzo g.burst = lim; 1877ce3ee1e7SLuigi Rizzo if (g.burst < g.frags) 1878ce3ee1e7SLuigi Rizzo g.burst = g.frags; 187917885a7bSLuigi Rizzo x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 188017885a7bSLuigi Rizzo g.tx_period.tv_nsec = x; 18811cb4c501SLuigi Rizzo g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 18821cb4c501SLuigi Rizzo g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 18831cb4c501SLuigi Rizzo } 1884ce3ee1e7SLuigi Rizzo if (g.td_body == sender_body) 1885ce3ee1e7SLuigi Rizzo D("Sending %d packets every %ld.%09ld s", 1886ce3ee1e7SLuigi Rizzo g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 188768b8534bSLuigi Rizzo /* Wait for PHY reset. */ 188868b8534bSLuigi Rizzo D("Wait %d secs for phy reset", wait_link); 188968b8534bSLuigi Rizzo sleep(wait_link); 189068b8534bSLuigi Rizzo D("Ready..."); 189168b8534bSLuigi Rizzo 189268b8534bSLuigi Rizzo /* Install ^C handler. */ 189368b8534bSLuigi Rizzo global_nthreads = g.nthreads; 189468b8534bSLuigi Rizzo signal(SIGINT, sigint_h); 189568b8534bSLuigi Rizzo 1896f8e4e36aSLuigi Rizzo start_threads(&g); 1897f8e4e36aSLuigi Rizzo main_thread(&g); 1898f8e4e36aSLuigi Rizzo return 0; 189968b8534bSLuigi Rizzo } 190068b8534bSLuigi Rizzo 190168b8534bSLuigi Rizzo /* end of file */ 1902