168b8534bSLuigi Rizzo /* 268b8534bSLuigi Rizzo * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved. 368b8534bSLuigi Rizzo * 468b8534bSLuigi Rizzo * Redistribution and use in source and binary forms, with or without 568b8534bSLuigi Rizzo * modification, are permitted provided that the following conditions 668b8534bSLuigi Rizzo * are met: 768b8534bSLuigi Rizzo * 1. Redistributions of source code must retain the above copyright 868b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer. 968b8534bSLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 1068b8534bSLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 1168b8534bSLuigi Rizzo * documentation and/or other materials provided with the distribution. 1268b8534bSLuigi Rizzo * 1368b8534bSLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1468b8534bSLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1568b8534bSLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1668b8534bSLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1768b8534bSLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1868b8534bSLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1968b8534bSLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2068b8534bSLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2168b8534bSLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2268b8534bSLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2368b8534bSLuigi Rizzo * SUCH DAMAGE. 2468b8534bSLuigi Rizzo */ 2568b8534bSLuigi Rizzo 2668b8534bSLuigi Rizzo /* 2768b8534bSLuigi Rizzo * $FreeBSD$ 2899fb123fSLuigi Rizzo * $Id: pkt-gen.c 10967 2012-05-03 11:29:23Z luigi $ 2968b8534bSLuigi Rizzo * 3068b8534bSLuigi Rizzo * Example program to show how to build a multithreaded packet 3168b8534bSLuigi Rizzo * source/sink using the netmap device. 3268b8534bSLuigi Rizzo * 3368b8534bSLuigi Rizzo * In this example we create a programmable number of threads 3468b8534bSLuigi Rizzo * to take care of all the queues of the interface used to 3568b8534bSLuigi Rizzo * send or receive traffic. 3668b8534bSLuigi Rizzo * 3768b8534bSLuigi Rizzo */ 3868b8534bSLuigi Rizzo 3968b8534bSLuigi Rizzo const char *default_payload="netmap pkt-gen Luigi Rizzo and Matteo Landi\n" 4068b8534bSLuigi Rizzo "http://info.iet.unipi.it/~luigi/netmap/ "; 4168b8534bSLuigi Rizzo 4268b8534bSLuigi Rizzo #include <errno.h> 4368b8534bSLuigi Rizzo #include <pthread.h> /* pthread_* */ 4468b8534bSLuigi Rizzo #include <pthread_np.h> /* pthread w/ affinity */ 4568b8534bSLuigi Rizzo #include <signal.h> /* signal */ 4668b8534bSLuigi Rizzo #include <stdlib.h> 4768b8534bSLuigi Rizzo #include <stdio.h> 48506cc70cSLuigi Rizzo #include <inttypes.h> /* PRI* macros */ 4968b8534bSLuigi Rizzo #include <string.h> /* strcmp */ 5068b8534bSLuigi Rizzo #include <fcntl.h> /* open */ 5168b8534bSLuigi Rizzo #include <unistd.h> /* close */ 5268b8534bSLuigi Rizzo #include <ifaddrs.h> /* getifaddrs */ 5368b8534bSLuigi Rizzo 5468b8534bSLuigi Rizzo #include <sys/mman.h> /* PROT_* */ 5568b8534bSLuigi Rizzo #include <sys/ioctl.h> /* ioctl */ 5668b8534bSLuigi Rizzo #include <sys/poll.h> 5768b8534bSLuigi Rizzo #include <sys/socket.h> /* sockaddr.. */ 5868b8534bSLuigi Rizzo #include <arpa/inet.h> /* ntohs */ 5968b8534bSLuigi Rizzo #include <sys/param.h> 6068b8534bSLuigi Rizzo #include <sys/cpuset.h> /* cpu_set */ 6168b8534bSLuigi Rizzo #include <sys/sysctl.h> /* sysctl */ 6268b8534bSLuigi Rizzo #include <sys/time.h> /* timersub */ 6368b8534bSLuigi Rizzo 6468b8534bSLuigi Rizzo #include <net/ethernet.h> 6568b8534bSLuigi Rizzo #include <net/if.h> /* ifreq */ 6668b8534bSLuigi Rizzo #include <net/if_dl.h> /* LLADDR */ 6768b8534bSLuigi Rizzo 6868b8534bSLuigi Rizzo #include <netinet/in.h> 6968b8534bSLuigi Rizzo #include <netinet/ip.h> 7068b8534bSLuigi Rizzo #include <netinet/udp.h> 7168b8534bSLuigi Rizzo 7268b8534bSLuigi Rizzo #include <net/netmap.h> 7368b8534bSLuigi Rizzo #include <net/netmap_user.h> 7468b8534bSLuigi Rizzo #include <pcap/pcap.h> 7568b8534bSLuigi Rizzo 7668b8534bSLuigi Rizzo 7768b8534bSLuigi Rizzo static inline int min(int a, int b) { return a < b ? a : b; } 7868b8534bSLuigi Rizzo 7968b8534bSLuigi Rizzo /* debug support */ 8068b8534bSLuigi Rizzo #define D(format, ...) \ 8168b8534bSLuigi Rizzo fprintf(stderr, "%s [%d] " format "\n", \ 8268b8534bSLuigi Rizzo __FUNCTION__, __LINE__, ##__VA_ARGS__) 8368b8534bSLuigi Rizzo 8468b8534bSLuigi Rizzo #ifndef EXPERIMENTAL 8568b8534bSLuigi Rizzo #define EXPERIMENTAL 0 8668b8534bSLuigi Rizzo #endif 8768b8534bSLuigi Rizzo 8868b8534bSLuigi Rizzo int verbose = 0; 8968b8534bSLuigi Rizzo #define MAX_QUEUES 64 /* no need to limit */ 9068b8534bSLuigi Rizzo 9168b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */ 9268b8534bSLuigi Rizzo 9399fb123fSLuigi Rizzo inline void prefetch (const void *x) 9499fb123fSLuigi Rizzo { 9599fb123fSLuigi Rizzo __asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x)); 9699fb123fSLuigi Rizzo } 9799fb123fSLuigi Rizzo 988585b1b8SEd Maste // XXX only for multiples of 64 bytes, non overlapped. 9999fb123fSLuigi Rizzo static inline void 10099fb123fSLuigi Rizzo pkt_copy(void *_src, void *_dst, int l) 10199fb123fSLuigi Rizzo { 10299fb123fSLuigi Rizzo uint64_t *src = _src; 10399fb123fSLuigi Rizzo uint64_t *dst = _dst; 10499fb123fSLuigi Rizzo #define likely(x) __builtin_expect(!!(x), 1) 10599fb123fSLuigi Rizzo #define unlikely(x) __builtin_expect(!!(x), 0) 10699fb123fSLuigi Rizzo if (unlikely(l >= 1024)) { 10799fb123fSLuigi Rizzo bcopy(src, dst, l); 10899fb123fSLuigi Rizzo return; 10999fb123fSLuigi Rizzo } 11099fb123fSLuigi Rizzo for (; l > 0; l-=64) { 11199fb123fSLuigi Rizzo *dst++ = *src++; 11299fb123fSLuigi Rizzo *dst++ = *src++; 11399fb123fSLuigi Rizzo *dst++ = *src++; 11499fb123fSLuigi Rizzo *dst++ = *src++; 11599fb123fSLuigi Rizzo *dst++ = *src++; 11699fb123fSLuigi Rizzo *dst++ = *src++; 11799fb123fSLuigi Rizzo *dst++ = *src++; 11899fb123fSLuigi Rizzo *dst++ = *src++; 11999fb123fSLuigi Rizzo } 12099fb123fSLuigi Rizzo } 12199fb123fSLuigi Rizzo 12299fb123fSLuigi Rizzo 12368b8534bSLuigi Rizzo #if EXPERIMENTAL 12468b8534bSLuigi Rizzo /* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */ 12568b8534bSLuigi Rizzo #define netmap_rdtsc(t) \ 12668b8534bSLuigi Rizzo do { \ 12768b8534bSLuigi Rizzo u_int __regs[4]; \ 12868b8534bSLuigi Rizzo \ 12968b8534bSLuigi Rizzo do_cpuid(0, __regs); \ 13068b8534bSLuigi Rizzo (t) = rdtsc(); \ 13168b8534bSLuigi Rizzo } while (0) 13268b8534bSLuigi Rizzo 13368b8534bSLuigi Rizzo static __inline void 13468b8534bSLuigi Rizzo do_cpuid(u_int ax, u_int *p) 13568b8534bSLuigi Rizzo { 13668b8534bSLuigi Rizzo __asm __volatile("cpuid" 13768b8534bSLuigi Rizzo : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) 13868b8534bSLuigi Rizzo : "0" (ax)); 13968b8534bSLuigi Rizzo } 14068b8534bSLuigi Rizzo 14168b8534bSLuigi Rizzo static __inline uint64_t 14268b8534bSLuigi Rizzo rdtsc(void) 14368b8534bSLuigi Rizzo { 14468b8534bSLuigi Rizzo uint64_t rv; 14568b8534bSLuigi Rizzo 14668b8534bSLuigi Rizzo __asm __volatile("rdtsc" : "=A" (rv)); 14768b8534bSLuigi Rizzo return (rv); 14868b8534bSLuigi Rizzo } 14968b8534bSLuigi Rizzo #define MAX_SAMPLES 100000 15068b8534bSLuigi Rizzo #endif /* EXPERIMENTAL */ 15168b8534bSLuigi Rizzo 15268b8534bSLuigi Rizzo 15368b8534bSLuigi Rizzo struct pkt { 15468b8534bSLuigi Rizzo struct ether_header eh; 15568b8534bSLuigi Rizzo struct ip ip; 15668b8534bSLuigi Rizzo struct udphdr udp; 1575819da83SLuigi Rizzo uint8_t body[2048]; // XXX hardwired 15868b8534bSLuigi Rizzo } __attribute__((__packed__)); 15968b8534bSLuigi Rizzo 16068b8534bSLuigi Rizzo /* 16168b8534bSLuigi Rizzo * global arguments for all threads 16268b8534bSLuigi Rizzo */ 16368b8534bSLuigi Rizzo struct glob_arg { 16468b8534bSLuigi Rizzo const char *src_ip; 16568b8534bSLuigi Rizzo const char *dst_ip; 16668b8534bSLuigi Rizzo const char *src_mac; 16768b8534bSLuigi Rizzo const char *dst_mac; 16868b8534bSLuigi Rizzo int pkt_size; 16968b8534bSLuigi Rizzo int burst; 17068b8534bSLuigi Rizzo int npackets; /* total packets to send */ 17168b8534bSLuigi Rizzo int nthreads; 17268b8534bSLuigi Rizzo int cpus; 17399fb123fSLuigi Rizzo int options; /* testing */ 17499fb123fSLuigi Rizzo #define OPT_PREFETCH 1 17599fb123fSLuigi Rizzo #define OPT_ACCESS 2 17699fb123fSLuigi Rizzo #define OPT_COPY 4 17799fb123fSLuigi Rizzo #define OPT_MEMCPY 8 17868b8534bSLuigi Rizzo int use_pcap; 17968b8534bSLuigi Rizzo pcap_t *p; 18068b8534bSLuigi Rizzo }; 18168b8534bSLuigi Rizzo 18268b8534bSLuigi Rizzo struct mystat { 18368b8534bSLuigi Rizzo uint64_t containers[8]; 18468b8534bSLuigi Rizzo }; 18568b8534bSLuigi Rizzo 18668b8534bSLuigi Rizzo /* 18768b8534bSLuigi Rizzo * Arguments for a new thread. The same structure is used by 18868b8534bSLuigi Rizzo * the source and the sink 18968b8534bSLuigi Rizzo */ 19068b8534bSLuigi Rizzo struct targ { 19168b8534bSLuigi Rizzo struct glob_arg *g; 19268b8534bSLuigi Rizzo int used; 19368b8534bSLuigi Rizzo int completed; 1943fe77e68SEd Maste int cancel; 19568b8534bSLuigi Rizzo int fd; 19668b8534bSLuigi Rizzo struct nmreq nmr; 19768b8534bSLuigi Rizzo struct netmap_if *nifp; 19868b8534bSLuigi Rizzo uint16_t qfirst, qlast; /* range of queues to scan */ 19968b8534bSLuigi Rizzo uint64_t count; 20068b8534bSLuigi Rizzo struct timeval tic, toc; 20168b8534bSLuigi Rizzo int me; 20268b8534bSLuigi Rizzo pthread_t thread; 20368b8534bSLuigi Rizzo int affinity; 20468b8534bSLuigi Rizzo 20568b8534bSLuigi Rizzo uint8_t dst_mac[6]; 20668b8534bSLuigi Rizzo uint8_t src_mac[6]; 20768b8534bSLuigi Rizzo u_int dst_mac_range; 20868b8534bSLuigi Rizzo u_int src_mac_range; 20968b8534bSLuigi Rizzo uint32_t dst_ip; 21068b8534bSLuigi Rizzo uint32_t src_ip; 21168b8534bSLuigi Rizzo u_int dst_ip_range; 21268b8534bSLuigi Rizzo u_int src_ip_range; 21368b8534bSLuigi Rizzo 21468b8534bSLuigi Rizzo struct pkt pkt; 21568b8534bSLuigi Rizzo }; 21668b8534bSLuigi Rizzo 21768b8534bSLuigi Rizzo 21868b8534bSLuigi Rizzo static struct targ *targs; 21968b8534bSLuigi Rizzo static int global_nthreads; 22068b8534bSLuigi Rizzo 22168b8534bSLuigi Rizzo /* control-C handler */ 22268b8534bSLuigi Rizzo static void 22368b8534bSLuigi Rizzo sigint_h(__unused int sig) 22468b8534bSLuigi Rizzo { 2253fe77e68SEd Maste for (int i = 0; i < global_nthreads; i++) 2263fe77e68SEd Maste targs[i].cancel = 1; 22768b8534bSLuigi Rizzo 22868b8534bSLuigi Rizzo signal(SIGINT, SIG_DFL); 22968b8534bSLuigi Rizzo } 23068b8534bSLuigi Rizzo 23168b8534bSLuigi Rizzo 23268b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */ 23368b8534bSLuigi Rizzo static int 23468b8534bSLuigi Rizzo system_ncpus(void) 23568b8534bSLuigi Rizzo { 23668b8534bSLuigi Rizzo int mib[2], ncpus; 23768b8534bSLuigi Rizzo size_t len; 23868b8534bSLuigi Rizzo 23968b8534bSLuigi Rizzo mib[0] = CTL_HW; 24068b8534bSLuigi Rizzo mib[1] = HW_NCPU; 24168b8534bSLuigi Rizzo len = sizeof(mib); 24268b8534bSLuigi Rizzo sysctl(mib, 2, &ncpus, &len, NULL, 0); 24368b8534bSLuigi Rizzo 24468b8534bSLuigi Rizzo return (ncpus); 24568b8534bSLuigi Rizzo } 24668b8534bSLuigi Rizzo 24768b8534bSLuigi Rizzo /* 24868b8534bSLuigi Rizzo * locate the src mac address for our interface, put it 24968b8534bSLuigi Rizzo * into the user-supplied buffer. return 0 if ok, -1 on error. 25068b8534bSLuigi Rizzo */ 25168b8534bSLuigi Rizzo static int 25268b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf) 25368b8534bSLuigi Rizzo { 25468b8534bSLuigi Rizzo struct ifaddrs *ifaphead, *ifap; 25568b8534bSLuigi Rizzo int l = sizeof(ifap->ifa_name); 25668b8534bSLuigi Rizzo 25768b8534bSLuigi Rizzo if (getifaddrs(&ifaphead) != 0) { 25868b8534bSLuigi Rizzo D("getifaddrs %s failed", ifname); 25968b8534bSLuigi Rizzo return (-1); 26068b8534bSLuigi Rizzo } 26168b8534bSLuigi Rizzo 26268b8534bSLuigi Rizzo for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 26368b8534bSLuigi Rizzo struct sockaddr_dl *sdl = 26468b8534bSLuigi Rizzo (struct sockaddr_dl *)ifap->ifa_addr; 26568b8534bSLuigi Rizzo uint8_t *mac; 26668b8534bSLuigi Rizzo 26768b8534bSLuigi Rizzo if (!sdl || sdl->sdl_family != AF_LINK) 26868b8534bSLuigi Rizzo continue; 26968b8534bSLuigi Rizzo if (strncmp(ifap->ifa_name, ifname, l) != 0) 27068b8534bSLuigi Rizzo continue; 27168b8534bSLuigi Rizzo mac = (uint8_t *)LLADDR(sdl); 27268b8534bSLuigi Rizzo sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 27368b8534bSLuigi Rizzo mac[0], mac[1], mac[2], 27468b8534bSLuigi Rizzo mac[3], mac[4], mac[5]); 27568b8534bSLuigi Rizzo if (verbose) 27668b8534bSLuigi Rizzo D("source hwaddr %s", buf); 27768b8534bSLuigi Rizzo break; 27868b8534bSLuigi Rizzo } 27968b8534bSLuigi Rizzo freeifaddrs(ifaphead); 28068b8534bSLuigi Rizzo return ifap ? 0 : 1; 28168b8534bSLuigi Rizzo } 28268b8534bSLuigi Rizzo 28368b8534bSLuigi Rizzo 28468b8534bSLuigi Rizzo /* set the thread affinity. */ 28568b8534bSLuigi Rizzo static int 28668b8534bSLuigi Rizzo setaffinity(pthread_t me, int i) 28768b8534bSLuigi Rizzo { 28868b8534bSLuigi Rizzo cpuset_t cpumask; 28968b8534bSLuigi Rizzo 29068b8534bSLuigi Rizzo if (i == -1) 29168b8534bSLuigi Rizzo return 0; 29268b8534bSLuigi Rizzo 29368b8534bSLuigi Rizzo /* Set thread affinity affinity.*/ 29468b8534bSLuigi Rizzo CPU_ZERO(&cpumask); 29568b8534bSLuigi Rizzo CPU_SET(i, &cpumask); 29668b8534bSLuigi Rizzo 29768b8534bSLuigi Rizzo if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 29868b8534bSLuigi Rizzo D("Unable to set affinity"); 29968b8534bSLuigi Rizzo return 1; 30068b8534bSLuigi Rizzo } 30168b8534bSLuigi Rizzo return 0; 30268b8534bSLuigi Rizzo } 30368b8534bSLuigi Rizzo 30468b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */ 30568b8534bSLuigi Rizzo static uint16_t 30668b8534bSLuigi Rizzo checksum(const void *data, uint16_t len) 30768b8534bSLuigi Rizzo { 30868b8534bSLuigi Rizzo const uint8_t *addr = data; 30968b8534bSLuigi Rizzo uint32_t sum = 0; 31068b8534bSLuigi Rizzo 31168b8534bSLuigi Rizzo while (len > 1) { 31268b8534bSLuigi Rizzo sum += addr[0] * 256 + addr[1]; 31368b8534bSLuigi Rizzo addr += 2; 31468b8534bSLuigi Rizzo len -= 2; 31568b8534bSLuigi Rizzo } 31668b8534bSLuigi Rizzo 31768b8534bSLuigi Rizzo if (len == 1) 31868b8534bSLuigi Rizzo sum += *addr * 256; 31968b8534bSLuigi Rizzo 32068b8534bSLuigi Rizzo sum = (sum >> 16) + (sum & 0xffff); 32168b8534bSLuigi Rizzo sum += (sum >> 16); 32268b8534bSLuigi Rizzo 32368b8534bSLuigi Rizzo sum = htons(sum); 32468b8534bSLuigi Rizzo 32568b8534bSLuigi Rizzo return ~sum; 32668b8534bSLuigi Rizzo } 32768b8534bSLuigi Rizzo 32868b8534bSLuigi Rizzo /* 32968b8534bSLuigi Rizzo * Fill a packet with some payload. 33068b8534bSLuigi Rizzo */ 33168b8534bSLuigi Rizzo static void 33268b8534bSLuigi Rizzo initialize_packet(struct targ *targ) 33368b8534bSLuigi Rizzo { 33468b8534bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 33568b8534bSLuigi Rizzo struct ether_header *eh; 33668b8534bSLuigi Rizzo struct ip *ip; 33768b8534bSLuigi Rizzo struct udphdr *udp; 33868b8534bSLuigi Rizzo uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip); 33968b8534bSLuigi Rizzo int i, l, l0 = strlen(default_payload); 34068b8534bSLuigi Rizzo char *p; 34168b8534bSLuigi Rizzo 34268b8534bSLuigi Rizzo for (i = 0; i < paylen;) { 34368b8534bSLuigi Rizzo l = min(l0, paylen - i); 34468b8534bSLuigi Rizzo bcopy(default_payload, pkt->body + i, l); 34568b8534bSLuigi Rizzo i += l; 34668b8534bSLuigi Rizzo } 34768b8534bSLuigi Rizzo pkt->body[i-1] = '\0'; 34868b8534bSLuigi Rizzo 34968b8534bSLuigi Rizzo udp = &pkt->udp; 35068b8534bSLuigi Rizzo udp->uh_sport = htons(1234); 35168b8534bSLuigi Rizzo udp->uh_dport = htons(4321); 35268b8534bSLuigi Rizzo udp->uh_ulen = htons(paylen); 35368b8534bSLuigi Rizzo udp->uh_sum = 0; // checksum(udp, sizeof(*udp)); 35468b8534bSLuigi Rizzo 35568b8534bSLuigi Rizzo ip = &pkt->ip; 35668b8534bSLuigi Rizzo ip->ip_v = IPVERSION; 35768b8534bSLuigi Rizzo ip->ip_hl = 5; 35868b8534bSLuigi Rizzo ip->ip_id = 0; 35968b8534bSLuigi Rizzo ip->ip_tos = IPTOS_LOWDELAY; 36068b8534bSLuigi Rizzo ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 36168b8534bSLuigi Rizzo ip->ip_id = 0; 36268b8534bSLuigi Rizzo ip->ip_off = htons(IP_DF); /* Don't fragment */ 36368b8534bSLuigi Rizzo ip->ip_ttl = IPDEFTTL; 36468b8534bSLuigi Rizzo ip->ip_p = IPPROTO_UDP; 36568b8534bSLuigi Rizzo inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src); 36668b8534bSLuigi Rizzo inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst); 36768b8534bSLuigi Rizzo targ->dst_ip = ip->ip_dst.s_addr; 36868b8534bSLuigi Rizzo targ->src_ip = ip->ip_src.s_addr; 36968b8534bSLuigi Rizzo p = index(targ->g->src_ip, '-'); 37068b8534bSLuigi Rizzo if (p) { 37168b8534bSLuigi Rizzo targ->dst_ip_range = atoi(p+1); 37268b8534bSLuigi Rizzo D("dst-ip sweep %d addresses", targ->dst_ip_range); 37368b8534bSLuigi Rizzo } 37468b8534bSLuigi Rizzo ip->ip_sum = checksum(ip, sizeof(*ip)); 37568b8534bSLuigi Rizzo 37668b8534bSLuigi Rizzo eh = &pkt->eh; 37768b8534bSLuigi Rizzo bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6); 37868b8534bSLuigi Rizzo bcopy(targ->src_mac, eh->ether_shost, 6); 37968b8534bSLuigi Rizzo p = index(targ->g->src_mac, '-'); 38068b8534bSLuigi Rizzo if (p) 38168b8534bSLuigi Rizzo targ->src_mac_range = atoi(p+1); 38268b8534bSLuigi Rizzo 38368b8534bSLuigi Rizzo bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 38468b8534bSLuigi Rizzo bcopy(targ->dst_mac, eh->ether_dhost, 6); 38568b8534bSLuigi Rizzo p = index(targ->g->dst_mac, '-'); 38668b8534bSLuigi Rizzo if (p) 38768b8534bSLuigi Rizzo targ->dst_mac_range = atoi(p+1); 38868b8534bSLuigi Rizzo eh->ether_type = htons(ETHERTYPE_IP); 38968b8534bSLuigi Rizzo } 39068b8534bSLuigi Rizzo 39168b8534bSLuigi Rizzo /* Check the payload of the packet for errors (use it for debug). 39268b8534bSLuigi Rizzo * Look for consecutive ascii representations of the size of the packet. 39368b8534bSLuigi Rizzo */ 39468b8534bSLuigi Rizzo static void 39568b8534bSLuigi Rizzo check_payload(char *p, int psize) 39668b8534bSLuigi Rizzo { 39768b8534bSLuigi Rizzo char temp[64]; 39868b8534bSLuigi Rizzo int n_read, size, sizelen; 39968b8534bSLuigi Rizzo 40068b8534bSLuigi Rizzo /* get the length in ASCII of the length of the packet. */ 40168b8534bSLuigi Rizzo sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace 40268b8534bSLuigi Rizzo 40368b8534bSLuigi Rizzo /* dummy payload. */ 40468b8534bSLuigi Rizzo p += 14; /* skip packet header. */ 40568b8534bSLuigi Rizzo n_read = 14; 40668b8534bSLuigi Rizzo while (psize - n_read >= sizelen) { 40768b8534bSLuigi Rizzo sscanf(p, "%d", &size); 40868b8534bSLuigi Rizzo if (size != psize) { 40968b8534bSLuigi Rizzo D("Read %d instead of %d", size, psize); 41068b8534bSLuigi Rizzo break; 41168b8534bSLuigi Rizzo } 41268b8534bSLuigi Rizzo 41368b8534bSLuigi Rizzo p += sizelen; 41468b8534bSLuigi Rizzo n_read += sizelen; 41568b8534bSLuigi Rizzo } 41668b8534bSLuigi Rizzo } 41768b8534bSLuigi Rizzo 41868b8534bSLuigi Rizzo 41968b8534bSLuigi Rizzo /* 42068b8534bSLuigi Rizzo * create and enqueue a batch of packets on a ring. 42168b8534bSLuigi Rizzo * On the last one set NS_REPORT to tell the driver to generate 42268b8534bSLuigi Rizzo * an interrupt when done. 42368b8534bSLuigi Rizzo */ 42468b8534bSLuigi Rizzo static int 42568b8534bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, 42699fb123fSLuigi Rizzo int size, u_int count, int options) 42768b8534bSLuigi Rizzo { 42868b8534bSLuigi Rizzo u_int sent, cur = ring->cur; 42968b8534bSLuigi Rizzo 43068b8534bSLuigi Rizzo if (ring->avail < count) 43168b8534bSLuigi Rizzo count = ring->avail; 43268b8534bSLuigi Rizzo 43399fb123fSLuigi Rizzo #if 0 43499fb123fSLuigi Rizzo if (options & (OPT_COPY | OPT_PREFETCH) ) { 43568b8534bSLuigi Rizzo for (sent = 0; sent < count; sent++) { 43668b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 43768b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 43868b8534bSLuigi Rizzo 43999fb123fSLuigi Rizzo prefetch(p); 44099fb123fSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 44199fb123fSLuigi Rizzo } 44299fb123fSLuigi Rizzo cur = ring->cur; 44399fb123fSLuigi Rizzo } 44499fb123fSLuigi Rizzo #endif 44599fb123fSLuigi Rizzo for (sent = 0; sent < count; sent++) { 44699fb123fSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 44799fb123fSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 44899fb123fSLuigi Rizzo 44999fb123fSLuigi Rizzo if (options & OPT_COPY) 45099fb123fSLuigi Rizzo pkt_copy(pkt, p, size); 45199fb123fSLuigi Rizzo else if (options & OPT_MEMCPY) 45268b8534bSLuigi Rizzo memcpy(p, pkt, size); 45399fb123fSLuigi Rizzo else if (options & OPT_PREFETCH) 45499fb123fSLuigi Rizzo prefetch(p); 45568b8534bSLuigi Rizzo 45668b8534bSLuigi Rizzo slot->len = size; 45768b8534bSLuigi Rizzo if (sent == count - 1) 45868b8534bSLuigi Rizzo slot->flags |= NS_REPORT; 45968b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 46068b8534bSLuigi Rizzo } 46168b8534bSLuigi Rizzo ring->avail -= sent; 46268b8534bSLuigi Rizzo ring->cur = cur; 46368b8534bSLuigi Rizzo 46468b8534bSLuigi Rizzo return (sent); 46568b8534bSLuigi Rizzo } 46668b8534bSLuigi Rizzo 46768b8534bSLuigi Rizzo static void * 46868b8534bSLuigi Rizzo sender_body(void *data) 46968b8534bSLuigi Rizzo { 47068b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 47168b8534bSLuigi Rizzo 47268b8534bSLuigi Rizzo struct pollfd fds[1]; 47368b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 47468b8534bSLuigi Rizzo struct netmap_ring *txring; 47568b8534bSLuigi Rizzo int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 47699fb123fSLuigi Rizzo int options = targ->g->options | OPT_COPY; 47799fb123fSLuigi Rizzo D("start"); 47868b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 47968b8534bSLuigi Rizzo goto quit; 4808ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 48168b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 48268b8534bSLuigi Rizzo fds[0].fd = targ->fd; 48368b8534bSLuigi Rizzo fds[0].events = (POLLOUT); 48468b8534bSLuigi Rizzo 48568b8534bSLuigi Rizzo /* main loop.*/ 48668b8534bSLuigi Rizzo gettimeofday(&targ->tic, NULL); 48768b8534bSLuigi Rizzo if (targ->g->use_pcap) { 48868b8534bSLuigi Rizzo int size = targ->g->pkt_size; 48968b8534bSLuigi Rizzo void *pkt = &targ->pkt; 49068b8534bSLuigi Rizzo pcap_t *p = targ->g->p; 49168b8534bSLuigi Rizzo 4923fe77e68SEd Maste for (i = 0; sent < n && !targ->cancel; i++) { 49399fb123fSLuigi Rizzo if (pcap_inject(p, pkt, size) != -1) 49499fb123fSLuigi Rizzo sent++; 49599fb123fSLuigi Rizzo if (i > 10000) { 49699fb123fSLuigi Rizzo targ->count = sent; 49799fb123fSLuigi Rizzo i = 0; 49899fb123fSLuigi Rizzo } 49968b8534bSLuigi Rizzo } 50068b8534bSLuigi Rizzo } else { 50168b8534bSLuigi Rizzo while (sent < n) { 50268b8534bSLuigi Rizzo 50368b8534bSLuigi Rizzo /* 50468b8534bSLuigi Rizzo * wait for available room in the send queue(s) 50568b8534bSLuigi Rizzo */ 50668b8534bSLuigi Rizzo if (poll(fds, 1, 2000) <= 0) { 5073fe77e68SEd Maste if (targ->cancel) 5083fe77e68SEd Maste break; 50968b8534bSLuigi Rizzo D("poll error/timeout on queue %d\n", targ->me); 51068b8534bSLuigi Rizzo goto quit; 51168b8534bSLuigi Rizzo } 51268b8534bSLuigi Rizzo /* 51368b8534bSLuigi Rizzo * scan our queues and send on those with room 51468b8534bSLuigi Rizzo */ 51599fb123fSLuigi Rizzo if (sent > 100000 && !(targ->g->options & OPT_COPY) ) 51699fb123fSLuigi Rizzo options &= ~OPT_COPY; 5173fe77e68SEd Maste for (i = targ->qfirst; i < targ->qlast && !targ->cancel; i++) { 51868b8534bSLuigi Rizzo int m, limit = MIN(n - sent, targ->g->burst); 51968b8534bSLuigi Rizzo 52068b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 52168b8534bSLuigi Rizzo if (txring->avail == 0) 52268b8534bSLuigi Rizzo continue; 52368b8534bSLuigi Rizzo m = send_packets(txring, &targ->pkt, targ->g->pkt_size, 52499fb123fSLuigi Rizzo limit, options); 52568b8534bSLuigi Rizzo sent += m; 52668b8534bSLuigi Rizzo targ->count = sent; 52768b8534bSLuigi Rizzo } 5283fe77e68SEd Maste if (targ->cancel) 5293fe77e68SEd Maste break; 53068b8534bSLuigi Rizzo } 53199fb123fSLuigi Rizzo /* flush any remaining packets */ 53268b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 53368b8534bSLuigi Rizzo 53468b8534bSLuigi Rizzo /* final part: wait all the TX queues to be empty. */ 53568b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 53668b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 53768b8534bSLuigi Rizzo while (!NETMAP_TX_RING_EMPTY(txring)) { 53868b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 53968b8534bSLuigi Rizzo usleep(1); /* wait 1 tick */ 54068b8534bSLuigi Rizzo } 54168b8534bSLuigi Rizzo } 54268b8534bSLuigi Rizzo } 54368b8534bSLuigi Rizzo 54468b8534bSLuigi Rizzo gettimeofday(&targ->toc, NULL); 54568b8534bSLuigi Rizzo targ->completed = 1; 54668b8534bSLuigi Rizzo targ->count = sent; 54768b8534bSLuigi Rizzo 54868b8534bSLuigi Rizzo quit: 54968b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 55068b8534bSLuigi Rizzo targ->used = 0; 55168b8534bSLuigi Rizzo 55268b8534bSLuigi Rizzo return (NULL); 55368b8534bSLuigi Rizzo } 55468b8534bSLuigi Rizzo 55568b8534bSLuigi Rizzo 55668b8534bSLuigi Rizzo static void 55768b8534bSLuigi Rizzo receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h, 55868b8534bSLuigi Rizzo __unused const u_char * bytes) 55968b8534bSLuigi Rizzo { 56068b8534bSLuigi Rizzo int *count = (int *)user; 56168b8534bSLuigi Rizzo (*count)++; 56268b8534bSLuigi Rizzo } 56368b8534bSLuigi Rizzo 56468b8534bSLuigi Rizzo static int 56568b8534bSLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload) 56668b8534bSLuigi Rizzo { 56768b8534bSLuigi Rizzo u_int cur, rx; 56868b8534bSLuigi Rizzo 56968b8534bSLuigi Rizzo cur = ring->cur; 57068b8534bSLuigi Rizzo if (ring->avail < limit) 57168b8534bSLuigi Rizzo limit = ring->avail; 57268b8534bSLuigi Rizzo for (rx = 0; rx < limit; rx++) { 57368b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 57468b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 57568b8534bSLuigi Rizzo 57668b8534bSLuigi Rizzo if (!skip_payload) 57768b8534bSLuigi Rizzo check_payload(p, slot->len); 57868b8534bSLuigi Rizzo 57968b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 58068b8534bSLuigi Rizzo } 58168b8534bSLuigi Rizzo ring->avail -= rx; 58268b8534bSLuigi Rizzo ring->cur = cur; 58368b8534bSLuigi Rizzo 58468b8534bSLuigi Rizzo return (rx); 58568b8534bSLuigi Rizzo } 58668b8534bSLuigi Rizzo 58768b8534bSLuigi Rizzo static void * 58868b8534bSLuigi Rizzo receiver_body(void *data) 58968b8534bSLuigi Rizzo { 59068b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 59168b8534bSLuigi Rizzo struct pollfd fds[1]; 59268b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 59368b8534bSLuigi Rizzo struct netmap_ring *rxring; 59468b8534bSLuigi Rizzo int i, received = 0; 59568b8534bSLuigi Rizzo 59668b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 59768b8534bSLuigi Rizzo goto quit; 59868b8534bSLuigi Rizzo 5998ce070c1SUlrich Spörlein /* setup poll(2) mechanism. */ 60068b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 60168b8534bSLuigi Rizzo fds[0].fd = targ->fd; 60268b8534bSLuigi Rizzo fds[0].events = (POLLIN); 60368b8534bSLuigi Rizzo 60468b8534bSLuigi Rizzo /* unbounded wait for the first packet. */ 60568b8534bSLuigi Rizzo for (;;) { 60668b8534bSLuigi Rizzo i = poll(fds, 1, 1000); 60768b8534bSLuigi Rizzo if (i > 0 && !(fds[0].revents & POLLERR)) 60868b8534bSLuigi Rizzo break; 60968b8534bSLuigi Rizzo D("waiting for initial packets, poll returns %d %d", i, fds[0].revents); 61068b8534bSLuigi Rizzo } 61168b8534bSLuigi Rizzo 61268b8534bSLuigi Rizzo /* main loop, exit after 1s silence */ 61368b8534bSLuigi Rizzo gettimeofday(&targ->tic, NULL); 61468b8534bSLuigi Rizzo if (targ->g->use_pcap) { 6153fe77e68SEd Maste while (!targ->cancel) { 61668b8534bSLuigi Rizzo pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 61768b8534bSLuigi Rizzo } 61868b8534bSLuigi Rizzo } else { 6193fe77e68SEd Maste while (!targ->cancel) { 62068b8534bSLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 62168b8534bSLuigi Rizzo before quitting. */ 62268b8534bSLuigi Rizzo if (poll(fds, 1, 1 * 1000) <= 0) { 62368b8534bSLuigi Rizzo gettimeofday(&targ->toc, NULL); 6248ce070c1SUlrich Spörlein targ->toc.tv_sec -= 1; /* Subtract timeout time. */ 62568b8534bSLuigi Rizzo break; 62668b8534bSLuigi Rizzo } 62768b8534bSLuigi Rizzo 62868b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 62968b8534bSLuigi Rizzo int m; 63068b8534bSLuigi Rizzo 63168b8534bSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 63268b8534bSLuigi Rizzo if (rxring->avail == 0) 63368b8534bSLuigi Rizzo continue; 63468b8534bSLuigi Rizzo 63568b8534bSLuigi Rizzo m = receive_packets(rxring, targ->g->burst, 63668b8534bSLuigi Rizzo SKIP_PAYLOAD); 63768b8534bSLuigi Rizzo received += m; 63868b8534bSLuigi Rizzo targ->count = received; 63968b8534bSLuigi Rizzo } 64068b8534bSLuigi Rizzo 64168b8534bSLuigi Rizzo // tell the card we have read the data 64268b8534bSLuigi Rizzo //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 64368b8534bSLuigi Rizzo } 64468b8534bSLuigi Rizzo } 64568b8534bSLuigi Rizzo 64668b8534bSLuigi Rizzo targ->completed = 1; 64768b8534bSLuigi Rizzo targ->count = received; 64868b8534bSLuigi Rizzo 64968b8534bSLuigi Rizzo quit: 65068b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 65168b8534bSLuigi Rizzo targ->used = 0; 65268b8534bSLuigi Rizzo 65368b8534bSLuigi Rizzo return (NULL); 65468b8534bSLuigi Rizzo } 65568b8534bSLuigi Rizzo 65666a698c9SEd Maste static char * 65766a698c9SEd Maste scaled_val(double val) 65866a698c9SEd Maste { 65966a698c9SEd Maste static char buf[64]; 66066a698c9SEd Maste const char *units[] = {"", "K", "M", "G"}; 66166a698c9SEd Maste int i = 0; 66266a698c9SEd Maste 66366a698c9SEd Maste while (val >= 1000 && i < 3) { 66466a698c9SEd Maste val /= 1000; 66566a698c9SEd Maste i++; 66666a698c9SEd Maste } 66766a698c9SEd Maste snprintf(buf, sizeof(buf), "%.2f%s", val, units[i]); 66866a698c9SEd Maste return (buf); 66966a698c9SEd Maste } 67066a698c9SEd Maste 67168b8534bSLuigi Rizzo static void 67268b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta) 67368b8534bSLuigi Rizzo { 67466a698c9SEd Maste uint64_t bytes_sent = sent * size; 67566a698c9SEd Maste double bw = 8.0 * bytes_sent / delta; 67668b8534bSLuigi Rizzo double pps = sent / delta; 67766a698c9SEd Maste /* 67866a698c9SEd Maste * Assume Ethernet overhead of 24 bytes per packet excluding header: 67966a698c9SEd Maste * FCS 4 bytes 68066a698c9SEd Maste * Preamble 8 bytes 68166a698c9SEd Maste * IFG 12 bytes 68266a698c9SEd Maste */ 68366a698c9SEd Maste double bw_with_overhead = 8.0 * (bytes_sent + sent * 24) / delta; 68468b8534bSLuigi Rizzo 685506cc70cSLuigi Rizzo printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n", 68668b8534bSLuigi Rizzo sent, size, delta); 68766a698c9SEd Maste printf("Speed: %spps. ", scaled_val(pps)); 68866a698c9SEd Maste printf("Bandwidth: %sbps ", scaled_val(bw)); 68966a698c9SEd Maste printf("(%sbps with overhead).\n", scaled_val(bw_with_overhead)); 69066a698c9SEd Maste 69168b8534bSLuigi Rizzo } 69268b8534bSLuigi Rizzo 69368b8534bSLuigi Rizzo 69468b8534bSLuigi Rizzo static void 69568b8534bSLuigi Rizzo rx_output(uint64_t received, double delta) 69668b8534bSLuigi Rizzo { 69768b8534bSLuigi Rizzo 69868b8534bSLuigi Rizzo double pps = received / delta; 69968b8534bSLuigi Rizzo char units[4] = { '\0', 'K', 'M', 'G' }; 70068b8534bSLuigi Rizzo int punit = 0; 70168b8534bSLuigi Rizzo 70268b8534bSLuigi Rizzo while (pps >= 1000) { 70368b8534bSLuigi Rizzo pps /= 1000; 70468b8534bSLuigi Rizzo punit += 1; 70568b8534bSLuigi Rizzo } 70668b8534bSLuigi Rizzo 707506cc70cSLuigi Rizzo printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta); 70868b8534bSLuigi Rizzo printf("Speed: %.2f%cpps.\n", pps, units[punit]); 70968b8534bSLuigi Rizzo } 71068b8534bSLuigi Rizzo 71168b8534bSLuigi Rizzo static void 71268b8534bSLuigi Rizzo usage(void) 71368b8534bSLuigi Rizzo { 71468b8534bSLuigi Rizzo const char *cmd = "pkt-gen"; 71568b8534bSLuigi Rizzo fprintf(stderr, 71668b8534bSLuigi Rizzo "Usage:\n" 71768b8534bSLuigi Rizzo "%s arguments\n" 71868b8534bSLuigi Rizzo "\t-i interface interface name\n" 71968b8534bSLuigi Rizzo "\t-t pkts_to_send also forces send mode\n" 72068b8534bSLuigi Rizzo "\t-r pkts_to_receive also forces receive mode\n" 72168b8534bSLuigi Rizzo "\t-l pkts_size in bytes excluding CRC\n" 72268b8534bSLuigi Rizzo "\t-d dst-ip end with %%n to sweep n addresses\n" 72368b8534bSLuigi Rizzo "\t-s src-ip end with %%n to sweep n addresses\n" 72468b8534bSLuigi Rizzo "\t-D dst-mac end with %%n to sweep n addresses\n" 72568b8534bSLuigi Rizzo "\t-S src-mac end with %%n to sweep n addresses\n" 72668b8534bSLuigi Rizzo "\t-b burst size testing, mostly\n" 72768b8534bSLuigi Rizzo "\t-c cores cores to use\n" 72868b8534bSLuigi Rizzo "\t-p threads processes/threads to use\n" 72968b8534bSLuigi Rizzo "\t-T report_ms milliseconds between reports\n" 73068b8534bSLuigi Rizzo "\t-w wait_for_link_time in seconds\n" 73168b8534bSLuigi Rizzo "", 73268b8534bSLuigi Rizzo cmd); 73368b8534bSLuigi Rizzo 73468b8534bSLuigi Rizzo exit(0); 73568b8534bSLuigi Rizzo } 73668b8534bSLuigi Rizzo 73768b8534bSLuigi Rizzo 73868b8534bSLuigi Rizzo int 73968b8534bSLuigi Rizzo main(int arc, char **argv) 74068b8534bSLuigi Rizzo { 74168b8534bSLuigi Rizzo int i, fd; 74299fb123fSLuigi Rizzo char pcap_errbuf[PCAP_ERRBUF_SIZE]; 74368b8534bSLuigi Rizzo 74468b8534bSLuigi Rizzo struct glob_arg g; 74568b8534bSLuigi Rizzo 74668b8534bSLuigi Rizzo struct nmreq nmr; 74768b8534bSLuigi Rizzo void *mmap_addr; /* the mmap address */ 74868b8534bSLuigi Rizzo void *(*td_body)(void *) = receiver_body; 74968b8534bSLuigi Rizzo int ch; 75068b8534bSLuigi Rizzo int report_interval = 1000; /* report interval */ 75168b8534bSLuigi Rizzo char *ifname = NULL; 75268b8534bSLuigi Rizzo int wait_link = 2; 75368b8534bSLuigi Rizzo int devqueues = 1; /* how many device queues */ 75468b8534bSLuigi Rizzo 75568b8534bSLuigi Rizzo bzero(&g, sizeof(g)); 75668b8534bSLuigi Rizzo 75768b8534bSLuigi Rizzo g.src_ip = "10.0.0.1"; 75868b8534bSLuigi Rizzo g.dst_ip = "10.1.0.1"; 75968b8534bSLuigi Rizzo g.dst_mac = "ff:ff:ff:ff:ff:ff"; 76068b8534bSLuigi Rizzo g.src_mac = NULL; 76168b8534bSLuigi Rizzo g.pkt_size = 60; 76268b8534bSLuigi Rizzo g.burst = 512; // default 76368b8534bSLuigi Rizzo g.nthreads = 1; 76468b8534bSLuigi Rizzo g.cpus = 1; 76568b8534bSLuigi Rizzo 76668b8534bSLuigi Rizzo while ( (ch = getopt(arc, argv, 76799fb123fSLuigi Rizzo "i:t:r:l:d:s:D:S:b:c:o:p:PT:w:v")) != -1) { 76868b8534bSLuigi Rizzo switch(ch) { 76968b8534bSLuigi Rizzo default: 77068b8534bSLuigi Rizzo D("bad option %c %s", ch, optarg); 77168b8534bSLuigi Rizzo usage(); 77268b8534bSLuigi Rizzo break; 77399fb123fSLuigi Rizzo case 'o': 77499fb123fSLuigi Rizzo g.options = atoi(optarg); 77599fb123fSLuigi Rizzo break; 77668b8534bSLuigi Rizzo case 'i': /* interface */ 77768b8534bSLuigi Rizzo ifname = optarg; 77868b8534bSLuigi Rizzo break; 77968b8534bSLuigi Rizzo case 't': /* send */ 78068b8534bSLuigi Rizzo td_body = sender_body; 78168b8534bSLuigi Rizzo g.npackets = atoi(optarg); 78268b8534bSLuigi Rizzo break; 78368b8534bSLuigi Rizzo case 'r': /* receive */ 78468b8534bSLuigi Rizzo td_body = receiver_body; 78568b8534bSLuigi Rizzo g.npackets = atoi(optarg); 78668b8534bSLuigi Rizzo break; 78768b8534bSLuigi Rizzo case 'l': /* pkt_size */ 78868b8534bSLuigi Rizzo g.pkt_size = atoi(optarg); 78968b8534bSLuigi Rizzo break; 79068b8534bSLuigi Rizzo case 'd': 79168b8534bSLuigi Rizzo g.dst_ip = optarg; 79268b8534bSLuigi Rizzo break; 79368b8534bSLuigi Rizzo case 's': 79468b8534bSLuigi Rizzo g.src_ip = optarg; 79568b8534bSLuigi Rizzo break; 79668b8534bSLuigi Rizzo case 'T': /* report interval */ 79768b8534bSLuigi Rizzo report_interval = atoi(optarg); 79868b8534bSLuigi Rizzo break; 79968b8534bSLuigi Rizzo case 'w': 80068b8534bSLuigi Rizzo wait_link = atoi(optarg); 80168b8534bSLuigi Rizzo break; 80268b8534bSLuigi Rizzo case 'b': /* burst */ 80368b8534bSLuigi Rizzo g.burst = atoi(optarg); 80468b8534bSLuigi Rizzo break; 80568b8534bSLuigi Rizzo case 'c': 80668b8534bSLuigi Rizzo g.cpus = atoi(optarg); 80768b8534bSLuigi Rizzo break; 80868b8534bSLuigi Rizzo case 'p': 80968b8534bSLuigi Rizzo g.nthreads = atoi(optarg); 81068b8534bSLuigi Rizzo break; 81168b8534bSLuigi Rizzo 81268b8534bSLuigi Rizzo case 'P': 81368b8534bSLuigi Rizzo g.use_pcap = 1; 81468b8534bSLuigi Rizzo break; 81568b8534bSLuigi Rizzo 81668b8534bSLuigi Rizzo case 'D': /* destination mac */ 81768b8534bSLuigi Rizzo g.dst_mac = optarg; 81868b8534bSLuigi Rizzo { 81968b8534bSLuigi Rizzo struct ether_addr *mac = ether_aton(g.dst_mac); 82068b8534bSLuigi Rizzo D("ether_aton(%s) gives %p", g.dst_mac, mac); 82168b8534bSLuigi Rizzo } 82268b8534bSLuigi Rizzo break; 82368b8534bSLuigi Rizzo case 'S': /* source mac */ 82468b8534bSLuigi Rizzo g.src_mac = optarg; 82568b8534bSLuigi Rizzo break; 82668b8534bSLuigi Rizzo case 'v': 82768b8534bSLuigi Rizzo verbose++; 82868b8534bSLuigi Rizzo } 82968b8534bSLuigi Rizzo } 83068b8534bSLuigi Rizzo 83168b8534bSLuigi Rizzo if (ifname == NULL) { 83268b8534bSLuigi Rizzo D("missing ifname"); 83368b8534bSLuigi Rizzo usage(); 83468b8534bSLuigi Rizzo } 83568b8534bSLuigi Rizzo { 83668b8534bSLuigi Rizzo int n = system_ncpus(); 83768b8534bSLuigi Rizzo if (g.cpus < 0 || g.cpus > n) { 83868b8534bSLuigi Rizzo D("%d cpus is too high, have only %d cpus", g.cpus, n); 83968b8534bSLuigi Rizzo usage(); 84068b8534bSLuigi Rizzo } 84168b8534bSLuigi Rizzo if (g.cpus == 0) 84268b8534bSLuigi Rizzo g.cpus = n; 84368b8534bSLuigi Rizzo } 84468b8534bSLuigi Rizzo if (g.pkt_size < 16 || g.pkt_size > 1536) { 84568b8534bSLuigi Rizzo D("bad pktsize %d\n", g.pkt_size); 84668b8534bSLuigi Rizzo usage(); 84768b8534bSLuigi Rizzo } 84868b8534bSLuigi Rizzo 84999fb123fSLuigi Rizzo if (td_body == sender_body && g.src_mac == NULL) { 85099fb123fSLuigi Rizzo static char mybuf[20] = "ff:ff:ff:ff:ff:ff"; 85199fb123fSLuigi Rizzo /* retrieve source mac address. */ 85299fb123fSLuigi Rizzo if (source_hwaddr(ifname, mybuf) == -1) { 85399fb123fSLuigi Rizzo D("Unable to retrieve source mac"); 85499fb123fSLuigi Rizzo // continue, fail later 85599fb123fSLuigi Rizzo } 85699fb123fSLuigi Rizzo g.src_mac = mybuf; 85799fb123fSLuigi Rizzo } 85899fb123fSLuigi Rizzo 85999fb123fSLuigi Rizzo if (g.use_pcap) { 86099fb123fSLuigi Rizzo D("using pcap on %s", ifname); 86199fb123fSLuigi Rizzo g.p = pcap_open_live(ifname, 0, 1, 100, pcap_errbuf); 86299fb123fSLuigi Rizzo if (g.p == NULL) { 86399fb123fSLuigi Rizzo D("cannot open pcap on %s", ifname); 86499fb123fSLuigi Rizzo usage(); 86599fb123fSLuigi Rizzo } 86699fb123fSLuigi Rizzo mmap_addr = NULL; 86799fb123fSLuigi Rizzo fd = -1; 86899fb123fSLuigi Rizzo } else { 86968b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 87064ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 87168b8534bSLuigi Rizzo /* 87268b8534bSLuigi Rizzo * Open the netmap device to fetch the number of queues of our 87368b8534bSLuigi Rizzo * interface. 87468b8534bSLuigi Rizzo * 87568b8534bSLuigi Rizzo * The first NIOCREGIF also detaches the card from the 87668b8534bSLuigi Rizzo * protocol stack and may cause a reset of the card, 87768b8534bSLuigi Rizzo * which in turn may take some time for the PHY to 87868b8534bSLuigi Rizzo * reconfigure. 87968b8534bSLuigi Rizzo */ 88068b8534bSLuigi Rizzo fd = open("/dev/netmap", O_RDWR); 88168b8534bSLuigi Rizzo if (fd == -1) { 88268b8534bSLuigi Rizzo D("Unable to open /dev/netmap"); 88368b8534bSLuigi Rizzo // fail later 88468b8534bSLuigi Rizzo } else { 88568b8534bSLuigi Rizzo if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 88668b8534bSLuigi Rizzo D("Unable to get if info without name"); 88768b8534bSLuigi Rizzo } else { 88868b8534bSLuigi Rizzo D("map size is %d Kb", nmr.nr_memsize >> 10); 88968b8534bSLuigi Rizzo } 89068b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 89164ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 89268b8534bSLuigi Rizzo strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name)); 89368b8534bSLuigi Rizzo if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 89468b8534bSLuigi Rizzo D("Unable to get if info for %s", ifname); 89568b8534bSLuigi Rizzo } 89664ae02c3SLuigi Rizzo devqueues = nmr.nr_rx_rings; 89768b8534bSLuigi Rizzo } 89868b8534bSLuigi Rizzo 89968b8534bSLuigi Rizzo /* validate provided nthreads. */ 90068b8534bSLuigi Rizzo if (g.nthreads < 1 || g.nthreads > devqueues) { 90168b8534bSLuigi Rizzo D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 90268b8534bSLuigi Rizzo // continue, fail later 90368b8534bSLuigi Rizzo } 90468b8534bSLuigi Rizzo 90568b8534bSLuigi Rizzo /* 90668b8534bSLuigi Rizzo * Map the netmap shared memory: instead of issuing mmap() 90768b8534bSLuigi Rizzo * inside the body of the threads, we prefer to keep this 90868b8534bSLuigi Rizzo * operation here to simplify the thread logic. 90968b8534bSLuigi Rizzo */ 91068b8534bSLuigi Rizzo D("mmapping %d Kbytes", nmr.nr_memsize>>10); 91168b8534bSLuigi Rizzo mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 91268b8534bSLuigi Rizzo PROT_WRITE | PROT_READ, 91368b8534bSLuigi Rizzo MAP_SHARED, fd, 0); 91468b8534bSLuigi Rizzo if (mmap_addr == MAP_FAILED) { 91568b8534bSLuigi Rizzo D("Unable to mmap %d KB", nmr.nr_memsize >> 10); 91668b8534bSLuigi Rizzo // continue, fail later 91768b8534bSLuigi Rizzo } 91868b8534bSLuigi Rizzo 91968b8534bSLuigi Rizzo /* 92068b8534bSLuigi Rizzo * Register the interface on the netmap device: from now on, 92168b8534bSLuigi Rizzo * we can operate on the network interface without any 92268b8534bSLuigi Rizzo * interference from the legacy network stack. 92368b8534bSLuigi Rizzo * 92468b8534bSLuigi Rizzo * We decide to put the first interface registration here to 92568b8534bSLuigi Rizzo * give time to cards that take a long time to reset the PHY. 92668b8534bSLuigi Rizzo */ 92764ae02c3SLuigi Rizzo nmr.nr_version = NETMAP_API; 92868b8534bSLuigi Rizzo if (ioctl(fd, NIOCREGIF, &nmr) == -1) { 92968b8534bSLuigi Rizzo D("Unable to register interface %s", ifname); 93068b8534bSLuigi Rizzo //continue, fail later 93168b8534bSLuigi Rizzo } 93268b8534bSLuigi Rizzo 93368b8534bSLuigi Rizzo 93468b8534bSLuigi Rizzo /* Print some debug information. */ 93568b8534bSLuigi Rizzo fprintf(stdout, 93668b8534bSLuigi Rizzo "%s %s: %d queues, %d threads and %d cpus.\n", 93768b8534bSLuigi Rizzo (td_body == sender_body) ? "Sending on" : "Receiving from", 93868b8534bSLuigi Rizzo ifname, 93968b8534bSLuigi Rizzo devqueues, 94068b8534bSLuigi Rizzo g.nthreads, 94168b8534bSLuigi Rizzo g.cpus); 94268b8534bSLuigi Rizzo if (td_body == sender_body) { 94368b8534bSLuigi Rizzo fprintf(stdout, "%s -> %s (%s -> %s)\n", 94468b8534bSLuigi Rizzo g.src_ip, g.dst_ip, 94568b8534bSLuigi Rizzo g.src_mac, g.dst_mac); 94668b8534bSLuigi Rizzo } 94768b8534bSLuigi Rizzo 94868b8534bSLuigi Rizzo /* Exit if something went wrong. */ 94968b8534bSLuigi Rizzo if (fd < 0) { 95068b8534bSLuigi Rizzo D("aborting"); 95168b8534bSLuigi Rizzo usage(); 95268b8534bSLuigi Rizzo } 95399fb123fSLuigi Rizzo } 95468b8534bSLuigi Rizzo 95599fb123fSLuigi Rizzo if (g.options) { 95699fb123fSLuigi Rizzo D("special options:%s%s%s%s\n", 95799fb123fSLuigi Rizzo g.options & OPT_PREFETCH ? " prefetch" : "", 95899fb123fSLuigi Rizzo g.options & OPT_ACCESS ? " access" : "", 95999fb123fSLuigi Rizzo g.options & OPT_MEMCPY ? " memcpy" : "", 96099fb123fSLuigi Rizzo g.options & OPT_COPY ? " copy" : ""); 96199fb123fSLuigi Rizzo } 96268b8534bSLuigi Rizzo /* Wait for PHY reset. */ 96368b8534bSLuigi Rizzo D("Wait %d secs for phy reset", wait_link); 96468b8534bSLuigi Rizzo sleep(wait_link); 96568b8534bSLuigi Rizzo D("Ready..."); 96668b8534bSLuigi Rizzo 96768b8534bSLuigi Rizzo /* Install ^C handler. */ 96868b8534bSLuigi Rizzo global_nthreads = g.nthreads; 96968b8534bSLuigi Rizzo signal(SIGINT, sigint_h); 97068b8534bSLuigi Rizzo 97168b8534bSLuigi Rizzo if (g.use_pcap) { 97299fb123fSLuigi Rizzo g.p = pcap_open_live(ifname, 0, 1, 100, NULL); 97399fb123fSLuigi Rizzo if (g.p == NULL) { 97499fb123fSLuigi Rizzo D("cannot open pcap on %s", ifname); 97599fb123fSLuigi Rizzo usage(); 97699fb123fSLuigi Rizzo } else 97799fb123fSLuigi Rizzo D("using pcap %p on %s", g.p, ifname); 97868b8534bSLuigi Rizzo } 97968b8534bSLuigi Rizzo 98068b8534bSLuigi Rizzo targs = calloc(g.nthreads, sizeof(*targs)); 98168b8534bSLuigi Rizzo /* 98268b8534bSLuigi Rizzo * Now create the desired number of threads, each one 98368b8534bSLuigi Rizzo * using a single descriptor. 98468b8534bSLuigi Rizzo */ 98568b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 98668b8534bSLuigi Rizzo struct netmap_if *tnifp; 98768b8534bSLuigi Rizzo struct nmreq tifreq; 98868b8534bSLuigi Rizzo int tfd; 98968b8534bSLuigi Rizzo 99068b8534bSLuigi Rizzo if (g.use_pcap) { 99168b8534bSLuigi Rizzo tfd = -1; 99268b8534bSLuigi Rizzo tnifp = NULL; 99368b8534bSLuigi Rizzo } else { 99468b8534bSLuigi Rizzo /* register interface. */ 99568b8534bSLuigi Rizzo tfd = open("/dev/netmap", O_RDWR); 99668b8534bSLuigi Rizzo if (tfd == -1) { 99768b8534bSLuigi Rizzo D("Unable to open /dev/netmap"); 99868b8534bSLuigi Rizzo continue; 99968b8534bSLuigi Rizzo } 100068b8534bSLuigi Rizzo 100168b8534bSLuigi Rizzo bzero(&tifreq, sizeof(tifreq)); 100268b8534bSLuigi Rizzo strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name)); 100364ae02c3SLuigi Rizzo tifreq.nr_version = NETMAP_API; 100468b8534bSLuigi Rizzo tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 100568b8534bSLuigi Rizzo 100668b8534bSLuigi Rizzo /* 100768b8534bSLuigi Rizzo * if we are acting as a receiver only, do not touch the transmit ring. 100868b8534bSLuigi Rizzo * This is not the default because many apps may use the interface 100968b8534bSLuigi Rizzo * in both directions, but a pure receiver does not. 101068b8534bSLuigi Rizzo */ 101168b8534bSLuigi Rizzo if (td_body == receiver_body) { 101268b8534bSLuigi Rizzo tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 101368b8534bSLuigi Rizzo } 101468b8534bSLuigi Rizzo 101568b8534bSLuigi Rizzo if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 101668b8534bSLuigi Rizzo D("Unable to register %s", ifname); 101768b8534bSLuigi Rizzo continue; 101868b8534bSLuigi Rizzo } 101968b8534bSLuigi Rizzo tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset); 102068b8534bSLuigi Rizzo } 102168b8534bSLuigi Rizzo /* start threads. */ 102268b8534bSLuigi Rizzo bzero(&targs[i], sizeof(targs[i])); 102368b8534bSLuigi Rizzo targs[i].g = &g; 102468b8534bSLuigi Rizzo targs[i].used = 1; 102568b8534bSLuigi Rizzo targs[i].completed = 0; 102668b8534bSLuigi Rizzo targs[i].fd = tfd; 102768b8534bSLuigi Rizzo targs[i].nmr = tifreq; 102868b8534bSLuigi Rizzo targs[i].nifp = tnifp; 102968b8534bSLuigi Rizzo targs[i].qfirst = (g.nthreads > 1) ? i : 0; 103064ae02c3SLuigi Rizzo targs[i].qlast = (g.nthreads > 1) ? i+1 : 103164ae02c3SLuigi Rizzo (td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings); 103268b8534bSLuigi Rizzo targs[i].me = i; 103368b8534bSLuigi Rizzo targs[i].affinity = g.cpus ? i % g.cpus : -1; 103468b8534bSLuigi Rizzo if (td_body == sender_body) { 103568b8534bSLuigi Rizzo /* initialize the packet to send. */ 103668b8534bSLuigi Rizzo initialize_packet(&targs[i]); 103768b8534bSLuigi Rizzo } 103868b8534bSLuigi Rizzo 103968b8534bSLuigi Rizzo if (pthread_create(&targs[i].thread, NULL, td_body, 104068b8534bSLuigi Rizzo &targs[i]) == -1) { 104168b8534bSLuigi Rizzo D("Unable to create thread %d", i); 104268b8534bSLuigi Rizzo targs[i].used = 0; 104368b8534bSLuigi Rizzo } 104468b8534bSLuigi Rizzo } 104568b8534bSLuigi Rizzo 104668b8534bSLuigi Rizzo { 104768b8534bSLuigi Rizzo uint64_t my_count = 0, prev = 0; 104868b8534bSLuigi Rizzo uint64_t count = 0; 104968b8534bSLuigi Rizzo double delta_t; 105068b8534bSLuigi Rizzo struct timeval tic, toc; 105168b8534bSLuigi Rizzo 105268b8534bSLuigi Rizzo gettimeofday(&toc, NULL); 105368b8534bSLuigi Rizzo for (;;) { 105468b8534bSLuigi Rizzo struct timeval now, delta; 105568b8534bSLuigi Rizzo uint64_t pps; 105668b8534bSLuigi Rizzo int done = 0; 105768b8534bSLuigi Rizzo 105868b8534bSLuigi Rizzo delta.tv_sec = report_interval/1000; 105968b8534bSLuigi Rizzo delta.tv_usec = (report_interval%1000)*1000; 106068b8534bSLuigi Rizzo select(0, NULL, NULL, NULL, &delta); 106168b8534bSLuigi Rizzo gettimeofday(&now, NULL); 106268b8534bSLuigi Rizzo timersub(&now, &toc, &toc); 106368b8534bSLuigi Rizzo my_count = 0; 106468b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 106568b8534bSLuigi Rizzo my_count += targs[i].count; 106668b8534bSLuigi Rizzo if (targs[i].used == 0) 106768b8534bSLuigi Rizzo done++; 106868b8534bSLuigi Rizzo } 106968b8534bSLuigi Rizzo pps = toc.tv_sec* 1000000 + toc.tv_usec; 107068b8534bSLuigi Rizzo if (pps < 10000) 107168b8534bSLuigi Rizzo continue; 107268b8534bSLuigi Rizzo pps = (my_count - prev)*1000000 / pps; 1073506cc70cSLuigi Rizzo D("%" PRIu64 " pps", pps); 107468b8534bSLuigi Rizzo prev = my_count; 107568b8534bSLuigi Rizzo toc = now; 107668b8534bSLuigi Rizzo if (done == g.nthreads) 107768b8534bSLuigi Rizzo break; 107868b8534bSLuigi Rizzo } 107968b8534bSLuigi Rizzo 108068b8534bSLuigi Rizzo timerclear(&tic); 108168b8534bSLuigi Rizzo timerclear(&toc); 108268b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 108368b8534bSLuigi Rizzo /* 108468b8534bSLuigi Rizzo * Join active threads, unregister interfaces and close 108568b8534bSLuigi Rizzo * file descriptors. 108668b8534bSLuigi Rizzo */ 108768b8534bSLuigi Rizzo pthread_join(targs[i].thread, NULL); 108868b8534bSLuigi Rizzo ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr); 108968b8534bSLuigi Rizzo close(targs[i].fd); 109068b8534bSLuigi Rizzo 109168b8534bSLuigi Rizzo if (targs[i].completed == 0) 109268b8534bSLuigi Rizzo continue; 109368b8534bSLuigi Rizzo 109468b8534bSLuigi Rizzo /* 10958ce070c1SUlrich Spörlein * Collect threads output and extract information about 10968ce070c1SUlrich Spörlein * how long it took to send all the packets. 109768b8534bSLuigi Rizzo */ 109868b8534bSLuigi Rizzo count += targs[i].count; 109968b8534bSLuigi Rizzo if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <)) 110068b8534bSLuigi Rizzo tic = targs[i].tic; 110168b8534bSLuigi Rizzo if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >)) 110268b8534bSLuigi Rizzo toc = targs[i].toc; 110368b8534bSLuigi Rizzo } 110468b8534bSLuigi Rizzo 110568b8534bSLuigi Rizzo /* print output. */ 110668b8534bSLuigi Rizzo timersub(&toc, &tic, &toc); 110768b8534bSLuigi Rizzo delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 110868b8534bSLuigi Rizzo if (td_body == sender_body) 110968b8534bSLuigi Rizzo tx_output(count, g.pkt_size, delta_t); 111068b8534bSLuigi Rizzo else 111168b8534bSLuigi Rizzo rx_output(count, delta_t); 111268b8534bSLuigi Rizzo } 111368b8534bSLuigi Rizzo 111499fb123fSLuigi Rizzo if (g.use_pcap == 0) { 111568b8534bSLuigi Rizzo ioctl(fd, NIOCUNREGIF, &nmr); 111668b8534bSLuigi Rizzo munmap(mmap_addr, nmr.nr_memsize); 111768b8534bSLuigi Rizzo close(fd); 111899fb123fSLuigi Rizzo } 111968b8534bSLuigi Rizzo 112068b8534bSLuigi Rizzo return (0); 112168b8534bSLuigi Rizzo } 112268b8534bSLuigi Rizzo /* end of file */ 1123