168b8534bSLuigi Rizzo /* 217885a7bSLuigi Rizzo * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. 337e3a6d3SLuigi Rizzo * Copyright (C) 2013-2015 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 40f0ea3689SLuigi Rizzo #define _GNU_SOURCE /* for CPU_SET() */ 41f0ea3689SLuigi Rizzo #include <stdio.h> 42f0ea3689SLuigi Rizzo #define NETMAP_WITH_LIBS 43f0ea3689SLuigi Rizzo #include <net/netmap_user.h> 44f0ea3689SLuigi Rizzo 45f8e4e36aSLuigi Rizzo 46b303f675SLuigi Rizzo #include <ctype.h> // isprint() 47f0ea3689SLuigi Rizzo #include <unistd.h> // sysconf() 48f0ea3689SLuigi Rizzo #include <sys/poll.h> 49f0ea3689SLuigi Rizzo #include <arpa/inet.h> /* ntohs */ 5037e3a6d3SLuigi Rizzo #ifndef _WIN32 51f0ea3689SLuigi Rizzo #include <sys/sysctl.h> /* sysctl */ 5237e3a6d3SLuigi Rizzo #endif 53f0ea3689SLuigi Rizzo #include <ifaddrs.h> /* getifaddrs */ 54f0ea3689SLuigi Rizzo #include <net/ethernet.h> 55f0ea3689SLuigi Rizzo #include <netinet/in.h> 56f0ea3689SLuigi Rizzo #include <netinet/ip.h> 57f0ea3689SLuigi Rizzo #include <netinet/udp.h> 5837e3a6d3SLuigi Rizzo #include <assert.h> 5937e3a6d3SLuigi Rizzo #include <math.h> 60f0ea3689SLuigi Rizzo 61f0ea3689SLuigi Rizzo #include <pthread.h> 62b303f675SLuigi Rizzo 63f2637526SLuigi Rizzo #ifndef NO_PCAP 64f2637526SLuigi Rizzo #include <pcap/pcap.h> 65f2637526SLuigi Rizzo #endif 66f0ea3689SLuigi Rizzo 6737e3a6d3SLuigi Rizzo #include "ctrs.h" 6837e3a6d3SLuigi Rizzo 6937e3a6d3SLuigi Rizzo #ifdef _WIN32 7037e3a6d3SLuigi Rizzo #define cpuset_t DWORD_PTR //uint64_t 7137e3a6d3SLuigi Rizzo static inline void CPU_ZERO(cpuset_t *p) 7237e3a6d3SLuigi Rizzo { 7337e3a6d3SLuigi Rizzo *p = 0; 7437e3a6d3SLuigi Rizzo } 7537e3a6d3SLuigi Rizzo 7637e3a6d3SLuigi Rizzo static inline void CPU_SET(uint32_t i, cpuset_t *p) 7737e3a6d3SLuigi Rizzo { 7837e3a6d3SLuigi Rizzo *p |= 1<< (i & 0x3f); 7937e3a6d3SLuigi Rizzo } 8037e3a6d3SLuigi Rizzo 8137e3a6d3SLuigi Rizzo #define pthread_setaffinity_np(a, b, c) !SetThreadAffinityMask(a, *c) //((void)a, 0) 8237e3a6d3SLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 8337e3a6d3SLuigi Rizzo #define AF_LINK 18 //defined in winsocks.h 8437e3a6d3SLuigi Rizzo #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 8537e3a6d3SLuigi Rizzo #include <net/if_dl.h> 8637e3a6d3SLuigi Rizzo 8737e3a6d3SLuigi Rizzo /* 8837e3a6d3SLuigi Rizzo * Convert an ASCII representation of an ethernet address to 8937e3a6d3SLuigi Rizzo * binary form. 9037e3a6d3SLuigi Rizzo */ 9137e3a6d3SLuigi Rizzo struct ether_addr * 9237e3a6d3SLuigi Rizzo ether_aton(const char *a) 9337e3a6d3SLuigi Rizzo { 9437e3a6d3SLuigi Rizzo int i; 9537e3a6d3SLuigi Rizzo static struct ether_addr o; 9637e3a6d3SLuigi Rizzo unsigned int o0, o1, o2, o3, o4, o5; 9737e3a6d3SLuigi Rizzo 9837e3a6d3SLuigi Rizzo i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); 9937e3a6d3SLuigi Rizzo 10037e3a6d3SLuigi Rizzo if (i != 6) 10137e3a6d3SLuigi Rizzo return (NULL); 10237e3a6d3SLuigi Rizzo 10337e3a6d3SLuigi Rizzo o.octet[0]=o0; 10437e3a6d3SLuigi Rizzo o.octet[1]=o1; 10537e3a6d3SLuigi Rizzo o.octet[2]=o2; 10637e3a6d3SLuigi Rizzo o.octet[3]=o3; 10737e3a6d3SLuigi Rizzo o.octet[4]=o4; 10837e3a6d3SLuigi Rizzo o.octet[5]=o5; 10937e3a6d3SLuigi Rizzo 11037e3a6d3SLuigi Rizzo return ((struct ether_addr *)&o); 11137e3a6d3SLuigi Rizzo } 11237e3a6d3SLuigi Rizzo 11337e3a6d3SLuigi Rizzo /* 11437e3a6d3SLuigi Rizzo * Convert a binary representation of an ethernet address to 11537e3a6d3SLuigi Rizzo * an ASCII string. 11637e3a6d3SLuigi Rizzo */ 11737e3a6d3SLuigi Rizzo char * 11837e3a6d3SLuigi Rizzo ether_ntoa(const struct ether_addr *n) 11937e3a6d3SLuigi Rizzo { 12037e3a6d3SLuigi Rizzo int i; 12137e3a6d3SLuigi Rizzo static char a[18]; 12237e3a6d3SLuigi Rizzo 12337e3a6d3SLuigi Rizzo i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x", 12437e3a6d3SLuigi Rizzo n->octet[0], n->octet[1], n->octet[2], 12537e3a6d3SLuigi Rizzo n->octet[3], n->octet[4], n->octet[5]); 12637e3a6d3SLuigi Rizzo return (i < 17 ? NULL : (char *)&a); 12737e3a6d3SLuigi Rizzo } 12837e3a6d3SLuigi Rizzo #endif /* _WIN32 */ 12937e3a6d3SLuigi Rizzo 130f0ea3689SLuigi Rizzo #ifdef linux 131f0ea3689SLuigi Rizzo 132f0ea3689SLuigi Rizzo #define cpuset_t cpu_set_t 133f0ea3689SLuigi Rizzo 134f0ea3689SLuigi Rizzo #define ifr_flagshigh ifr_flags /* only the low 16 bits here */ 135f0ea3689SLuigi Rizzo #define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */ 136f0ea3689SLuigi Rizzo #include <linux/ethtool.h> 137f0ea3689SLuigi Rizzo #include <linux/sockios.h> 138f0ea3689SLuigi Rizzo 139f0ea3689SLuigi Rizzo #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME 140f0ea3689SLuigi Rizzo #include <netinet/ether.h> /* ether_aton */ 141f0ea3689SLuigi Rizzo #include <linux/if_packet.h> /* sockaddr_ll */ 142f0ea3689SLuigi Rizzo #endif /* linux */ 143f0ea3689SLuigi Rizzo 144f0ea3689SLuigi Rizzo #ifdef __FreeBSD__ 145f0ea3689SLuigi Rizzo #include <sys/endian.h> /* le64toh */ 146f0ea3689SLuigi Rizzo #include <machine/param.h> 147f0ea3689SLuigi Rizzo 148f0ea3689SLuigi Rizzo #include <pthread_np.h> /* pthread w/ affinity */ 149f0ea3689SLuigi Rizzo #include <sys/cpuset.h> /* cpu_set */ 150f0ea3689SLuigi Rizzo #include <net/if_dl.h> /* LLADDR */ 151f0ea3689SLuigi Rizzo #endif /* __FreeBSD__ */ 152f0ea3689SLuigi Rizzo 153f0ea3689SLuigi Rizzo #ifdef __APPLE__ 154f0ea3689SLuigi Rizzo 155f0ea3689SLuigi Rizzo #define cpuset_t uint64_t // XXX 156f0ea3689SLuigi Rizzo static inline void CPU_ZERO(cpuset_t *p) 157f0ea3689SLuigi Rizzo { 158f0ea3689SLuigi Rizzo *p = 0; 159f0ea3689SLuigi Rizzo } 160f0ea3689SLuigi Rizzo 161f0ea3689SLuigi Rizzo static inline void CPU_SET(uint32_t i, cpuset_t *p) 162f0ea3689SLuigi Rizzo { 163f0ea3689SLuigi Rizzo *p |= 1<< (i & 0x3f); 164f0ea3689SLuigi Rizzo } 165f0ea3689SLuigi Rizzo 166f0ea3689SLuigi Rizzo #define pthread_setaffinity_np(a, b, c) ((void)a, 0) 167f0ea3689SLuigi Rizzo 168f0ea3689SLuigi Rizzo #define ifr_flagshigh ifr_flags // XXX 169f0ea3689SLuigi Rizzo #define IFF_PPROMISC IFF_PROMISC 170f0ea3689SLuigi Rizzo #include <net/if_dl.h> /* LLADDR */ 171f0ea3689SLuigi Rizzo #define clock_gettime(a,b) \ 172f0ea3689SLuigi Rizzo do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) 173f0ea3689SLuigi Rizzo #endif /* __APPLE__ */ 174f0ea3689SLuigi Rizzo 175ce3ee1e7SLuigi Rizzo const char *default_payload="netmap pkt-gen DIRECT payload\n" 176ce3ee1e7SLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 177ce3ee1e7SLuigi Rizzo 178ce3ee1e7SLuigi Rizzo const char *indirect_payload="netmap pkt-gen indirect payload\n" 17968b8534bSLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 18068b8534bSLuigi Rizzo 18168b8534bSLuigi Rizzo int verbose = 0; 18268b8534bSLuigi Rizzo 183f0ea3689SLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */ 18468b8534bSLuigi Rizzo 18517885a7bSLuigi Rizzo 18617885a7bSLuigi Rizzo #define VIRT_HDR_1 10 /* length of a base vnet-hdr */ 18717885a7bSLuigi Rizzo #define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ 18817885a7bSLuigi Rizzo #define VIRT_HDR_MAX VIRT_HDR_2 18917885a7bSLuigi Rizzo struct virt_header { 19017885a7bSLuigi Rizzo uint8_t fields[VIRT_HDR_MAX]; 19117885a7bSLuigi Rizzo }; 19217885a7bSLuigi Rizzo 1934bf50f18SLuigi Rizzo #define MAX_BODYSIZE 16384 1944bf50f18SLuigi Rizzo 19568b8534bSLuigi Rizzo struct pkt { 19617885a7bSLuigi Rizzo struct virt_header vh; 19768b8534bSLuigi Rizzo struct ether_header eh; 19868b8534bSLuigi Rizzo struct ip ip; 19968b8534bSLuigi Rizzo struct udphdr udp; 2004bf50f18SLuigi Rizzo uint8_t body[MAX_BODYSIZE]; // XXX hardwired 20168b8534bSLuigi Rizzo } __attribute__((__packed__)); 20268b8534bSLuigi Rizzo 203f8e4e36aSLuigi Rizzo struct ip_range { 204f8e4e36aSLuigi Rizzo char *name; 205ce3ee1e7SLuigi Rizzo uint32_t start, end; /* same as struct in_addr */ 206ce3ee1e7SLuigi Rizzo uint16_t port0, port1; 207f8e4e36aSLuigi Rizzo }; 208f8e4e36aSLuigi Rizzo 209f8e4e36aSLuigi Rizzo struct mac_range { 210f8e4e36aSLuigi Rizzo char *name; 211f8e4e36aSLuigi Rizzo struct ether_addr start, end; 212f8e4e36aSLuigi Rizzo }; 213f8e4e36aSLuigi Rizzo 214f0ea3689SLuigi Rizzo /* ifname can be netmap:foo-xxxx */ 215f0ea3689SLuigi Rizzo #define MAX_IFNAMELEN 64 /* our buffer for ifname */ 2164bf50f18SLuigi Rizzo //#define MAX_PKTSIZE 1536 2174bf50f18SLuigi Rizzo #define MAX_PKTSIZE MAX_BODYSIZE /* XXX: + IP_HDR + ETH_HDR */ 2184bf50f18SLuigi Rizzo 2194bf50f18SLuigi Rizzo /* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */ 2204bf50f18SLuigi Rizzo struct tstamp { 2214bf50f18SLuigi Rizzo uint32_t sec; 2224bf50f18SLuigi Rizzo uint32_t nsec; 2234bf50f18SLuigi Rizzo }; 2244bf50f18SLuigi Rizzo 22568b8534bSLuigi Rizzo /* 22668b8534bSLuigi Rizzo * global arguments for all threads 22768b8534bSLuigi Rizzo */ 228f8e4e36aSLuigi Rizzo 22968b8534bSLuigi Rizzo struct glob_arg { 230f8e4e36aSLuigi Rizzo struct ip_range src_ip; 231f8e4e36aSLuigi Rizzo struct ip_range dst_ip; 232f8e4e36aSLuigi Rizzo struct mac_range dst_mac; 233f8e4e36aSLuigi Rizzo struct mac_range src_mac; 23468b8534bSLuigi Rizzo int pkt_size; 23568b8534bSLuigi Rizzo int burst; 236f8e4e36aSLuigi Rizzo int forever; 23737e3a6d3SLuigi Rizzo uint64_t npackets; /* total packets to send */ 238ce3ee1e7SLuigi Rizzo int frags; /* fragments per packet */ 23968b8534bSLuigi Rizzo int nthreads; 24037e3a6d3SLuigi Rizzo int cpus; /* cpus used for running */ 24137e3a6d3SLuigi Rizzo int system_cpus; /* cpus on the system */ 24237e3a6d3SLuigi Rizzo 24399fb123fSLuigi Rizzo int options; /* testing */ 24499fb123fSLuigi Rizzo #define OPT_PREFETCH 1 24599fb123fSLuigi Rizzo #define OPT_ACCESS 2 24699fb123fSLuigi Rizzo #define OPT_COPY 4 24799fb123fSLuigi Rizzo #define OPT_MEMCPY 8 248f8e4e36aSLuigi Rizzo #define OPT_TS 16 /* add a timestamp */ 249b303f675SLuigi Rizzo #define OPT_INDIRECT 32 /* use indirect buffers, tx only */ 250b303f675SLuigi Rizzo #define OPT_DUMP 64 /* dump rx/tx traffic */ 25137e3a6d3SLuigi Rizzo #define OPT_RUBBISH 256 /* send wathever the buffers contain */ 25256717743SAdrian Chadd #define OPT_RANDOM_SRC 512 25356717743SAdrian Chadd #define OPT_RANDOM_DST 1024 25437e3a6d3SLuigi Rizzo #define OPT_PPS_STATS 2048 255f8e4e36aSLuigi Rizzo int dev_type; 256f2637526SLuigi Rizzo #ifndef NO_PCAP 25768b8534bSLuigi Rizzo pcap_t *p; 258f2637526SLuigi Rizzo #endif 25968b8534bSLuigi Rizzo 2601cb4c501SLuigi Rizzo int tx_rate; 2611cb4c501SLuigi Rizzo struct timespec tx_period; 2621cb4c501SLuigi Rizzo 263f8e4e36aSLuigi Rizzo int affinity; 264f8e4e36aSLuigi Rizzo int main_fd; 265f0ea3689SLuigi Rizzo struct nm_desc *nmd; 266f2637526SLuigi Rizzo int report_interval; /* milliseconds between prints */ 267f8e4e36aSLuigi Rizzo void *(*td_body)(void *); 26837e3a6d3SLuigi Rizzo int td_type; 269f8e4e36aSLuigi Rizzo void *mmap_addr; 270f0ea3689SLuigi Rizzo char ifname[MAX_IFNAMELEN]; 271ce3ee1e7SLuigi Rizzo char *nmr_config; 272ce3ee1e7SLuigi Rizzo int dummy_send; 27317885a7bSLuigi Rizzo int virt_header; /* send also the virt_header */ 274f0ea3689SLuigi Rizzo int extra_bufs; /* goes in nr_arg3 */ 27537e3a6d3SLuigi Rizzo int extra_pipes; /* goes in nr_arg1 */ 276f284c737SGeorge V. Neville-Neil char *packet_file; /* -P option */ 27737e3a6d3SLuigi Rizzo #define STATS_WIN 15 27837e3a6d3SLuigi Rizzo int win_idx; 27937e3a6d3SLuigi Rizzo int64_t win[STATS_WIN]; 28068b8534bSLuigi Rizzo }; 281f8e4e36aSLuigi Rizzo enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; 282f8e4e36aSLuigi Rizzo 28368b8534bSLuigi Rizzo 28468b8534bSLuigi Rizzo /* 28568b8534bSLuigi Rizzo * Arguments for a new thread. The same structure is used by 28668b8534bSLuigi Rizzo * the source and the sink 28768b8534bSLuigi Rizzo */ 28868b8534bSLuigi Rizzo struct targ { 28968b8534bSLuigi Rizzo struct glob_arg *g; 29068b8534bSLuigi Rizzo int used; 29168b8534bSLuigi Rizzo int completed; 2923fe77e68SEd Maste int cancel; 29368b8534bSLuigi Rizzo int fd; 294f0ea3689SLuigi Rizzo struct nm_desc *nmd; 29537e3a6d3SLuigi Rizzo /* these ought to be volatile, but they are 29637e3a6d3SLuigi Rizzo * only sampled and errors should not accumulate 29737e3a6d3SLuigi Rizzo */ 29837e3a6d3SLuigi Rizzo struct my_ctrs ctr; 29937e3a6d3SLuigi Rizzo 3001cb4c501SLuigi Rizzo struct timespec tic, toc; 30168b8534bSLuigi Rizzo int me; 30268b8534bSLuigi Rizzo pthread_t thread; 30368b8534bSLuigi Rizzo int affinity; 30468b8534bSLuigi Rizzo 30568b8534bSLuigi Rizzo struct pkt pkt; 306f284c737SGeorge V. Neville-Neil void *frame; 30768b8534bSLuigi Rizzo }; 30868b8534bSLuigi Rizzo 30968b8534bSLuigi Rizzo 310f8e4e36aSLuigi Rizzo /* 311f8e4e36aSLuigi Rizzo * extract the extremes from a range of ipv4 addresses. 312f8e4e36aSLuigi Rizzo * addr_lo[-addr_hi][:port_lo[-port_hi]] 313f8e4e36aSLuigi Rizzo */ 314f8e4e36aSLuigi Rizzo static void 315f8e4e36aSLuigi Rizzo extract_ip_range(struct ip_range *r) 316f8e4e36aSLuigi Rizzo { 317ce3ee1e7SLuigi Rizzo char *ap, *pp; 318ce3ee1e7SLuigi Rizzo struct in_addr a; 319f8e4e36aSLuigi Rizzo 32017885a7bSLuigi Rizzo if (verbose) 321f8e4e36aSLuigi Rizzo D("extract IP range from %s", r->name); 322ce3ee1e7SLuigi Rizzo r->port0 = r->port1 = 0; 323ce3ee1e7SLuigi Rizzo r->start = r->end = 0; 324ce3ee1e7SLuigi Rizzo 325ce3ee1e7SLuigi Rizzo /* the first - splits start/end of range */ 326ce3ee1e7SLuigi Rizzo ap = index(r->name, '-'); /* do we have ports ? */ 327ce3ee1e7SLuigi Rizzo if (ap) { 328ce3ee1e7SLuigi Rizzo *ap++ = '\0'; 329ce3ee1e7SLuigi Rizzo } 330ce3ee1e7SLuigi Rizzo /* grab the initial values (mandatory) */ 331ce3ee1e7SLuigi Rizzo pp = index(r->name, ':'); 332ce3ee1e7SLuigi Rizzo if (pp) { 333ce3ee1e7SLuigi Rizzo *pp++ = '\0'; 334ce3ee1e7SLuigi Rizzo r->port0 = r->port1 = strtol(pp, NULL, 0); 335ce3ee1e7SLuigi Rizzo }; 336ce3ee1e7SLuigi Rizzo inet_aton(r->name, &a); 337ce3ee1e7SLuigi Rizzo r->start = r->end = ntohl(a.s_addr); 338ce3ee1e7SLuigi Rizzo if (ap) { 339ce3ee1e7SLuigi Rizzo pp = index(ap, ':'); 340ce3ee1e7SLuigi Rizzo if (pp) { 341ce3ee1e7SLuigi Rizzo *pp++ = '\0'; 342ce3ee1e7SLuigi Rizzo if (*pp) 343ce3ee1e7SLuigi Rizzo r->port1 = strtol(pp, NULL, 0); 344ce3ee1e7SLuigi Rizzo } 345ce3ee1e7SLuigi Rizzo if (*ap) { 346ce3ee1e7SLuigi Rizzo inet_aton(ap, &a); 347ce3ee1e7SLuigi Rizzo r->end = ntohl(a.s_addr); 348ce3ee1e7SLuigi Rizzo } 349ce3ee1e7SLuigi Rizzo } 350ce3ee1e7SLuigi Rizzo if (r->port0 > r->port1) { 351ce3ee1e7SLuigi Rizzo uint16_t tmp = r->port0; 352f8e4e36aSLuigi Rizzo r->port0 = r->port1; 353ce3ee1e7SLuigi Rizzo r->port1 = tmp; 354f8e4e36aSLuigi Rizzo } 355ce3ee1e7SLuigi Rizzo if (r->start > r->end) { 356ce3ee1e7SLuigi Rizzo uint32_t tmp = r->start; 357f8e4e36aSLuigi Rizzo r->start = r->end; 358ce3ee1e7SLuigi Rizzo r->end = tmp; 359f8e4e36aSLuigi Rizzo } 360ce3ee1e7SLuigi Rizzo { 361ce3ee1e7SLuigi Rizzo struct in_addr a; 362ce3ee1e7SLuigi Rizzo char buf1[16]; // one ip address 363ce3ee1e7SLuigi Rizzo 364ce3ee1e7SLuigi Rizzo a.s_addr = htonl(r->end); 365ce3ee1e7SLuigi Rizzo strncpy(buf1, inet_ntoa(a), sizeof(buf1)); 366ce3ee1e7SLuigi Rizzo a.s_addr = htonl(r->start); 36717885a7bSLuigi Rizzo if (1) 368ce3ee1e7SLuigi Rizzo D("range is %s:%d to %s:%d", 369ce3ee1e7SLuigi Rizzo inet_ntoa(a), r->port0, buf1, r->port1); 370ce3ee1e7SLuigi Rizzo } 371f8e4e36aSLuigi Rizzo } 372f8e4e36aSLuigi Rizzo 373f8e4e36aSLuigi Rizzo static void 374f8e4e36aSLuigi Rizzo extract_mac_range(struct mac_range *r) 375f8e4e36aSLuigi Rizzo { 37617885a7bSLuigi Rizzo if (verbose) 377f8e4e36aSLuigi Rizzo D("extract MAC range from %s", r->name); 378f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->start, 6); 379f8e4e36aSLuigi Rizzo bcopy(ether_aton(r->name), &r->end, 6); 380f8e4e36aSLuigi Rizzo #if 0 381f8e4e36aSLuigi Rizzo bcopy(targ->src_mac, eh->ether_shost, 6); 382f8e4e36aSLuigi Rizzo p = index(targ->g->src_mac, '-'); 383f8e4e36aSLuigi Rizzo if (p) 384f8e4e36aSLuigi Rizzo targ->src_mac_range = atoi(p+1); 385f8e4e36aSLuigi Rizzo 386f8e4e36aSLuigi Rizzo bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 387f8e4e36aSLuigi Rizzo bcopy(targ->dst_mac, eh->ether_dhost, 6); 388f8e4e36aSLuigi Rizzo p = index(targ->g->dst_mac, '-'); 389f8e4e36aSLuigi Rizzo if (p) 390f8e4e36aSLuigi Rizzo targ->dst_mac_range = atoi(p+1); 391f8e4e36aSLuigi Rizzo #endif 39217885a7bSLuigi Rizzo if (verbose) 393f8e4e36aSLuigi Rizzo D("%s starts at %s", r->name, ether_ntoa(&r->start)); 394f8e4e36aSLuigi Rizzo } 395f8e4e36aSLuigi Rizzo 39668b8534bSLuigi Rizzo static struct targ *targs; 39768b8534bSLuigi Rizzo static int global_nthreads; 39868b8534bSLuigi Rizzo 39968b8534bSLuigi Rizzo /* control-C handler */ 40068b8534bSLuigi Rizzo static void 401f8e4e36aSLuigi Rizzo sigint_h(int sig) 40268b8534bSLuigi Rizzo { 403f8e4e36aSLuigi Rizzo int i; 40468b8534bSLuigi Rizzo 405f8e4e36aSLuigi Rizzo (void)sig; /* UNUSED */ 40637e3a6d3SLuigi Rizzo D("received control-C on thread %p", (void *)pthread_self()); 407f8e4e36aSLuigi Rizzo for (i = 0; i < global_nthreads; i++) { 408f8e4e36aSLuigi Rizzo targs[i].cancel = 1; 409f8e4e36aSLuigi Rizzo } 41068b8534bSLuigi Rizzo } 41168b8534bSLuigi Rizzo 41268b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */ 41368b8534bSLuigi Rizzo static int 41468b8534bSLuigi Rizzo system_ncpus(void) 41568b8534bSLuigi Rizzo { 416f0ea3689SLuigi Rizzo int ncpus; 417f0ea3689SLuigi Rizzo #if defined (__FreeBSD__) 418f0ea3689SLuigi Rizzo int mib[2] = { CTL_HW, HW_NCPU }; 419f0ea3689SLuigi Rizzo size_t len = sizeof(mib); 42068b8534bSLuigi Rizzo sysctl(mib, 2, &ncpus, &len, NULL, 0); 421f0ea3689SLuigi Rizzo #elif defined(linux) 422f0ea3689SLuigi Rizzo ncpus = sysconf(_SC_NPROCESSORS_ONLN); 42337e3a6d3SLuigi Rizzo #elif defined(_WIN32) 42437e3a6d3SLuigi Rizzo { 42537e3a6d3SLuigi Rizzo SYSTEM_INFO sysinfo; 42637e3a6d3SLuigi Rizzo GetSystemInfo(&sysinfo); 42737e3a6d3SLuigi Rizzo ncpus = sysinfo.dwNumberOfProcessors; 42837e3a6d3SLuigi Rizzo } 429f0ea3689SLuigi Rizzo #else /* others */ 430f0ea3689SLuigi Rizzo ncpus = 1; 431f0ea3689SLuigi Rizzo #endif /* others */ 43268b8534bSLuigi Rizzo return (ncpus); 43368b8534bSLuigi Rizzo } 43468b8534bSLuigi Rizzo 435f8e4e36aSLuigi Rizzo #ifdef __linux__ 436f8e4e36aSLuigi Rizzo #define sockaddr_dl sockaddr_ll 437f8e4e36aSLuigi Rizzo #define sdl_family sll_family 438f8e4e36aSLuigi Rizzo #define AF_LINK AF_PACKET 439f8e4e36aSLuigi Rizzo #define LLADDR(s) s->sll_addr; 440f8e4e36aSLuigi Rizzo #include <linux/if_tun.h> 441f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/net/tun" 442f8e4e36aSLuigi Rizzo #endif /* __linux__ */ 443f8e4e36aSLuigi Rizzo 444f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 445f8e4e36aSLuigi Rizzo #include <net/if_tun.h> 446f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 447f8e4e36aSLuigi Rizzo #endif /* __FreeBSD */ 448f8e4e36aSLuigi Rizzo 449f8e4e36aSLuigi Rizzo #ifdef __APPLE__ 450f8e4e36aSLuigi Rizzo // #warning TAP not supported on apple ? 451f8e4e36aSLuigi Rizzo #include <net/if_utun.h> 452f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV "/dev/tap" 453f8e4e36aSLuigi Rizzo #endif /* __APPLE__ */ 454f8e4e36aSLuigi Rizzo 455f8e4e36aSLuigi Rizzo 45668b8534bSLuigi Rizzo /* 457ce3ee1e7SLuigi Rizzo * parse the vale configuration in conf and put it in nmr. 458f0ea3689SLuigi Rizzo * Return the flag set if necessary. 459ce3ee1e7SLuigi Rizzo * The configuration may consist of 0 to 4 numbers separated 460fc6eb28bSHiren Panchasara * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. 461ce3ee1e7SLuigi Rizzo * Missing numbers or zeroes stand for default values. 462ce3ee1e7SLuigi Rizzo * As an additional convenience, if exactly one number 463fc6eb28bSHiren Panchasara * is specified, then this is assigned to both #tx-slots and #rx-slots. 464fc6eb28bSHiren Panchasara * If there is no 4th number, then the 3rd is assigned to both #tx-rings 465ce3ee1e7SLuigi Rizzo * and #rx-rings. 466ce3ee1e7SLuigi Rizzo */ 467f0ea3689SLuigi Rizzo int 468f0ea3689SLuigi Rizzo parse_nmr_config(const char* conf, struct nmreq *nmr) 469ce3ee1e7SLuigi Rizzo { 470ce3ee1e7SLuigi Rizzo char *w, *tok; 471ce3ee1e7SLuigi Rizzo int i, v; 472ce3ee1e7SLuigi Rizzo 473ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings = nmr->nr_rx_rings = 0; 474ce3ee1e7SLuigi Rizzo nmr->nr_tx_slots = nmr->nr_rx_slots = 0; 475ce3ee1e7SLuigi Rizzo if (conf == NULL || ! *conf) 476f0ea3689SLuigi Rizzo return 0; 477ce3ee1e7SLuigi Rizzo w = strdup(conf); 478ce3ee1e7SLuigi Rizzo for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { 479ce3ee1e7SLuigi Rizzo v = atoi(tok); 480ce3ee1e7SLuigi Rizzo switch (i) { 481ce3ee1e7SLuigi Rizzo case 0: 482ce3ee1e7SLuigi Rizzo nmr->nr_tx_slots = nmr->nr_rx_slots = v; 483ce3ee1e7SLuigi Rizzo break; 484ce3ee1e7SLuigi Rizzo case 1: 485ce3ee1e7SLuigi Rizzo nmr->nr_rx_slots = v; 486ce3ee1e7SLuigi Rizzo break; 487ce3ee1e7SLuigi Rizzo case 2: 488ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings = nmr->nr_rx_rings = v; 489ce3ee1e7SLuigi Rizzo break; 490ce3ee1e7SLuigi Rizzo case 3: 491ce3ee1e7SLuigi Rizzo nmr->nr_rx_rings = v; 492ce3ee1e7SLuigi Rizzo break; 493ce3ee1e7SLuigi Rizzo default: 494ce3ee1e7SLuigi Rizzo D("ignored config: %s", tok); 495ce3ee1e7SLuigi Rizzo break; 496ce3ee1e7SLuigi Rizzo } 497ce3ee1e7SLuigi Rizzo } 498ce3ee1e7SLuigi Rizzo D("txr %d txd %d rxr %d rxd %d", 499ce3ee1e7SLuigi Rizzo nmr->nr_tx_rings, nmr->nr_tx_slots, 500ce3ee1e7SLuigi Rizzo nmr->nr_rx_rings, nmr->nr_rx_slots); 501ce3ee1e7SLuigi Rizzo free(w); 502f0ea3689SLuigi Rizzo return (nmr->nr_tx_rings || nmr->nr_tx_slots || 503f0ea3689SLuigi Rizzo nmr->nr_rx_rings || nmr->nr_rx_slots) ? 504f0ea3689SLuigi Rizzo NM_OPEN_RING_CFG : 0; 505ce3ee1e7SLuigi Rizzo } 506ce3ee1e7SLuigi Rizzo 507ce3ee1e7SLuigi Rizzo 508ce3ee1e7SLuigi Rizzo /* 50968b8534bSLuigi Rizzo * locate the src mac address for our interface, put it 51068b8534bSLuigi Rizzo * into the user-supplied buffer. return 0 if ok, -1 on error. 51168b8534bSLuigi Rizzo */ 51268b8534bSLuigi Rizzo static int 51368b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf) 51468b8534bSLuigi Rizzo { 51568b8534bSLuigi Rizzo struct ifaddrs *ifaphead, *ifap; 51668b8534bSLuigi Rizzo int l = sizeof(ifap->ifa_name); 51768b8534bSLuigi Rizzo 51868b8534bSLuigi Rizzo if (getifaddrs(&ifaphead) != 0) { 51968b8534bSLuigi Rizzo D("getifaddrs %s failed", ifname); 52068b8534bSLuigi Rizzo return (-1); 52168b8534bSLuigi Rizzo } 52268b8534bSLuigi Rizzo 52368b8534bSLuigi Rizzo for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 52468b8534bSLuigi Rizzo struct sockaddr_dl *sdl = 52568b8534bSLuigi Rizzo (struct sockaddr_dl *)ifap->ifa_addr; 52668b8534bSLuigi Rizzo uint8_t *mac; 52768b8534bSLuigi Rizzo 52868b8534bSLuigi Rizzo if (!sdl || sdl->sdl_family != AF_LINK) 52968b8534bSLuigi Rizzo continue; 53068b8534bSLuigi Rizzo if (strncmp(ifap->ifa_name, ifname, l) != 0) 53168b8534bSLuigi Rizzo continue; 53268b8534bSLuigi Rizzo mac = (uint8_t *)LLADDR(sdl); 53368b8534bSLuigi Rizzo sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 53468b8534bSLuigi Rizzo mac[0], mac[1], mac[2], 53568b8534bSLuigi Rizzo mac[3], mac[4], mac[5]); 53668b8534bSLuigi Rizzo if (verbose) 53768b8534bSLuigi Rizzo D("source hwaddr %s", buf); 53868b8534bSLuigi Rizzo break; 53968b8534bSLuigi Rizzo } 54068b8534bSLuigi Rizzo freeifaddrs(ifaphead); 54168b8534bSLuigi Rizzo return ifap ? 0 : 1; 54268b8534bSLuigi Rizzo } 54368b8534bSLuigi Rizzo 54468b8534bSLuigi Rizzo 54568b8534bSLuigi Rizzo /* set the thread affinity. */ 54668b8534bSLuigi Rizzo static int 54768b8534bSLuigi Rizzo setaffinity(pthread_t me, int i) 54868b8534bSLuigi Rizzo { 54968b8534bSLuigi Rizzo cpuset_t cpumask; 55068b8534bSLuigi Rizzo 55168b8534bSLuigi Rizzo if (i == -1) 55268b8534bSLuigi Rizzo return 0; 55368b8534bSLuigi Rizzo 55468b8534bSLuigi Rizzo /* Set thread affinity affinity.*/ 55568b8534bSLuigi Rizzo CPU_ZERO(&cpumask); 55668b8534bSLuigi Rizzo CPU_SET(i, &cpumask); 55768b8534bSLuigi Rizzo 55868b8534bSLuigi Rizzo if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 55917885a7bSLuigi Rizzo D("Unable to set affinity: %s", strerror(errno)); 56068b8534bSLuigi Rizzo return 1; 56168b8534bSLuigi Rizzo } 56268b8534bSLuigi Rizzo return 0; 56368b8534bSLuigi Rizzo } 56468b8534bSLuigi Rizzo 56568b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */ 56668b8534bSLuigi Rizzo static uint16_t 567f8e4e36aSLuigi Rizzo checksum(const void *data, uint16_t len, uint32_t sum) 56868b8534bSLuigi Rizzo { 56968b8534bSLuigi Rizzo const uint8_t *addr = data; 570f8e4e36aSLuigi Rizzo uint32_t i; 57168b8534bSLuigi Rizzo 572f8e4e36aSLuigi Rizzo /* Checksum all the pairs of bytes first... */ 573f8e4e36aSLuigi Rizzo for (i = 0; i < (len & ~1U); i += 2) { 574f8e4e36aSLuigi Rizzo sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); 575f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 576f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 577f8e4e36aSLuigi Rizzo } 578f8e4e36aSLuigi Rizzo /* 579f8e4e36aSLuigi Rizzo * If there's a single byte left over, checksum it, too. 580f8e4e36aSLuigi Rizzo * Network byte order is big-endian, so the remaining byte is 581f8e4e36aSLuigi Rizzo * the high byte. 582f8e4e36aSLuigi Rizzo */ 583f8e4e36aSLuigi Rizzo if (i < len) { 584f8e4e36aSLuigi Rizzo sum += addr[i] << 8; 585f8e4e36aSLuigi Rizzo if (sum > 0xFFFF) 586f8e4e36aSLuigi Rizzo sum -= 0xFFFF; 587f8e4e36aSLuigi Rizzo } 588f8e4e36aSLuigi Rizzo return sum; 58968b8534bSLuigi Rizzo } 59068b8534bSLuigi Rizzo 591f8e4e36aSLuigi Rizzo static u_int16_t 592f8e4e36aSLuigi Rizzo wrapsum(u_int32_t sum) 593f8e4e36aSLuigi Rizzo { 594f8e4e36aSLuigi Rizzo sum = ~sum & 0xFFFF; 595f8e4e36aSLuigi Rizzo return (htons(sum)); 59668b8534bSLuigi Rizzo } 59768b8534bSLuigi Rizzo 598b303f675SLuigi Rizzo /* Check the payload of the packet for errors (use it for debug). 599b303f675SLuigi Rizzo * Look for consecutive ascii representations of the size of the packet. 600b303f675SLuigi Rizzo */ 601b303f675SLuigi Rizzo static void 60237e3a6d3SLuigi Rizzo dump_payload(const char *_p, int len, struct netmap_ring *ring, int cur) 603b303f675SLuigi Rizzo { 604b303f675SLuigi Rizzo char buf[128]; 605b303f675SLuigi Rizzo int i, j, i0; 60637e3a6d3SLuigi Rizzo const unsigned char *p = (const unsigned char *)_p; 607b303f675SLuigi Rizzo 608b303f675SLuigi Rizzo /* get the length in ASCII of the length of the packet. */ 609b303f675SLuigi Rizzo 610ce3ee1e7SLuigi Rizzo printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", 611ce3ee1e7SLuigi Rizzo ring, cur, ring->slot[cur].buf_idx, 612ce3ee1e7SLuigi Rizzo ring->slot[cur].flags, len); 613b303f675SLuigi Rizzo /* hexdump routine */ 614b303f675SLuigi Rizzo for (i = 0; i < len; ) { 615b797f66cSConrad Meyer memset(buf, ' ', sizeof(buf)); 616b303f675SLuigi Rizzo sprintf(buf, "%5d: ", i); 617b303f675SLuigi Rizzo i0 = i; 618b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 619b303f675SLuigi Rizzo sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); 620b303f675SLuigi Rizzo i = i0; 621b303f675SLuigi Rizzo for (j=0; j < 16 && i < len; i++, j++) 622b303f675SLuigi Rizzo sprintf(buf+7+j + 48, "%c", 623b303f675SLuigi Rizzo isprint(p[i]) ? p[i] : '.'); 624b303f675SLuigi Rizzo printf("%s\n", buf); 625b303f675SLuigi Rizzo } 626b303f675SLuigi Rizzo } 627b303f675SLuigi Rizzo 62868b8534bSLuigi Rizzo /* 62968b8534bSLuigi Rizzo * Fill a packet with some payload. 630f8e4e36aSLuigi Rizzo * We create a UDP packet so the payload starts at 631f8e4e36aSLuigi Rizzo * 14+20+8 = 42 bytes. 63268b8534bSLuigi Rizzo */ 633f8e4e36aSLuigi Rizzo #ifdef __linux__ 634f8e4e36aSLuigi Rizzo #define uh_sport source 635f8e4e36aSLuigi Rizzo #define uh_dport dest 636f8e4e36aSLuigi Rizzo #define uh_ulen len 637f8e4e36aSLuigi Rizzo #define uh_sum check 638f8e4e36aSLuigi Rizzo #endif /* linux */ 639b303f675SLuigi Rizzo 640ce3ee1e7SLuigi Rizzo /* 641ce3ee1e7SLuigi Rizzo * increment the addressed in the packet, 642ce3ee1e7SLuigi Rizzo * starting from the least significant field. 643ce3ee1e7SLuigi Rizzo * DST_IP DST_PORT SRC_IP SRC_PORT 644ce3ee1e7SLuigi Rizzo */ 645ce3ee1e7SLuigi Rizzo static void 646ce3ee1e7SLuigi Rizzo update_addresses(struct pkt *pkt, struct glob_arg *g) 647ce3ee1e7SLuigi Rizzo { 648ce3ee1e7SLuigi Rizzo uint32_t a; 649ce3ee1e7SLuigi Rizzo uint16_t p; 650ce3ee1e7SLuigi Rizzo struct ip *ip = &pkt->ip; 651ce3ee1e7SLuigi Rizzo struct udphdr *udp = &pkt->udp; 652ce3ee1e7SLuigi Rizzo 653f2637526SLuigi Rizzo do { 65456717743SAdrian Chadd /* XXX for now it doesn't handle non-random src, random dst */ 65556717743SAdrian Chadd if (g->options & OPT_RANDOM_SRC) { 65656717743SAdrian Chadd udp->uh_sport = random(); 65756717743SAdrian Chadd ip->ip_src.s_addr = random(); 65856717743SAdrian Chadd } else { 659ce3ee1e7SLuigi Rizzo p = ntohs(udp->uh_sport); 660ce3ee1e7SLuigi Rizzo if (p < g->src_ip.port1) { /* just inc, no wrap */ 661ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(p + 1); 662f2637526SLuigi Rizzo break; 663ce3ee1e7SLuigi Rizzo } 664ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(g->src_ip.port0); 665ce3ee1e7SLuigi Rizzo 666ce3ee1e7SLuigi Rizzo a = ntohl(ip->ip_src.s_addr); 667ce3ee1e7SLuigi Rizzo if (a < g->src_ip.end) { /* just inc, no wrap */ 668ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(a + 1); 669f2637526SLuigi Rizzo break; 670ce3ee1e7SLuigi Rizzo } 671ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(g->src_ip.start); 672ce3ee1e7SLuigi Rizzo 673ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(g->src_ip.port0); 67456717743SAdrian Chadd } 67556717743SAdrian Chadd 67656717743SAdrian Chadd if (g->options & OPT_RANDOM_DST) { 67756717743SAdrian Chadd udp->uh_dport = random(); 67856717743SAdrian Chadd ip->ip_dst.s_addr = random(); 67956717743SAdrian Chadd } else { 680ce3ee1e7SLuigi Rizzo p = ntohs(udp->uh_dport); 681ce3ee1e7SLuigi Rizzo if (p < g->dst_ip.port1) { /* just inc, no wrap */ 682ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(p + 1); 683f2637526SLuigi Rizzo break; 684ce3ee1e7SLuigi Rizzo } 685ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(g->dst_ip.port0); 686ce3ee1e7SLuigi Rizzo 687ce3ee1e7SLuigi Rizzo a = ntohl(ip->ip_dst.s_addr); 688ce3ee1e7SLuigi Rizzo if (a < g->dst_ip.end) { /* just inc, no wrap */ 689ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(a + 1); 690f2637526SLuigi Rizzo break; 691ce3ee1e7SLuigi Rizzo } 69256717743SAdrian Chadd } 693ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(g->dst_ip.start); 694f2637526SLuigi Rizzo } while (0); 695f2637526SLuigi Rizzo // update checksum 696ce3ee1e7SLuigi Rizzo } 697ce3ee1e7SLuigi Rizzo 698ce3ee1e7SLuigi Rizzo /* 699ce3ee1e7SLuigi Rizzo * initialize one packet and prepare for the next one. 700ce3ee1e7SLuigi Rizzo * The copy could be done better instead of repeating it each time. 701ce3ee1e7SLuigi Rizzo */ 70268b8534bSLuigi Rizzo static void 70368b8534bSLuigi Rizzo initialize_packet(struct targ *targ) 70468b8534bSLuigi Rizzo { 70568b8534bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 70668b8534bSLuigi Rizzo struct ether_header *eh; 70768b8534bSLuigi Rizzo struct ip *ip; 70868b8534bSLuigi Rizzo struct udphdr *udp; 709f8e4e36aSLuigi Rizzo uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); 710b303f675SLuigi Rizzo const char *payload = targ->g->options & OPT_INDIRECT ? 711ce3ee1e7SLuigi Rizzo indirect_payload : default_payload; 712f2637526SLuigi Rizzo int i, l0 = strlen(payload); 71368b8534bSLuigi Rizzo 71437e3a6d3SLuigi Rizzo #ifndef NO_PCAP 715f284c737SGeorge V. Neville-Neil char errbuf[PCAP_ERRBUF_SIZE]; 716f284c737SGeorge V. Neville-Neil pcap_t *file; 717f284c737SGeorge V. Neville-Neil struct pcap_pkthdr *header; 718f284c737SGeorge V. Neville-Neil const unsigned char *packet; 719f284c737SGeorge V. Neville-Neil 720f284c737SGeorge V. Neville-Neil /* Read a packet from a PCAP file if asked. */ 721f284c737SGeorge V. Neville-Neil if (targ->g->packet_file != NULL) { 722f284c737SGeorge V. Neville-Neil if ((file = pcap_open_offline(targ->g->packet_file, 723f284c737SGeorge V. Neville-Neil errbuf)) == NULL) 724f284c737SGeorge V. Neville-Neil D("failed to open pcap file %s", 725f284c737SGeorge V. Neville-Neil targ->g->packet_file); 726f284c737SGeorge V. Neville-Neil if (pcap_next_ex(file, &header, &packet) < 0) 727f284c737SGeorge V. Neville-Neil D("failed to read packet from %s", 728f284c737SGeorge V. Neville-Neil targ->g->packet_file); 729f284c737SGeorge V. Neville-Neil if ((targ->frame = malloc(header->caplen)) == NULL) 730f284c737SGeorge V. Neville-Neil D("out of memory"); 731f284c737SGeorge V. Neville-Neil bcopy(packet, (unsigned char *)targ->frame, header->caplen); 732f284c737SGeorge V. Neville-Neil targ->g->pkt_size = header->caplen; 733f284c737SGeorge V. Neville-Neil pcap_close(file); 734f284c737SGeorge V. Neville-Neil return; 735f284c737SGeorge V. Neville-Neil } 73637e3a6d3SLuigi Rizzo #endif 737f284c737SGeorge V. Neville-Neil 738ce3ee1e7SLuigi Rizzo /* create a nice NUL-terminated string */ 739f2637526SLuigi Rizzo for (i = 0; i < paylen; i += l0) { 740f2637526SLuigi Rizzo if (l0 > paylen - i) 741f2637526SLuigi Rizzo l0 = paylen - i; // last round 742f2637526SLuigi Rizzo bcopy(payload, pkt->body + i, l0); 74368b8534bSLuigi Rizzo } 74468b8534bSLuigi Rizzo pkt->body[i-1] = '\0'; 74568b8534bSLuigi Rizzo ip = &pkt->ip; 746f8e4e36aSLuigi Rizzo 747ce3ee1e7SLuigi Rizzo /* prepare the headers */ 74868b8534bSLuigi Rizzo ip->ip_v = IPVERSION; 74968b8534bSLuigi Rizzo ip->ip_hl = 5; 75068b8534bSLuigi Rizzo ip->ip_id = 0; 75168b8534bSLuigi Rizzo ip->ip_tos = IPTOS_LOWDELAY; 75268b8534bSLuigi Rizzo ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 75368b8534bSLuigi Rizzo ip->ip_id = 0; 75468b8534bSLuigi Rizzo ip->ip_off = htons(IP_DF); /* Don't fragment */ 75568b8534bSLuigi Rizzo ip->ip_ttl = IPDEFTTL; 75668b8534bSLuigi Rizzo ip->ip_p = IPPROTO_UDP; 757ce3ee1e7SLuigi Rizzo ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); 758ce3ee1e7SLuigi Rizzo ip->ip_src.s_addr = htonl(targ->g->src_ip.start); 759f8e4e36aSLuigi Rizzo ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); 760f8e4e36aSLuigi Rizzo 761f8e4e36aSLuigi Rizzo 762f8e4e36aSLuigi Rizzo udp = &pkt->udp; 763ce3ee1e7SLuigi Rizzo udp->uh_sport = htons(targ->g->src_ip.port0); 764ce3ee1e7SLuigi Rizzo udp->uh_dport = htons(targ->g->dst_ip.port0); 765f8e4e36aSLuigi Rizzo udp->uh_ulen = htons(paylen); 766f8e4e36aSLuigi Rizzo /* Magic: taken from sbin/dhclient/packet.c */ 767f8e4e36aSLuigi Rizzo udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), 768f8e4e36aSLuigi Rizzo checksum(pkt->body, 769f8e4e36aSLuigi Rizzo paylen - sizeof(*udp), 770f8e4e36aSLuigi Rizzo checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), 771f8e4e36aSLuigi Rizzo IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) 772f8e4e36aSLuigi Rizzo ) 773f8e4e36aSLuigi Rizzo ) 774f8e4e36aSLuigi Rizzo )); 77568b8534bSLuigi Rizzo 77668b8534bSLuigi Rizzo eh = &pkt->eh; 777f8e4e36aSLuigi Rizzo bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); 778f8e4e36aSLuigi Rizzo bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); 77968b8534bSLuigi Rizzo eh->ether_type = htons(ETHERTYPE_IP); 78017885a7bSLuigi Rizzo 78117885a7bSLuigi Rizzo bzero(&pkt->vh, sizeof(pkt->vh)); 782b303f675SLuigi Rizzo // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); 78368b8534bSLuigi Rizzo } 78468b8534bSLuigi Rizzo 7854bf50f18SLuigi Rizzo static void 78637e3a6d3SLuigi Rizzo get_vnet_hdr_len(struct glob_arg *g) 7874bf50f18SLuigi Rizzo { 78837e3a6d3SLuigi Rizzo struct nmreq req; 78937e3a6d3SLuigi Rizzo int err; 79037e3a6d3SLuigi Rizzo 79137e3a6d3SLuigi Rizzo memset(&req, 0, sizeof(req)); 79237e3a6d3SLuigi Rizzo bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 79337e3a6d3SLuigi Rizzo req.nr_version = NETMAP_API; 79437e3a6d3SLuigi Rizzo req.nr_cmd = NETMAP_VNET_HDR_GET; 79537e3a6d3SLuigi Rizzo err = ioctl(g->main_fd, NIOCREGIF, &req); 79637e3a6d3SLuigi Rizzo if (err) { 79737e3a6d3SLuigi Rizzo D("Unable to get virtio-net header length"); 79837e3a6d3SLuigi Rizzo return; 79937e3a6d3SLuigi Rizzo } 80037e3a6d3SLuigi Rizzo 80137e3a6d3SLuigi Rizzo g->virt_header = req.nr_arg1; 80237e3a6d3SLuigi Rizzo if (g->virt_header) { 80337e3a6d3SLuigi Rizzo D("Port requires virtio-net header, length = %d", 80437e3a6d3SLuigi Rizzo g->virt_header); 80537e3a6d3SLuigi Rizzo } 80637e3a6d3SLuigi Rizzo } 80737e3a6d3SLuigi Rizzo 80837e3a6d3SLuigi Rizzo static void 80937e3a6d3SLuigi Rizzo set_vnet_hdr_len(struct glob_arg *g) 81037e3a6d3SLuigi Rizzo { 81137e3a6d3SLuigi Rizzo int err, l = g->virt_header; 8124bf50f18SLuigi Rizzo struct nmreq req; 8134bf50f18SLuigi Rizzo 8144bf50f18SLuigi Rizzo if (l == 0) 8154bf50f18SLuigi Rizzo return; 8164bf50f18SLuigi Rizzo 8174bf50f18SLuigi Rizzo memset(&req, 0, sizeof(req)); 81837e3a6d3SLuigi Rizzo bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); 8194bf50f18SLuigi Rizzo req.nr_version = NETMAP_API; 8204bf50f18SLuigi Rizzo req.nr_cmd = NETMAP_BDG_VNET_HDR; 8214bf50f18SLuigi Rizzo req.nr_arg1 = l; 82237e3a6d3SLuigi Rizzo err = ioctl(g->main_fd, NIOCREGIF, &req); 8234bf50f18SLuigi Rizzo if (err) { 82437e3a6d3SLuigi Rizzo D("Unable to set virtio-net header length %d", l); 8254bf50f18SLuigi Rizzo } 8264bf50f18SLuigi Rizzo } 82768b8534bSLuigi Rizzo 82868b8534bSLuigi Rizzo 82968b8534bSLuigi Rizzo /* 83068b8534bSLuigi Rizzo * create and enqueue a batch of packets on a ring. 83168b8534bSLuigi Rizzo * On the last one set NS_REPORT to tell the driver to generate 83268b8534bSLuigi Rizzo * an interrupt when done. 83368b8534bSLuigi Rizzo */ 83468b8534bSLuigi Rizzo static int 83517885a7bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, 83617885a7bSLuigi Rizzo int size, struct glob_arg *g, u_int count, int options, 83717885a7bSLuigi Rizzo u_int nfrags) 83868b8534bSLuigi Rizzo { 83917885a7bSLuigi Rizzo u_int n, sent, cur = ring->cur; 840f2637526SLuigi Rizzo u_int fcnt; 84168b8534bSLuigi Rizzo 84217885a7bSLuigi Rizzo n = nm_ring_space(ring); 84317885a7bSLuigi Rizzo if (n < count) 84417885a7bSLuigi Rizzo count = n; 845ce3ee1e7SLuigi Rizzo if (count < nfrags) { 846ce3ee1e7SLuigi Rizzo D("truncating packet, no room for frags %d %d", 847ce3ee1e7SLuigi Rizzo count, nfrags); 848ce3ee1e7SLuigi Rizzo } 84999fb123fSLuigi Rizzo #if 0 85099fb123fSLuigi Rizzo if (options & (OPT_COPY | OPT_PREFETCH) ) { 85168b8534bSLuigi Rizzo for (sent = 0; sent < count; sent++) { 85268b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 85368b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 85468b8534bSLuigi Rizzo 855f2637526SLuigi Rizzo __builtin_prefetch(p); 85617885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 85799fb123fSLuigi Rizzo } 85899fb123fSLuigi Rizzo cur = ring->cur; 85999fb123fSLuigi Rizzo } 86099fb123fSLuigi Rizzo #endif 861ce3ee1e7SLuigi Rizzo for (fcnt = nfrags, sent = 0; sent < count; sent++) { 86299fb123fSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 86399fb123fSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 86437e3a6d3SLuigi Rizzo int buf_changed = slot->flags & NS_BUF_CHANGED; 86599fb123fSLuigi Rizzo 866b303f675SLuigi Rizzo slot->flags = 0; 86737e3a6d3SLuigi Rizzo if (options & OPT_RUBBISH) { 86837e3a6d3SLuigi Rizzo /* do nothing */ 86937e3a6d3SLuigi Rizzo } else if (options & OPT_INDIRECT) { 870b303f675SLuigi Rizzo slot->flags |= NS_INDIRECT; 87137e3a6d3SLuigi Rizzo slot->ptr = (uint64_t)((uintptr_t)frame); 87237e3a6d3SLuigi Rizzo } else if ((options & OPT_COPY) || buf_changed) { 873f0ea3689SLuigi Rizzo nm_pkt_copy(frame, p, size); 874f2637526SLuigi Rizzo if (fcnt == nfrags) 875ce3ee1e7SLuigi Rizzo update_addresses(pkt, g); 876ce3ee1e7SLuigi Rizzo } else if (options & OPT_MEMCPY) { 87717885a7bSLuigi Rizzo memcpy(p, frame, size); 878f2637526SLuigi Rizzo if (fcnt == nfrags) 879ce3ee1e7SLuigi Rizzo update_addresses(pkt, g); 880ce3ee1e7SLuigi Rizzo } else if (options & OPT_PREFETCH) { 881f2637526SLuigi Rizzo __builtin_prefetch(p); 882ce3ee1e7SLuigi Rizzo } 883ce3ee1e7SLuigi Rizzo if (options & OPT_DUMP) 884ce3ee1e7SLuigi Rizzo dump_payload(p, size, ring, cur); 88568b8534bSLuigi Rizzo slot->len = size; 886ce3ee1e7SLuigi Rizzo if (--fcnt > 0) 887ce3ee1e7SLuigi Rizzo slot->flags |= NS_MOREFRAG; 888ce3ee1e7SLuigi Rizzo else 889ce3ee1e7SLuigi Rizzo fcnt = nfrags; 890ce3ee1e7SLuigi Rizzo if (sent == count - 1) { 891ce3ee1e7SLuigi Rizzo slot->flags &= ~NS_MOREFRAG; 89268b8534bSLuigi Rizzo slot->flags |= NS_REPORT; 893ce3ee1e7SLuigi Rizzo } 89417885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 89568b8534bSLuigi Rizzo } 89617885a7bSLuigi Rizzo ring->head = ring->cur = cur; 89768b8534bSLuigi Rizzo 89868b8534bSLuigi Rizzo return (sent); 89968b8534bSLuigi Rizzo } 90068b8534bSLuigi Rizzo 901f8e4e36aSLuigi Rizzo /* 90237e3a6d3SLuigi Rizzo * Index of the highest bit set 90337e3a6d3SLuigi Rizzo */ 90437e3a6d3SLuigi Rizzo uint32_t 90537e3a6d3SLuigi Rizzo msb64(uint64_t x) 90637e3a6d3SLuigi Rizzo { 90737e3a6d3SLuigi Rizzo uint64_t m = 1ULL << 63; 90837e3a6d3SLuigi Rizzo int i; 90937e3a6d3SLuigi Rizzo 91037e3a6d3SLuigi Rizzo for (i = 63; i >= 0; i--, m >>=1) 91137e3a6d3SLuigi Rizzo if (m & x) 91237e3a6d3SLuigi Rizzo return i; 91337e3a6d3SLuigi Rizzo return 0; 91437e3a6d3SLuigi Rizzo } 91537e3a6d3SLuigi Rizzo 91637e3a6d3SLuigi Rizzo /* 917f8e4e36aSLuigi Rizzo * Send a packet, and wait for a response. 918f8e4e36aSLuigi Rizzo * The payload (after UDP header, ofs 42) has a 4-byte sequence 919f8e4e36aSLuigi Rizzo * followed by a struct timeval (or bintime?) 920f8e4e36aSLuigi Rizzo */ 921f8e4e36aSLuigi Rizzo #define PAY_OFS 42 /* where in the pkt... */ 922f8e4e36aSLuigi Rizzo 92368b8534bSLuigi Rizzo static void * 924f8e4e36aSLuigi Rizzo pinger_body(void *data) 92568b8534bSLuigi Rizzo { 92668b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 927f0ea3689SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 928f0ea3689SLuigi Rizzo struct netmap_if *nifp = targ->nmd->nifp; 92937e3a6d3SLuigi Rizzo int i, rx = 0; 93017885a7bSLuigi Rizzo void *frame; 93117885a7bSLuigi Rizzo int size; 932f0ea3689SLuigi Rizzo struct timespec ts, now, last_print; 93337e3a6d3SLuigi Rizzo uint64_t sent = 0, n = targ->g->npackets; 93437e3a6d3SLuigi Rizzo uint64_t count = 0, t_cur, t_min = ~0, av = 0; 93537e3a6d3SLuigi Rizzo uint64_t buckets[64]; /* bins for delays, ns */ 93617885a7bSLuigi Rizzo 93717885a7bSLuigi Rizzo frame = &targ->pkt; 93817885a7bSLuigi Rizzo frame += sizeof(targ->pkt.vh) - targ->g->virt_header; 93917885a7bSLuigi Rizzo size = targ->g->pkt_size + targ->g->virt_header; 940e5ecae38SEd Maste 94137e3a6d3SLuigi Rizzo 942f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 943f8e4e36aSLuigi Rizzo D("can only ping with 1 thread"); 944f8e4e36aSLuigi Rizzo return NULL; 945f95a30bdSEd Maste } 946f8e4e36aSLuigi Rizzo 94737e3a6d3SLuigi Rizzo bzero(&buckets, sizeof(buckets)); 948f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); 94917885a7bSLuigi Rizzo now = last_print; 95037e3a6d3SLuigi Rizzo while (!targ->cancel && (n == 0 || sent < n)) { 951f8e4e36aSLuigi Rizzo struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); 952f8e4e36aSLuigi Rizzo struct netmap_slot *slot; 953f8e4e36aSLuigi Rizzo char *p; 95417885a7bSLuigi Rizzo for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ 955f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 95617885a7bSLuigi Rizzo slot->len = size; 957f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 958f8e4e36aSLuigi Rizzo 95917885a7bSLuigi Rizzo if (nm_ring_empty(ring)) { 960f8e4e36aSLuigi Rizzo D("-- ouch, cannot send"); 961f8e4e36aSLuigi Rizzo } else { 9624bf50f18SLuigi Rizzo struct tstamp *tp; 963f0ea3689SLuigi Rizzo nm_pkt_copy(frame, p, size); 964f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &ts); 965f8e4e36aSLuigi Rizzo bcopy(&sent, p+42, sizeof(sent)); 9664bf50f18SLuigi Rizzo tp = (struct tstamp *)(p+46); 9674bf50f18SLuigi Rizzo tp->sec = (uint32_t)ts.tv_sec; 9684bf50f18SLuigi Rizzo tp->nsec = (uint32_t)ts.tv_nsec; 969f8e4e36aSLuigi Rizzo sent++; 97017885a7bSLuigi Rizzo ring->head = ring->cur = nm_ring_next(ring, ring->cur); 971f8e4e36aSLuigi Rizzo } 972f8e4e36aSLuigi Rizzo } 973f8e4e36aSLuigi Rizzo /* should use a parameter to decide how often to send */ 974f0ea3689SLuigi Rizzo if (poll(&pfd, 1, 3000) <= 0) { 97517885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 97617885a7bSLuigi Rizzo strerror(errno)); 977f8e4e36aSLuigi Rizzo continue; 978f8e4e36aSLuigi Rizzo } 979f8e4e36aSLuigi Rizzo /* see what we got back */ 980f0ea3689SLuigi Rizzo for (i = targ->nmd->first_tx_ring; 981f0ea3689SLuigi Rizzo i <= targ->nmd->last_tx_ring; i++) { 982f8e4e36aSLuigi Rizzo ring = NETMAP_RXRING(nifp, i); 98317885a7bSLuigi Rizzo while (!nm_ring_empty(ring)) { 984f8e4e36aSLuigi Rizzo uint32_t seq; 9854bf50f18SLuigi Rizzo struct tstamp *tp; 98637e3a6d3SLuigi Rizzo int pos; 98737e3a6d3SLuigi Rizzo 988f8e4e36aSLuigi Rizzo slot = &ring->slot[ring->cur]; 989f8e4e36aSLuigi Rizzo p = NETMAP_BUF(ring, slot->buf_idx); 990f8e4e36aSLuigi Rizzo 991f8e4e36aSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &now); 992f8e4e36aSLuigi Rizzo bcopy(p+42, &seq, sizeof(seq)); 9934bf50f18SLuigi Rizzo tp = (struct tstamp *)(p+46); 9944bf50f18SLuigi Rizzo ts.tv_sec = (time_t)tp->sec; 9954bf50f18SLuigi Rizzo ts.tv_nsec = (long)tp->nsec; 996f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - ts.tv_sec; 997f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - ts.tv_nsec; 998f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 999f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 1000f8e4e36aSLuigi Rizzo ts.tv_sec--; 1001f8e4e36aSLuigi Rizzo } 100237e3a6d3SLuigi Rizzo if (0) D("seq %d/%lu delta %d.%09d", seq, sent, 1003f8e4e36aSLuigi Rizzo (int)ts.tv_sec, (int)ts.tv_nsec); 100437e3a6d3SLuigi Rizzo t_cur = ts.tv_sec * 1000000000UL + ts.tv_nsec; 100537e3a6d3SLuigi Rizzo if (t_cur < t_min) 100637e3a6d3SLuigi Rizzo t_min = t_cur; 1007f8e4e36aSLuigi Rizzo count ++; 100837e3a6d3SLuigi Rizzo av += t_cur; 100937e3a6d3SLuigi Rizzo pos = msb64(t_cur); 101037e3a6d3SLuigi Rizzo buckets[pos]++; 101137e3a6d3SLuigi Rizzo /* now store it in a bucket */ 101217885a7bSLuigi Rizzo ring->head = ring->cur = nm_ring_next(ring, ring->cur); 1013f8e4e36aSLuigi Rizzo rx++; 1014f8e4e36aSLuigi Rizzo } 1015f8e4e36aSLuigi Rizzo } 1016f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 1017f8e4e36aSLuigi Rizzo //usleep(100000); 1018f8e4e36aSLuigi Rizzo ts.tv_sec = now.tv_sec - last_print.tv_sec; 1019f8e4e36aSLuigi Rizzo ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; 1020f8e4e36aSLuigi Rizzo if (ts.tv_nsec < 0) { 1021f8e4e36aSLuigi Rizzo ts.tv_nsec += 1000000000; 1022f8e4e36aSLuigi Rizzo ts.tv_sec--; 1023f8e4e36aSLuigi Rizzo } 1024f8e4e36aSLuigi Rizzo if (ts.tv_sec >= 1) { 102537e3a6d3SLuigi Rizzo D("count %d RTT: min %d av %d ns", 102637e3a6d3SLuigi Rizzo (int)count, (int)t_min, (int)(av/count)); 102737e3a6d3SLuigi Rizzo int k, j, kmin; 102837e3a6d3SLuigi Rizzo char buf[512]; 102937e3a6d3SLuigi Rizzo 103037e3a6d3SLuigi Rizzo for (kmin = 0; kmin < 64; kmin ++) 103137e3a6d3SLuigi Rizzo if (buckets[kmin]) 103237e3a6d3SLuigi Rizzo break; 103337e3a6d3SLuigi Rizzo for (k = 63; k >= kmin; k--) 103437e3a6d3SLuigi Rizzo if (buckets[k]) 103537e3a6d3SLuigi Rizzo break; 103637e3a6d3SLuigi Rizzo buf[0] = '\0'; 103737e3a6d3SLuigi Rizzo for (j = kmin; j <= k; j++) 103837e3a6d3SLuigi Rizzo sprintf(buf, "%s %5d", buf, (int)buckets[j]); 103937e3a6d3SLuigi Rizzo D("k: %d .. %d\n\t%s", 1<<kmin, 1<<k, buf); 104037e3a6d3SLuigi Rizzo bzero(&buckets, sizeof(buckets)); 1041f8e4e36aSLuigi Rizzo count = 0; 1042f8e4e36aSLuigi Rizzo av = 0; 104337e3a6d3SLuigi Rizzo t_min = ~0; 1044f8e4e36aSLuigi Rizzo last_print = now; 1045f8e4e36aSLuigi Rizzo } 1046f8e4e36aSLuigi Rizzo } 104737e3a6d3SLuigi Rizzo 104837e3a6d3SLuigi Rizzo /* reset the ``used`` flag. */ 104937e3a6d3SLuigi Rizzo targ->used = 0; 105037e3a6d3SLuigi Rizzo 1051f8e4e36aSLuigi Rizzo return NULL; 1052f8e4e36aSLuigi Rizzo } 1053f8e4e36aSLuigi Rizzo 1054f8e4e36aSLuigi Rizzo 1055f8e4e36aSLuigi Rizzo /* 1056f8e4e36aSLuigi Rizzo * reply to ping requests 1057f8e4e36aSLuigi Rizzo */ 1058f8e4e36aSLuigi Rizzo static void * 1059f8e4e36aSLuigi Rizzo ponger_body(void *data) 1060f8e4e36aSLuigi Rizzo { 1061f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 1062f0ea3689SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 1063f0ea3689SLuigi Rizzo struct netmap_if *nifp = targ->nmd->nifp; 1064f8e4e36aSLuigi Rizzo struct netmap_ring *txring, *rxring; 106537e3a6d3SLuigi Rizzo int i, rx = 0; 106637e3a6d3SLuigi Rizzo uint64_t sent = 0, n = targ->g->npackets; 1067f8e4e36aSLuigi Rizzo 1068f8e4e36aSLuigi Rizzo if (targ->g->nthreads > 1) { 1069f8e4e36aSLuigi Rizzo D("can only reply ping with 1 thread"); 1070f8e4e36aSLuigi Rizzo return NULL; 1071f8e4e36aSLuigi Rizzo } 107237e3a6d3SLuigi Rizzo D("understood ponger %lu but don't know how to do it", n); 107337e3a6d3SLuigi Rizzo while (!targ->cancel && (n == 0 || sent < n)) { 1074f8e4e36aSLuigi Rizzo uint32_t txcur, txavail; 1075f8e4e36aSLuigi Rizzo //#define BUSYWAIT 1076f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 1077f0ea3689SLuigi Rizzo ioctl(pfd.fd, NIOCRXSYNC, NULL); 1078f8e4e36aSLuigi Rizzo #else 1079f0ea3689SLuigi Rizzo if (poll(&pfd, 1, 1000) <= 0) { 108017885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 108117885a7bSLuigi Rizzo strerror(errno)); 1082f8e4e36aSLuigi Rizzo continue; 1083f8e4e36aSLuigi Rizzo } 1084f8e4e36aSLuigi Rizzo #endif 1085f8e4e36aSLuigi Rizzo txring = NETMAP_TXRING(nifp, 0); 1086f8e4e36aSLuigi Rizzo txcur = txring->cur; 108717885a7bSLuigi Rizzo txavail = nm_ring_space(txring); 1088f8e4e36aSLuigi Rizzo /* see what we got back */ 1089f0ea3689SLuigi Rizzo for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 1090f8e4e36aSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 109117885a7bSLuigi Rizzo while (!nm_ring_empty(rxring)) { 1092f8e4e36aSLuigi Rizzo uint16_t *spkt, *dpkt; 1093f8e4e36aSLuigi Rizzo uint32_t cur = rxring->cur; 1094f8e4e36aSLuigi Rizzo struct netmap_slot *slot = &rxring->slot[cur]; 1095f8e4e36aSLuigi Rizzo char *src, *dst; 1096f8e4e36aSLuigi Rizzo src = NETMAP_BUF(rxring, slot->buf_idx); 1097f8e4e36aSLuigi Rizzo //D("got pkt %p of size %d", src, slot->len); 109817885a7bSLuigi Rizzo rxring->head = rxring->cur = nm_ring_next(rxring, cur); 1099f8e4e36aSLuigi Rizzo rx++; 1100f8e4e36aSLuigi Rizzo if (txavail == 0) 1101f8e4e36aSLuigi Rizzo continue; 1102f8e4e36aSLuigi Rizzo dst = NETMAP_BUF(txring, 1103f8e4e36aSLuigi Rizzo txring->slot[txcur].buf_idx); 1104f8e4e36aSLuigi Rizzo /* copy... */ 1105f8e4e36aSLuigi Rizzo dpkt = (uint16_t *)dst; 1106f8e4e36aSLuigi Rizzo spkt = (uint16_t *)src; 1107f0ea3689SLuigi Rizzo nm_pkt_copy(src, dst, slot->len); 1108f8e4e36aSLuigi Rizzo dpkt[0] = spkt[3]; 1109f8e4e36aSLuigi Rizzo dpkt[1] = spkt[4]; 1110f8e4e36aSLuigi Rizzo dpkt[2] = spkt[5]; 1111f8e4e36aSLuigi Rizzo dpkt[3] = spkt[0]; 1112f8e4e36aSLuigi Rizzo dpkt[4] = spkt[1]; 1113f8e4e36aSLuigi Rizzo dpkt[5] = spkt[2]; 1114f8e4e36aSLuigi Rizzo txring->slot[txcur].len = slot->len; 1115f8e4e36aSLuigi Rizzo /* XXX swap src dst mac */ 111617885a7bSLuigi Rizzo txcur = nm_ring_next(txring, txcur); 1117f8e4e36aSLuigi Rizzo txavail--; 1118f8e4e36aSLuigi Rizzo sent++; 1119f8e4e36aSLuigi Rizzo } 1120f8e4e36aSLuigi Rizzo } 112117885a7bSLuigi Rizzo txring->head = txring->cur = txcur; 112237e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 1123f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT 1124f0ea3689SLuigi Rizzo ioctl(pfd.fd, NIOCTXSYNC, NULL); 1125f8e4e36aSLuigi Rizzo #endif 1126f8e4e36aSLuigi Rizzo //D("tx %d rx %d", sent, rx); 1127f8e4e36aSLuigi Rizzo } 112837e3a6d3SLuigi Rizzo 112937e3a6d3SLuigi Rizzo /* reset the ``used`` flag. */ 113037e3a6d3SLuigi Rizzo targ->used = 0; 113137e3a6d3SLuigi Rizzo 1132f8e4e36aSLuigi Rizzo return NULL; 1133f8e4e36aSLuigi Rizzo } 1134f8e4e36aSLuigi Rizzo 1135f8e4e36aSLuigi Rizzo 113617885a7bSLuigi Rizzo /* 113717885a7bSLuigi Rizzo * wait until ts, either busy or sleeping if more than 1ms. 113817885a7bSLuigi Rizzo * Return wakeup time. 113917885a7bSLuigi Rizzo */ 114017885a7bSLuigi Rizzo static struct timespec 114117885a7bSLuigi Rizzo wait_time(struct timespec ts) 114217885a7bSLuigi Rizzo { 114317885a7bSLuigi Rizzo for (;;) { 114417885a7bSLuigi Rizzo struct timespec w, cur; 114517885a7bSLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &cur); 114617885a7bSLuigi Rizzo w = timespec_sub(ts, cur); 114717885a7bSLuigi Rizzo if (w.tv_sec < 0) 114817885a7bSLuigi Rizzo return cur; 114917885a7bSLuigi Rizzo else if (w.tv_sec > 0 || w.tv_nsec > 1000000) 115017885a7bSLuigi Rizzo poll(NULL, 0, 1); 115117885a7bSLuigi Rizzo } 115217885a7bSLuigi Rizzo } 115317885a7bSLuigi Rizzo 1154f8e4e36aSLuigi Rizzo static void * 1155f8e4e36aSLuigi Rizzo sender_body(void *data) 1156f8e4e36aSLuigi Rizzo { 1157f8e4e36aSLuigi Rizzo struct targ *targ = (struct targ *) data; 1158f0ea3689SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 11594bf50f18SLuigi Rizzo struct netmap_if *nifp; 116037e3a6d3SLuigi Rizzo struct netmap_ring *txring = NULL; 116137e3a6d3SLuigi Rizzo int i; 116237e3a6d3SLuigi Rizzo uint64_t n = targ->g->npackets / targ->g->nthreads; 116337e3a6d3SLuigi Rizzo uint64_t sent = 0; 116437e3a6d3SLuigi Rizzo uint64_t event = 0; 1165f8e4e36aSLuigi Rizzo int options = targ->g->options | OPT_COPY; 116617885a7bSLuigi Rizzo struct timespec nexttime = { 0, 0}; // XXX silence compiler 11671cb4c501SLuigi Rizzo int rate_limit = targ->g->tx_rate; 116817885a7bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 116917885a7bSLuigi Rizzo void *frame; 117017885a7bSLuigi Rizzo int size; 117117885a7bSLuigi Rizzo 1172f284c737SGeorge V. Neville-Neil if (targ->frame == NULL) { 117317885a7bSLuigi Rizzo frame = pkt; 117417885a7bSLuigi Rizzo frame += sizeof(pkt->vh) - targ->g->virt_header; 117517885a7bSLuigi Rizzo size = targ->g->pkt_size + targ->g->virt_header; 1176f284c737SGeorge V. Neville-Neil } else { 1177f284c737SGeorge V. Neville-Neil frame = targ->frame; 1178f284c737SGeorge V. Neville-Neil size = targ->g->pkt_size; 1179f284c737SGeorge V. Neville-Neil } 1180b303f675SLuigi Rizzo 11814bf50f18SLuigi Rizzo D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 118268b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 118368b8534bSLuigi Rizzo goto quit; 118468b8534bSLuigi Rizzo 118568b8534bSLuigi Rizzo /* main loop.*/ 11861cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 11871cb4c501SLuigi Rizzo if (rate_limit) { 118817885a7bSLuigi Rizzo targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 11891cb4c501SLuigi Rizzo targ->tic.tv_nsec = 0; 119017885a7bSLuigi Rizzo wait_time(targ->tic); 11911cb4c501SLuigi Rizzo nexttime = targ->tic; 11921cb4c501SLuigi Rizzo } 1193f2637526SLuigi Rizzo if (targ->g->dev_type == DEV_TAP) { 1194f8e4e36aSLuigi Rizzo D("writing to file desc %d", targ->g->main_fd); 1195f8e4e36aSLuigi Rizzo 1196f8e4e36aSLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 119717885a7bSLuigi Rizzo if (write(targ->g->main_fd, frame, size) != -1) 1198f8e4e36aSLuigi Rizzo sent++; 1199ce3ee1e7SLuigi Rizzo update_addresses(pkt, targ->g); 1200f8e4e36aSLuigi Rizzo if (i > 10000) { 120137e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 120237e3a6d3SLuigi Rizzo targ->ctr.bytes = sent*size; 120337e3a6d3SLuigi Rizzo targ->ctr.events = sent; 1204f8e4e36aSLuigi Rizzo i = 0; 1205f8e4e36aSLuigi Rizzo } 1206f8e4e36aSLuigi Rizzo } 1207f2637526SLuigi Rizzo #ifndef NO_PCAP 1208f2637526SLuigi Rizzo } else if (targ->g->dev_type == DEV_PCAP) { 1209f2637526SLuigi Rizzo pcap_t *p = targ->g->p; 1210f2637526SLuigi Rizzo 1211f2637526SLuigi Rizzo for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { 1212f2637526SLuigi Rizzo if (pcap_inject(p, frame, size) != -1) 1213f2637526SLuigi Rizzo sent++; 1214f2637526SLuigi Rizzo update_addresses(pkt, targ->g); 1215f2637526SLuigi Rizzo if (i > 10000) { 121637e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 121737e3a6d3SLuigi Rizzo targ->ctr.bytes = sent*size; 121837e3a6d3SLuigi Rizzo targ->ctr.events = sent; 1219f2637526SLuigi Rizzo i = 0; 1220f2637526SLuigi Rizzo } 1221f2637526SLuigi Rizzo } 1222f2637526SLuigi Rizzo #endif /* NO_PCAP */ 122368b8534bSLuigi Rizzo } else { 12241cb4c501SLuigi Rizzo int tosend = 0; 1225ce3ee1e7SLuigi Rizzo int frags = targ->g->frags; 1226ce3ee1e7SLuigi Rizzo 12274bf50f18SLuigi Rizzo nifp = targ->nmd->nifp; 1228f8e4e36aSLuigi Rizzo while (!targ->cancel && (n == 0 || sent < n)) { 122968b8534bSLuigi Rizzo 12301cb4c501SLuigi Rizzo if (rate_limit && tosend <= 0) { 12311cb4c501SLuigi Rizzo tosend = targ->g->burst; 123217885a7bSLuigi Rizzo nexttime = timespec_add(nexttime, targ->g->tx_period); 123317885a7bSLuigi Rizzo wait_time(nexttime); 12341cb4c501SLuigi Rizzo } 12351cb4c501SLuigi Rizzo 123668b8534bSLuigi Rizzo /* 123768b8534bSLuigi Rizzo * wait for available room in the send queue(s) 123868b8534bSLuigi Rizzo */ 123937e3a6d3SLuigi Rizzo #ifdef BUSYWAIT 124037e3a6d3SLuigi Rizzo if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) { 124137e3a6d3SLuigi Rizzo D("ioctl error on queue %d: %s", targ->me, 124237e3a6d3SLuigi Rizzo strerror(errno)); 124337e3a6d3SLuigi Rizzo goto quit; 124437e3a6d3SLuigi Rizzo } 124537e3a6d3SLuigi Rizzo #else /* !BUSYWAIT */ 1246f0ea3689SLuigi Rizzo if (poll(&pfd, 1, 2000) <= 0) { 12473fe77e68SEd Maste if (targ->cancel) 12483fe77e68SEd Maste break; 124917885a7bSLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 125017885a7bSLuigi Rizzo strerror(errno)); 1251f0ea3689SLuigi Rizzo // goto quit; 125217885a7bSLuigi Rizzo } 1253f0ea3689SLuigi Rizzo if (pfd.revents & POLLERR) { 125437e3a6d3SLuigi Rizzo D("poll error on %d ring %d-%d", pfd.fd, 125537e3a6d3SLuigi Rizzo targ->nmd->first_tx_ring, targ->nmd->last_tx_ring); 125668b8534bSLuigi Rizzo goto quit; 125768b8534bSLuigi Rizzo } 125837e3a6d3SLuigi Rizzo #endif /* !BUSYWAIT */ 125968b8534bSLuigi Rizzo /* 126068b8534bSLuigi Rizzo * scan our queues and send on those with room 126168b8534bSLuigi Rizzo */ 1262f8e4e36aSLuigi Rizzo if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { 1263f8e4e36aSLuigi Rizzo D("drop copy"); 126499fb123fSLuigi Rizzo options &= ~OPT_COPY; 1265f8e4e36aSLuigi Rizzo } 1266f0ea3689SLuigi Rizzo for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 126737e3a6d3SLuigi Rizzo int m; 126837e3a6d3SLuigi Rizzo uint64_t limit = rate_limit ? tosend : targ->g->burst; 1269f8e4e36aSLuigi Rizzo if (n > 0 && n - sent < limit) 1270f8e4e36aSLuigi Rizzo limit = n - sent; 127168b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 127217885a7bSLuigi Rizzo if (nm_ring_empty(txring)) 127368b8534bSLuigi Rizzo continue; 1274ce3ee1e7SLuigi Rizzo if (frags > 1) 1275ce3ee1e7SLuigi Rizzo limit = ((limit + frags - 1) / frags) * frags; 1276ce3ee1e7SLuigi Rizzo 127717885a7bSLuigi Rizzo m = send_packets(txring, pkt, frame, size, targ->g, 1278ce3ee1e7SLuigi Rizzo limit, options, frags); 1279f2637526SLuigi Rizzo ND("limit %d tail %d frags %d m %d", 1280f2637526SLuigi Rizzo limit, txring->tail, frags, m); 128168b8534bSLuigi Rizzo sent += m; 128237e3a6d3SLuigi Rizzo if (m > 0) //XXX-ste: can m be 0? 128337e3a6d3SLuigi Rizzo event++; 128437e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 128537e3a6d3SLuigi Rizzo targ->ctr.bytes = sent*size; 128637e3a6d3SLuigi Rizzo targ->ctr.events = event; 1287ce3ee1e7SLuigi Rizzo if (rate_limit) { 1288ce3ee1e7SLuigi Rizzo tosend -= m; 1289ce3ee1e7SLuigi Rizzo if (tosend <= 0) 1290ce3ee1e7SLuigi Rizzo break; 1291ce3ee1e7SLuigi Rizzo } 129268b8534bSLuigi Rizzo } 129368b8534bSLuigi Rizzo } 129499fb123fSLuigi Rizzo /* flush any remaining packets */ 12954bf50f18SLuigi Rizzo D("flush tail %d head %d on thread %p", 12964bf50f18SLuigi Rizzo txring->tail, txring->head, 129737e3a6d3SLuigi Rizzo (void *)pthread_self()); 1298f0ea3689SLuigi Rizzo ioctl(pfd.fd, NIOCTXSYNC, NULL); 129968b8534bSLuigi Rizzo 130068b8534bSLuigi Rizzo /* final part: wait all the TX queues to be empty. */ 1301f0ea3689SLuigi Rizzo for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { 130268b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 130337e3a6d3SLuigi Rizzo while (!targ->cancel && nm_tx_pending(txring)) { 13044bf50f18SLuigi Rizzo RD(5, "pending tx tail %d head %d on ring %d", 13054bf50f18SLuigi Rizzo txring->tail, txring->head, i); 1306f0ea3689SLuigi Rizzo ioctl(pfd.fd, NIOCTXSYNC, NULL); 130768b8534bSLuigi Rizzo usleep(1); /* wait 1 tick */ 130868b8534bSLuigi Rizzo } 130968b8534bSLuigi Rizzo } 1310f2637526SLuigi Rizzo } /* end DEV_NETMAP */ 131168b8534bSLuigi Rizzo 13121cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 131368b8534bSLuigi Rizzo targ->completed = 1; 131437e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 131537e3a6d3SLuigi Rizzo targ->ctr.bytes = sent*size; 131637e3a6d3SLuigi Rizzo targ->ctr.events = event; 131768b8534bSLuigi Rizzo quit: 131868b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 131968b8534bSLuigi Rizzo targ->used = 0; 132068b8534bSLuigi Rizzo 132168b8534bSLuigi Rizzo return (NULL); 132268b8534bSLuigi Rizzo } 132368b8534bSLuigi Rizzo 132468b8534bSLuigi Rizzo 1325f2637526SLuigi Rizzo #ifndef NO_PCAP 132668b8534bSLuigi Rizzo static void 1327f8e4e36aSLuigi Rizzo receive_pcap(u_char *user, const struct pcap_pkthdr * h, 1328f8e4e36aSLuigi Rizzo const u_char * bytes) 132968b8534bSLuigi Rizzo { 133037e3a6d3SLuigi Rizzo struct my_ctrs *ctr = (struct my_ctrs *)user; 1331f8e4e36aSLuigi Rizzo (void)bytes; /* UNUSED */ 133237e3a6d3SLuigi Rizzo ctr->bytes += h->len; 133337e3a6d3SLuigi Rizzo ctr->pkts++; 133468b8534bSLuigi Rizzo } 1335f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 133668b8534bSLuigi Rizzo 133737e3a6d3SLuigi Rizzo 133868b8534bSLuigi Rizzo static int 133937e3a6d3SLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int dump, uint64_t *bytes) 134068b8534bSLuigi Rizzo { 134117885a7bSLuigi Rizzo u_int cur, rx, n; 134237e3a6d3SLuigi Rizzo uint64_t b = 0; 134337e3a6d3SLuigi Rizzo 134437e3a6d3SLuigi Rizzo if (bytes == NULL) 134537e3a6d3SLuigi Rizzo bytes = &b; 134668b8534bSLuigi Rizzo 134768b8534bSLuigi Rizzo cur = ring->cur; 134817885a7bSLuigi Rizzo n = nm_ring_space(ring); 134917885a7bSLuigi Rizzo if (n < limit) 135017885a7bSLuigi Rizzo limit = n; 135168b8534bSLuigi Rizzo for (rx = 0; rx < limit; rx++) { 135268b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 135368b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 135468b8534bSLuigi Rizzo 135537e3a6d3SLuigi Rizzo *bytes += slot->len; 1356b303f675SLuigi Rizzo if (dump) 1357b303f675SLuigi Rizzo dump_payload(p, slot->len, ring, cur); 135868b8534bSLuigi Rizzo 135917885a7bSLuigi Rizzo cur = nm_ring_next(ring, cur); 136068b8534bSLuigi Rizzo } 136117885a7bSLuigi Rizzo ring->head = ring->cur = cur; 136268b8534bSLuigi Rizzo 136368b8534bSLuigi Rizzo return (rx); 136468b8534bSLuigi Rizzo } 136568b8534bSLuigi Rizzo 136668b8534bSLuigi Rizzo static void * 136768b8534bSLuigi Rizzo receiver_body(void *data) 136868b8534bSLuigi Rizzo { 136968b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 1370f0ea3689SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 13714bf50f18SLuigi Rizzo struct netmap_if *nifp; 137268b8534bSLuigi Rizzo struct netmap_ring *rxring; 1373f8e4e36aSLuigi Rizzo int i; 137437e3a6d3SLuigi Rizzo struct my_ctrs cur; 137537e3a6d3SLuigi Rizzo 137637e3a6d3SLuigi Rizzo cur.pkts = cur.bytes = cur.events = cur.min_space = 0; 137737e3a6d3SLuigi Rizzo cur.t.tv_usec = cur.t.tv_sec = 0; // unused, just silence the compiler 137868b8534bSLuigi Rizzo 137968b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 138068b8534bSLuigi Rizzo goto quit; 138168b8534bSLuigi Rizzo 13824bf50f18SLuigi Rizzo D("reading from %s fd %d main_fd %d", 13834bf50f18SLuigi Rizzo targ->g->ifname, targ->fd, targ->g->main_fd); 138468b8534bSLuigi Rizzo /* unbounded wait for the first packet. */ 13854bf50f18SLuigi Rizzo for (;!targ->cancel;) { 1386f0ea3689SLuigi Rizzo i = poll(&pfd, 1, 1000); 1387f0ea3689SLuigi Rizzo if (i > 0 && !(pfd.revents & POLLERR)) 138868b8534bSLuigi Rizzo break; 1389f0ea3689SLuigi Rizzo RD(1, "waiting for initial packets, poll returns %d %d", 1390f0ea3689SLuigi Rizzo i, pfd.revents); 139168b8534bSLuigi Rizzo } 139268b8534bSLuigi Rizzo /* main loop, exit after 1s silence */ 13931cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 1394f2637526SLuigi Rizzo if (targ->g->dev_type == DEV_TAP) { 1395f8e4e36aSLuigi Rizzo while (!targ->cancel) { 13964bf50f18SLuigi Rizzo char buf[MAX_BODYSIZE]; 1397f8e4e36aSLuigi Rizzo /* XXX should we poll ? */ 139837e3a6d3SLuigi Rizzo i = read(targ->g->main_fd, buf, sizeof(buf)); 139937e3a6d3SLuigi Rizzo if (i > 0) { 140037e3a6d3SLuigi Rizzo targ->ctr.pkts++; 140137e3a6d3SLuigi Rizzo targ->ctr.bytes += i; 140237e3a6d3SLuigi Rizzo targ->ctr.events++; 140337e3a6d3SLuigi Rizzo } 1404f8e4e36aSLuigi Rizzo } 1405f2637526SLuigi Rizzo #ifndef NO_PCAP 1406f2637526SLuigi Rizzo } else if (targ->g->dev_type == DEV_PCAP) { 1407f2637526SLuigi Rizzo while (!targ->cancel) { 1408f2637526SLuigi Rizzo /* XXX should we poll ? */ 14094bf50f18SLuigi Rizzo pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, 141037e3a6d3SLuigi Rizzo (u_char *)&targ->ctr); 141137e3a6d3SLuigi Rizzo targ->ctr.events++; 1412f2637526SLuigi Rizzo } 1413f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 141468b8534bSLuigi Rizzo } else { 1415b303f675SLuigi Rizzo int dump = targ->g->options & OPT_DUMP; 14164bf50f18SLuigi Rizzo 14174bf50f18SLuigi Rizzo nifp = targ->nmd->nifp; 14183fe77e68SEd Maste while (!targ->cancel) { 141968b8534bSLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 142068b8534bSLuigi Rizzo before quitting. */ 142137e3a6d3SLuigi Rizzo #ifdef BUSYWAIT 142237e3a6d3SLuigi Rizzo if (ioctl(pfd.fd, NIOCRXSYNC, NULL) < 0) { 142337e3a6d3SLuigi Rizzo D("ioctl error on queue %d: %s", targ->me, 142437e3a6d3SLuigi Rizzo strerror(errno)); 142537e3a6d3SLuigi Rizzo goto quit; 142637e3a6d3SLuigi Rizzo } 142737e3a6d3SLuigi Rizzo #else /* !BUSYWAIT */ 142837e3a6d3SLuigi Rizzo if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 142937e3a6d3SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 143037e3a6d3SLuigi Rizzo targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 143137e3a6d3SLuigi Rizzo goto out; 143237e3a6d3SLuigi Rizzo } 143337e3a6d3SLuigi Rizzo 143437e3a6d3SLuigi Rizzo if (pfd.revents & POLLERR) { 143537e3a6d3SLuigi Rizzo D("poll err"); 143637e3a6d3SLuigi Rizzo goto quit; 143737e3a6d3SLuigi Rizzo } 143837e3a6d3SLuigi Rizzo #endif /* !BUSYWAIT */ 143937e3a6d3SLuigi Rizzo uint64_t cur_space = 0; 144037e3a6d3SLuigi Rizzo for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { 144137e3a6d3SLuigi Rizzo int m; 144237e3a6d3SLuigi Rizzo 144337e3a6d3SLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 144437e3a6d3SLuigi Rizzo /* compute free space in the ring */ 144537e3a6d3SLuigi Rizzo m = rxring->head + rxring->num_slots - rxring->tail; 144637e3a6d3SLuigi Rizzo if (m >= (int) rxring->num_slots) 144737e3a6d3SLuigi Rizzo m -= rxring->num_slots; 144837e3a6d3SLuigi Rizzo cur_space += m; 144937e3a6d3SLuigi Rizzo if (nm_ring_empty(rxring)) 145037e3a6d3SLuigi Rizzo continue; 145137e3a6d3SLuigi Rizzo 145237e3a6d3SLuigi Rizzo m = receive_packets(rxring, targ->g->burst, dump, &cur.bytes); 145337e3a6d3SLuigi Rizzo cur.pkts += m; 145437e3a6d3SLuigi Rizzo if (m > 0) //XXX-ste: can m be 0? 145537e3a6d3SLuigi Rizzo cur.events++; 145637e3a6d3SLuigi Rizzo } 145737e3a6d3SLuigi Rizzo cur.min_space = targ->ctr.min_space; 145837e3a6d3SLuigi Rizzo if (cur_space < cur.min_space) 145937e3a6d3SLuigi Rizzo cur.min_space = cur_space; 146037e3a6d3SLuigi Rizzo targ->ctr = cur; 146137e3a6d3SLuigi Rizzo } 146237e3a6d3SLuigi Rizzo } 146337e3a6d3SLuigi Rizzo 146437e3a6d3SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 146537e3a6d3SLuigi Rizzo 146637e3a6d3SLuigi Rizzo #if !defined(BUSYWAIT) 146737e3a6d3SLuigi Rizzo out: 146837e3a6d3SLuigi Rizzo #endif 146937e3a6d3SLuigi Rizzo targ->completed = 1; 147037e3a6d3SLuigi Rizzo targ->ctr = cur; 147137e3a6d3SLuigi Rizzo 147237e3a6d3SLuigi Rizzo quit: 147337e3a6d3SLuigi Rizzo /* reset the ``used`` flag. */ 147437e3a6d3SLuigi Rizzo targ->used = 0; 147537e3a6d3SLuigi Rizzo 147637e3a6d3SLuigi Rizzo return (NULL); 147737e3a6d3SLuigi Rizzo } 147837e3a6d3SLuigi Rizzo 147937e3a6d3SLuigi Rizzo static void * 148037e3a6d3SLuigi Rizzo txseq_body(void *data) 148137e3a6d3SLuigi Rizzo { 148237e3a6d3SLuigi Rizzo struct targ *targ = (struct targ *) data; 148337e3a6d3SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; 148437e3a6d3SLuigi Rizzo struct netmap_ring *ring; 148537e3a6d3SLuigi Rizzo int64_t sent = 0; 148637e3a6d3SLuigi Rizzo uint64_t event = 0; 148737e3a6d3SLuigi Rizzo int options = targ->g->options | OPT_COPY; 148837e3a6d3SLuigi Rizzo struct timespec nexttime = {0, 0}; 148937e3a6d3SLuigi Rizzo int rate_limit = targ->g->tx_rate; 149037e3a6d3SLuigi Rizzo struct pkt *pkt = &targ->pkt; 149137e3a6d3SLuigi Rizzo int frags = targ->g->frags; 149237e3a6d3SLuigi Rizzo uint32_t sequence = 0; 149337e3a6d3SLuigi Rizzo int budget = 0; 149437e3a6d3SLuigi Rizzo void *frame; 149537e3a6d3SLuigi Rizzo int size; 149637e3a6d3SLuigi Rizzo 149737e3a6d3SLuigi Rizzo if (targ->g->nthreads > 1) { 149837e3a6d3SLuigi Rizzo D("can only txseq ping with 1 thread"); 149937e3a6d3SLuigi Rizzo return NULL; 150037e3a6d3SLuigi Rizzo } 150137e3a6d3SLuigi Rizzo 150237e3a6d3SLuigi Rizzo if (targ->g->npackets > 0) { 150337e3a6d3SLuigi Rizzo D("Ignoring -n argument"); 150437e3a6d3SLuigi Rizzo } 150537e3a6d3SLuigi Rizzo 150637e3a6d3SLuigi Rizzo frame = pkt; 150737e3a6d3SLuigi Rizzo frame += sizeof(pkt->vh) - targ->g->virt_header; 150837e3a6d3SLuigi Rizzo size = targ->g->pkt_size + targ->g->virt_header; 150937e3a6d3SLuigi Rizzo 151037e3a6d3SLuigi Rizzo D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); 151137e3a6d3SLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 151237e3a6d3SLuigi Rizzo goto quit; 151337e3a6d3SLuigi Rizzo 151437e3a6d3SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 151537e3a6d3SLuigi Rizzo if (rate_limit) { 151637e3a6d3SLuigi Rizzo targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); 151737e3a6d3SLuigi Rizzo targ->tic.tv_nsec = 0; 151837e3a6d3SLuigi Rizzo wait_time(targ->tic); 151937e3a6d3SLuigi Rizzo nexttime = targ->tic; 152037e3a6d3SLuigi Rizzo } 152137e3a6d3SLuigi Rizzo 152237e3a6d3SLuigi Rizzo /* Only use the first queue. */ 152337e3a6d3SLuigi Rizzo ring = NETMAP_TXRING(targ->nmd->nifp, targ->nmd->first_tx_ring); 152437e3a6d3SLuigi Rizzo 152537e3a6d3SLuigi Rizzo while (!targ->cancel) { 152637e3a6d3SLuigi Rizzo int64_t limit; 152737e3a6d3SLuigi Rizzo unsigned int space; 152837e3a6d3SLuigi Rizzo unsigned int head; 152937e3a6d3SLuigi Rizzo int fcnt; 153037e3a6d3SLuigi Rizzo 153137e3a6d3SLuigi Rizzo if (!rate_limit) { 153237e3a6d3SLuigi Rizzo budget = targ->g->burst; 153337e3a6d3SLuigi Rizzo 153437e3a6d3SLuigi Rizzo } else if (budget <= 0) { 153537e3a6d3SLuigi Rizzo budget = targ->g->burst; 153637e3a6d3SLuigi Rizzo nexttime = timespec_add(nexttime, targ->g->tx_period); 153737e3a6d3SLuigi Rizzo wait_time(nexttime); 153837e3a6d3SLuigi Rizzo } 153937e3a6d3SLuigi Rizzo 154037e3a6d3SLuigi Rizzo /* wait for available room in the send queue */ 154137e3a6d3SLuigi Rizzo if (poll(&pfd, 1, 2000) <= 0) { 154237e3a6d3SLuigi Rizzo if (targ->cancel) 154337e3a6d3SLuigi Rizzo break; 154437e3a6d3SLuigi Rizzo D("poll error/timeout on queue %d: %s", targ->me, 154537e3a6d3SLuigi Rizzo strerror(errno)); 154637e3a6d3SLuigi Rizzo } 154737e3a6d3SLuigi Rizzo if (pfd.revents & POLLERR) { 154837e3a6d3SLuigi Rizzo D("poll error on %d ring %d-%d", pfd.fd, 154937e3a6d3SLuigi Rizzo targ->nmd->first_tx_ring, targ->nmd->last_tx_ring); 155037e3a6d3SLuigi Rizzo goto quit; 155137e3a6d3SLuigi Rizzo } 155237e3a6d3SLuigi Rizzo 155337e3a6d3SLuigi Rizzo /* If no room poll() again. */ 155437e3a6d3SLuigi Rizzo space = nm_ring_space(ring); 155537e3a6d3SLuigi Rizzo if (!space) { 155637e3a6d3SLuigi Rizzo continue; 155737e3a6d3SLuigi Rizzo } 155837e3a6d3SLuigi Rizzo 155937e3a6d3SLuigi Rizzo limit = budget; 156037e3a6d3SLuigi Rizzo 156137e3a6d3SLuigi Rizzo if (space < limit) { 156237e3a6d3SLuigi Rizzo limit = space; 156337e3a6d3SLuigi Rizzo } 156437e3a6d3SLuigi Rizzo 156537e3a6d3SLuigi Rizzo /* Cut off ``limit`` to make sure is multiple of ``frags``. */ 156637e3a6d3SLuigi Rizzo if (frags > 1) { 156737e3a6d3SLuigi Rizzo limit = (limit / frags) * frags; 156837e3a6d3SLuigi Rizzo } 156937e3a6d3SLuigi Rizzo 157037e3a6d3SLuigi Rizzo limit = sent + limit; /* Convert to absolute. */ 157137e3a6d3SLuigi Rizzo 157237e3a6d3SLuigi Rizzo for (fcnt = frags, head = ring->head; 157337e3a6d3SLuigi Rizzo sent < limit; sent++, sequence++) { 157437e3a6d3SLuigi Rizzo struct netmap_slot *slot = &ring->slot[head]; 157537e3a6d3SLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 157637e3a6d3SLuigi Rizzo 157737e3a6d3SLuigi Rizzo slot->flags = 0; 157837e3a6d3SLuigi Rizzo pkt->body[0] = sequence >> 24; 157937e3a6d3SLuigi Rizzo pkt->body[1] = (sequence >> 16) & 0xff; 158037e3a6d3SLuigi Rizzo pkt->body[2] = (sequence >> 8) & 0xff; 158137e3a6d3SLuigi Rizzo pkt->body[3] = sequence & 0xff; 158237e3a6d3SLuigi Rizzo nm_pkt_copy(frame, p, size); 158337e3a6d3SLuigi Rizzo if (fcnt == frags) { 158437e3a6d3SLuigi Rizzo update_addresses(pkt, targ->g); 158537e3a6d3SLuigi Rizzo } 158637e3a6d3SLuigi Rizzo 158737e3a6d3SLuigi Rizzo if (options & OPT_DUMP) { 158837e3a6d3SLuigi Rizzo dump_payload(p, size, ring, head); 158937e3a6d3SLuigi Rizzo } 159037e3a6d3SLuigi Rizzo 159137e3a6d3SLuigi Rizzo slot->len = size; 159237e3a6d3SLuigi Rizzo 159337e3a6d3SLuigi Rizzo if (--fcnt > 0) { 159437e3a6d3SLuigi Rizzo slot->flags |= NS_MOREFRAG; 159537e3a6d3SLuigi Rizzo } else { 159637e3a6d3SLuigi Rizzo fcnt = frags; 159737e3a6d3SLuigi Rizzo } 159837e3a6d3SLuigi Rizzo 159937e3a6d3SLuigi Rizzo if (sent == limit - 1) { 160037e3a6d3SLuigi Rizzo /* Make sure we don't push an incomplete 160137e3a6d3SLuigi Rizzo * packet. */ 160237e3a6d3SLuigi Rizzo assert(!(slot->flags & NS_MOREFRAG)); 160337e3a6d3SLuigi Rizzo slot->flags |= NS_REPORT; 160437e3a6d3SLuigi Rizzo } 160537e3a6d3SLuigi Rizzo 160637e3a6d3SLuigi Rizzo head = nm_ring_next(ring, head); 160737e3a6d3SLuigi Rizzo if (rate_limit) { 160837e3a6d3SLuigi Rizzo budget--; 160937e3a6d3SLuigi Rizzo } 161037e3a6d3SLuigi Rizzo } 161137e3a6d3SLuigi Rizzo 161237e3a6d3SLuigi Rizzo ring->cur = ring->head = head; 161337e3a6d3SLuigi Rizzo 161437e3a6d3SLuigi Rizzo event ++; 161537e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 161637e3a6d3SLuigi Rizzo targ->ctr.bytes = sent * size; 161737e3a6d3SLuigi Rizzo targ->ctr.events = event; 161837e3a6d3SLuigi Rizzo } 161937e3a6d3SLuigi Rizzo 162037e3a6d3SLuigi Rizzo /* flush any remaining packets */ 162137e3a6d3SLuigi Rizzo D("flush tail %d head %d on thread %p", 162237e3a6d3SLuigi Rizzo ring->tail, ring->head, 162337e3a6d3SLuigi Rizzo (void *)pthread_self()); 162437e3a6d3SLuigi Rizzo ioctl(pfd.fd, NIOCTXSYNC, NULL); 162537e3a6d3SLuigi Rizzo 162637e3a6d3SLuigi Rizzo /* final part: wait the TX queues to become empty. */ 162737e3a6d3SLuigi Rizzo while (!targ->cancel && nm_tx_pending(ring)) { 162837e3a6d3SLuigi Rizzo RD(5, "pending tx tail %d head %d on ring %d", 162937e3a6d3SLuigi Rizzo ring->tail, ring->head, targ->nmd->first_tx_ring); 163037e3a6d3SLuigi Rizzo ioctl(pfd.fd, NIOCTXSYNC, NULL); 163137e3a6d3SLuigi Rizzo usleep(1); /* wait 1 tick */ 163237e3a6d3SLuigi Rizzo } 163337e3a6d3SLuigi Rizzo 163437e3a6d3SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 163537e3a6d3SLuigi Rizzo targ->completed = 1; 163637e3a6d3SLuigi Rizzo targ->ctr.pkts = sent; 163737e3a6d3SLuigi Rizzo targ->ctr.bytes = sent * size; 163837e3a6d3SLuigi Rizzo targ->ctr.events = event; 163937e3a6d3SLuigi Rizzo quit: 164037e3a6d3SLuigi Rizzo /* reset the ``used`` flag. */ 164137e3a6d3SLuigi Rizzo targ->used = 0; 164237e3a6d3SLuigi Rizzo 164337e3a6d3SLuigi Rizzo return (NULL); 164437e3a6d3SLuigi Rizzo } 164537e3a6d3SLuigi Rizzo 164637e3a6d3SLuigi Rizzo 164737e3a6d3SLuigi Rizzo static char * 164837e3a6d3SLuigi Rizzo multi_slot_to_string(struct netmap_ring *ring, unsigned int head, 164937e3a6d3SLuigi Rizzo unsigned int nfrags, char *strbuf, size_t strbuflen) 165037e3a6d3SLuigi Rizzo { 165137e3a6d3SLuigi Rizzo unsigned int f; 165237e3a6d3SLuigi Rizzo char *ret = strbuf; 165337e3a6d3SLuigi Rizzo 165437e3a6d3SLuigi Rizzo for (f = 0; f < nfrags; f++) { 165537e3a6d3SLuigi Rizzo struct netmap_slot *slot = &ring->slot[head]; 165637e3a6d3SLuigi Rizzo int m = snprintf(strbuf, strbuflen, "|%u,%x|", slot->len, 165737e3a6d3SLuigi Rizzo slot->flags); 165837e3a6d3SLuigi Rizzo if (m >= (int)strbuflen) { 165937e3a6d3SLuigi Rizzo break; 166037e3a6d3SLuigi Rizzo } 166137e3a6d3SLuigi Rizzo strbuf += m; 166237e3a6d3SLuigi Rizzo strbuflen -= m; 166337e3a6d3SLuigi Rizzo 166437e3a6d3SLuigi Rizzo head = nm_ring_next(ring, head); 166537e3a6d3SLuigi Rizzo } 166637e3a6d3SLuigi Rizzo 166737e3a6d3SLuigi Rizzo return ret; 166837e3a6d3SLuigi Rizzo } 166937e3a6d3SLuigi Rizzo 167037e3a6d3SLuigi Rizzo static void * 167137e3a6d3SLuigi Rizzo rxseq_body(void *data) 167237e3a6d3SLuigi Rizzo { 167337e3a6d3SLuigi Rizzo struct targ *targ = (struct targ *) data; 167437e3a6d3SLuigi Rizzo struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; 167537e3a6d3SLuigi Rizzo int dump = targ->g->options & OPT_DUMP; 167637e3a6d3SLuigi Rizzo struct netmap_ring *ring; 167737e3a6d3SLuigi Rizzo unsigned int frags_exp = 1; 167837e3a6d3SLuigi Rizzo uint32_t seq_exp = 0; 167937e3a6d3SLuigi Rizzo struct my_ctrs cur; 168037e3a6d3SLuigi Rizzo unsigned int frags = 0; 168137e3a6d3SLuigi Rizzo int first_packet = 1; 168237e3a6d3SLuigi Rizzo int first_slot = 1; 168337e3a6d3SLuigi Rizzo int i; 168437e3a6d3SLuigi Rizzo 168537e3a6d3SLuigi Rizzo cur.pkts = cur.bytes = cur.events = cur.min_space = 0; 168637e3a6d3SLuigi Rizzo cur.t.tv_usec = cur.t.tv_sec = 0; // unused, just silence the compiler 168737e3a6d3SLuigi Rizzo 168837e3a6d3SLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 168937e3a6d3SLuigi Rizzo goto quit; 169037e3a6d3SLuigi Rizzo 169137e3a6d3SLuigi Rizzo D("reading from %s fd %d main_fd %d", 169237e3a6d3SLuigi Rizzo targ->g->ifname, targ->fd, targ->g->main_fd); 169337e3a6d3SLuigi Rizzo /* unbounded wait for the first packet. */ 169437e3a6d3SLuigi Rizzo for (;!targ->cancel;) { 169537e3a6d3SLuigi Rizzo i = poll(&pfd, 1, 1000); 169637e3a6d3SLuigi Rizzo if (i > 0 && !(pfd.revents & POLLERR)) 169737e3a6d3SLuigi Rizzo break; 169837e3a6d3SLuigi Rizzo RD(1, "waiting for initial packets, poll returns %d %d", 169937e3a6d3SLuigi Rizzo i, pfd.revents); 170037e3a6d3SLuigi Rizzo } 170137e3a6d3SLuigi Rizzo 170237e3a6d3SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); 170337e3a6d3SLuigi Rizzo 170437e3a6d3SLuigi Rizzo ring = NETMAP_RXRING(targ->nmd->nifp, targ->nmd->first_rx_ring); 170537e3a6d3SLuigi Rizzo 170637e3a6d3SLuigi Rizzo while (!targ->cancel) { 170737e3a6d3SLuigi Rizzo unsigned int head; 170837e3a6d3SLuigi Rizzo uint32_t seq; 170937e3a6d3SLuigi Rizzo int limit; 171037e3a6d3SLuigi Rizzo 171137e3a6d3SLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 171237e3a6d3SLuigi Rizzo before quitting. */ 1713f0ea3689SLuigi Rizzo if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { 17141cb4c501SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 17158ce070c1SUlrich Spörlein targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 1716f0ea3689SLuigi Rizzo goto out; 171768b8534bSLuigi Rizzo } 171868b8534bSLuigi Rizzo 1719f0ea3689SLuigi Rizzo if (pfd.revents & POLLERR) { 172017885a7bSLuigi Rizzo D("poll err"); 172117885a7bSLuigi Rizzo goto quit; 172217885a7bSLuigi Rizzo } 172317885a7bSLuigi Rizzo 172437e3a6d3SLuigi Rizzo if (nm_ring_empty(ring)) 172568b8534bSLuigi Rizzo continue; 172668b8534bSLuigi Rizzo 172737e3a6d3SLuigi Rizzo limit = nm_ring_space(ring); 172837e3a6d3SLuigi Rizzo if (limit > targ->g->burst) 172937e3a6d3SLuigi Rizzo limit = targ->g->burst; 173037e3a6d3SLuigi Rizzo 173137e3a6d3SLuigi Rizzo #if 0 173237e3a6d3SLuigi Rizzo /* Enable this if 173337e3a6d3SLuigi Rizzo * 1) we remove the early-return optimization from 173437e3a6d3SLuigi Rizzo * the netmap poll implementation, or 173537e3a6d3SLuigi Rizzo * 2) pipes get NS_MOREFRAG support. 173637e3a6d3SLuigi Rizzo * With the current netmap implementation, an experiment like 173737e3a6d3SLuigi Rizzo * pkt-gen -i vale:1{1 -f txseq -F 9 173837e3a6d3SLuigi Rizzo * pkt-gen -i vale:1}1 -f rxseq 173937e3a6d3SLuigi Rizzo * would get stuck as soon as we find nm_ring_space(ring) < 9, 174037e3a6d3SLuigi Rizzo * since here limit is rounded to 0 and 174137e3a6d3SLuigi Rizzo * pipe rxsync is not called anymore by the poll() of this loop. 174237e3a6d3SLuigi Rizzo */ 174337e3a6d3SLuigi Rizzo if (frags_exp > 1) { 174437e3a6d3SLuigi Rizzo int o = limit; 174537e3a6d3SLuigi Rizzo /* Cut off to the closest smaller multiple. */ 174637e3a6d3SLuigi Rizzo limit = (limit / frags_exp) * frags_exp; 174737e3a6d3SLuigi Rizzo RD(2, "LIMIT %d --> %d", o, limit); 174868b8534bSLuigi Rizzo } 174937e3a6d3SLuigi Rizzo #endif 175037e3a6d3SLuigi Rizzo 175137e3a6d3SLuigi Rizzo for (head = ring->head, i = 0; i < limit; i++) { 175237e3a6d3SLuigi Rizzo struct netmap_slot *slot = &ring->slot[head]; 175337e3a6d3SLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 175437e3a6d3SLuigi Rizzo int len = slot->len; 175537e3a6d3SLuigi Rizzo struct pkt *pkt; 175637e3a6d3SLuigi Rizzo 175737e3a6d3SLuigi Rizzo if (dump) { 175837e3a6d3SLuigi Rizzo dump_payload(p, slot->len, ring, head); 175968b8534bSLuigi Rizzo } 176037e3a6d3SLuigi Rizzo 176137e3a6d3SLuigi Rizzo frags++; 176237e3a6d3SLuigi Rizzo if (!(slot->flags & NS_MOREFRAG)) { 176337e3a6d3SLuigi Rizzo if (first_packet) { 176437e3a6d3SLuigi Rizzo first_packet = 0; 176537e3a6d3SLuigi Rizzo } else if (frags != frags_exp) { 176637e3a6d3SLuigi Rizzo char prbuf[512]; 176737e3a6d3SLuigi Rizzo RD(1, "Received packets with %u frags, " 176837e3a6d3SLuigi Rizzo "expected %u, '%s'", frags, frags_exp, 176937e3a6d3SLuigi Rizzo multi_slot_to_string(ring, head-frags+1, frags, 177037e3a6d3SLuigi Rizzo prbuf, sizeof(prbuf))); 177137e3a6d3SLuigi Rizzo } 177237e3a6d3SLuigi Rizzo first_packet = 0; 177337e3a6d3SLuigi Rizzo frags_exp = frags; 177437e3a6d3SLuigi Rizzo frags = 0; 177537e3a6d3SLuigi Rizzo } 177637e3a6d3SLuigi Rizzo 177737e3a6d3SLuigi Rizzo p -= sizeof(pkt->vh) - targ->g->virt_header; 177837e3a6d3SLuigi Rizzo len += sizeof(pkt->vh) - targ->g->virt_header; 177937e3a6d3SLuigi Rizzo pkt = (struct pkt *)p; 178037e3a6d3SLuigi Rizzo 178137e3a6d3SLuigi Rizzo if ((char *)pkt + len < ((char *)pkt->body) + sizeof(seq)) { 178237e3a6d3SLuigi Rizzo RD(1, "%s: packet too small (len=%u)", __func__, 178337e3a6d3SLuigi Rizzo slot->len); 178437e3a6d3SLuigi Rizzo } else { 178537e3a6d3SLuigi Rizzo seq = (pkt->body[0] << 24) | (pkt->body[1] << 16) 178637e3a6d3SLuigi Rizzo | (pkt->body[2] << 8) | pkt->body[3]; 178737e3a6d3SLuigi Rizzo if (first_slot) { 178837e3a6d3SLuigi Rizzo /* Grab the first one, whatever it 178937e3a6d3SLuigi Rizzo is. */ 179037e3a6d3SLuigi Rizzo seq_exp = seq; 179137e3a6d3SLuigi Rizzo first_slot = 0; 179237e3a6d3SLuigi Rizzo } else if (seq != seq_exp) { 179337e3a6d3SLuigi Rizzo uint32_t delta = seq - seq_exp; 179437e3a6d3SLuigi Rizzo 179537e3a6d3SLuigi Rizzo if (delta < (0xFFFFFFFF >> 1)) { 179637e3a6d3SLuigi Rizzo RD(2, "Sequence GAP: exp %u found %u", 179737e3a6d3SLuigi Rizzo seq_exp, seq); 179837e3a6d3SLuigi Rizzo } else { 179937e3a6d3SLuigi Rizzo RD(2, "Sequence OUT OF ORDER: " 180037e3a6d3SLuigi Rizzo "exp %u found %u", seq_exp, seq); 180137e3a6d3SLuigi Rizzo } 180237e3a6d3SLuigi Rizzo seq_exp = seq; 180337e3a6d3SLuigi Rizzo } 180437e3a6d3SLuigi Rizzo seq_exp++; 180537e3a6d3SLuigi Rizzo } 180637e3a6d3SLuigi Rizzo 180737e3a6d3SLuigi Rizzo cur.bytes += slot->len; 180837e3a6d3SLuigi Rizzo head = nm_ring_next(ring, head); 180937e3a6d3SLuigi Rizzo cur.pkts++; 181037e3a6d3SLuigi Rizzo } 181137e3a6d3SLuigi Rizzo 181237e3a6d3SLuigi Rizzo ring->cur = ring->head = head; 181337e3a6d3SLuigi Rizzo 181437e3a6d3SLuigi Rizzo cur.events++; 181537e3a6d3SLuigi Rizzo targ->ctr = cur; 181668b8534bSLuigi Rizzo } 181768b8534bSLuigi Rizzo 1818f0ea3689SLuigi Rizzo clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); 1819f0ea3689SLuigi Rizzo 1820f0ea3689SLuigi Rizzo out: 182168b8534bSLuigi Rizzo targ->completed = 1; 182237e3a6d3SLuigi Rizzo targ->ctr = cur; 182368b8534bSLuigi Rizzo 182468b8534bSLuigi Rizzo quit: 182568b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 182668b8534bSLuigi Rizzo targ->used = 0; 182768b8534bSLuigi Rizzo 182868b8534bSLuigi Rizzo return (NULL); 182968b8534bSLuigi Rizzo } 183068b8534bSLuigi Rizzo 183166a698c9SEd Maste 183268b8534bSLuigi Rizzo static void 183337e3a6d3SLuigi Rizzo tx_output(struct my_ctrs *cur, double delta, const char *msg) 183468b8534bSLuigi Rizzo { 183537e3a6d3SLuigi Rizzo double bw, raw_bw, pps, abs; 1836f8e4e36aSLuigi Rizzo char b1[40], b2[80], b3[80]; 183737e3a6d3SLuigi Rizzo int size; 183868b8534bSLuigi Rizzo 183937e3a6d3SLuigi Rizzo if (cur->pkts == 0) { 184037e3a6d3SLuigi Rizzo printf("%s nothing.\n", msg); 184137e3a6d3SLuigi Rizzo return; 184237e3a6d3SLuigi Rizzo } 184337e3a6d3SLuigi Rizzo 184437e3a6d3SLuigi Rizzo size = (int)(cur->bytes / cur->pkts); 184537e3a6d3SLuigi Rizzo 184637e3a6d3SLuigi Rizzo printf("%s %llu packets %llu bytes %llu events %d bytes each in %.2f seconds.\n", 184737e3a6d3SLuigi Rizzo msg, 184837e3a6d3SLuigi Rizzo (unsigned long long)cur->pkts, 184937e3a6d3SLuigi Rizzo (unsigned long long)cur->bytes, 185037e3a6d3SLuigi Rizzo (unsigned long long)cur->events, size, delta); 1851f8e4e36aSLuigi Rizzo if (delta == 0) 1852f8e4e36aSLuigi Rizzo delta = 1e-6; 1853f8e4e36aSLuigi Rizzo if (size < 60) /* correct for min packet size */ 1854f8e4e36aSLuigi Rizzo size = 60; 185537e3a6d3SLuigi Rizzo pps = cur->pkts / delta; 185637e3a6d3SLuigi Rizzo bw = (8.0 * cur->bytes) / delta; 1857f8e4e36aSLuigi Rizzo /* raw packets have4 bytes crc + 20 bytes framing */ 185837e3a6d3SLuigi Rizzo raw_bw = (8.0 * (cur->pkts * 24 + cur->bytes)) / delta; 185937e3a6d3SLuigi Rizzo abs = cur->pkts / (double)(cur->events); 186066a698c9SEd Maste 186137e3a6d3SLuigi Rizzo printf("Speed: %spps Bandwidth: %sbps (raw %sbps). Average batch: %.2f pkts\n", 186237e3a6d3SLuigi Rizzo norm(b1, pps), norm(b2, bw), norm(b3, raw_bw), abs); 186368b8534bSLuigi Rizzo } 186468b8534bSLuigi Rizzo 186568b8534bSLuigi Rizzo static void 186668b8534bSLuigi Rizzo usage(void) 186768b8534bSLuigi Rizzo { 186868b8534bSLuigi Rizzo const char *cmd = "pkt-gen"; 186968b8534bSLuigi Rizzo fprintf(stderr, 187068b8534bSLuigi Rizzo "Usage:\n" 187168b8534bSLuigi Rizzo "%s arguments\n" 187268b8534bSLuigi Rizzo "\t-i interface interface name\n" 187337e3a6d3SLuigi Rizzo "\t-f function tx rx ping pong txseq rxseq\n" 1874f8e4e36aSLuigi Rizzo "\t-n count number of iterations (can be 0)\n" 1875f8e4e36aSLuigi Rizzo "\t-t pkts_to_send also forces tx mode\n" 1876f8e4e36aSLuigi Rizzo "\t-r pkts_to_receive also forces rx mode\n" 1877ce3ee1e7SLuigi Rizzo "\t-l pkt_size in bytes excluding CRC\n" 1878ce3ee1e7SLuigi Rizzo "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" 1879ce3ee1e7SLuigi Rizzo "\t-s src_ip[:port[-src_ip:port]] single or range\n" 1880ce3ee1e7SLuigi Rizzo "\t-D dst-mac\n" 1881ce3ee1e7SLuigi Rizzo "\t-S src-mac\n" 1882f8e4e36aSLuigi Rizzo "\t-a cpu_id use setaffinity\n" 188368b8534bSLuigi Rizzo "\t-b burst size testing, mostly\n" 188468b8534bSLuigi Rizzo "\t-c cores cores to use\n" 188568b8534bSLuigi Rizzo "\t-p threads processes/threads to use\n" 188668b8534bSLuigi Rizzo "\t-T report_ms milliseconds between reports\n" 188768b8534bSLuigi Rizzo "\t-w wait_for_link_time in seconds\n" 1888ce3ee1e7SLuigi Rizzo "\t-R rate in packets per second\n" 1889ce3ee1e7SLuigi Rizzo "\t-X dump payload\n" 189017885a7bSLuigi Rizzo "\t-H len add empty virtio-net-header with size 'len'\n" 189137e3a6d3SLuigi Rizzo "\t-E pipes allocate extra space for a number of pipes\n" 189237e3a6d3SLuigi Rizzo "\t-r do not touch the buffers (send rubbish)\n" 189356717743SAdrian Chadd "\t-P file load packet from pcap file\n" 189456717743SAdrian Chadd "\t-z use random IPv4 src address/port\n" 189556717743SAdrian Chadd "\t-Z use random IPv4 dst address/port\n" 189637e3a6d3SLuigi Rizzo "\t-F num_frags send multi-slot packets\n" 189737e3a6d3SLuigi Rizzo "\t-A activate pps stats on receiver\n" 189868b8534bSLuigi Rizzo "", 189968b8534bSLuigi Rizzo cmd); 190068b8534bSLuigi Rizzo 190168b8534bSLuigi Rizzo exit(0); 190268b8534bSLuigi Rizzo } 190368b8534bSLuigi Rizzo 190437e3a6d3SLuigi Rizzo enum { 190537e3a6d3SLuigi Rizzo TD_TYPE_SENDER = 1, 190637e3a6d3SLuigi Rizzo TD_TYPE_RECEIVER, 190737e3a6d3SLuigi Rizzo TD_TYPE_OTHER, 190837e3a6d3SLuigi Rizzo }; 190937e3a6d3SLuigi Rizzo 1910f8e4e36aSLuigi Rizzo static void 1911f8e4e36aSLuigi Rizzo start_threads(struct glob_arg *g) 1912f8e4e36aSLuigi Rizzo { 1913f8e4e36aSLuigi Rizzo int i; 1914f8e4e36aSLuigi Rizzo 1915f8e4e36aSLuigi Rizzo targs = calloc(g->nthreads, sizeof(*targs)); 1916f8e4e36aSLuigi Rizzo /* 1917f8e4e36aSLuigi Rizzo * Now create the desired number of threads, each one 1918f8e4e36aSLuigi Rizzo * using a single descriptor. 1919f8e4e36aSLuigi Rizzo */ 1920f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 1921f0ea3689SLuigi Rizzo struct targ *t = &targs[i]; 1922f0ea3689SLuigi Rizzo 1923f0ea3689SLuigi Rizzo bzero(t, sizeof(*t)); 1924f0ea3689SLuigi Rizzo t->fd = -1; /* default, with pcap */ 1925f0ea3689SLuigi Rizzo t->g = g; 1926f8e4e36aSLuigi Rizzo 1927f8e4e36aSLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 1928f0ea3689SLuigi Rizzo struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ 19294bf50f18SLuigi Rizzo uint64_t nmd_flags = 0; 19304bf50f18SLuigi Rizzo nmd.self = &nmd; 1931f8e4e36aSLuigi Rizzo 193237e3a6d3SLuigi Rizzo if (i > 0) { 193337e3a6d3SLuigi Rizzo /* the first thread uses the fd opened by the main 193437e3a6d3SLuigi Rizzo * thread, the other threads re-open /dev/netmap 193537e3a6d3SLuigi Rizzo */ 1936f0ea3689SLuigi Rizzo if (g->nthreads > 1) { 193737e3a6d3SLuigi Rizzo nmd.req.nr_flags = 193837e3a6d3SLuigi Rizzo g->nmd->req.nr_flags & ~NR_REG_MASK; 193937e3a6d3SLuigi Rizzo nmd.req.nr_flags |= NR_REG_ONE_NIC; 1940f0ea3689SLuigi Rizzo nmd.req.nr_ringid = i; 194117885a7bSLuigi Rizzo } 1942f0ea3689SLuigi Rizzo /* Only touch one of the rings (rx is already ok) */ 194337e3a6d3SLuigi Rizzo if (g->td_type == TD_TYPE_RECEIVER) 19444bf50f18SLuigi Rizzo nmd_flags |= NETMAP_NO_TX_POLL; 1945f8e4e36aSLuigi Rizzo 1946f0ea3689SLuigi Rizzo /* register interface. Override ifname and ringid etc. */ 19474bf50f18SLuigi Rizzo t->nmd = nm_open(t->g->ifname, NULL, nmd_flags | 19484bf50f18SLuigi Rizzo NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); 1949f0ea3689SLuigi Rizzo if (t->nmd == NULL) { 1950f0ea3689SLuigi Rizzo D("Unable to open %s: %s", 1951f0ea3689SLuigi Rizzo t->g->ifname, strerror(errno)); 1952f8e4e36aSLuigi Rizzo continue; 1953f8e4e36aSLuigi Rizzo } 195437e3a6d3SLuigi Rizzo } else { 195537e3a6d3SLuigi Rizzo t->nmd = g->nmd; 195637e3a6d3SLuigi Rizzo } 1957f0ea3689SLuigi Rizzo t->fd = t->nmd->fd; 1958f0ea3689SLuigi Rizzo 1959f8e4e36aSLuigi Rizzo } else { 1960f8e4e36aSLuigi Rizzo targs[i].fd = g->main_fd; 1961f8e4e36aSLuigi Rizzo } 1962f0ea3689SLuigi Rizzo t->used = 1; 1963f0ea3689SLuigi Rizzo t->me = i; 1964f8e4e36aSLuigi Rizzo if (g->affinity >= 0) { 196537e3a6d3SLuigi Rizzo t->affinity = (g->affinity + i) % g->system_cpus; 1966f0ea3689SLuigi Rizzo } else { 1967f0ea3689SLuigi Rizzo t->affinity = -1; 1968f0ea3689SLuigi Rizzo } 1969f8e4e36aSLuigi Rizzo /* default, init packets */ 1970f0ea3689SLuigi Rizzo initialize_packet(t); 1971f8e4e36aSLuigi Rizzo 1972f0ea3689SLuigi Rizzo if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { 197317885a7bSLuigi Rizzo D("Unable to create thread %d: %s", i, strerror(errno)); 1974f0ea3689SLuigi Rizzo t->used = 0; 1975f8e4e36aSLuigi Rizzo } 1976f8e4e36aSLuigi Rizzo } 1977f8e4e36aSLuigi Rizzo } 1978f8e4e36aSLuigi Rizzo 1979f8e4e36aSLuigi Rizzo static void 1980f8e4e36aSLuigi Rizzo main_thread(struct glob_arg *g) 1981f8e4e36aSLuigi Rizzo { 1982f8e4e36aSLuigi Rizzo int i; 1983f8e4e36aSLuigi Rizzo 198437e3a6d3SLuigi Rizzo struct my_ctrs prev, cur; 1985f8e4e36aSLuigi Rizzo double delta_t; 1986f8e4e36aSLuigi Rizzo struct timeval tic, toc; 1987f8e4e36aSLuigi Rizzo 198837e3a6d3SLuigi Rizzo prev.pkts = prev.bytes = prev.events = 0; 198937e3a6d3SLuigi Rizzo gettimeofday(&prev.t, NULL); 1990f8e4e36aSLuigi Rizzo for (;;) { 199137e3a6d3SLuigi Rizzo char b1[40], b2[40], b3[40], b4[70]; 199237e3a6d3SLuigi Rizzo uint64_t pps, usec; 199337e3a6d3SLuigi Rizzo struct my_ctrs x; 199437e3a6d3SLuigi Rizzo double abs; 1995f8e4e36aSLuigi Rizzo int done = 0; 1996f8e4e36aSLuigi Rizzo 199737e3a6d3SLuigi Rizzo usec = wait_for_next_report(&prev.t, &cur.t, 199837e3a6d3SLuigi Rizzo g->report_interval); 199937e3a6d3SLuigi Rizzo 200037e3a6d3SLuigi Rizzo cur.pkts = cur.bytes = cur.events = 0; 200137e3a6d3SLuigi Rizzo cur.min_space = 0; 200237e3a6d3SLuigi Rizzo if (usec < 10000) /* too short to be meaningful */ 200337e3a6d3SLuigi Rizzo continue; 200437e3a6d3SLuigi Rizzo /* accumulate counts for all threads */ 2005f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 200637e3a6d3SLuigi Rizzo cur.pkts += targs[i].ctr.pkts; 200737e3a6d3SLuigi Rizzo cur.bytes += targs[i].ctr.bytes; 200837e3a6d3SLuigi Rizzo cur.events += targs[i].ctr.events; 200937e3a6d3SLuigi Rizzo cur.min_space += targs[i].ctr.min_space; 201037e3a6d3SLuigi Rizzo targs[i].ctr.min_space = 99999; 2011f8e4e36aSLuigi Rizzo if (targs[i].used == 0) 2012f8e4e36aSLuigi Rizzo done++; 2013f8e4e36aSLuigi Rizzo } 201437e3a6d3SLuigi Rizzo x.pkts = cur.pkts - prev.pkts; 201537e3a6d3SLuigi Rizzo x.bytes = cur.bytes - prev.bytes; 201637e3a6d3SLuigi Rizzo x.events = cur.events - prev.events; 201737e3a6d3SLuigi Rizzo pps = (x.pkts*1000000 + usec/2) / usec; 201837e3a6d3SLuigi Rizzo abs = (x.events > 0) ? (x.pkts / (double) x.events) : 0; 201937e3a6d3SLuigi Rizzo 202037e3a6d3SLuigi Rizzo if (!(g->options & OPT_PPS_STATS)) { 202137e3a6d3SLuigi Rizzo strcpy(b4, ""); 202237e3a6d3SLuigi Rizzo } else { 202337e3a6d3SLuigi Rizzo /* Compute some pps stats using a sliding window. */ 202437e3a6d3SLuigi Rizzo double ppsavg = 0.0, ppsdev = 0.0; 202537e3a6d3SLuigi Rizzo int nsamples = 0; 202637e3a6d3SLuigi Rizzo 202737e3a6d3SLuigi Rizzo g->win[g->win_idx] = pps; 202837e3a6d3SLuigi Rizzo g->win_idx = (g->win_idx + 1) % STATS_WIN; 202937e3a6d3SLuigi Rizzo 203037e3a6d3SLuigi Rizzo for (i = 0; i < STATS_WIN; i++) { 203137e3a6d3SLuigi Rizzo ppsavg += g->win[i]; 203237e3a6d3SLuigi Rizzo if (g->win[i]) { 203337e3a6d3SLuigi Rizzo nsamples ++; 203437e3a6d3SLuigi Rizzo } 203537e3a6d3SLuigi Rizzo } 203637e3a6d3SLuigi Rizzo ppsavg /= nsamples; 203737e3a6d3SLuigi Rizzo 203837e3a6d3SLuigi Rizzo for (i = 0; i < STATS_WIN; i++) { 203937e3a6d3SLuigi Rizzo if (g->win[i] == 0) { 2040f8e4e36aSLuigi Rizzo continue; 204137e3a6d3SLuigi Rizzo } 204237e3a6d3SLuigi Rizzo ppsdev += (g->win[i] - ppsavg) * (g->win[i] - ppsavg); 204337e3a6d3SLuigi Rizzo } 204437e3a6d3SLuigi Rizzo ppsdev /= nsamples; 204537e3a6d3SLuigi Rizzo ppsdev = sqrt(ppsdev); 204637e3a6d3SLuigi Rizzo 204737e3a6d3SLuigi Rizzo snprintf(b4, sizeof(b4), "[avg/std %s/%s pps]", 204837e3a6d3SLuigi Rizzo norm(b1, ppsavg), norm(b2, ppsdev)); 204937e3a6d3SLuigi Rizzo } 205037e3a6d3SLuigi Rizzo 205137e3a6d3SLuigi Rizzo D("%spps %s(%spkts %sbps in %llu usec) %.2f avg_batch %d min_space", 205237e3a6d3SLuigi Rizzo norm(b1, pps), b4, 205337e3a6d3SLuigi Rizzo norm(b2, (double)x.pkts), 205437e3a6d3SLuigi Rizzo norm(b3, (double)x.bytes*8), 205537e3a6d3SLuigi Rizzo (unsigned long long)usec, 205637e3a6d3SLuigi Rizzo abs, (int)cur.min_space); 205737e3a6d3SLuigi Rizzo prev = cur; 205837e3a6d3SLuigi Rizzo 2059f8e4e36aSLuigi Rizzo if (done == g->nthreads) 2060f8e4e36aSLuigi Rizzo break; 2061f8e4e36aSLuigi Rizzo } 2062f8e4e36aSLuigi Rizzo 2063f8e4e36aSLuigi Rizzo timerclear(&tic); 2064f8e4e36aSLuigi Rizzo timerclear(&toc); 206537e3a6d3SLuigi Rizzo cur.pkts = cur.bytes = cur.events = 0; 206637e3a6d3SLuigi Rizzo /* final round */ 2067f8e4e36aSLuigi Rizzo for (i = 0; i < g->nthreads; i++) { 20681cb4c501SLuigi Rizzo struct timespec t_tic, t_toc; 2069f8e4e36aSLuigi Rizzo /* 2070f8e4e36aSLuigi Rizzo * Join active threads, unregister interfaces and close 2071f8e4e36aSLuigi Rizzo * file descriptors. 2072f8e4e36aSLuigi Rizzo */ 20731cb4c501SLuigi Rizzo if (targs[i].used) 207437e3a6d3SLuigi Rizzo pthread_join(targs[i].thread, NULL); /* blocking */ 207537e3a6d3SLuigi Rizzo if (g->dev_type == DEV_NETMAP) { 207637e3a6d3SLuigi Rizzo nm_close(targs[i].nmd); 207737e3a6d3SLuigi Rizzo targs[i].nmd = NULL; 207837e3a6d3SLuigi Rizzo } else { 2079f8e4e36aSLuigi Rizzo close(targs[i].fd); 208037e3a6d3SLuigi Rizzo } 2081f8e4e36aSLuigi Rizzo 2082f8e4e36aSLuigi Rizzo if (targs[i].completed == 0) 2083f8e4e36aSLuigi Rizzo D("ouch, thread %d exited with error", i); 2084f8e4e36aSLuigi Rizzo 2085f8e4e36aSLuigi Rizzo /* 2086f8e4e36aSLuigi Rizzo * Collect threads output and extract information about 2087f8e4e36aSLuigi Rizzo * how long it took to send all the packets. 2088f8e4e36aSLuigi Rizzo */ 208937e3a6d3SLuigi Rizzo cur.pkts += targs[i].ctr.pkts; 209037e3a6d3SLuigi Rizzo cur.bytes += targs[i].ctr.bytes; 209137e3a6d3SLuigi Rizzo cur.events += targs[i].ctr.events; 209237e3a6d3SLuigi Rizzo /* collect the largest start (tic) and end (toc) times, 209337e3a6d3SLuigi Rizzo * XXX maybe we should do the earliest tic, or do a weighted 209437e3a6d3SLuigi Rizzo * average ? 209537e3a6d3SLuigi Rizzo */ 20961cb4c501SLuigi Rizzo t_tic = timeval2spec(&tic); 20971cb4c501SLuigi Rizzo t_toc = timeval2spec(&toc); 20981cb4c501SLuigi Rizzo if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) 20991cb4c501SLuigi Rizzo tic = timespec2val(&targs[i].tic); 21001cb4c501SLuigi Rizzo if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) 21011cb4c501SLuigi Rizzo toc = timespec2val(&targs[i].toc); 2102f8e4e36aSLuigi Rizzo } 2103f8e4e36aSLuigi Rizzo 2104f8e4e36aSLuigi Rizzo /* print output. */ 2105f8e4e36aSLuigi Rizzo timersub(&toc, &tic, &toc); 2106f8e4e36aSLuigi Rizzo delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 210737e3a6d3SLuigi Rizzo if (g->td_type == TD_TYPE_SENDER) 210837e3a6d3SLuigi Rizzo tx_output(&cur, delta_t, "Sent"); 2109f8e4e36aSLuigi Rizzo else 211037e3a6d3SLuigi Rizzo tx_output(&cur, delta_t, "Received"); 2111f8e4e36aSLuigi Rizzo } 2112f8e4e36aSLuigi Rizzo 211337e3a6d3SLuigi Rizzo struct td_desc { 211437e3a6d3SLuigi Rizzo int ty; 2115f8e4e36aSLuigi Rizzo char *key; 2116f8e4e36aSLuigi Rizzo void *f; 2117f8e4e36aSLuigi Rizzo }; 2118f8e4e36aSLuigi Rizzo 211937e3a6d3SLuigi Rizzo static struct td_desc func[] = { 212037e3a6d3SLuigi Rizzo { TD_TYPE_SENDER, "tx", sender_body }, 212137e3a6d3SLuigi Rizzo { TD_TYPE_RECEIVER, "rx", receiver_body }, 212237e3a6d3SLuigi Rizzo { TD_TYPE_OTHER, "ping", pinger_body }, 212337e3a6d3SLuigi Rizzo { TD_TYPE_OTHER, "pong", ponger_body }, 212437e3a6d3SLuigi Rizzo { TD_TYPE_SENDER, "txseq", txseq_body }, 212537e3a6d3SLuigi Rizzo { TD_TYPE_RECEIVER, "rxseq", rxseq_body }, 212637e3a6d3SLuigi Rizzo { 0, NULL, NULL } 2127f8e4e36aSLuigi Rizzo }; 2128f8e4e36aSLuigi Rizzo 2129f8e4e36aSLuigi Rizzo static int 2130f8e4e36aSLuigi Rizzo tap_alloc(char *dev) 2131f8e4e36aSLuigi Rizzo { 2132f8e4e36aSLuigi Rizzo struct ifreq ifr; 2133f8e4e36aSLuigi Rizzo int fd, err; 2134f8e4e36aSLuigi Rizzo char *clonedev = TAP_CLONEDEV; 2135f8e4e36aSLuigi Rizzo 2136f8e4e36aSLuigi Rizzo (void)err; 2137f8e4e36aSLuigi Rizzo (void)dev; 2138f8e4e36aSLuigi Rizzo /* Arguments taken by the function: 2139f8e4e36aSLuigi Rizzo * 2140f8e4e36aSLuigi Rizzo * char *dev: the name of an interface (or '\0'). MUST have enough 2141f8e4e36aSLuigi Rizzo * space to hold the interface name if '\0' is passed 2142f8e4e36aSLuigi Rizzo * int flags: interface flags (eg, IFF_TUN etc.) 2143f8e4e36aSLuigi Rizzo */ 2144f8e4e36aSLuigi Rizzo 2145f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__ 2146f8e4e36aSLuigi Rizzo if (dev[3]) { /* tapSomething */ 2147f8e4e36aSLuigi Rizzo static char buf[128]; 2148f8e4e36aSLuigi Rizzo snprintf(buf, sizeof(buf), "/dev/%s", dev); 2149f8e4e36aSLuigi Rizzo clonedev = buf; 2150f8e4e36aSLuigi Rizzo } 2151f8e4e36aSLuigi Rizzo #endif 2152f8e4e36aSLuigi Rizzo /* open the device */ 2153f8e4e36aSLuigi Rizzo if( (fd = open(clonedev, O_RDWR)) < 0 ) { 2154f8e4e36aSLuigi Rizzo return fd; 2155f8e4e36aSLuigi Rizzo } 2156f8e4e36aSLuigi Rizzo D("%s open successful", clonedev); 2157f8e4e36aSLuigi Rizzo 2158f8e4e36aSLuigi Rizzo /* preparation of the struct ifr, of type "struct ifreq" */ 2159f8e4e36aSLuigi Rizzo memset(&ifr, 0, sizeof(ifr)); 2160f8e4e36aSLuigi Rizzo 2161f8e4e36aSLuigi Rizzo #ifdef linux 2162f8e4e36aSLuigi Rizzo ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 2163f8e4e36aSLuigi Rizzo 2164f8e4e36aSLuigi Rizzo if (*dev) { 2165f8e4e36aSLuigi Rizzo /* if a device name was specified, put it in the structure; otherwise, 2166f8e4e36aSLuigi Rizzo * the kernel will try to allocate the "next" device of the 2167f8e4e36aSLuigi Rizzo * specified type */ 2168f8e4e36aSLuigi Rizzo strncpy(ifr.ifr_name, dev, IFNAMSIZ); 2169f8e4e36aSLuigi Rizzo } 2170f8e4e36aSLuigi Rizzo 2171f8e4e36aSLuigi Rizzo /* try to create the device */ 2172f8e4e36aSLuigi Rizzo if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { 217317885a7bSLuigi Rizzo D("failed to to a TUNSETIFF: %s", strerror(errno)); 2174f8e4e36aSLuigi Rizzo close(fd); 2175f8e4e36aSLuigi Rizzo return err; 2176f8e4e36aSLuigi Rizzo } 2177f8e4e36aSLuigi Rizzo 2178f8e4e36aSLuigi Rizzo /* if the operation was successful, write back the name of the 2179f8e4e36aSLuigi Rizzo * interface to the variable "dev", so the caller can know 2180f8e4e36aSLuigi Rizzo * it. Note that the caller MUST reserve space in *dev (see calling 2181f8e4e36aSLuigi Rizzo * code below) */ 2182f8e4e36aSLuigi Rizzo strcpy(dev, ifr.ifr_name); 2183f8e4e36aSLuigi Rizzo D("new name is %s", dev); 2184f8e4e36aSLuigi Rizzo #endif /* linux */ 2185f8e4e36aSLuigi Rizzo 2186f8e4e36aSLuigi Rizzo /* this is the special file descriptor that the caller will use to talk 2187f8e4e36aSLuigi Rizzo * with the virtual interface */ 2188f8e4e36aSLuigi Rizzo return fd; 2189f8e4e36aSLuigi Rizzo } 219068b8534bSLuigi Rizzo 219168b8534bSLuigi Rizzo int 219268b8534bSLuigi Rizzo main(int arc, char **argv) 219368b8534bSLuigi Rizzo { 2194f8e4e36aSLuigi Rizzo int i; 219537e3a6d3SLuigi Rizzo struct sigaction sa; 219637e3a6d3SLuigi Rizzo sigset_t ss; 219768b8534bSLuigi Rizzo 219868b8534bSLuigi Rizzo struct glob_arg g; 219968b8534bSLuigi Rizzo 220068b8534bSLuigi Rizzo int ch; 220168b8534bSLuigi Rizzo int wait_link = 2; 220268b8534bSLuigi Rizzo int devqueues = 1; /* how many device queues */ 220368b8534bSLuigi Rizzo 220468b8534bSLuigi Rizzo bzero(&g, sizeof(g)); 220568b8534bSLuigi Rizzo 2206f8e4e36aSLuigi Rizzo g.main_fd = -1; 2207f8e4e36aSLuigi Rizzo g.td_body = receiver_body; 220837e3a6d3SLuigi Rizzo g.td_type = TD_TYPE_RECEIVER; 2209f8e4e36aSLuigi Rizzo g.report_interval = 1000; /* report interval */ 2210f8e4e36aSLuigi Rizzo g.affinity = -1; 2211f8e4e36aSLuigi Rizzo /* ip addresses can also be a range x.x.x.x-x.x.x.y */ 2212f8e4e36aSLuigi Rizzo g.src_ip.name = "10.0.0.1"; 2213f8e4e36aSLuigi Rizzo g.dst_ip.name = "10.1.0.1"; 2214f8e4e36aSLuigi Rizzo g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; 2215f8e4e36aSLuigi Rizzo g.src_mac.name = NULL; 221668b8534bSLuigi Rizzo g.pkt_size = 60; 221768b8534bSLuigi Rizzo g.burst = 512; // default 221868b8534bSLuigi Rizzo g.nthreads = 1; 221937e3a6d3SLuigi Rizzo g.cpus = 1; // default 2220b303f675SLuigi Rizzo g.forever = 1; 22211cb4c501SLuigi Rizzo g.tx_rate = 0; 2222ce3ee1e7SLuigi Rizzo g.frags = 1; 2223ce3ee1e7SLuigi Rizzo g.nmr_config = ""; 222417885a7bSLuigi Rizzo g.virt_header = 0; 222568b8534bSLuigi Rizzo 222668b8534bSLuigi Rizzo while ( (ch = getopt(arc, argv, 222737e3a6d3SLuigi Rizzo "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:E:m:rP:zZA")) != -1) { 222837e3a6d3SLuigi Rizzo struct td_desc *fn; 2229f8e4e36aSLuigi Rizzo 223068b8534bSLuigi Rizzo switch(ch) { 223168b8534bSLuigi Rizzo default: 223268b8534bSLuigi Rizzo D("bad option %c %s", ch, optarg); 223368b8534bSLuigi Rizzo usage(); 223468b8534bSLuigi Rizzo break; 2235f8e4e36aSLuigi Rizzo 2236f8e4e36aSLuigi Rizzo case 'n': 223737e3a6d3SLuigi Rizzo g.npackets = strtoull(optarg, NULL, 10); 2238f8e4e36aSLuigi Rizzo break; 2239f8e4e36aSLuigi Rizzo 2240ce3ee1e7SLuigi Rizzo case 'F': 2241ce3ee1e7SLuigi Rizzo i = atoi(optarg); 2242ce3ee1e7SLuigi Rizzo if (i < 1 || i > 63) { 2243ce3ee1e7SLuigi Rizzo D("invalid frags %d [1..63], ignore", i); 2244ce3ee1e7SLuigi Rizzo break; 2245ce3ee1e7SLuigi Rizzo } 2246ce3ee1e7SLuigi Rizzo g.frags = i; 2247ce3ee1e7SLuigi Rizzo break; 2248ce3ee1e7SLuigi Rizzo 2249f8e4e36aSLuigi Rizzo case 'f': 2250f8e4e36aSLuigi Rizzo for (fn = func; fn->key; fn++) { 2251f8e4e36aSLuigi Rizzo if (!strcmp(fn->key, optarg)) 2252f8e4e36aSLuigi Rizzo break; 2253f8e4e36aSLuigi Rizzo } 225437e3a6d3SLuigi Rizzo if (fn->key) { 2255f8e4e36aSLuigi Rizzo g.td_body = fn->f; 225637e3a6d3SLuigi Rizzo g.td_type = fn->ty; 225737e3a6d3SLuigi Rizzo } else { 2258f8e4e36aSLuigi Rizzo D("unrecognised function %s", optarg); 225937e3a6d3SLuigi Rizzo } 2260f8e4e36aSLuigi Rizzo break; 2261f8e4e36aSLuigi Rizzo 2262f8e4e36aSLuigi Rizzo case 'o': /* data generation options */ 226399fb123fSLuigi Rizzo g.options = atoi(optarg); 226499fb123fSLuigi Rizzo break; 2265f8e4e36aSLuigi Rizzo 2266f8e4e36aSLuigi Rizzo case 'a': /* force affinity */ 2267f8e4e36aSLuigi Rizzo g.affinity = atoi(optarg); 2268f8e4e36aSLuigi Rizzo break; 2269f8e4e36aSLuigi Rizzo 227068b8534bSLuigi Rizzo case 'i': /* interface */ 2271f2637526SLuigi Rizzo /* a prefix of tap: netmap: or pcap: forces the mode. 2272f2637526SLuigi Rizzo * otherwise we guess 2273f2637526SLuigi Rizzo */ 2274f2637526SLuigi Rizzo D("interface is %s", optarg); 2275f0ea3689SLuigi Rizzo if (strlen(optarg) > MAX_IFNAMELEN - 8) { 2276f0ea3689SLuigi Rizzo D("ifname too long %s", optarg); 2277f0ea3689SLuigi Rizzo break; 2278f0ea3689SLuigi Rizzo } 2279f0ea3689SLuigi Rizzo strcpy(g.ifname, optarg); 2280f2637526SLuigi Rizzo if (!strcmp(optarg, "null")) { 2281f8e4e36aSLuigi Rizzo g.dev_type = DEV_NETMAP; 2282ce3ee1e7SLuigi Rizzo g.dummy_send = 1; 2283f2637526SLuigi Rizzo } else if (!strncmp(optarg, "tap:", 4)) { 2284f2637526SLuigi Rizzo g.dev_type = DEV_TAP; 2285f0ea3689SLuigi Rizzo strcpy(g.ifname, optarg + 4); 2286f2637526SLuigi Rizzo } else if (!strncmp(optarg, "pcap:", 5)) { 2287f2637526SLuigi Rizzo g.dev_type = DEV_PCAP; 2288f0ea3689SLuigi Rizzo strcpy(g.ifname, optarg + 5); 2289f0ea3689SLuigi Rizzo } else if (!strncmp(optarg, "netmap:", 7) || 2290f0ea3689SLuigi Rizzo !strncmp(optarg, "vale", 4)) { 2291f2637526SLuigi Rizzo g.dev_type = DEV_NETMAP; 2292f2637526SLuigi Rizzo } else if (!strncmp(optarg, "tap", 3)) { 2293f2637526SLuigi Rizzo g.dev_type = DEV_TAP; 2294f0ea3689SLuigi Rizzo } else { /* prepend netmap: */ 2295f2637526SLuigi Rizzo g.dev_type = DEV_NETMAP; 2296f0ea3689SLuigi Rizzo sprintf(g.ifname, "netmap:%s", optarg); 2297f2637526SLuigi Rizzo } 229868b8534bSLuigi Rizzo break; 2299f8e4e36aSLuigi Rizzo 2300b303f675SLuigi Rizzo case 'I': 2301b303f675SLuigi Rizzo g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ 2302b303f675SLuigi Rizzo break; 2303b303f675SLuigi Rizzo 230468b8534bSLuigi Rizzo case 'l': /* pkt_size */ 230568b8534bSLuigi Rizzo g.pkt_size = atoi(optarg); 230668b8534bSLuigi Rizzo break; 2307f8e4e36aSLuigi Rizzo 230868b8534bSLuigi Rizzo case 'd': 2309f8e4e36aSLuigi Rizzo g.dst_ip.name = optarg; 231068b8534bSLuigi Rizzo break; 2311f8e4e36aSLuigi Rizzo 231268b8534bSLuigi Rizzo case 's': 2313f8e4e36aSLuigi Rizzo g.src_ip.name = optarg; 231468b8534bSLuigi Rizzo break; 2315f8e4e36aSLuigi Rizzo 231668b8534bSLuigi Rizzo case 'T': /* report interval */ 2317f8e4e36aSLuigi Rizzo g.report_interval = atoi(optarg); 231868b8534bSLuigi Rizzo break; 2319f8e4e36aSLuigi Rizzo 232068b8534bSLuigi Rizzo case 'w': 232168b8534bSLuigi Rizzo wait_link = atoi(optarg); 232268b8534bSLuigi Rizzo break; 2323f8e4e36aSLuigi Rizzo 2324b303f675SLuigi Rizzo case 'W': /* XXX changed default */ 2325b303f675SLuigi Rizzo g.forever = 0; /* do not exit rx even with no traffic */ 2326f8e4e36aSLuigi Rizzo break; 2327f8e4e36aSLuigi Rizzo 232868b8534bSLuigi Rizzo case 'b': /* burst */ 232968b8534bSLuigi Rizzo g.burst = atoi(optarg); 233068b8534bSLuigi Rizzo break; 233168b8534bSLuigi Rizzo case 'c': 233268b8534bSLuigi Rizzo g.cpus = atoi(optarg); 233368b8534bSLuigi Rizzo break; 233468b8534bSLuigi Rizzo case 'p': 233568b8534bSLuigi Rizzo g.nthreads = atoi(optarg); 233668b8534bSLuigi Rizzo break; 233768b8534bSLuigi Rizzo 233868b8534bSLuigi Rizzo case 'D': /* destination mac */ 2339f8e4e36aSLuigi Rizzo g.dst_mac.name = optarg; 234068b8534bSLuigi Rizzo break; 2341f8e4e36aSLuigi Rizzo 234268b8534bSLuigi Rizzo case 'S': /* source mac */ 2343f8e4e36aSLuigi Rizzo g.src_mac.name = optarg; 234468b8534bSLuigi Rizzo break; 234568b8534bSLuigi Rizzo case 'v': 234668b8534bSLuigi Rizzo verbose++; 23471cb4c501SLuigi Rizzo break; 23481cb4c501SLuigi Rizzo case 'R': 23491cb4c501SLuigi Rizzo g.tx_rate = atoi(optarg); 23501cb4c501SLuigi Rizzo break; 2351b303f675SLuigi Rizzo case 'X': 2352b303f675SLuigi Rizzo g.options |= OPT_DUMP; 2353ce3ee1e7SLuigi Rizzo break; 2354ce3ee1e7SLuigi Rizzo case 'C': 2355ce3ee1e7SLuigi Rizzo g.nmr_config = strdup(optarg); 235617885a7bSLuigi Rizzo break; 235717885a7bSLuigi Rizzo case 'H': 235817885a7bSLuigi Rizzo g.virt_header = atoi(optarg); 2359f2637526SLuigi Rizzo break; 2360f0ea3689SLuigi Rizzo case 'e': /* extra bufs */ 2361f0ea3689SLuigi Rizzo g.extra_bufs = atoi(optarg); 2362f2637526SLuigi Rizzo break; 236337e3a6d3SLuigi Rizzo case 'E': 236437e3a6d3SLuigi Rizzo g.extra_pipes = atoi(optarg); 23654bf50f18SLuigi Rizzo break; 2366f284c737SGeorge V. Neville-Neil case 'P': 2367f284c737SGeorge V. Neville-Neil g.packet_file = strdup(optarg); 2368f284c737SGeorge V. Neville-Neil break; 236937e3a6d3SLuigi Rizzo case 'm': 237037e3a6d3SLuigi Rizzo /* ignored */ 237137e3a6d3SLuigi Rizzo break; 237237e3a6d3SLuigi Rizzo case 'r': 237337e3a6d3SLuigi Rizzo g.options |= OPT_RUBBISH; 237437e3a6d3SLuigi Rizzo break; 237556717743SAdrian Chadd case 'z': 237656717743SAdrian Chadd g.options |= OPT_RANDOM_SRC; 237756717743SAdrian Chadd break; 237856717743SAdrian Chadd case 'Z': 237956717743SAdrian Chadd g.options |= OPT_RANDOM_DST; 238056717743SAdrian Chadd break; 238137e3a6d3SLuigi Rizzo case 'A': 238237e3a6d3SLuigi Rizzo g.options |= OPT_PPS_STATS; 238337e3a6d3SLuigi Rizzo break; 238468b8534bSLuigi Rizzo } 238568b8534bSLuigi Rizzo } 238668b8534bSLuigi Rizzo 2387db6784f2SGeorge V. Neville-Neil if (strlen(g.ifname) <=0 ) { 238868b8534bSLuigi Rizzo D("missing ifname"); 238968b8534bSLuigi Rizzo usage(); 239068b8534bSLuigi Rizzo } 2391f8e4e36aSLuigi Rizzo 239237e3a6d3SLuigi Rizzo g.system_cpus = i = system_ncpus(); 2393f8e4e36aSLuigi Rizzo if (g.cpus < 0 || g.cpus > i) { 2394f8e4e36aSLuigi Rizzo D("%d cpus is too high, have only %d cpus", g.cpus, i); 239568b8534bSLuigi Rizzo usage(); 239668b8534bSLuigi Rizzo } 239737e3a6d3SLuigi Rizzo D("running on %d cpus (have %d)", g.cpus, i); 239868b8534bSLuigi Rizzo if (g.cpus == 0) 2399f8e4e36aSLuigi Rizzo g.cpus = i; 2400f8e4e36aSLuigi Rizzo 24014bf50f18SLuigi Rizzo if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { 24024bf50f18SLuigi Rizzo D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); 240368b8534bSLuigi Rizzo usage(); 240468b8534bSLuigi Rizzo } 240568b8534bSLuigi Rizzo 2406f8e4e36aSLuigi Rizzo if (g.src_mac.name == NULL) { 2407f8e4e36aSLuigi Rizzo static char mybuf[20] = "00:00:00:00:00:00"; 240899fb123fSLuigi Rizzo /* retrieve source mac address. */ 2409f8e4e36aSLuigi Rizzo if (source_hwaddr(g.ifname, mybuf) == -1) { 241099fb123fSLuigi Rizzo D("Unable to retrieve source mac"); 241199fb123fSLuigi Rizzo // continue, fail later 241299fb123fSLuigi Rizzo } 2413f8e4e36aSLuigi Rizzo g.src_mac.name = mybuf; 241499fb123fSLuigi Rizzo } 2415f8e4e36aSLuigi Rizzo /* extract address ranges */ 2416f8e4e36aSLuigi Rizzo extract_ip_range(&g.src_ip); 2417f8e4e36aSLuigi Rizzo extract_ip_range(&g.dst_ip); 2418f8e4e36aSLuigi Rizzo extract_mac_range(&g.src_mac); 2419f8e4e36aSLuigi Rizzo extract_mac_range(&g.dst_mac); 242099fb123fSLuigi Rizzo 2421f2637526SLuigi Rizzo if (g.src_ip.start != g.src_ip.end || 2422f2637526SLuigi Rizzo g.src_ip.port0 != g.src_ip.port1 || 2423f2637526SLuigi Rizzo g.dst_ip.start != g.dst_ip.end || 2424f2637526SLuigi Rizzo g.dst_ip.port0 != g.dst_ip.port1) 2425f2637526SLuigi Rizzo g.options |= OPT_COPY; 2426f2637526SLuigi Rizzo 242717885a7bSLuigi Rizzo if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 242817885a7bSLuigi Rizzo && g.virt_header != VIRT_HDR_2) { 242917885a7bSLuigi Rizzo D("bad virtio-net-header length"); 243017885a7bSLuigi Rizzo usage(); 243117885a7bSLuigi Rizzo } 243217885a7bSLuigi Rizzo 2433f8e4e36aSLuigi Rizzo if (g.dev_type == DEV_TAP) { 2434f8e4e36aSLuigi Rizzo D("want to use tap %s", g.ifname); 2435f8e4e36aSLuigi Rizzo g.main_fd = tap_alloc(g.ifname); 2436f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 2437f8e4e36aSLuigi Rizzo D("cannot open tap %s", g.ifname); 243899fb123fSLuigi Rizzo usage(); 243999fb123fSLuigi Rizzo } 2440f2637526SLuigi Rizzo #ifndef NO_PCAP 2441f2637526SLuigi Rizzo } else if (g.dev_type == DEV_PCAP) { 2442f8e4e36aSLuigi Rizzo char pcap_errbuf[PCAP_ERRBUF_SIZE]; 2443f8e4e36aSLuigi Rizzo 2444f8e4e36aSLuigi Rizzo pcap_errbuf[0] = '\0'; // init the buffer 24454bf50f18SLuigi Rizzo g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); 2446f8e4e36aSLuigi Rizzo if (g.p == NULL) { 2447f8e4e36aSLuigi Rizzo D("cannot open pcap on %s", g.ifname); 2448f8e4e36aSLuigi Rizzo usage(); 2449f8e4e36aSLuigi Rizzo } 24504bf50f18SLuigi Rizzo g.main_fd = pcap_fileno(g.p); 24514bf50f18SLuigi Rizzo D("using pcap on %s fileno %d", g.ifname, g.main_fd); 2452f2637526SLuigi Rizzo #endif /* !NO_PCAP */ 2453f2637526SLuigi Rizzo } else if (g.dummy_send) { /* but DEV_NETMAP */ 2454ce3ee1e7SLuigi Rizzo D("using a dummy send routine"); 245599fb123fSLuigi Rizzo } else { 24564bf50f18SLuigi Rizzo struct nmreq base_nmd; 2457f0ea3689SLuigi Rizzo 2458f0ea3689SLuigi Rizzo bzero(&base_nmd, sizeof(base_nmd)); 2459f0ea3689SLuigi Rizzo 24604bf50f18SLuigi Rizzo parse_nmr_config(g.nmr_config, &base_nmd); 2461f0ea3689SLuigi Rizzo if (g.extra_bufs) { 24624bf50f18SLuigi Rizzo base_nmd.nr_arg3 = g.extra_bufs; 2463f0ea3689SLuigi Rizzo } 246437e3a6d3SLuigi Rizzo if (g.extra_pipes) { 246537e3a6d3SLuigi Rizzo base_nmd.nr_arg1 = g.extra_pipes; 246637e3a6d3SLuigi Rizzo } 246737e3a6d3SLuigi Rizzo 246837e3a6d3SLuigi Rizzo base_nmd.nr_flags |= NR_ACCEPT_VNET_HDR; 2469f0ea3689SLuigi Rizzo 247068b8534bSLuigi Rizzo /* 2471f0ea3689SLuigi Rizzo * Open the netmap device using nm_open(). 247268b8534bSLuigi Rizzo * 247368b8534bSLuigi Rizzo * protocol stack and may cause a reset of the card, 247468b8534bSLuigi Rizzo * which in turn may take some time for the PHY to 2475f0ea3689SLuigi Rizzo * reconfigure. We do the open here to have time to reset. 247668b8534bSLuigi Rizzo */ 24774bf50f18SLuigi Rizzo g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL); 2478f0ea3689SLuigi Rizzo if (g.nmd == NULL) { 2479f0ea3689SLuigi Rizzo D("Unable to open %s: %s", g.ifname, strerror(errno)); 2480f0ea3689SLuigi Rizzo goto out; 248168b8534bSLuigi Rizzo } 248237e3a6d3SLuigi Rizzo 248337e3a6d3SLuigi Rizzo if (g.nthreads > 1) { 248437e3a6d3SLuigi Rizzo struct nm_desc saved_desc = *g.nmd; 248537e3a6d3SLuigi Rizzo saved_desc.self = &saved_desc; 248637e3a6d3SLuigi Rizzo saved_desc.mem = NULL; 248737e3a6d3SLuigi Rizzo nm_close(g.nmd); 248837e3a6d3SLuigi Rizzo saved_desc.req.nr_flags &= ~NR_REG_MASK; 248937e3a6d3SLuigi Rizzo saved_desc.req.nr_flags |= NR_REG_ONE_NIC; 249037e3a6d3SLuigi Rizzo saved_desc.req.nr_ringid = 0; 249137e3a6d3SLuigi Rizzo g.nmd = nm_open(g.ifname, &base_nmd, NM_OPEN_IFNAME, &saved_desc); 249237e3a6d3SLuigi Rizzo if (g.nmd == NULL) { 249337e3a6d3SLuigi Rizzo D("Unable to open %s: %s", g.ifname, strerror(errno)); 249437e3a6d3SLuigi Rizzo goto out; 249537e3a6d3SLuigi Rizzo } 249637e3a6d3SLuigi Rizzo } 2497f0ea3689SLuigi Rizzo g.main_fd = g.nmd->fd; 2498f0ea3689SLuigi Rizzo D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); 2499f0ea3689SLuigi Rizzo 250037e3a6d3SLuigi Rizzo if (g.virt_header) { 250137e3a6d3SLuigi Rizzo /* Set the virtio-net header length, since the user asked 250237e3a6d3SLuigi Rizzo * for it explicitely. */ 250337e3a6d3SLuigi Rizzo set_vnet_hdr_len(&g); 250437e3a6d3SLuigi Rizzo } else { 250537e3a6d3SLuigi Rizzo /* Check whether the netmap port we opened requires us to send 250637e3a6d3SLuigi Rizzo * and receive frames with virtio-net header. */ 250737e3a6d3SLuigi Rizzo get_vnet_hdr_len(&g); 250837e3a6d3SLuigi Rizzo } 250937e3a6d3SLuigi Rizzo 25104bf50f18SLuigi Rizzo /* get num of queues in tx or rx */ 251137e3a6d3SLuigi Rizzo if (g.td_type == TD_TYPE_SENDER) 25124bf50f18SLuigi Rizzo devqueues = g.nmd->req.nr_tx_rings; 25134bf50f18SLuigi Rizzo else 2514f0ea3689SLuigi Rizzo devqueues = g.nmd->req.nr_rx_rings; 251568b8534bSLuigi Rizzo 251668b8534bSLuigi Rizzo /* validate provided nthreads. */ 251768b8534bSLuigi Rizzo if (g.nthreads < 1 || g.nthreads > devqueues) { 251868b8534bSLuigi Rizzo D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 251968b8534bSLuigi Rizzo // continue, fail later 252068b8534bSLuigi Rizzo } 252168b8534bSLuigi Rizzo 2522f2637526SLuigi Rizzo if (verbose) { 2523f0ea3689SLuigi Rizzo struct netmap_if *nifp = g.nmd->nifp; 2524f0ea3689SLuigi Rizzo struct nmreq *req = &g.nmd->req; 252568b8534bSLuigi Rizzo 2526f0ea3689SLuigi Rizzo D("nifp at offset %d, %d tx %d rx region %d", 2527f0ea3689SLuigi Rizzo req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, 2528f0ea3689SLuigi Rizzo req->nr_arg2); 2529f0ea3689SLuigi Rizzo for (i = 0; i <= req->nr_tx_rings; i++) { 25304bf50f18SLuigi Rizzo struct netmap_ring *ring = NETMAP_TXRING(nifp, i); 253137e3a6d3SLuigi Rizzo D(" TX%d at 0x%p slots %d", i, 253237e3a6d3SLuigi Rizzo (void *)((char *)ring - (char *)nifp), ring->num_slots); 2533f2637526SLuigi Rizzo } 2534f0ea3689SLuigi Rizzo for (i = 0; i <= req->nr_rx_rings; i++) { 25354bf50f18SLuigi Rizzo struct netmap_ring *ring = NETMAP_RXRING(nifp, i); 253637e3a6d3SLuigi Rizzo D(" RX%d at 0x%p slots %d", i, 253737e3a6d3SLuigi Rizzo (void *)((char *)ring - (char *)nifp), ring->num_slots); 2538f2637526SLuigi Rizzo } 2539f2637526SLuigi Rizzo } 254068b8534bSLuigi Rizzo 254168b8534bSLuigi Rizzo /* Print some debug information. */ 254268b8534bSLuigi Rizzo fprintf(stdout, 254368b8534bSLuigi Rizzo "%s %s: %d queues, %d threads and %d cpus.\n", 254437e3a6d3SLuigi Rizzo (g.td_type == TD_TYPE_SENDER) ? "Sending on" : 254537e3a6d3SLuigi Rizzo ((g.td_type == TD_TYPE_RECEIVER) ? "Receiving from" : 254637e3a6d3SLuigi Rizzo "Working on"), 2547f8e4e36aSLuigi Rizzo g.ifname, 254868b8534bSLuigi Rizzo devqueues, 254968b8534bSLuigi Rizzo g.nthreads, 255068b8534bSLuigi Rizzo g.cpus); 255137e3a6d3SLuigi Rizzo if (g.td_type == TD_TYPE_SENDER) { 255268b8534bSLuigi Rizzo fprintf(stdout, "%s -> %s (%s -> %s)\n", 2553f8e4e36aSLuigi Rizzo g.src_ip.name, g.dst_ip.name, 2554f8e4e36aSLuigi Rizzo g.src_mac.name, g.dst_mac.name); 255568b8534bSLuigi Rizzo } 255668b8534bSLuigi Rizzo 2557f0ea3689SLuigi Rizzo out: 255868b8534bSLuigi Rizzo /* Exit if something went wrong. */ 2559f8e4e36aSLuigi Rizzo if (g.main_fd < 0) { 256068b8534bSLuigi Rizzo D("aborting"); 256168b8534bSLuigi Rizzo usage(); 256268b8534bSLuigi Rizzo } 256399fb123fSLuigi Rizzo } 256468b8534bSLuigi Rizzo 2565ce3ee1e7SLuigi Rizzo 256699fb123fSLuigi Rizzo if (g.options) { 256737e3a6d3SLuigi Rizzo D("--- SPECIAL OPTIONS:%s%s%s%s%s%s\n", 256899fb123fSLuigi Rizzo g.options & OPT_PREFETCH ? " prefetch" : "", 256999fb123fSLuigi Rizzo g.options & OPT_ACCESS ? " access" : "", 257099fb123fSLuigi Rizzo g.options & OPT_MEMCPY ? " memcpy" : "", 2571b303f675SLuigi Rizzo g.options & OPT_INDIRECT ? " indirect" : "", 257237e3a6d3SLuigi Rizzo g.options & OPT_COPY ? " copy" : "", 257337e3a6d3SLuigi Rizzo g.options & OPT_RUBBISH ? " rubbish " : ""); 257499fb123fSLuigi Rizzo } 25751cb4c501SLuigi Rizzo 2576ce3ee1e7SLuigi Rizzo g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; 2577ce3ee1e7SLuigi Rizzo if (g.tx_rate > 0) { 2578ce3ee1e7SLuigi Rizzo /* try to have at least something every second, 257917885a7bSLuigi Rizzo * reducing the burst size to some 0.01s worth of data 2580ce3ee1e7SLuigi Rizzo * (but no less than one full set of fragments) 2581ce3ee1e7SLuigi Rizzo */ 258217885a7bSLuigi Rizzo uint64_t x; 258317885a7bSLuigi Rizzo int lim = (g.tx_rate)/300; 258417885a7bSLuigi Rizzo if (g.burst > lim) 258517885a7bSLuigi Rizzo g.burst = lim; 2586ce3ee1e7SLuigi Rizzo if (g.burst < g.frags) 2587ce3ee1e7SLuigi Rizzo g.burst = g.frags; 258817885a7bSLuigi Rizzo x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; 258917885a7bSLuigi Rizzo g.tx_period.tv_nsec = x; 25901cb4c501SLuigi Rizzo g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; 25911cb4c501SLuigi Rizzo g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; 25921cb4c501SLuigi Rizzo } 259337e3a6d3SLuigi Rizzo if (g.td_type == TD_TYPE_SENDER) 2594ce3ee1e7SLuigi Rizzo D("Sending %d packets every %ld.%09ld s", 2595ce3ee1e7SLuigi Rizzo g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); 259668b8534bSLuigi Rizzo /* Wait for PHY reset. */ 259768b8534bSLuigi Rizzo D("Wait %d secs for phy reset", wait_link); 259868b8534bSLuigi Rizzo sleep(wait_link); 259968b8534bSLuigi Rizzo D("Ready..."); 260068b8534bSLuigi Rizzo 260168b8534bSLuigi Rizzo /* Install ^C handler. */ 260268b8534bSLuigi Rizzo global_nthreads = g.nthreads; 260337e3a6d3SLuigi Rizzo sigemptyset(&ss); 260437e3a6d3SLuigi Rizzo sigaddset(&ss, SIGINT); 260537e3a6d3SLuigi Rizzo /* block SIGINT now, so that all created threads will inherit the mask */ 260637e3a6d3SLuigi Rizzo if (pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) { 260737e3a6d3SLuigi Rizzo D("failed to block SIGINT: %s", strerror(errno)); 260837e3a6d3SLuigi Rizzo } 2609f8e4e36aSLuigi Rizzo start_threads(&g); 261037e3a6d3SLuigi Rizzo /* Install the handler and re-enable SIGINT for the main thread */ 261137e3a6d3SLuigi Rizzo sa.sa_handler = sigint_h; 261237e3a6d3SLuigi Rizzo if (sigaction(SIGINT, &sa, NULL) < 0) { 261337e3a6d3SLuigi Rizzo D("failed to install ^C handler: %s", strerror(errno)); 261437e3a6d3SLuigi Rizzo } 261537e3a6d3SLuigi Rizzo 261637e3a6d3SLuigi Rizzo if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL) < 0) { 261737e3a6d3SLuigi Rizzo D("failed to re-enable SIGINT: %s", strerror(errno)); 261837e3a6d3SLuigi Rizzo } 2619f8e4e36aSLuigi Rizzo main_thread(&g); 262037e3a6d3SLuigi Rizzo free(targs); 2621f8e4e36aSLuigi Rizzo return 0; 262268b8534bSLuigi Rizzo } 262368b8534bSLuigi Rizzo 262468b8534bSLuigi Rizzo /* end of file */ 2625