168b8534bSLuigi Rizzo /* 2f8e4e36aSLuigi Rizzo * Copyright (C) 2011-2012 Matteo Landi, Luigi Rizzo. All rights reserved. 368b8534bSLuigi Rizzo * 468b8534bSLuigi Rizzo * Redistribution and use in source and binary forms, with or without 568b8534bSLuigi Rizzo * modification, are permitted provided that the following conditions 668b8534bSLuigi Rizzo * are met: 768b8534bSLuigi Rizzo * 1. Redistributions of source code must retain the above copyright 868b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer. 968b8534bSLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 1068b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 1168b8534bSLuigi Rizzo * documentation and/or other materials provided with the distribution. 1268b8534bSLuigi Rizzo * 1368b8534bSLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1468b8534bSLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1568b8534bSLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1668b8534bSLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1768b8534bSLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1868b8534bSLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1968b8534bSLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2068b8534bSLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2168b8534bSLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2268b8534bSLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2368b8534bSLuigi Rizzo * SUCH DAMAGE. 2468b8534bSLuigi Rizzo */ 2568b8534bSLuigi Rizzo 2668b8534bSLuigi Rizzo /* 2768b8534bSLuigi Rizzo * $FreeBSD$ 281cb4c501SLuigi Rizzo * $Id$ 2968b8534bSLuigi Rizzo * 3068b8534bSLuigi Rizzo * Example program to show how to build a multithreaded packet 3168b8534bSLuigi Rizzo * source/sink using the netmap device. 3268b8534bSLuigi Rizzo * 3368b8534bSLuigi Rizzo * In this example we create a programmable number of threads 3468b8534bSLuigi Rizzo * to take care of all the queues of the interface used to 3568b8534bSLuigi Rizzo * send or receive traffic. 3668b8534bSLuigi Rizzo * 3768b8534bSLuigi Rizzo */ 3868b8534bSLuigi Rizzo 39f8e4e36aSLuigi Rizzo #include "nm_util.h" 40f8e4e36aSLuigi Rizzo 41b303f675SLuigi Rizzo #include <ctype.h> // isprint() 42b303f675SLuigi Rizzo 43f8e4e36aSLuigi Rizzo const char *default_payload="netmap pkt-gen payload\n" 4468b8534bSLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 4568b8534bSLuigi Rizzo 46f8e4e36aSLuigi Rizzo int time_second; // support for RD() debugging macro 4768b8534bSLuigi Rizzo 4868b8534bSLuigi Rizzo int verbose = 0; 4968b8534bSLuigi Rizzo 5068b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */ 5168b8534bSLuigi Rizzo 5268b8534bSLuigi Rizzo struct pkt { 5368b8534bSLuigi Rizzo struct ether_header eh; 5468b8534bSLuigi Rizzo struct ip ip; 5568b8534bSLuigi Rizzo struct udphdr udp; 565819da83SLuigi Rizzo uint8_t body[2048]; // XXX hardwired 5768b8534bSLuigi Rizzo } __attribute__((__packed__)); 5868b8534bSLuigi Rizzo 59f8e4e36aSLuigi Rizzo struct ip_range { 60f8e4e36aSLuigi Rizzo char *name; 61f8e4e36aSLuigi Rizzo struct in_addr start, end, cur; 62f8e4e36aSLuigi Rizzo uint16_t port0, port1, cur_p; 63f8e4e36aSLuigi Rizzo }; 64f8e4e36aSLuigi Rizzo 65f8e4e36aSLuigi Rizzo struct mac_range { 66f8e4e36aSLuigi Rizzo char *name; 67f8e4e36aSLuigi Rizzo struct ether_addr start, end; 68f8e4e36aSLuigi Rizzo }; 69f8e4e36aSLuigi Rizzo 7068b8534bSLuigi Rizzo /* 7168b8534bSLuigi Rizzo * global arguments for all threads 7268b8534bSLuigi Rizzo */ 73f8e4e36aSLuigi Rizzo 7468b8534bSLuigi Rizzo struct glob_arg { 75f8e4e36aSLuigi Rizzo struct ip_range src_ip; 76f8e4e36aSLuigi Rizzo struct ip_range dst_ip; 77f8e4e36aSLuigi Rizzo struct mac_range dst_mac; 78f8e4e36aSLuigi Rizzo struct mac_range src_mac; 7968b8534bSLuigi Rizzo int pkt_size; 8068b8534bSLuigi Rizzo int burst; 81f8e4e36aSLuigi Rizzo int forever; 8268b8534bSLuigi Rizzo int npackets; /* total packets to send */ 8368b8534bSLuigi Rizzo int nthreads; 8468b8534bSLuigi Rizzo int cpus; 8599fb123fSLuigi Rizzo int options; /* testing */ 8699fb123fSLuigi Rizzo #define OPT_PREFETCH 1 8799fb123fSLuigi Rizzo #define OPT_ACCESS 2 8899fb123fSLuigi Rizzo #define OPT_COPY 4 8999fb123fSLuigi Rizzo #define OPT_MEMCPY 8 90f8e4e36aSLuigi Rizzo #define OPT_TS 16 /* add a timestamp */ 91b303f675SLuigi Rizzo #define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 92b303f675SLuigi Rizzo #define OPT_DUMP 64 /* dump rx/tx traffic */ 93f8e4e36aSLuigi Rizzo int dev_type; 9468b8534bSLuigi Rizzo pcap_t *p; 9568b8534bSLuigi Rizzo 961cb4c501SLuigi Rizzo int tx_rate; 971cb4c501SLuigi Rizzo struct timespec tx_period; 981cb4c501SLuigi Rizzo 99f8e4e36aSLuigi Rizzo int affinity; 100f8e4e36aSLuigi Rizzo int main_fd; 101f8e4e36aSLuigi Rizzo int report_interval; 102f8e4e36aSLuigi Rizzo void *(*td_body)(void *); 103f8e4e36aSLuigi Rizzo void *mmap_addr; 104f8e4e36aSLuigi Rizzo int mmap_size; 105f8e4e36aSLuigi Rizzo char *ifname; 10668b8534bSLuigi Rizzo }; 107f8e4e36aSLuigi Rizzo enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 108f8e4e36aSLuigi Rizzo 10968b8534bSLuigi Rizzo 11068b8534bSLuigi Rizzo /* 11168b8534bSLuigi Rizzo * Arguments for a new thread. The same structure is used by 11268b8534bSLuigi Rizzo * the source and the sink 11368b8534bSLuigi Rizzo */ 11468b8534bSLuigi Rizzo struct targ { 11568b8534bSLuigi Rizzo struct glob_arg *g; 11668b8534bSLuigi Rizzo int used; 11768b8534bSLuigi Rizzo int completed; 1183fe77e68SEd Maste int cancel; 11968b8534bSLuigi Rizzo int fd; 12068b8534bSLuigi Rizzo struct nmreq nmr; 12168b8534bSLuigi Rizzo struct netmap_if *nifp; 12268b8534bSLuigi Rizzo uint16_t qfirst, qlast; /* range of queues to scan */ 123f8e4e36aSLuigi Rizzo volatile uint64_t count; 1241cb4c501SLuigi Rizzo struct timespec tic, toc; 12568b8534bSLuigi Rizzo int me; 12668b8534bSLuigi Rizzo pthread_t thread; 12768b8534bSLuigi Rizzo int affinity; 12868b8534bSLuigi Rizzo 12968b8534bSLuigi Rizzo struct pkt pkt; 13068b8534bSLuigi Rizzo }; 13168b8534bSLuigi Rizzo 13268b8534bSLuigi Rizzo 133f8e4e36aSLuigi Rizzo /* 134f8e4e36aSLuigi Rizzo * extract the extremes from a range of ipv4 addresses. 135f8e4e36aSLuigi Rizzo * addr_lo[-addr_hi][:port_lo[-port_hi]] 136f8e4e36aSLuigi Rizzo */ 137f8e4e36aSLuigi Rizzo static void 138f8e4e36aSLuigi Rizzo extract_ip_range(struct ip_range *r) 139f8e4e36aSLuigi Rizzo { 140f8e4e36aSLuigi Rizzo char *p_lo, *p_hi; 141f8e4e36aSLuigi Rizzo char buf1[16]; // one ip address 142f8e4e36aSLuigi Rizzo 143f8e4e36aSLuigi Rizzo D("extract IP range from %s", r->name); 144f8e4e36aSLuigi Rizzo p_lo = index(r->name, ':'); /* do we have ports ? */ 145f8e4e36aSLuigi Rizzo if (p_lo) { 146f8e4e36aSLuigi Rizzo D(" found ports at %s", p_lo); 147f8e4e36aSLuigi Rizzo *p_lo++ = '\0'; 148f8e4e36aSLuigi Rizzo p_hi = index(p_lo, '-'); 149f8e4e36aSLuigi Rizzo if (p_hi) 150f8e4e36aSLuigi Rizzo *p_hi++ = '\0'; 151f8e4e36aSLuigi Rizzo else 152f8e4e36aSLuigi Rizzo p_hi = p_lo; 153f8e4e36aSLuigi Rizzo r->port0 = strtol(p_lo, NULL, 0); 154f8e4e36aSLuigi Rizzo r->port1 = strtol(p_hi, NULL, 0); 155f8e4e36aSLuigi Rizzo if (r->port1 < r->port0) { 156f8e4e36aSLuigi Rizzo r->cur_p = r->port0; 157f8e4e36aSLuigi Rizzo r->port0 = r->port1; 158f8e4e36aSLuigi Rizzo r->port1 = r->cur_p; 159f8e4e36aSLuigi Rizzo } 160f8e4e36aSLuigi Rizzo r->cur_p = r->port0; 161f8e4e36aSLuigi Rizzo D("ports are %d to %d", r->port0, r->port1); 162f8e4e36aSLuigi Rizzo } 163f8e4e36aSLuigi Rizzo p_hi = index(r->name, '-'); /* do we have upper ip ? */ 164f8e4e36aSLuigi Rizzo if (p_hi) { 165f8e4e36aSLuigi Rizzo *p_hi++ = '\0'; 166f8e4e36aSLuigi Rizzo } else 167f8e4e36aSLuigi Rizzo p_hi = r->name; 168f8e4e36aSLuigi Rizzo inet_aton(r->name, &r->start); 169f8e4e36aSLuigi Rizzo inet_aton(p_hi, &r->end); 170f8e4e36aSLuigi Rizzo if (r->start.s_addr > r->end.s_addr) { 171f8e4e36aSLuigi Rizzo r->cur = r->start; 172f8e4e36aSLuigi Rizzo r->start = r->end; 173f8e4e36aSLuigi Rizzo r->end = r->cur; 174f8e4e36aSLuigi Rizzo } 175f8e4e36aSLuigi Rizzo r->cur = r->start; 176f8e4e36aSLuigi Rizzo strncpy(buf1, inet_ntoa(r->end), sizeof(buf1)); 177f8e4e36aSLuigi Rizzo D("range is %s %d to %s %d", inet_ntoa(r->start), r->port0, 178f8e4e36aSLuigi Rizzo buf1, r->port1); 179f8e4e36aSLuigi Rizzo } 180f8e4e36aSLuigi Rizzo 181f8e4e36aSLuigi Rizzo static void 182f8e4e36aSLuigi Rizzo extract_mac_range(struct mac_range *r) 183f8e4e36aSLuigi Rizzo { 184f8e4e36aSLuigi Rizzo D("extract MAC range from %s", r->name); 185f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->start, 6); 186f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->end, 6); 187f8e4e36aSLuigi Rizzo #if 0 188f8e4e36aSLuigi Rizzo bcopy(targ->src_mac, eh->ether_shost, 6); 189f8e4e36aSLuigi Rizzo p = index(targ->g->src_mac, '-'); 190f8e4e36aSLuigi Rizzo if (p) 191f8e4e36aSLuigi Rizzo targ->src_mac_range = atoi(p+1); 192f8e4e36aSLuigi Rizzo 193f8e4e36aSLuigi Rizzo bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 194f8e4e36aSLuigi Rizzo bcopy(targ->dst_mac, eh->ether_dhost, 6); 195f8e4e36aSLuigi Rizzo p = index(targ->g->dst_mac, '-'); 196f8e4e36aSLuigi Rizzo if (p) 197f8e4e36aSLuigi Rizzo targ->dst_mac_range = atoi(p+1); 198f8e4e36aSLuigi Rizzo #endif 199f8e4e36aSLuigi Rizzo D("%s starts at %s", r->name, ether_ntoa(&r->start)); 200f8e4e36aSLuigi Rizzo } 201f8e4e36aSLuigi Rizzo 20268b8534bSLuigi Rizzo static struct targ *targs; 20368b8534bSLuigi Rizzo static int global_nthreads; 20468b8534bSLuigi Rizzo 20568b8534bSLuigi Rizzo /* control-C handler */ 20668b8534bSLuigi Rizzo static void 207f8e4e36aSLuigi Rizzo sigint_h(int sig) 20868b8534bSLuigi Rizzo { 209f8e4e36aSLuigi Rizzo int i; 21068b8534bSLuigi Rizzo 211f8e4e36aSLuigi Rizzo (void)sig; /* UNUSED */ 212f8e4e36aSLuigi Rizzo for (i = 0; i < global_nthreads; i++) { 213f8e4e36aSLuigi Rizzo targs[i].cancel = 1; 214f8e4e36aSLuigi Rizzo } 21568b8534bSLuigi Rizzo signal(SIGINT, SIG_DFL); 21668b8534bSLuigi Rizzo } 21768b8534bSLuigi Rizzo 21868b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */ 21968b8534bSLuigi Rizzo static int 22068b8534bSLuigi Rizzo system_ncpus(void) 22168b8534bSLuigi Rizzo { 222f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 22368b8534bSLuigi Rizzo int mib[2], ncpus; 22468b8534bSLuigi Rizzo size_t len; 22568b8534bSLuigi Rizzo 22668b8534bSLuigi Rizzo mib[0] = CTL_HW; 22768b8534bSLuigi Rizzo mib[1] = HW_NCPU; 22868b8534bSLuigi Rizzo len = sizeof(mib); 22968b8534bSLuigi Rizzo sysctl(mib, 2, &ncpus, &len, NULL, 0); 23068b8534bSLuigi Rizzo 23168b8534bSLuigi Rizzo return (ncpus); 232f8e4e36aSLuigi Rizzo #else 233f8e4e36aSLuigi Rizzo return 1; 234f8e4e36aSLuigi Rizzo #endif /* !__FreeBSD__ */ 23568b8534bSLuigi Rizzo } 23668b8534bSLuigi Rizzo 237f8e4e36aSLuigi Rizzo #ifdef __linux__ 238f8e4e36aSLuigi Rizzo #define sockaddr_dl sockaddr_ll 239f8e4e36aSLuigi Rizzo #define sdl_family sll_family 240f8e4e36aSLuigi Rizzo #define AF_LINK AF_PACKET 241f8e4e36aSLuigi Rizzo #define LLADDR(s) s->sll_addr; 242f8e4e36aSLuigi Rizzo #include <linux/if_tun.h> 243f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/net/tun" 244f8e4e36aSLuigi Rizzo #endif /* __linux__ */ 245f8e4e36aSLuigi Rizzo 246f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 247f8e4e36aSLuigi Rizzo #include <net/if_tun.h> 248f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 249f8e4e36aSLuigi Rizzo #endif /* __FreeBSD */ 250f8e4e36aSLuigi Rizzo 251f8e4e36aSLuigi Rizzo #ifdef __APPLE__ 252f8e4e36aSLuigi Rizzo // #warning TAP not supported on apple ? 253f8e4e36aSLuigi Rizzo #include <net/if_utun.h> 254f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 255f8e4e36aSLuigi Rizzo #endif /* __APPLE__ */ 256f8e4e36aSLuigi Rizzo 257f8e4e36aSLuigi Rizzo 25868b8534bSLuigi Rizzo /* 25968b8534bSLuigi Rizzo * locate the src mac address for our interface, put it 26068b8534bSLuigi Rizzo * into the user-supplied buffer. return 0 if ok, -1 on error. 26168b8534bSLuigi Rizzo */ 26268b8534bSLuigi Rizzo static int 26368b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf) 26468b8534bSLuigi Rizzo { 26568b8534bSLuigi Rizzo struct ifaddrs *ifaphead, *ifap; 26668b8534bSLuigi Rizzo int l = sizeof(ifap->ifa_name); 26768b8534bSLuigi Rizzo 26868b8534bSLuigi Rizzo if (getifaddrs(&ifaphead) != 0) { 26968b8534bSLuigi Rizzo D("getifaddrs %s failed", ifname); 27068b8534bSLuigi Rizzo return (-1); 27168b8534bSLuigi Rizzo } 27268b8534bSLuigi Rizzo 27368b8534bSLuigi Rizzo for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 27468b8534bSLuigi Rizzo struct sockaddr_dl *sdl = 27568b8534bSLuigi Rizzo (struct sockaddr_dl *)ifap->ifa_addr; 27668b8534bSLuigi Rizzo uint8_t *mac; 27768b8534bSLuigi Rizzo 27868b8534bSLuigi Rizzo if (!sdl || sdl->sdl_family != AF_LINK) 27968b8534bSLuigi Rizzo continue; 28068b8534bSLuigi Rizzo if (strncmp(ifap->ifa_name, ifname, l) != 0) 28168b8534bSLuigi Rizzo continue; 28268b8534bSLuigi Rizzo mac = (uint8_t *)LLADDR(sdl); 28368b8534bSLuigi Rizzo sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 28468b8534bSLuigi Rizzo mac[0], mac[1], mac[2], 28568b8534bSLuigi Rizzo mac[3], mac[4], mac[5]); 28668b8534bSLuigi Rizzo if (verbose) 28768b8534bSLuigi Rizzo D("source hwaddr %s", buf); 28868b8534bSLuigi Rizzo break; 28968b8534bSLuigi Rizzo } 29068b8534bSLuigi Rizzo freeifaddrs(ifaphead); 29168b8534bSLuigi Rizzo return ifap ? 0 : 1; 29268b8534bSLuigi Rizzo } 29368b8534bSLuigi Rizzo 29468b8534bSLuigi Rizzo 29568b8534bSLuigi Rizzo /* set the thread affinity. */ 29668b8534bSLuigi Rizzo static int 29768b8534bSLuigi Rizzo setaffinity(pthread_t me, int i) 29868b8534bSLuigi Rizzo { 299f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 30068b8534bSLuigi Rizzo cpuset_t cpumask; 30168b8534bSLuigi Rizzo 30268b8534bSLuigi Rizzo if (i == -1) 30368b8534bSLuigi Rizzo return 0; 30468b8534bSLuigi Rizzo 30568b8534bSLuigi Rizzo /* Set thread affinity affinity.*/ 30668b8534bSLuigi Rizzo CPU_ZERO(&cpumask); 30768b8534bSLuigi Rizzo CPU_SET(i, &cpumask); 30868b8534bSLuigi Rizzo 30968b8534bSLuigi Rizzo if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 31068b8534bSLuigi Rizzo D("Unable to set affinity"); 31168b8534bSLuigi Rizzo return 1; 31268b8534bSLuigi Rizzo } 313f8e4e36aSLuigi Rizzo #else 314f8e4e36aSLuigi Rizzo (void)me; /* suppress 'unused' warnings */ 315f8e4e36aSLuigi Rizzo (void)i; 316f8e4e36aSLuigi Rizzo #endif /* __FreeBSD__ */ 31768b8534bSLuigi Rizzo return 0; 31868b8534bSLuigi Rizzo } 31968b8534bSLuigi Rizzo 32068b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */ 32168b8534bSLuigi Rizzo static uint16_t 322f8e4e36aSLuigi Rizzo checksum(const void *data, uint16_t len, uint32_t sum) 32368b8534bSLuigi Rizzo { 32468b8534bSLuigi Rizzo const uint8_t *addr = data; 325f8e4e36aSLuigi Rizzo uint32_t i; 32668b8534bSLuigi Rizzo 327f8e4e36aSLuigi Rizzo /* Checksum all the pairs of bytes first... */ 328f8e4e36aSLuigi Rizzo for (i = 0; i < (len & ~1U); i += 2) { 329f8e4e36aSLuigi Rizzo sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 330f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 331f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 332f8e4e36aSLuigi Rizzo } 333f8e4e36aSLuigi Rizzo /* 334f8e4e36aSLuigi Rizzo * If there's a single byte left over, checksum it, too. 335f8e4e36aSLuigi Rizzo * Network byte order is big-endian, so the remaining byte is 336f8e4e36aSLuigi Rizzo * the high byte. 337f8e4e36aSLuigi Rizzo */ 338f8e4e36aSLuigi Rizzo if (i < len) { 339f8e4e36aSLuigi Rizzo sum += addr[i] << 8; 340f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 341f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 342f8e4e36aSLuigi Rizzo } 343f8e4e36aSLuigi Rizzo return sum; 34468b8534bSLuigi Rizzo } 34568b8534bSLuigi Rizzo 346f8e4e36aSLuigi Rizzo static u_int16_t 347f8e4e36aSLuigi Rizzo wrapsum(u_int32_t sum) 348f8e4e36aSLuigi Rizzo { 349f8e4e36aSLuigi Rizzo sum = ~sum & 0xFFFF; 350f8e4e36aSLuigi Rizzo return (htons(sum)); 35168b8534bSLuigi Rizzo } 35268b8534bSLuigi Rizzo 353b303f675SLuigi Rizzo /* Check the payload of the packet for errors (use it for debug). 354b303f675SLuigi Rizzo * Look for consecutive ascii representations of the size of the packet. 355b303f675SLuigi Rizzo */ 356b303f675SLuigi Rizzo static void 357b303f675SLuigi Rizzo dump_payload(char *p, int len, struct netmap_ring *ring, int cur) 358b303f675SLuigi Rizzo { 359b303f675SLuigi Rizzo char buf[128]; 360b303f675SLuigi Rizzo int i, j, i0; 361b303f675SLuigi Rizzo 362b303f675SLuigi Rizzo /* get the length in ASCII of the length of the packet. */ 363b303f675SLuigi Rizzo 364b303f675SLuigi Rizzo printf("ring %p cur %5d len %5d buf %p\n", ring, cur, len, p); 365b303f675SLuigi Rizzo /* hexdump routine */ 366b303f675SLuigi Rizzo for (i = 0; i < len; ) { 367b303f675SLuigi Rizzo memset(buf, sizeof(buf), ' '); 368b303f675SLuigi Rizzo sprintf(buf, "%5d: ", i); 369b303f675SLuigi Rizzo i0 = i; 370b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 371b303f675SLuigi Rizzo sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 372b303f675SLuigi Rizzo i = i0; 373b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 374b303f675SLuigi Rizzo sprintf(buf+7+j + 48, "%c", 375b303f675SLuigi Rizzo isprint(p[i]) ? p[i] : '.'); 376b303f675SLuigi Rizzo printf("%s\n", buf); 377b303f675SLuigi Rizzo } 378b303f675SLuigi Rizzo } 379b303f675SLuigi Rizzo 38068b8534bSLuigi Rizzo /* 38168b8534bSLuigi Rizzo * Fill a packet with some payload. 382f8e4e36aSLuigi Rizzo * We create a UDP packet so the payload starts at 383f8e4e36aSLuigi Rizzo * 14+20+8 = 42 bytes. 38468b8534bSLuigi Rizzo */ 385f8e4e36aSLuigi Rizzo #ifdef __linux__ 386f8e4e36aSLuigi Rizzo #define uh_sport source 387f8e4e36aSLuigi Rizzo #define uh_dport dest 388f8e4e36aSLuigi Rizzo #define uh_ulen len 389f8e4e36aSLuigi Rizzo #define uh_sum check 390f8e4e36aSLuigi Rizzo #endif /* linux */ 391b303f675SLuigi Rizzo 39268b8534bSLuigi Rizzo static void 39368b8534bSLuigi Rizzo initialize_packet(struct targ *targ) 39468b8534bSLuigi Rizzo { 39568b8534bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 39668b8534bSLuigi Rizzo struct ether_header *eh; 39768b8534bSLuigi Rizzo struct ip *ip; 39868b8534bSLuigi Rizzo struct udphdr *udp; 399f8e4e36aSLuigi Rizzo uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 400b303f675SLuigi Rizzo const char *payload = targ->g->options & OPT_INDIRECT ? 401b303f675SLuigi Rizzo "XXXXXXXXXXXXXXXXXXXXXX" : default_payload; 402b303f675SLuigi Rizzo int i, l, l0 = strlen(payload); 40368b8534bSLuigi Rizzo 40468b8534bSLuigi Rizzo for (i = 0; i < paylen;) { 40568b8534bSLuigi Rizzo l = min(l0, paylen - i); 406b303f675SLuigi Rizzo bcopy(payload, pkt->body + i, l); 40768b8534bSLuigi Rizzo i += l; 40868b8534bSLuigi Rizzo } 40968b8534bSLuigi Rizzo pkt->body[i-1] = '\0'; 41068b8534bSLuigi Rizzo ip = &pkt->ip; 411f8e4e36aSLuigi Rizzo 41268b8534bSLuigi Rizzo ip->ip_v = IPVERSION; 41368b8534bSLuigi Rizzo ip->ip_hl = 5; 41468b8534bSLuigi Rizzo ip->ip_id = 0; 41568b8534bSLuigi Rizzo ip->ip_tos = IPTOS_LOWDELAY; 41668b8534bSLuigi Rizzo ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 41768b8534bSLuigi Rizzo ip->ip_id = 0; 41868b8534bSLuigi Rizzo ip->ip_off = htons(IP_DF); /* Don't fragment */ 41968b8534bSLuigi Rizzo ip->ip_ttl = IPDEFTTL; 42068b8534bSLuigi Rizzo ip->ip_p = IPPROTO_UDP; 421f8e4e36aSLuigi Rizzo ip->ip_dst.s_addr = targ->g->dst_ip.cur.s_addr; 422f8e4e36aSLuigi Rizzo if (++targ->g->dst_ip.cur.s_addr > targ->g->dst_ip.end.s_addr) 423f8e4e36aSLuigi Rizzo targ->g->dst_ip.cur.s_addr = targ->g->dst_ip.start.s_addr; 424f8e4e36aSLuigi Rizzo ip->ip_src.s_addr = targ->g->src_ip.cur.s_addr; 425f8e4e36aSLuigi Rizzo if (++targ->g->src_ip.cur.s_addr > targ->g->src_ip.end.s_addr) 426f8e4e36aSLuigi Rizzo targ->g->src_ip.cur.s_addr = targ->g->src_ip.start.s_addr; 427f8e4e36aSLuigi Rizzo ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 428f8e4e36aSLuigi Rizzo 429f8e4e36aSLuigi Rizzo 430f8e4e36aSLuigi Rizzo udp = &pkt->udp; 431f8e4e36aSLuigi Rizzo udp->uh_sport = htons(targ->g->src_ip.cur_p); 432f8e4e36aSLuigi Rizzo if (++targ->g->src_ip.cur_p > targ->g->src_ip.port1) 433f8e4e36aSLuigi Rizzo targ->g->src_ip.cur_p = targ->g->src_ip.port0; 434f8e4e36aSLuigi Rizzo udp->uh_dport = htons(targ->g->dst_ip.cur_p); 435f8e4e36aSLuigi Rizzo if (++targ->g->dst_ip.cur_p > targ->g->dst_ip.port1) 436f8e4e36aSLuigi Rizzo targ->g->dst_ip.cur_p = targ->g->dst_ip.port0; 437f8e4e36aSLuigi Rizzo udp->uh_ulen = htons(paylen); 438f8e4e36aSLuigi Rizzo /* Magic: taken from sbin/dhclient/packet.c */ 439f8e4e36aSLuigi Rizzo udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 440f8e4e36aSLuigi Rizzo checksum(pkt->body, 441f8e4e36aSLuigi Rizzo paylen - sizeof(*udp), 442f8e4e36aSLuigi Rizzo checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 443f8e4e36aSLuigi Rizzo IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 444f8e4e36aSLuigi Rizzo ) 445f8e4e36aSLuigi Rizzo ) 446f8e4e36aSLuigi Rizzo )); 44768b8534bSLuigi Rizzo 44868b8534bSLuigi Rizzo eh = &pkt->eh; 449f8e4e36aSLuigi Rizzo bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 450f8e4e36aSLuigi Rizzo bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 45168b8534bSLuigi Rizzo eh->ether_type = htons(ETHERTYPE_IP); 452b303f675SLuigi Rizzo // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 45368b8534bSLuigi Rizzo } 45468b8534bSLuigi Rizzo 45568b8534bSLuigi Rizzo 45668b8534bSLuigi Rizzo 45768b8534bSLuigi Rizzo /* 45868b8534bSLuigi Rizzo * create and enqueue a batch of packets on a ring. 45968b8534bSLuigi Rizzo * On the last one set NS_REPORT to tell the driver to generate 46068b8534bSLuigi Rizzo * an interrupt when done. 46168b8534bSLuigi Rizzo */ 46268b8534bSLuigi Rizzo static int 46368b8534bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, 46499fb123fSLuigi Rizzo int size, u_int count, int options) 46568b8534bSLuigi Rizzo { 46668b8534bSLuigi Rizzo u_int sent, cur = ring->cur; 46768b8534bSLuigi Rizzo 46868b8534bSLuigi Rizzo if (ring->avail < count) 46968b8534bSLuigi Rizzo count = ring->avail; 47068b8534bSLuigi Rizzo 47199fb123fSLuigi Rizzo #if 0 47299fb123fSLuigi Rizzo if (options & (OPT_COPY | OPT_PREFETCH) ) { 47368b8534bSLuigi Rizzo for (sent = 0; sent < count; sent++) { 47468b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 47568b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 47668b8534bSLuigi Rizzo 47799fb123fSLuigi Rizzo prefetch(p); 47899fb123fSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 47999fb123fSLuigi Rizzo } 48099fb123fSLuigi Rizzo cur = ring->cur; 48199fb123fSLuigi Rizzo } 48299fb123fSLuigi Rizzo #endif 48399fb123fSLuigi Rizzo for (sent = 0; sent < count; sent++) { 48499fb123fSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 48599fb123fSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 48699fb123fSLuigi Rizzo 487b303f675SLuigi Rizzo slot->flags = 0; 488b303f675SLuigi Rizzo if (options & OPT_DUMP) 489b303f675SLuigi Rizzo dump_payload(p, size, ring, cur); 490b303f675SLuigi Rizzo if (options & OPT_INDIRECT) { 491b303f675SLuigi Rizzo slot->flags |= NS_INDIRECT; 492b303f675SLuigi Rizzo *((struct pkt **)(void *)p) = pkt; 493b303f675SLuigi Rizzo } else if (options & OPT_COPY) 49499fb123fSLuigi Rizzo pkt_copy(pkt, p, size); 49599fb123fSLuigi Rizzo else if (options & OPT_MEMCPY) 49668b8534bSLuigi Rizzo memcpy(p, pkt, size); 49799fb123fSLuigi Rizzo else if (options & OPT_PREFETCH) 49899fb123fSLuigi Rizzo prefetch(p); 49968b8534bSLuigi Rizzo slot->len = size; 50068b8534bSLuigi Rizzo if (sent == count - 1) 50168b8534bSLuigi Rizzo slot->flags |= NS_REPORT; 50268b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 50368b8534bSLuigi Rizzo } 50468b8534bSLuigi Rizzo ring->avail -= sent; 50568b8534bSLuigi Rizzo ring->cur = cur; 50668b8534bSLuigi Rizzo 50768b8534bSLuigi Rizzo return (sent); 50868b8534bSLuigi Rizzo } 50968b8534bSLuigi Rizzo 510f8e4e36aSLuigi Rizzo /* 511f8e4e36aSLuigi Rizzo * Send a packet, and wait for a response. 512f8e4e36aSLuigi Rizzo * The payload (after UDP header, ofs 42) has a 4-byte sequence 513f8e4e36aSLuigi Rizzo * followed by a struct timeval (or bintime?) 514f8e4e36aSLuigi Rizzo */ 515f8e4e36aSLuigi Rizzo #define PAY_OFS 42 /* where in the pkt... */ 516f8e4e36aSLuigi Rizzo 51768b8534bSLuigi Rizzo static void * 518f8e4e36aSLuigi Rizzo pinger_body(void *data) 51968b8534bSLuigi Rizzo { 52068b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 52168b8534bSLuigi Rizzo struct pollfd fds[1]; 52268b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 523f8e4e36aSLuigi Rizzo int i, rx = 0, n = targ->g->npackets; 524e5ecae38SEd Maste 525f8e4e36aSLuigi Rizzo fds[0].fd = targ->fd; 526f8e4e36aSLuigi Rizzo fds[0].events = (POLLIN); 527f8e4e36aSLuigi Rizzo static uint32_t sent; 528f8e4e36aSLuigi Rizzo struct timespec ts, now, last_print; 529f8e4e36aSLuigi Rizzo uint32_t count = 0, min = 1000000000, av = 0; 530f8e4e36aSLuigi Rizzo 531f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 532f8e4e36aSLuigi Rizzo D("can only ping with 1 thread"); 533f8e4e36aSLuigi Rizzo return NULL; 534f95a30bdSEd Maste } 535f8e4e36aSLuigi Rizzo 536f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 537f8e4e36aSLuigi Rizzo while (n == 0 || (int)sent < n) { 538f8e4e36aSLuigi Rizzo struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 539f8e4e36aSLuigi Rizzo struct netmap_slot *slot; 540f8e4e36aSLuigi Rizzo char *p; 541f8e4e36aSLuigi Rizzo for (i = 0; i < 1; i++) { 542f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 543f8e4e36aSLuigi Rizzo slot->len = targ->g->pkt_size; 544f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 545f8e4e36aSLuigi Rizzo 546f8e4e36aSLuigi Rizzo if (ring->avail == 0) { 547f8e4e36aSLuigi Rizzo D("-- ouch, cannot send"); 548f8e4e36aSLuigi Rizzo } else { 549f8e4e36aSLuigi Rizzo pkt_copy(&targ->pkt, p, targ->g->pkt_size); 550f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 551f8e4e36aSLuigi Rizzo bcopy(&sent, p+42, sizeof(sent)); 552f8e4e36aSLuigi Rizzo bcopy(&ts, p+46, sizeof(ts)); 553f8e4e36aSLuigi Rizzo sent++; 554f8e4e36aSLuigi Rizzo ring->cur = NETMAP_RING_NEXT(ring, ring->cur); 555f8e4e36aSLuigi Rizzo ring->avail--; 556f8e4e36aSLuigi Rizzo } 557f8e4e36aSLuigi Rizzo } 558f8e4e36aSLuigi Rizzo /* should use a parameter to decide how often to send */ 559f8e4e36aSLuigi Rizzo if (poll(fds, 1, 3000) <= 0) { 560f8e4e36aSLuigi Rizzo D("poll error/timeout on queue %d", targ->me); 561f8e4e36aSLuigi Rizzo continue; 562f8e4e36aSLuigi Rizzo } 563f8e4e36aSLuigi Rizzo /* see what we got back */ 564f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 565f8e4e36aSLuigi Rizzo ring = NETMAP_RXRING(nifp, i); 566f8e4e36aSLuigi Rizzo while (ring->avail > 0) { 567f8e4e36aSLuigi Rizzo uint32_t seq; 568f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 569f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 570f8e4e36aSLuigi Rizzo 571f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &now); 572f8e4e36aSLuigi Rizzo bcopy(p+42, &seq, sizeof(seq)); 573f8e4e36aSLuigi Rizzo bcopy(p+46, &ts, sizeof(ts)); 574f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - ts.tv_sec; 575f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 576f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 577f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 578f8e4e36aSLuigi Rizzo ts.tv_sec--; 579f8e4e36aSLuigi Rizzo } 580f8e4e36aSLuigi Rizzo if (1) D("seq %d/%d delta %d.%09d", seq, sent, 581f8e4e36aSLuigi Rizzo (int)ts.tv_sec, (int)ts.tv_nsec); 582f8e4e36aSLuigi Rizzo if (ts.tv_nsec < (int)min) 583f8e4e36aSLuigi Rizzo min = ts.tv_nsec; 584f8e4e36aSLuigi Rizzo count ++; 585f8e4e36aSLuigi Rizzo av += ts.tv_nsec; 586f8e4e36aSLuigi Rizzo ring->avail--; 587f8e4e36aSLuigi Rizzo ring->cur = NETMAP_RING_NEXT(ring, ring->cur); 588f8e4e36aSLuigi Rizzo rx++; 589f8e4e36aSLuigi Rizzo } 590f8e4e36aSLuigi Rizzo } 591f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 592f8e4e36aSLuigi Rizzo //usleep(100000); 593f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - last_print.tv_sec; 594f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 595f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 596f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 597f8e4e36aSLuigi Rizzo ts.tv_sec--; 598f8e4e36aSLuigi Rizzo } 599f8e4e36aSLuigi Rizzo if (ts.tv_sec >= 1) { 600f8e4e36aSLuigi Rizzo D("count %d min %d av %d", 601f8e4e36aSLuigi Rizzo count, min, av/count); 602f8e4e36aSLuigi Rizzo count = 0; 603f8e4e36aSLuigi Rizzo av = 0; 604f8e4e36aSLuigi Rizzo min = 100000000; 605f8e4e36aSLuigi Rizzo last_print = now; 606f8e4e36aSLuigi Rizzo } 607f8e4e36aSLuigi Rizzo } 608f8e4e36aSLuigi Rizzo return NULL; 609f8e4e36aSLuigi Rizzo } 610f8e4e36aSLuigi Rizzo 611f8e4e36aSLuigi Rizzo 612f8e4e36aSLuigi Rizzo /* 613f8e4e36aSLuigi Rizzo * reply to ping requests 614f8e4e36aSLuigi Rizzo */ 615f8e4e36aSLuigi Rizzo static void * 616f8e4e36aSLuigi Rizzo ponger_body(void *data) 617f8e4e36aSLuigi Rizzo { 618f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 619f8e4e36aSLuigi Rizzo struct pollfd fds[1]; 620f8e4e36aSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 621f8e4e36aSLuigi Rizzo struct netmap_ring *txring, *rxring; 622f8e4e36aSLuigi Rizzo int i, rx = 0, sent = 0, n = targ->g->npackets; 623f8e4e36aSLuigi Rizzo fds[0].fd = targ->fd; 624f8e4e36aSLuigi Rizzo fds[0].events = (POLLIN); 625f8e4e36aSLuigi Rizzo 626f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 627f8e4e36aSLuigi Rizzo D("can only reply ping with 1 thread"); 628f8e4e36aSLuigi Rizzo return NULL; 629f8e4e36aSLuigi Rizzo } 630f8e4e36aSLuigi Rizzo D("understood ponger %d but don't know how to do it", n); 631f8e4e36aSLuigi Rizzo while (n == 0 || sent < n) { 632f8e4e36aSLuigi Rizzo uint32_t txcur, txavail; 633f8e4e36aSLuigi Rizzo //#define BUSYWAIT 634f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 635f8e4e36aSLuigi Rizzo ioctl(fds[0].fd, NIOCRXSYNC, NULL); 636f8e4e36aSLuigi Rizzo #else 637f8e4e36aSLuigi Rizzo if (poll(fds, 1, 1000) <= 0) { 638f8e4e36aSLuigi Rizzo D("poll error/timeout on queue %d", targ->me); 639f8e4e36aSLuigi Rizzo continue; 640f8e4e36aSLuigi Rizzo } 641f8e4e36aSLuigi Rizzo #endif 642f8e4e36aSLuigi Rizzo txring = NETMAP_TXRING(nifp, 0); 643f8e4e36aSLuigi Rizzo txcur = txring->cur; 644f8e4e36aSLuigi Rizzo txavail = txring->avail; 645f8e4e36aSLuigi Rizzo /* see what we got back */ 646f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 647f8e4e36aSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 648f8e4e36aSLuigi Rizzo while (rxring->avail > 0) { 649f8e4e36aSLuigi Rizzo uint16_t *spkt, *dpkt; 650f8e4e36aSLuigi Rizzo uint32_t cur = rxring->cur; 651f8e4e36aSLuigi Rizzo struct netmap_slot *slot = &rxring->slot[cur]; 652f8e4e36aSLuigi Rizzo char *src, *dst; 653f8e4e36aSLuigi Rizzo src = NETMAP_BUF(rxring, slot->buf_idx); 654f8e4e36aSLuigi Rizzo //D("got pkt %p of size %d", src, slot->len); 655f8e4e36aSLuigi Rizzo rxring->avail--; 656f8e4e36aSLuigi Rizzo rxring->cur = NETMAP_RING_NEXT(rxring, cur); 657f8e4e36aSLuigi Rizzo rx++; 658f8e4e36aSLuigi Rizzo if (txavail == 0) 659f8e4e36aSLuigi Rizzo continue; 660f8e4e36aSLuigi Rizzo dst = NETMAP_BUF(txring, 661f8e4e36aSLuigi Rizzo txring->slot[txcur].buf_idx); 662f8e4e36aSLuigi Rizzo /* copy... */ 663f8e4e36aSLuigi Rizzo dpkt = (uint16_t *)dst; 664f8e4e36aSLuigi Rizzo spkt = (uint16_t *)src; 665f8e4e36aSLuigi Rizzo pkt_copy(src, dst, slot->len); 666f8e4e36aSLuigi Rizzo dpkt[0] = spkt[3]; 667f8e4e36aSLuigi Rizzo dpkt[1] = spkt[4]; 668f8e4e36aSLuigi Rizzo dpkt[2] = spkt[5]; 669f8e4e36aSLuigi Rizzo dpkt[3] = spkt[0]; 670f8e4e36aSLuigi Rizzo dpkt[4] = spkt[1]; 671f8e4e36aSLuigi Rizzo dpkt[5] = spkt[2]; 672f8e4e36aSLuigi Rizzo txring->slot[txcur].len = slot->len; 673f8e4e36aSLuigi Rizzo /* XXX swap src dst mac */ 674f8e4e36aSLuigi Rizzo txcur = NETMAP_RING_NEXT(txring, txcur); 675f8e4e36aSLuigi Rizzo txavail--; 676f8e4e36aSLuigi Rizzo sent++; 677f8e4e36aSLuigi Rizzo } 678f8e4e36aSLuigi Rizzo } 679f8e4e36aSLuigi Rizzo txring->cur = txcur; 680f8e4e36aSLuigi Rizzo txring->avail = txavail; 681f8e4e36aSLuigi Rizzo targ->count = sent; 682f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 683f8e4e36aSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 684f8e4e36aSLuigi Rizzo #endif 685f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 686f8e4e36aSLuigi Rizzo } 687f8e4e36aSLuigi Rizzo return NULL; 688f8e4e36aSLuigi Rizzo } 689f8e4e36aSLuigi Rizzo 6901cb4c501SLuigi Rizzo static __inline int 6911cb4c501SLuigi Rizzo timespec_ge(const struct timespec *a, const struct timespec *b) 6921cb4c501SLuigi Rizzo { 6931cb4c501SLuigi Rizzo 6941cb4c501SLuigi Rizzo if (a->tv_sec > b->tv_sec) 6951cb4c501SLuigi Rizzo return (1); 6961cb4c501SLuigi Rizzo if (a->tv_sec < b->tv_sec) 6971cb4c501SLuigi Rizzo return (0); 6981cb4c501SLuigi Rizzo if (a->tv_nsec >= b->tv_nsec) 6991cb4c501SLuigi Rizzo return (1); 7001cb4c501SLuigi Rizzo return (0); 7011cb4c501SLuigi Rizzo } 7021cb4c501SLuigi Rizzo 7031cb4c501SLuigi Rizzo static __inline struct timespec 7041cb4c501SLuigi Rizzo timeval2spec(const struct timeval *a) 7051cb4c501SLuigi Rizzo { 7061cb4c501SLuigi Rizzo struct timespec ts = { 7071cb4c501SLuigi Rizzo .tv_sec = a->tv_sec, 7081cb4c501SLuigi Rizzo .tv_nsec = a->tv_usec * 1000 7091cb4c501SLuigi Rizzo }; 7101cb4c501SLuigi Rizzo return ts; 7111cb4c501SLuigi Rizzo } 7121cb4c501SLuigi Rizzo 7131cb4c501SLuigi Rizzo static __inline struct timeval 7141cb4c501SLuigi Rizzo timespec2val(const struct timespec *a) 7151cb4c501SLuigi Rizzo { 7161cb4c501SLuigi Rizzo struct timeval tv = { 7171cb4c501SLuigi Rizzo .tv_sec = a->tv_sec, 7181cb4c501SLuigi Rizzo .tv_usec = a->tv_nsec / 1000 7191cb4c501SLuigi Rizzo }; 7201cb4c501SLuigi Rizzo return tv; 7211cb4c501SLuigi Rizzo } 7221cb4c501SLuigi Rizzo 7231cb4c501SLuigi Rizzo 7241cb4c501SLuigi Rizzo static int 7251cb4c501SLuigi Rizzo wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited) 7261cb4c501SLuigi Rizzo { 7271cb4c501SLuigi Rizzo struct timespec curtime; 7281cb4c501SLuigi Rizzo 7291cb4c501SLuigi Rizzo curtime.tv_sec = 0; 7301cb4c501SLuigi Rizzo curtime.tv_nsec = 0; 7311cb4c501SLuigi Rizzo 7321cb4c501SLuigi Rizzo if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) { 7331cb4c501SLuigi Rizzo D("clock_gettime: %s", strerror(errno)); 7341cb4c501SLuigi Rizzo return (-1); 7351cb4c501SLuigi Rizzo } 7361cb4c501SLuigi Rizzo while (timespec_ge(&ts, &curtime)) { 7371cb4c501SLuigi Rizzo if (waited != NULL) 7381cb4c501SLuigi Rizzo (*waited)++; 7391cb4c501SLuigi Rizzo if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) { 7401cb4c501SLuigi Rizzo D("clock_gettime"); 7411cb4c501SLuigi Rizzo return (-1); 7421cb4c501SLuigi Rizzo } 7431cb4c501SLuigi Rizzo } 7441cb4c501SLuigi Rizzo if (wakeup_ts != NULL) 7451cb4c501SLuigi Rizzo *wakeup_ts = curtime; 7461cb4c501SLuigi Rizzo return (0); 7471cb4c501SLuigi Rizzo } 7481cb4c501SLuigi Rizzo 7491cb4c501SLuigi Rizzo static __inline void 7501cb4c501SLuigi Rizzo timespec_add(struct timespec *tsa, struct timespec *tsb) 7511cb4c501SLuigi Rizzo { 7521cb4c501SLuigi Rizzo tsa->tv_sec += tsb->tv_sec; 7531cb4c501SLuigi Rizzo tsa->tv_nsec += tsb->tv_nsec; 7541cb4c501SLuigi Rizzo if (tsa->tv_nsec >= 1000000000) { 7551cb4c501SLuigi Rizzo tsa->tv_sec++; 7561cb4c501SLuigi Rizzo tsa->tv_nsec -= 1000000000; 7571cb4c501SLuigi Rizzo } 7581cb4c501SLuigi Rizzo } 7591cb4c501SLuigi Rizzo 760f8e4e36aSLuigi Rizzo 761f8e4e36aSLuigi Rizzo static void * 762f8e4e36aSLuigi Rizzo sender_body(void *data) 763f8e4e36aSLuigi Rizzo { 764f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 765f8e4e36aSLuigi Rizzo 766f8e4e36aSLuigi Rizzo struct pollfd fds[1]; 767f8e4e36aSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 768f8e4e36aSLuigi Rizzo struct netmap_ring *txring; 769f8e4e36aSLuigi Rizzo int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 770f8e4e36aSLuigi Rizzo int options = targ->g->options | OPT_COPY; 7711cb4c501SLuigi Rizzo struct timespec tmptime, nexttime = { 0, 0}; // XXX silence compiler 7721cb4c501SLuigi Rizzo int rate_limit = targ->g->tx_rate; 7731cb4c501SLuigi Rizzo long long waited = 0; 774b303f675SLuigi Rizzo 775f8e4e36aSLuigi Rizzo D("start"); 77668b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 77768b8534bSLuigi Rizzo goto quit; 7788ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 77968b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 78068b8534bSLuigi Rizzo fds[0].fd = targ->fd; 78168b8534bSLuigi Rizzo fds[0].events = (POLLOUT); 78268b8534bSLuigi Rizzo 78368b8534bSLuigi Rizzo /* main loop.*/ 7841cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 7851cb4c501SLuigi Rizzo if (rate_limit) { 7861cb4c501SLuigi Rizzo tmptime.tv_sec = 2; 7871cb4c501SLuigi Rizzo tmptime.tv_nsec = 0; 7881cb4c501SLuigi Rizzo timespec_add(&targ->tic, &tmptime); 7891cb4c501SLuigi Rizzo targ->tic.tv_nsec = 0; 7901cb4c501SLuigi Rizzo if (wait_time(targ->tic, NULL, NULL) == -1) { 7911cb4c501SLuigi Rizzo D("wait_time: %s", strerror(errno)); 7921cb4c501SLuigi Rizzo goto quit; 7931cb4c501SLuigi Rizzo } 7941cb4c501SLuigi Rizzo nexttime = targ->tic; 7951cb4c501SLuigi Rizzo } 796f8e4e36aSLuigi Rizzo if (targ->g->dev_type == DEV_PCAP) { 79768b8534bSLuigi Rizzo int size = targ->g->pkt_size; 79868b8534bSLuigi Rizzo void *pkt = &targ->pkt; 79968b8534bSLuigi Rizzo pcap_t *p = targ->g->p; 80068b8534bSLuigi Rizzo 801f8e4e36aSLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 80299fb123fSLuigi Rizzo if (pcap_inject(p, pkt, size) != -1) 80399fb123fSLuigi Rizzo sent++; 80499fb123fSLuigi Rizzo if (i > 10000) { 80599fb123fSLuigi Rizzo targ->count = sent; 80699fb123fSLuigi Rizzo i = 0; 80799fb123fSLuigi Rizzo } 80868b8534bSLuigi Rizzo } 809f8e4e36aSLuigi Rizzo } else if (targ->g->dev_type == DEV_TAP) { /* tap */ 810f8e4e36aSLuigi Rizzo int size = targ->g->pkt_size; 811f8e4e36aSLuigi Rizzo void *pkt = &targ->pkt; 812f8e4e36aSLuigi Rizzo D("writing to file desc %d", targ->g->main_fd); 813f8e4e36aSLuigi Rizzo 814f8e4e36aSLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 815f8e4e36aSLuigi Rizzo if (write(targ->g->main_fd, pkt, size) != -1) 816f8e4e36aSLuigi Rizzo sent++; 817f8e4e36aSLuigi Rizzo if (i > 10000) { 818f8e4e36aSLuigi Rizzo targ->count = sent; 819f8e4e36aSLuigi Rizzo i = 0; 820f8e4e36aSLuigi Rizzo } 821f8e4e36aSLuigi Rizzo } 82268b8534bSLuigi Rizzo } else { 8231cb4c501SLuigi Rizzo int tosend = 0; 824f8e4e36aSLuigi Rizzo while (!targ->cancel && (n == 0 || sent < n)) { 82568b8534bSLuigi Rizzo 8261cb4c501SLuigi Rizzo if (rate_limit && tosend <= 0) { 8271cb4c501SLuigi Rizzo tosend = targ->g->burst; 8281cb4c501SLuigi Rizzo timespec_add(&nexttime, &targ->g->tx_period); 8291cb4c501SLuigi Rizzo if (wait_time(nexttime, &tmptime, &waited) == -1) { 8301cb4c501SLuigi Rizzo D("wait_time"); 8311cb4c501SLuigi Rizzo goto quit; 8321cb4c501SLuigi Rizzo } 8331cb4c501SLuigi Rizzo } 8341cb4c501SLuigi Rizzo 83568b8534bSLuigi Rizzo /* 83668b8534bSLuigi Rizzo * wait for available room in the send queue(s) 83768b8534bSLuigi Rizzo */ 838f8e4e36aSLuigi Rizzo if (poll(fds, 1, 2000) <= 0) { 8393fe77e68SEd Maste if (targ->cancel) 8403fe77e68SEd Maste break; 841f8e4e36aSLuigi Rizzo D("poll error/timeout on queue %d", targ->me); 84268b8534bSLuigi Rizzo goto quit; 84368b8534bSLuigi Rizzo } 84468b8534bSLuigi Rizzo /* 84568b8534bSLuigi Rizzo * scan our queues and send on those with room 84668b8534bSLuigi Rizzo */ 847f8e4e36aSLuigi Rizzo if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 848f8e4e36aSLuigi Rizzo D("drop copy"); 84999fb123fSLuigi Rizzo options &= ~OPT_COPY; 850f8e4e36aSLuigi Rizzo } 851f8e4e36aSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 8521cb4c501SLuigi Rizzo int m, limit = rate_limit ? tosend : targ->g->burst; 853f8e4e36aSLuigi Rizzo if (n > 0 && n - sent < limit) 854f8e4e36aSLuigi Rizzo limit = n - sent; 85568b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 85668b8534bSLuigi Rizzo if (txring->avail == 0) 85768b8534bSLuigi Rizzo continue; 85868b8534bSLuigi Rizzo m = send_packets(txring, &targ->pkt, targ->g->pkt_size, 85999fb123fSLuigi Rizzo limit, options); 86068b8534bSLuigi Rizzo sent += m; 8611cb4c501SLuigi Rizzo tosend -= m; 86268b8534bSLuigi Rizzo targ->count = sent; 86368b8534bSLuigi Rizzo } 86468b8534bSLuigi Rizzo } 86599fb123fSLuigi Rizzo /* flush any remaining packets */ 86668b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 86768b8534bSLuigi Rizzo 86868b8534bSLuigi Rizzo /* final part: wait all the TX queues to be empty. */ 86968b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 87068b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 87168b8534bSLuigi Rizzo while (!NETMAP_TX_RING_EMPTY(txring)) { 87268b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 87368b8534bSLuigi Rizzo usleep(1); /* wait 1 tick */ 87468b8534bSLuigi Rizzo } 87568b8534bSLuigi Rizzo } 87668b8534bSLuigi Rizzo } 87768b8534bSLuigi Rizzo 8781cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 87968b8534bSLuigi Rizzo targ->completed = 1; 88068b8534bSLuigi Rizzo targ->count = sent; 88168b8534bSLuigi Rizzo 88268b8534bSLuigi Rizzo quit: 88368b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 88468b8534bSLuigi Rizzo targ->used = 0; 88568b8534bSLuigi Rizzo 88668b8534bSLuigi Rizzo return (NULL); 88768b8534bSLuigi Rizzo } 88868b8534bSLuigi Rizzo 88968b8534bSLuigi Rizzo 89068b8534bSLuigi Rizzo static void 891f8e4e36aSLuigi Rizzo receive_pcap(u_char *user, const struct pcap_pkthdr * h, 892f8e4e36aSLuigi Rizzo const u_char * bytes) 89368b8534bSLuigi Rizzo { 89468b8534bSLuigi Rizzo int *count = (int *)user; 895f8e4e36aSLuigi Rizzo (void)h; /* UNUSED */ 896f8e4e36aSLuigi Rizzo (void)bytes; /* UNUSED */ 89768b8534bSLuigi Rizzo (*count)++; 89868b8534bSLuigi Rizzo } 89968b8534bSLuigi Rizzo 90068b8534bSLuigi Rizzo static int 901b303f675SLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int dump) 90268b8534bSLuigi Rizzo { 90368b8534bSLuigi Rizzo u_int cur, rx; 90468b8534bSLuigi Rizzo 90568b8534bSLuigi Rizzo cur = ring->cur; 90668b8534bSLuigi Rizzo if (ring->avail < limit) 90768b8534bSLuigi Rizzo limit = ring->avail; 90868b8534bSLuigi Rizzo for (rx = 0; rx < limit; rx++) { 90968b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 91068b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 91168b8534bSLuigi Rizzo 912b303f675SLuigi Rizzo slot->flags = OPT_INDIRECT; // XXX 913b303f675SLuigi Rizzo if (dump) 914b303f675SLuigi Rizzo dump_payload(p, slot->len, ring, cur); 91568b8534bSLuigi Rizzo 91668b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 91768b8534bSLuigi Rizzo } 91868b8534bSLuigi Rizzo ring->avail -= rx; 91968b8534bSLuigi Rizzo ring->cur = cur; 92068b8534bSLuigi Rizzo 92168b8534bSLuigi Rizzo return (rx); 92268b8534bSLuigi Rizzo } 92368b8534bSLuigi Rizzo 92468b8534bSLuigi Rizzo static void * 92568b8534bSLuigi Rizzo receiver_body(void *data) 92668b8534bSLuigi Rizzo { 92768b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 92868b8534bSLuigi Rizzo struct pollfd fds[1]; 92968b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 93068b8534bSLuigi Rizzo struct netmap_ring *rxring; 931f8e4e36aSLuigi Rizzo int i; 932f8e4e36aSLuigi Rizzo uint64_t received = 0; 93368b8534bSLuigi Rizzo 93468b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 93568b8534bSLuigi Rizzo goto quit; 93668b8534bSLuigi Rizzo 9378ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 93868b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 93968b8534bSLuigi Rizzo fds[0].fd = targ->fd; 94068b8534bSLuigi Rizzo fds[0].events = (POLLIN); 94168b8534bSLuigi Rizzo 94268b8534bSLuigi Rizzo /* unbounded wait for the first packet. */ 943f8e4e36aSLuigi Rizzo for (;;) { 94468b8534bSLuigi Rizzo i = poll(fds, 1, 1000); 94568b8534bSLuigi Rizzo if (i > 0 && !(fds[0].revents & POLLERR)) 94668b8534bSLuigi Rizzo break; 94768b8534bSLuigi Rizzo D("waiting for initial packets, poll returns %d %d", i, fds[0].revents); 94868b8534bSLuigi Rizzo } 94968b8534bSLuigi Rizzo 95068b8534bSLuigi Rizzo /* main loop, exit after 1s silence */ 9511cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 952f8e4e36aSLuigi Rizzo if (targ->g->dev_type == DEV_PCAP) { 9533fe77e68SEd Maste while (!targ->cancel) { 954f8e4e36aSLuigi Rizzo /* XXX should we poll ? */ 95568b8534bSLuigi Rizzo pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 95668b8534bSLuigi Rizzo } 957f8e4e36aSLuigi Rizzo } else if (targ->g->dev_type == DEV_TAP) { 958f8e4e36aSLuigi Rizzo D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd); 959f8e4e36aSLuigi Rizzo while (!targ->cancel) { 960f8e4e36aSLuigi Rizzo char buf[2048]; 961f8e4e36aSLuigi Rizzo /* XXX should we poll ? */ 962f8e4e36aSLuigi Rizzo if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) 963f8e4e36aSLuigi Rizzo targ->count++; 964f8e4e36aSLuigi Rizzo } 96568b8534bSLuigi Rizzo } else { 966b303f675SLuigi Rizzo int dump = targ->g->options & OPT_DUMP; 9673fe77e68SEd Maste while (!targ->cancel) { 96868b8534bSLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 96968b8534bSLuigi Rizzo before quitting. */ 9701cb4c501SLuigi Rizzo if (poll(fds, 1, 1 * 1000) <= 0 && !targ->g->forever) { 9711cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 9728ce070c1SUlrich Spörlein targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 97368b8534bSLuigi Rizzo break; 97468b8534bSLuigi Rizzo } 97568b8534bSLuigi Rizzo 97668b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 97768b8534bSLuigi Rizzo int m; 97868b8534bSLuigi Rizzo 97968b8534bSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 98068b8534bSLuigi Rizzo if (rxring->avail == 0) 98168b8534bSLuigi Rizzo continue; 98268b8534bSLuigi Rizzo 983b303f675SLuigi Rizzo m = receive_packets(rxring, targ->g->burst, dump); 98468b8534bSLuigi Rizzo received += m; 98568b8534bSLuigi Rizzo } 986f8e4e36aSLuigi Rizzo targ->count = received; 98768b8534bSLuigi Rizzo 98868b8534bSLuigi Rizzo // tell the card we have read the data 98968b8534bSLuigi Rizzo //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 99068b8534bSLuigi Rizzo } 99168b8534bSLuigi Rizzo } 99268b8534bSLuigi Rizzo 99368b8534bSLuigi Rizzo targ->completed = 1; 99468b8534bSLuigi Rizzo targ->count = received; 99568b8534bSLuigi Rizzo 99668b8534bSLuigi Rizzo quit: 99768b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 99868b8534bSLuigi Rizzo targ->used = 0; 99968b8534bSLuigi Rizzo 100068b8534bSLuigi Rizzo return (NULL); 100168b8534bSLuigi Rizzo } 100268b8534bSLuigi Rizzo 1003f8e4e36aSLuigi Rizzo /* very crude code to print a number in normalized form. 1004f8e4e36aSLuigi Rizzo * Caller has to make sure that the buffer is large enough. 1005f8e4e36aSLuigi Rizzo */ 1006f8e4e36aSLuigi Rizzo static const char * 1007f8e4e36aSLuigi Rizzo norm(char *buf, double val) 100866a698c9SEd Maste { 1009f8e4e36aSLuigi Rizzo char *units[] = { "", "K", "M", "G" }; 1010f8e4e36aSLuigi Rizzo u_int i; 101166a698c9SEd Maste 1012f8e4e36aSLuigi Rizzo for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++) 101366a698c9SEd Maste val /= 1000; 1014f8e4e36aSLuigi Rizzo sprintf(buf, "%.2f %s", val, units[i]); 1015f8e4e36aSLuigi Rizzo return buf; 101666a698c9SEd Maste } 101766a698c9SEd Maste 101868b8534bSLuigi Rizzo static void 101968b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta) 102068b8534bSLuigi Rizzo { 1021f8e4e36aSLuigi Rizzo double bw, raw_bw, pps; 1022f8e4e36aSLuigi Rizzo char b1[40], b2[80], b3[80]; 102368b8534bSLuigi Rizzo 1024506cc70cSLuigi Rizzo printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n", 102568b8534bSLuigi Rizzo sent, size, delta); 1026f8e4e36aSLuigi Rizzo if (delta == 0) 1027f8e4e36aSLuigi Rizzo delta = 1e-6; 1028f8e4e36aSLuigi Rizzo if (size < 60) /* correct for min packet size */ 1029f8e4e36aSLuigi Rizzo size = 60; 1030f8e4e36aSLuigi Rizzo pps = sent / delta; 1031f8e4e36aSLuigi Rizzo bw = (8.0 * size * sent) / delta; 1032f8e4e36aSLuigi Rizzo /* raw packets have4 bytes crc + 20 bytes framing */ 1033f8e4e36aSLuigi Rizzo raw_bw = (8.0 * (size + 24) * sent) / delta; 103466a698c9SEd Maste 1035f8e4e36aSLuigi Rizzo printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", 1036f8e4e36aSLuigi Rizzo norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); 103768b8534bSLuigi Rizzo } 103868b8534bSLuigi Rizzo 103968b8534bSLuigi Rizzo 104068b8534bSLuigi Rizzo static void 104168b8534bSLuigi Rizzo rx_output(uint64_t received, double delta) 104268b8534bSLuigi Rizzo { 1043f8e4e36aSLuigi Rizzo double pps; 1044f8e4e36aSLuigi Rizzo char b1[40]; 104568b8534bSLuigi Rizzo 1046506cc70cSLuigi Rizzo printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta); 1047f8e4e36aSLuigi Rizzo 1048f8e4e36aSLuigi Rizzo if (delta == 0) 1049f8e4e36aSLuigi Rizzo delta = 1e-6; 1050f8e4e36aSLuigi Rizzo pps = received / delta; 1051f8e4e36aSLuigi Rizzo printf("Speed: %spps\n", norm(b1, pps)); 105268b8534bSLuigi Rizzo } 105368b8534bSLuigi Rizzo 105468b8534bSLuigi Rizzo static void 105568b8534bSLuigi Rizzo usage(void) 105668b8534bSLuigi Rizzo { 105768b8534bSLuigi Rizzo const char *cmd = "pkt-gen"; 105868b8534bSLuigi Rizzo fprintf(stderr, 105968b8534bSLuigi Rizzo "Usage:\n" 106068b8534bSLuigi Rizzo "%s arguments\n" 106168b8534bSLuigi Rizzo "\t-i interface interface name\n" 1062f8e4e36aSLuigi Rizzo "\t-f function tx rx ping pong\n" 1063f8e4e36aSLuigi Rizzo "\t-n count number of iterations (can be 0)\n" 1064f8e4e36aSLuigi Rizzo "\t-t pkts_to_send also forces tx mode\n" 1065f8e4e36aSLuigi Rizzo "\t-r pkts_to_receive also forces rx mode\n" 106668b8534bSLuigi Rizzo "\t-l pkts_size in bytes excluding CRC\n" 106768b8534bSLuigi Rizzo "\t-d dst-ip end with %%n to sweep n addresses\n" 106868b8534bSLuigi Rizzo "\t-s src-ip end with %%n to sweep n addresses\n" 106968b8534bSLuigi Rizzo "\t-D dst-mac end with %%n to sweep n addresses\n" 107068b8534bSLuigi Rizzo "\t-S src-mac end with %%n to sweep n addresses\n" 1071f8e4e36aSLuigi Rizzo "\t-a cpu_id use setaffinity\n" 107268b8534bSLuigi Rizzo "\t-b burst size testing, mostly\n" 107368b8534bSLuigi Rizzo "\t-c cores cores to use\n" 107468b8534bSLuigi Rizzo "\t-p threads processes/threads to use\n" 107568b8534bSLuigi Rizzo "\t-T report_ms milliseconds between reports\n" 1076f8e4e36aSLuigi Rizzo "\t-P use libpcap instead of netmap\n" 107768b8534bSLuigi Rizzo "\t-w wait_for_link_time in seconds\n" 107868b8534bSLuigi Rizzo "", 107968b8534bSLuigi Rizzo cmd); 108068b8534bSLuigi Rizzo 108168b8534bSLuigi Rizzo exit(0); 108268b8534bSLuigi Rizzo } 108368b8534bSLuigi Rizzo 1084f8e4e36aSLuigi Rizzo static void 1085f8e4e36aSLuigi Rizzo start_threads(struct glob_arg *g) 1086f8e4e36aSLuigi Rizzo { 1087f8e4e36aSLuigi Rizzo int i; 1088f8e4e36aSLuigi Rizzo 1089f8e4e36aSLuigi Rizzo targs = calloc(g->nthreads, sizeof(*targs)); 1090f8e4e36aSLuigi Rizzo /* 1091f8e4e36aSLuigi Rizzo * Now create the desired number of threads, each one 1092f8e4e36aSLuigi Rizzo * using a single descriptor. 1093f8e4e36aSLuigi Rizzo */ 1094f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 1095f8e4e36aSLuigi Rizzo bzero(&targs[i], sizeof(targs[i])); 1096f8e4e36aSLuigi Rizzo targs[i].fd = -1; /* default, with pcap */ 1097f8e4e36aSLuigi Rizzo targs[i].g = g; 1098f8e4e36aSLuigi Rizzo 1099f8e4e36aSLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 1100f8e4e36aSLuigi Rizzo struct nmreq tifreq; 1101f8e4e36aSLuigi Rizzo int tfd; 1102f8e4e36aSLuigi Rizzo 1103f8e4e36aSLuigi Rizzo /* register interface. */ 1104f8e4e36aSLuigi Rizzo tfd = open("/dev/netmap", O_RDWR); 1105f8e4e36aSLuigi Rizzo if (tfd == -1) { 1106f8e4e36aSLuigi Rizzo D("Unable to open /dev/netmap"); 1107f8e4e36aSLuigi Rizzo continue; 1108f8e4e36aSLuigi Rizzo } 1109f8e4e36aSLuigi Rizzo targs[i].fd = tfd; 1110f8e4e36aSLuigi Rizzo 1111f8e4e36aSLuigi Rizzo bzero(&tifreq, sizeof(tifreq)); 1112f8e4e36aSLuigi Rizzo strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name)); 1113f8e4e36aSLuigi Rizzo tifreq.nr_version = NETMAP_API; 1114f8e4e36aSLuigi Rizzo tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 1115f8e4e36aSLuigi Rizzo 1116f8e4e36aSLuigi Rizzo /* 1117f8e4e36aSLuigi Rizzo * if we are acting as a receiver only, do not touch the transmit ring. 1118f8e4e36aSLuigi Rizzo * This is not the default because many apps may use the interface 1119f8e4e36aSLuigi Rizzo * in both directions, but a pure receiver does not. 1120f8e4e36aSLuigi Rizzo */ 1121f8e4e36aSLuigi Rizzo if (g->td_body == receiver_body) { 1122f8e4e36aSLuigi Rizzo tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 1123f8e4e36aSLuigi Rizzo } 1124f8e4e36aSLuigi Rizzo 1125f8e4e36aSLuigi Rizzo if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 1126f8e4e36aSLuigi Rizzo D("Unable to register %s", g->ifname); 1127f8e4e36aSLuigi Rizzo continue; 1128f8e4e36aSLuigi Rizzo } 1129f8e4e36aSLuigi Rizzo targs[i].nmr = tifreq; 1130f8e4e36aSLuigi Rizzo targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset); 1131f8e4e36aSLuigi Rizzo /* start threads. */ 1132f8e4e36aSLuigi Rizzo targs[i].qfirst = (g->nthreads > 1) ? i : 0; 1133f8e4e36aSLuigi Rizzo targs[i].qlast = (g->nthreads > 1) ? i+1 : 1134f8e4e36aSLuigi Rizzo (g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings); 1135f8e4e36aSLuigi Rizzo } else { 1136f8e4e36aSLuigi Rizzo targs[i].fd = g->main_fd; 1137f8e4e36aSLuigi Rizzo } 1138f8e4e36aSLuigi Rizzo targs[i].used = 1; 1139f8e4e36aSLuigi Rizzo targs[i].me = i; 1140f8e4e36aSLuigi Rizzo if (g->affinity >= 0) { 1141f8e4e36aSLuigi Rizzo if (g->affinity < g->cpus) 1142f8e4e36aSLuigi Rizzo targs[i].affinity = g->affinity; 1143f8e4e36aSLuigi Rizzo else 1144f8e4e36aSLuigi Rizzo targs[i].affinity = i % g->cpus; 1145f8e4e36aSLuigi Rizzo } else 1146f8e4e36aSLuigi Rizzo targs[i].affinity = -1; 1147f8e4e36aSLuigi Rizzo /* default, init packets */ 1148f8e4e36aSLuigi Rizzo initialize_packet(&targs[i]); 1149f8e4e36aSLuigi Rizzo 1150f8e4e36aSLuigi Rizzo if (pthread_create(&targs[i].thread, NULL, g->td_body, 1151f8e4e36aSLuigi Rizzo &targs[i]) == -1) { 1152f8e4e36aSLuigi Rizzo D("Unable to create thread %d", i); 1153f8e4e36aSLuigi Rizzo targs[i].used = 0; 1154f8e4e36aSLuigi Rizzo } 1155f8e4e36aSLuigi Rizzo } 1156f8e4e36aSLuigi Rizzo } 1157f8e4e36aSLuigi Rizzo 1158f8e4e36aSLuigi Rizzo static void 1159f8e4e36aSLuigi Rizzo main_thread(struct glob_arg *g) 1160f8e4e36aSLuigi Rizzo { 1161f8e4e36aSLuigi Rizzo int i; 1162f8e4e36aSLuigi Rizzo 1163f8e4e36aSLuigi Rizzo uint64_t prev = 0; 1164f8e4e36aSLuigi Rizzo uint64_t count = 0; 1165f8e4e36aSLuigi Rizzo double delta_t; 1166f8e4e36aSLuigi Rizzo struct timeval tic, toc; 1167f8e4e36aSLuigi Rizzo 1168f8e4e36aSLuigi Rizzo gettimeofday(&toc, NULL); 1169f8e4e36aSLuigi Rizzo for (;;) { 1170f8e4e36aSLuigi Rizzo struct timeval now, delta; 1171f8e4e36aSLuigi Rizzo uint64_t pps, usec, my_count, npkts; 1172f8e4e36aSLuigi Rizzo int done = 0; 1173f8e4e36aSLuigi Rizzo 1174f8e4e36aSLuigi Rizzo delta.tv_sec = g->report_interval/1000; 1175f8e4e36aSLuigi Rizzo delta.tv_usec = (g->report_interval%1000)*1000; 1176f8e4e36aSLuigi Rizzo select(0, NULL, NULL, NULL, &delta); 1177f8e4e36aSLuigi Rizzo gettimeofday(&now, NULL); 1178f8e4e36aSLuigi Rizzo time_second = now.tv_sec; 1179f8e4e36aSLuigi Rizzo timersub(&now, &toc, &toc); 1180f8e4e36aSLuigi Rizzo my_count = 0; 1181f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 1182f8e4e36aSLuigi Rizzo my_count += targs[i].count; 1183f8e4e36aSLuigi Rizzo if (targs[i].used == 0) 1184f8e4e36aSLuigi Rizzo done++; 1185f8e4e36aSLuigi Rizzo } 1186f8e4e36aSLuigi Rizzo usec = toc.tv_sec* 1000000 + toc.tv_usec; 1187f8e4e36aSLuigi Rizzo if (usec < 10000) 1188f8e4e36aSLuigi Rizzo continue; 1189f8e4e36aSLuigi Rizzo npkts = my_count - prev; 1190f8e4e36aSLuigi Rizzo pps = (npkts*1000000 + usec/2) / usec; 1191f8e4e36aSLuigi Rizzo D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)", 1192f8e4e36aSLuigi Rizzo pps, npkts, usec); 1193f8e4e36aSLuigi Rizzo prev = my_count; 1194f8e4e36aSLuigi Rizzo toc = now; 1195f8e4e36aSLuigi Rizzo if (done == g->nthreads) 1196f8e4e36aSLuigi Rizzo break; 1197f8e4e36aSLuigi Rizzo } 1198f8e4e36aSLuigi Rizzo 1199f8e4e36aSLuigi Rizzo timerclear(&tic); 1200f8e4e36aSLuigi Rizzo timerclear(&toc); 1201f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 12021cb4c501SLuigi Rizzo struct timespec t_tic, t_toc; 1203f8e4e36aSLuigi Rizzo /* 1204f8e4e36aSLuigi Rizzo * Join active threads, unregister interfaces and close 1205f8e4e36aSLuigi Rizzo * file descriptors. 1206f8e4e36aSLuigi Rizzo */ 12071cb4c501SLuigi Rizzo if (targs[i].used) 1208f8e4e36aSLuigi Rizzo pthread_join(targs[i].thread, NULL); 1209f8e4e36aSLuigi Rizzo close(targs[i].fd); 1210f8e4e36aSLuigi Rizzo 1211f8e4e36aSLuigi Rizzo if (targs[i].completed == 0) 1212f8e4e36aSLuigi Rizzo D("ouch, thread %d exited with error", i); 1213f8e4e36aSLuigi Rizzo 1214f8e4e36aSLuigi Rizzo /* 1215f8e4e36aSLuigi Rizzo * Collect threads output and extract information about 1216f8e4e36aSLuigi Rizzo * how long it took to send all the packets. 1217f8e4e36aSLuigi Rizzo */ 1218f8e4e36aSLuigi Rizzo count += targs[i].count; 12191cb4c501SLuigi Rizzo t_tic = timeval2spec(&tic); 12201cb4c501SLuigi Rizzo t_toc = timeval2spec(&toc); 12211cb4c501SLuigi Rizzo if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 12221cb4c501SLuigi Rizzo tic = timespec2val(&targs[i].tic); 12231cb4c501SLuigi Rizzo if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 12241cb4c501SLuigi Rizzo toc = timespec2val(&targs[i].toc); 1225f8e4e36aSLuigi Rizzo } 1226f8e4e36aSLuigi Rizzo 1227f8e4e36aSLuigi Rizzo /* print output. */ 1228f8e4e36aSLuigi Rizzo timersub(&toc, &tic, &toc); 1229f8e4e36aSLuigi Rizzo delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 1230f8e4e36aSLuigi Rizzo if (g->td_body == sender_body) 1231f8e4e36aSLuigi Rizzo tx_output(count, g->pkt_size, delta_t); 1232f8e4e36aSLuigi Rizzo else 1233f8e4e36aSLuigi Rizzo rx_output(count, delta_t); 1234f8e4e36aSLuigi Rizzo 1235f8e4e36aSLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 1236f8e4e36aSLuigi Rizzo munmap(g->mmap_addr, g->mmap_size); 1237f8e4e36aSLuigi Rizzo close(g->main_fd); 1238f8e4e36aSLuigi Rizzo } 1239f8e4e36aSLuigi Rizzo } 1240f8e4e36aSLuigi Rizzo 1241f8e4e36aSLuigi Rizzo 1242f8e4e36aSLuigi Rizzo struct sf { 1243f8e4e36aSLuigi Rizzo char *key; 1244f8e4e36aSLuigi Rizzo void *f; 1245f8e4e36aSLuigi Rizzo }; 1246f8e4e36aSLuigi Rizzo 1247f8e4e36aSLuigi Rizzo static struct sf func[] = { 1248f8e4e36aSLuigi Rizzo { "tx", sender_body }, 1249f8e4e36aSLuigi Rizzo { "rx", receiver_body }, 1250f8e4e36aSLuigi Rizzo { "ping", pinger_body }, 1251f8e4e36aSLuigi Rizzo { "pong", ponger_body }, 1252f8e4e36aSLuigi Rizzo { NULL, NULL } 1253f8e4e36aSLuigi Rizzo }; 1254f8e4e36aSLuigi Rizzo 1255f8e4e36aSLuigi Rizzo static int 1256f8e4e36aSLuigi Rizzo tap_alloc(char *dev) 1257f8e4e36aSLuigi Rizzo { 1258f8e4e36aSLuigi Rizzo struct ifreq ifr; 1259f8e4e36aSLuigi Rizzo int fd, err; 1260f8e4e36aSLuigi Rizzo char *clonedev = TAP_CLONEDEV; 1261f8e4e36aSLuigi Rizzo 1262f8e4e36aSLuigi Rizzo (void)err; 1263f8e4e36aSLuigi Rizzo (void)dev; 1264f8e4e36aSLuigi Rizzo /* Arguments taken by the function: 1265f8e4e36aSLuigi Rizzo * 1266f8e4e36aSLuigi Rizzo * char *dev: the name of an interface (or '\0'). MUST have enough 1267f8e4e36aSLuigi Rizzo * space to hold the interface name if '\0' is passed 1268f8e4e36aSLuigi Rizzo * int flags: interface flags (eg, IFF_TUN etc.) 1269f8e4e36aSLuigi Rizzo */ 1270f8e4e36aSLuigi Rizzo 1271f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 1272f8e4e36aSLuigi Rizzo if (dev[3]) { /* tapSomething */ 1273f8e4e36aSLuigi Rizzo static char buf[128]; 1274f8e4e36aSLuigi Rizzo snprintf(buf, sizeof(buf), "/dev/%s", dev); 1275f8e4e36aSLuigi Rizzo clonedev = buf; 1276f8e4e36aSLuigi Rizzo } 1277f8e4e36aSLuigi Rizzo #endif 1278f8e4e36aSLuigi Rizzo /* open the device */ 1279f8e4e36aSLuigi Rizzo if( (fd = open(clonedev, O_RDWR)) < 0 ) { 1280f8e4e36aSLuigi Rizzo return fd; 1281f8e4e36aSLuigi Rizzo } 1282f8e4e36aSLuigi Rizzo D("%s open successful", clonedev); 1283f8e4e36aSLuigi Rizzo 1284f8e4e36aSLuigi Rizzo /* preparation of the struct ifr, of type "struct ifreq" */ 1285f8e4e36aSLuigi Rizzo memset(&ifr, 0, sizeof(ifr)); 1286f8e4e36aSLuigi Rizzo 1287f8e4e36aSLuigi Rizzo #ifdef linux 1288f8e4e36aSLuigi Rizzo ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1289f8e4e36aSLuigi Rizzo 1290f8e4e36aSLuigi Rizzo if (*dev) { 1291f8e4e36aSLuigi Rizzo /* if a device name was specified, put it in the structure; otherwise, 1292f8e4e36aSLuigi Rizzo * the kernel will try to allocate the "next" device of the 1293f8e4e36aSLuigi Rizzo * specified type */ 1294f8e4e36aSLuigi Rizzo strncpy(ifr.ifr_name, dev, IFNAMSIZ); 1295f8e4e36aSLuigi Rizzo } 1296f8e4e36aSLuigi Rizzo 1297f8e4e36aSLuigi Rizzo /* try to create the device */ 1298f8e4e36aSLuigi Rizzo if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 1299f8e4e36aSLuigi Rizzo D("failed to to a TUNSETIFF"); 1300f8e4e36aSLuigi Rizzo close(fd); 1301f8e4e36aSLuigi Rizzo return err; 1302f8e4e36aSLuigi Rizzo } 1303f8e4e36aSLuigi Rizzo 1304f8e4e36aSLuigi Rizzo /* if the operation was successful, write back the name of the 1305f8e4e36aSLuigi Rizzo * interface to the variable "dev", so the caller can know 1306f8e4e36aSLuigi Rizzo * it. Note that the caller MUST reserve space in *dev (see calling 1307f8e4e36aSLuigi Rizzo * code below) */ 1308f8e4e36aSLuigi Rizzo strcpy(dev, ifr.ifr_name); 1309f8e4e36aSLuigi Rizzo D("new name is %s", dev); 1310f8e4e36aSLuigi Rizzo #endif /* linux */ 1311f8e4e36aSLuigi Rizzo 1312f8e4e36aSLuigi Rizzo /* this is the special file descriptor that the caller will use to talk 1313f8e4e36aSLuigi Rizzo * with the virtual interface */ 1314f8e4e36aSLuigi Rizzo return fd; 1315f8e4e36aSLuigi Rizzo } 131668b8534bSLuigi Rizzo 131768b8534bSLuigi Rizzo int 131868b8534bSLuigi Rizzo main(int arc, char **argv) 131968b8534bSLuigi Rizzo { 1320f8e4e36aSLuigi Rizzo int i; 132168b8534bSLuigi Rizzo 132268b8534bSLuigi Rizzo struct glob_arg g; 132368b8534bSLuigi Rizzo 132468b8534bSLuigi Rizzo struct nmreq nmr; 132568b8534bSLuigi Rizzo int ch; 132668b8534bSLuigi Rizzo int wait_link = 2; 132768b8534bSLuigi Rizzo int devqueues = 1; /* how many device queues */ 132868b8534bSLuigi Rizzo 132968b8534bSLuigi Rizzo bzero(&g, sizeof(g)); 133068b8534bSLuigi Rizzo 1331f8e4e36aSLuigi Rizzo g.main_fd = -1; 1332f8e4e36aSLuigi Rizzo g.td_body = receiver_body; 1333f8e4e36aSLuigi Rizzo g.report_interval = 1000; /* report interval */ 1334f8e4e36aSLuigi Rizzo g.affinity = -1; 1335f8e4e36aSLuigi Rizzo /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 1336f8e4e36aSLuigi Rizzo g.src_ip.name = "10.0.0.1"; 1337f8e4e36aSLuigi Rizzo g.dst_ip.name = "10.1.0.1"; 1338f8e4e36aSLuigi Rizzo g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 1339f8e4e36aSLuigi Rizzo g.src_mac.name = NULL; 134068b8534bSLuigi Rizzo g.pkt_size = 60; 134168b8534bSLuigi Rizzo g.burst = 512; // default 134268b8534bSLuigi Rizzo g.nthreads = 1; 134368b8534bSLuigi Rizzo g.cpus = 1; 1344b303f675SLuigi Rizzo g.forever = 1; 13451cb4c501SLuigi Rizzo g.tx_rate = 0; 134668b8534bSLuigi Rizzo 134768b8534bSLuigi Rizzo while ( (ch = getopt(arc, argv, 1348b303f675SLuigi Rizzo "a:f:n:i:It:r:l:d:s:D:S:b:c:o:p:PT:w:WvR:X")) != -1) { 1349f8e4e36aSLuigi Rizzo struct sf *fn; 1350f8e4e36aSLuigi Rizzo 135168b8534bSLuigi Rizzo switch(ch) { 135268b8534bSLuigi Rizzo default: 135368b8534bSLuigi Rizzo D("bad option %c %s", ch, optarg); 135468b8534bSLuigi Rizzo usage(); 135568b8534bSLuigi Rizzo break; 1356f8e4e36aSLuigi Rizzo 1357f8e4e36aSLuigi Rizzo case 'n': 1358f8e4e36aSLuigi Rizzo g.npackets = atoi(optarg); 1359f8e4e36aSLuigi Rizzo break; 1360f8e4e36aSLuigi Rizzo 1361f8e4e36aSLuigi Rizzo case 'f': 1362f8e4e36aSLuigi Rizzo for (fn = func; fn->key; fn++) { 1363f8e4e36aSLuigi Rizzo if (!strcmp(fn->key, optarg)) 1364f8e4e36aSLuigi Rizzo break; 1365f8e4e36aSLuigi Rizzo } 1366f8e4e36aSLuigi Rizzo if (fn->key) 1367f8e4e36aSLuigi Rizzo g.td_body = fn->f; 1368f8e4e36aSLuigi Rizzo else 1369f8e4e36aSLuigi Rizzo D("unrecognised function %s", optarg); 1370f8e4e36aSLuigi Rizzo break; 1371f8e4e36aSLuigi Rizzo 1372f8e4e36aSLuigi Rizzo case 'o': /* data generation options */ 137399fb123fSLuigi Rizzo g.options = atoi(optarg); 137499fb123fSLuigi Rizzo break; 1375f8e4e36aSLuigi Rizzo 1376f8e4e36aSLuigi Rizzo case 'a': /* force affinity */ 1377f8e4e36aSLuigi Rizzo g.affinity = atoi(optarg); 1378f8e4e36aSLuigi Rizzo break; 1379f8e4e36aSLuigi Rizzo 138068b8534bSLuigi Rizzo case 'i': /* interface */ 1381f8e4e36aSLuigi Rizzo g.ifname = optarg; 1382f8e4e36aSLuigi Rizzo if (!strncmp(optarg, "tap", 3)) 1383f8e4e36aSLuigi Rizzo g.dev_type = DEV_TAP; 1384f8e4e36aSLuigi Rizzo else 1385f8e4e36aSLuigi Rizzo g.dev_type = DEV_NETMAP; 138668b8534bSLuigi Rizzo break; 1387f8e4e36aSLuigi Rizzo 1388b303f675SLuigi Rizzo case 'I': 1389b303f675SLuigi Rizzo g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 1390b303f675SLuigi Rizzo break; 1391b303f675SLuigi Rizzo 1392f8e4e36aSLuigi Rizzo case 't': /* send, deprecated */ 1393f8e4e36aSLuigi Rizzo D("-t deprecated, please use -f tx -n %s", optarg); 1394f8e4e36aSLuigi Rizzo g.td_body = sender_body; 139568b8534bSLuigi Rizzo g.npackets = atoi(optarg); 139668b8534bSLuigi Rizzo break; 1397f8e4e36aSLuigi Rizzo 139868b8534bSLuigi Rizzo case 'r': /* receive */ 1399f8e4e36aSLuigi Rizzo D("-r deprecated, please use -f rx -n %s", optarg); 1400f8e4e36aSLuigi Rizzo g.td_body = receiver_body; 140168b8534bSLuigi Rizzo g.npackets = atoi(optarg); 140268b8534bSLuigi Rizzo break; 1403f8e4e36aSLuigi Rizzo 140468b8534bSLuigi Rizzo case 'l': /* pkt_size */ 140568b8534bSLuigi Rizzo g.pkt_size = atoi(optarg); 140668b8534bSLuigi Rizzo break; 1407f8e4e36aSLuigi Rizzo 140868b8534bSLuigi Rizzo case 'd': 1409f8e4e36aSLuigi Rizzo g.dst_ip.name = optarg; 141068b8534bSLuigi Rizzo break; 1411f8e4e36aSLuigi Rizzo 141268b8534bSLuigi Rizzo case 's': 1413f8e4e36aSLuigi Rizzo g.src_ip.name = optarg; 141468b8534bSLuigi Rizzo break; 1415f8e4e36aSLuigi Rizzo 141668b8534bSLuigi Rizzo case 'T': /* report interval */ 1417f8e4e36aSLuigi Rizzo g.report_interval = atoi(optarg); 141868b8534bSLuigi Rizzo break; 1419f8e4e36aSLuigi Rizzo 142068b8534bSLuigi Rizzo case 'w': 142168b8534bSLuigi Rizzo wait_link = atoi(optarg); 142268b8534bSLuigi Rizzo break; 1423f8e4e36aSLuigi Rizzo 1424b303f675SLuigi Rizzo case 'W': /* XXX changed default */ 1425b303f675SLuigi Rizzo g.forever = 0; /* do not exit rx even with no traffic */ 1426f8e4e36aSLuigi Rizzo break; 1427f8e4e36aSLuigi Rizzo 142868b8534bSLuigi Rizzo case 'b': /* burst */ 142968b8534bSLuigi Rizzo g.burst = atoi(optarg); 143068b8534bSLuigi Rizzo break; 143168b8534bSLuigi Rizzo case 'c': 143268b8534bSLuigi Rizzo g.cpus = atoi(optarg); 143368b8534bSLuigi Rizzo break; 143468b8534bSLuigi Rizzo case 'p': 143568b8534bSLuigi Rizzo g.nthreads = atoi(optarg); 143668b8534bSLuigi Rizzo break; 143768b8534bSLuigi Rizzo 143868b8534bSLuigi Rizzo case 'P': 1439f8e4e36aSLuigi Rizzo g.dev_type = DEV_PCAP; 144068b8534bSLuigi Rizzo break; 144168b8534bSLuigi Rizzo 144268b8534bSLuigi Rizzo case 'D': /* destination mac */ 1443f8e4e36aSLuigi Rizzo g.dst_mac.name = optarg; 144468b8534bSLuigi Rizzo break; 1445f8e4e36aSLuigi Rizzo 144668b8534bSLuigi Rizzo case 'S': /* source mac */ 1447f8e4e36aSLuigi Rizzo g.src_mac.name = optarg; 144868b8534bSLuigi Rizzo break; 144968b8534bSLuigi Rizzo case 'v': 145068b8534bSLuigi Rizzo verbose++; 14511cb4c501SLuigi Rizzo break; 14521cb4c501SLuigi Rizzo case 'R': 14531cb4c501SLuigi Rizzo g.tx_rate = atoi(optarg); 14541cb4c501SLuigi Rizzo break; 1455b303f675SLuigi Rizzo case 'X': 1456b303f675SLuigi Rizzo g.options |= OPT_DUMP; 145768b8534bSLuigi Rizzo } 145868b8534bSLuigi Rizzo } 145968b8534bSLuigi Rizzo 1460f8e4e36aSLuigi Rizzo if (g.ifname == NULL) { 146168b8534bSLuigi Rizzo D("missing ifname"); 146268b8534bSLuigi Rizzo usage(); 146368b8534bSLuigi Rizzo } 1464f8e4e36aSLuigi Rizzo 1465f8e4e36aSLuigi Rizzo i = system_ncpus(); 1466f8e4e36aSLuigi Rizzo if (g.cpus < 0 || g.cpus > i) { 1467f8e4e36aSLuigi Rizzo D("%d cpus is too high, have only %d cpus", g.cpus, i); 146868b8534bSLuigi Rizzo usage(); 146968b8534bSLuigi Rizzo } 147068b8534bSLuigi Rizzo if (g.cpus == 0) 1471f8e4e36aSLuigi Rizzo g.cpus = i; 1472f8e4e36aSLuigi Rizzo 147368b8534bSLuigi Rizzo if (g.pkt_size < 16 || g.pkt_size > 1536) { 147468b8534bSLuigi Rizzo D("bad pktsize %d\n", g.pkt_size); 147568b8534bSLuigi Rizzo usage(); 147668b8534bSLuigi Rizzo } 147768b8534bSLuigi Rizzo 1478f8e4e36aSLuigi Rizzo if (g.src_mac.name == NULL) { 1479f8e4e36aSLuigi Rizzo static char mybuf[20] = "00:00:00:00:00:00"; 148099fb123fSLuigi Rizzo /* retrieve source mac address. */ 1481f8e4e36aSLuigi Rizzo if (source_hwaddr(g.ifname, mybuf) == -1) { 148299fb123fSLuigi Rizzo D("Unable to retrieve source mac"); 148399fb123fSLuigi Rizzo // continue, fail later 148499fb123fSLuigi Rizzo } 1485f8e4e36aSLuigi Rizzo g.src_mac.name = mybuf; 148699fb123fSLuigi Rizzo } 1487f8e4e36aSLuigi Rizzo /* extract address ranges */ 1488f8e4e36aSLuigi Rizzo extract_ip_range(&g.src_ip); 1489f8e4e36aSLuigi Rizzo extract_ip_range(&g.dst_ip); 1490f8e4e36aSLuigi Rizzo extract_mac_range(&g.src_mac); 1491f8e4e36aSLuigi Rizzo extract_mac_range(&g.dst_mac); 149299fb123fSLuigi Rizzo 1493f8e4e36aSLuigi Rizzo if (g.dev_type == DEV_TAP) { 1494f8e4e36aSLuigi Rizzo D("want to use tap %s", g.ifname); 1495f8e4e36aSLuigi Rizzo g.main_fd = tap_alloc(g.ifname); 1496f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 1497f8e4e36aSLuigi Rizzo D("cannot open tap %s", g.ifname); 149899fb123fSLuigi Rizzo usage(); 149999fb123fSLuigi Rizzo } 1500f8e4e36aSLuigi Rizzo } else if (g.dev_type > DEV_NETMAP) { 1501f8e4e36aSLuigi Rizzo char pcap_errbuf[PCAP_ERRBUF_SIZE]; 1502f8e4e36aSLuigi Rizzo 1503f8e4e36aSLuigi Rizzo D("using pcap on %s", g.ifname); 1504f8e4e36aSLuigi Rizzo pcap_errbuf[0] = '\0'; // init the buffer 1505f8e4e36aSLuigi Rizzo g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf); 1506f8e4e36aSLuigi Rizzo if (g.p == NULL) { 1507f8e4e36aSLuigi Rizzo D("cannot open pcap on %s", g.ifname); 1508f8e4e36aSLuigi Rizzo usage(); 1509f8e4e36aSLuigi Rizzo } 151099fb123fSLuigi Rizzo } else { 151168b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 151264ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 151368b8534bSLuigi Rizzo /* 151468b8534bSLuigi Rizzo * Open the netmap device to fetch the number of queues of our 151568b8534bSLuigi Rizzo * interface. 151668b8534bSLuigi Rizzo * 151768b8534bSLuigi Rizzo * The first NIOCREGIF also detaches the card from the 151868b8534bSLuigi Rizzo * protocol stack and may cause a reset of the card, 151968b8534bSLuigi Rizzo * which in turn may take some time for the PHY to 152068b8534bSLuigi Rizzo * reconfigure. 152168b8534bSLuigi Rizzo */ 1522f8e4e36aSLuigi Rizzo g.main_fd = open("/dev/netmap", O_RDWR); 1523f8e4e36aSLuigi Rizzo if (g.main_fd == -1) { 152468b8534bSLuigi Rizzo D("Unable to open /dev/netmap"); 1525f8e4e36aSLuigi Rizzo // fail later 152668b8534bSLuigi Rizzo } else { 1527f8e4e36aSLuigi Rizzo if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) { 152868b8534bSLuigi Rizzo D("Unable to get if info without name"); 152968b8534bSLuigi Rizzo } else { 153068b8534bSLuigi Rizzo D("map size is %d Kb", nmr.nr_memsize >> 10); 153168b8534bSLuigi Rizzo } 153268b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 153364ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 1534f8e4e36aSLuigi Rizzo strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name)); 1535f8e4e36aSLuigi Rizzo if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) { 1536f8e4e36aSLuigi Rizzo D("Unable to get if info for %s", g.ifname); 153768b8534bSLuigi Rizzo } 153864ae02c3SLuigi Rizzo devqueues = nmr.nr_rx_rings; 153968b8534bSLuigi Rizzo } 154068b8534bSLuigi Rizzo 154168b8534bSLuigi Rizzo /* validate provided nthreads. */ 154268b8534bSLuigi Rizzo if (g.nthreads < 1 || g.nthreads > devqueues) { 154368b8534bSLuigi Rizzo D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 154468b8534bSLuigi Rizzo // continue, fail later 154568b8534bSLuigi Rizzo } 154668b8534bSLuigi Rizzo 154768b8534bSLuigi Rizzo /* 154868b8534bSLuigi Rizzo * Map the netmap shared memory: instead of issuing mmap() 154968b8534bSLuigi Rizzo * inside the body of the threads, we prefer to keep this 155068b8534bSLuigi Rizzo * operation here to simplify the thread logic. 155168b8534bSLuigi Rizzo */ 1552f8e4e36aSLuigi Rizzo D("mapping %d Kbytes", nmr.nr_memsize>>10); 1553f8e4e36aSLuigi Rizzo g.mmap_size = nmr.nr_memsize; 1554f8e4e36aSLuigi Rizzo g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 155568b8534bSLuigi Rizzo PROT_WRITE | PROT_READ, 1556f8e4e36aSLuigi Rizzo MAP_SHARED, g.main_fd, 0); 1557f8e4e36aSLuigi Rizzo if (g.mmap_addr == MAP_FAILED) { 155868b8534bSLuigi Rizzo D("Unable to mmap %d KB", nmr.nr_memsize >> 10); 155968b8534bSLuigi Rizzo // continue, fail later 156068b8534bSLuigi Rizzo } 156168b8534bSLuigi Rizzo 156268b8534bSLuigi Rizzo /* 156368b8534bSLuigi Rizzo * Register the interface on the netmap device: from now on, 156468b8534bSLuigi Rizzo * we can operate on the network interface without any 156568b8534bSLuigi Rizzo * interference from the legacy network stack. 156668b8534bSLuigi Rizzo * 156768b8534bSLuigi Rizzo * We decide to put the first interface registration here to 156868b8534bSLuigi Rizzo * give time to cards that take a long time to reset the PHY. 156968b8534bSLuigi Rizzo */ 157064ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 1571f8e4e36aSLuigi Rizzo if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) { 1572f8e4e36aSLuigi Rizzo D("Unable to register interface %s", g.ifname); 157368b8534bSLuigi Rizzo //continue, fail later 157468b8534bSLuigi Rizzo } 157568b8534bSLuigi Rizzo 157668b8534bSLuigi Rizzo 157768b8534bSLuigi Rizzo /* Print some debug information. */ 157868b8534bSLuigi Rizzo fprintf(stdout, 157968b8534bSLuigi Rizzo "%s %s: %d queues, %d threads and %d cpus.\n", 1580f8e4e36aSLuigi Rizzo (g.td_body == sender_body) ? "Sending on" : "Receiving from", 1581f8e4e36aSLuigi Rizzo g.ifname, 158268b8534bSLuigi Rizzo devqueues, 158368b8534bSLuigi Rizzo g.nthreads, 158468b8534bSLuigi Rizzo g.cpus); 1585f8e4e36aSLuigi Rizzo if (g.td_body == sender_body) { 158668b8534bSLuigi Rizzo fprintf(stdout, "%s -> %s (%s -> %s)\n", 1587f8e4e36aSLuigi Rizzo g.src_ip.name, g.dst_ip.name, 1588f8e4e36aSLuigi Rizzo g.src_mac.name, g.dst_mac.name); 158968b8534bSLuigi Rizzo } 159068b8534bSLuigi Rizzo 159168b8534bSLuigi Rizzo /* Exit if something went wrong. */ 1592f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 159368b8534bSLuigi Rizzo D("aborting"); 159468b8534bSLuigi Rizzo usage(); 159568b8534bSLuigi Rizzo } 159699fb123fSLuigi Rizzo } 159768b8534bSLuigi Rizzo 159899fb123fSLuigi Rizzo if (g.options) { 1599b303f675SLuigi Rizzo D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", 160099fb123fSLuigi Rizzo g.options & OPT_PREFETCH ? " prefetch" : "", 160199fb123fSLuigi Rizzo g.options & OPT_ACCESS ? " access" : "", 160299fb123fSLuigi Rizzo g.options & OPT_MEMCPY ? " memcpy" : "", 1603b303f675SLuigi Rizzo g.options & OPT_INDIRECT ? " indirect" : "", 160499fb123fSLuigi Rizzo g.options & OPT_COPY ? " copy" : ""); 160599fb123fSLuigi Rizzo } 16061cb4c501SLuigi Rizzo 16071cb4c501SLuigi Rizzo if (g.tx_rate == 0) { 16081cb4c501SLuigi Rizzo g.tx_period.tv_sec = 0; 16091cb4c501SLuigi Rizzo g.tx_period.tv_nsec = 0; 16101cb4c501SLuigi Rizzo } else if (g.tx_rate == 1) { 16111cb4c501SLuigi Rizzo g.tx_period.tv_sec = 1; 16121cb4c501SLuigi Rizzo g.tx_period.tv_nsec = 0; 16131cb4c501SLuigi Rizzo } else { 16141cb4c501SLuigi Rizzo g.tx_period.tv_sec = 0; 16151cb4c501SLuigi Rizzo g.tx_period.tv_nsec = (1e9 / g.tx_rate) * g.burst; 16161cb4c501SLuigi Rizzo if (g.tx_period.tv_nsec > 1000000000) { 16171cb4c501SLuigi Rizzo g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 16181cb4c501SLuigi Rizzo g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 16191cb4c501SLuigi Rizzo } 16201cb4c501SLuigi Rizzo } 16211cb4c501SLuigi Rizzo D("Sending %d packets every %d.%09d ns", 16221cb4c501SLuigi Rizzo g.burst, (int)g.tx_period.tv_sec, (int)g.tx_period.tv_nsec); 162368b8534bSLuigi Rizzo /* Wait for PHY reset. */ 162468b8534bSLuigi Rizzo D("Wait %d secs for phy reset", wait_link); 162568b8534bSLuigi Rizzo sleep(wait_link); 162668b8534bSLuigi Rizzo D("Ready..."); 162768b8534bSLuigi Rizzo 162868b8534bSLuigi Rizzo /* Install ^C handler. */ 162968b8534bSLuigi Rizzo global_nthreads = g.nthreads; 163068b8534bSLuigi Rizzo signal(SIGINT, sigint_h); 163168b8534bSLuigi Rizzo 1632f8e4e36aSLuigi Rizzo #if 0 // XXX this is not needed, i believe 1633f8e4e36aSLuigi Rizzo if (g.dev_type > DEV_NETMAP) { 1634f8e4e36aSLuigi Rizzo g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL); 163599fb123fSLuigi Rizzo if (g.p == NULL) { 1636f8e4e36aSLuigi Rizzo D("cannot open pcap on %s", g.ifname); 163799fb123fSLuigi Rizzo usage(); 163899fb123fSLuigi Rizzo } else 1639f8e4e36aSLuigi Rizzo D("using pcap %p on %s", g.p, g.ifname); 1640f8e4e36aSLuigi Rizzo } 1641f8e4e36aSLuigi Rizzo #endif // XXX 1642f8e4e36aSLuigi Rizzo start_threads(&g); 1643f8e4e36aSLuigi Rizzo main_thread(&g); 1644f8e4e36aSLuigi Rizzo return 0; 164568b8534bSLuigi Rizzo } 164668b8534bSLuigi Rizzo 164768b8534bSLuigi Rizzo /* end of file */ 1648