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$ 2868b8534bSLuigi Rizzo * $Id: pkt-gen.c 9638 2011-11-07 18:07:43Z 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> 4868b8534bSLuigi Rizzo #include <string.h> /* strcmp */ 4968b8534bSLuigi Rizzo #include <fcntl.h> /* open */ 5068b8534bSLuigi Rizzo #include <unistd.h> /* close */ 5168b8534bSLuigi Rizzo #include <ifaddrs.h> /* getifaddrs */ 5268b8534bSLuigi Rizzo 5368b8534bSLuigi Rizzo #include <sys/mman.h> /* PROT_* */ 5468b8534bSLuigi Rizzo #include <sys/ioctl.h> /* ioctl */ 5568b8534bSLuigi Rizzo #include <sys/poll.h> 5668b8534bSLuigi Rizzo #include <sys/socket.h> /* sockaddr.. */ 5768b8534bSLuigi Rizzo #include <arpa/inet.h> /* ntohs */ 5868b8534bSLuigi Rizzo #include <sys/param.h> 5968b8534bSLuigi Rizzo #include <sys/cpuset.h> /* cpu_set */ 6068b8534bSLuigi Rizzo #include <sys/sysctl.h> /* sysctl */ 6168b8534bSLuigi Rizzo #include <sys/time.h> /* timersub */ 6268b8534bSLuigi Rizzo 6368b8534bSLuigi Rizzo #include <net/ethernet.h> 6468b8534bSLuigi Rizzo #include <net/if.h> /* ifreq */ 6568b8534bSLuigi Rizzo #include <net/if_dl.h> /* LLADDR */ 6668b8534bSLuigi Rizzo 6768b8534bSLuigi Rizzo #include <netinet/in.h> 6868b8534bSLuigi Rizzo #include <netinet/ip.h> 6968b8534bSLuigi Rizzo #include <netinet/udp.h> 7068b8534bSLuigi Rizzo 7168b8534bSLuigi Rizzo #include <net/netmap.h> 7268b8534bSLuigi Rizzo #include <net/netmap_user.h> 7368b8534bSLuigi Rizzo #include <pcap/pcap.h> 7468b8534bSLuigi Rizzo 7568b8534bSLuigi Rizzo 7668b8534bSLuigi Rizzo static inline int min(int a, int b) { return a < b ? a : b; } 7768b8534bSLuigi Rizzo 7868b8534bSLuigi Rizzo /* debug support */ 7968b8534bSLuigi Rizzo #define D(format, ...) \ 8068b8534bSLuigi Rizzo fprintf(stderr, "%s [%d] " format "\n", \ 8168b8534bSLuigi Rizzo __FUNCTION__, __LINE__, ##__VA_ARGS__) 8268b8534bSLuigi Rizzo 8368b8534bSLuigi Rizzo #ifndef EXPERIMENTAL 8468b8534bSLuigi Rizzo #define EXPERIMENTAL 0 8568b8534bSLuigi Rizzo #endif 8668b8534bSLuigi Rizzo 8768b8534bSLuigi Rizzo int verbose = 0; 8868b8534bSLuigi Rizzo #define MAX_QUEUES 64 /* no need to limit */ 8968b8534bSLuigi Rizzo 9068b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */ 9168b8534bSLuigi Rizzo 9268b8534bSLuigi Rizzo #if EXPERIMENTAL 9368b8534bSLuigi Rizzo /* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */ 9468b8534bSLuigi Rizzo #define netmap_rdtsc(t) \ 9568b8534bSLuigi Rizzo do { \ 9668b8534bSLuigi Rizzo u_int __regs[4]; \ 9768b8534bSLuigi Rizzo \ 9868b8534bSLuigi Rizzo do_cpuid(0, __regs); \ 9968b8534bSLuigi Rizzo (t) = rdtsc(); \ 10068b8534bSLuigi Rizzo } while (0) 10168b8534bSLuigi Rizzo 10268b8534bSLuigi Rizzo static __inline void 10368b8534bSLuigi Rizzo do_cpuid(u_int ax, u_int *p) 10468b8534bSLuigi Rizzo { 10568b8534bSLuigi Rizzo __asm __volatile("cpuid" 10668b8534bSLuigi Rizzo : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) 10768b8534bSLuigi Rizzo : "0" (ax)); 10868b8534bSLuigi Rizzo } 10968b8534bSLuigi Rizzo 11068b8534bSLuigi Rizzo static __inline uint64_t 11168b8534bSLuigi Rizzo rdtsc(void) 11268b8534bSLuigi Rizzo { 11368b8534bSLuigi Rizzo uint64_t rv; 11468b8534bSLuigi Rizzo 11568b8534bSLuigi Rizzo __asm __volatile("rdtsc" : "=A" (rv)); 11668b8534bSLuigi Rizzo return (rv); 11768b8534bSLuigi Rizzo } 11868b8534bSLuigi Rizzo #define MAX_SAMPLES 100000 11968b8534bSLuigi Rizzo #endif /* EXPERIMENTAL */ 12068b8534bSLuigi Rizzo 12168b8534bSLuigi Rizzo 12268b8534bSLuigi Rizzo struct pkt { 12368b8534bSLuigi Rizzo struct ether_header eh; 12468b8534bSLuigi Rizzo struct ip ip; 12568b8534bSLuigi Rizzo struct udphdr udp; 12668b8534bSLuigi Rizzo uint8_t body[NETMAP_BUF_SIZE]; 12768b8534bSLuigi Rizzo } __attribute__((__packed__)); 12868b8534bSLuigi Rizzo 12968b8534bSLuigi Rizzo /* 13068b8534bSLuigi Rizzo * global arguments for all threads 13168b8534bSLuigi Rizzo */ 13268b8534bSLuigi Rizzo struct glob_arg { 13368b8534bSLuigi Rizzo const char *src_ip; 13468b8534bSLuigi Rizzo const char *dst_ip; 13568b8534bSLuigi Rizzo const char *src_mac; 13668b8534bSLuigi Rizzo const char *dst_mac; 13768b8534bSLuigi Rizzo int pkt_size; 13868b8534bSLuigi Rizzo int burst; 13968b8534bSLuigi Rizzo int npackets; /* total packets to send */ 14068b8534bSLuigi Rizzo int nthreads; 14168b8534bSLuigi Rizzo int cpus; 14268b8534bSLuigi Rizzo int use_pcap; 14368b8534bSLuigi Rizzo pcap_t *p; 14468b8534bSLuigi Rizzo }; 14568b8534bSLuigi Rizzo 14668b8534bSLuigi Rizzo struct mystat { 14768b8534bSLuigi Rizzo uint64_t containers[8]; 14868b8534bSLuigi Rizzo }; 14968b8534bSLuigi Rizzo 15068b8534bSLuigi Rizzo /* 15168b8534bSLuigi Rizzo * Arguments for a new thread. The same structure is used by 15268b8534bSLuigi Rizzo * the source and the sink 15368b8534bSLuigi Rizzo */ 15468b8534bSLuigi Rizzo struct targ { 15568b8534bSLuigi Rizzo struct glob_arg *g; 15668b8534bSLuigi Rizzo int used; 15768b8534bSLuigi Rizzo int completed; 15868b8534bSLuigi Rizzo int fd; 15968b8534bSLuigi Rizzo struct nmreq nmr; 16068b8534bSLuigi Rizzo struct netmap_if *nifp; 16168b8534bSLuigi Rizzo uint16_t qfirst, qlast; /* range of queues to scan */ 16268b8534bSLuigi Rizzo uint64_t count; 16368b8534bSLuigi Rizzo struct timeval tic, toc; 16468b8534bSLuigi Rizzo int me; 16568b8534bSLuigi Rizzo pthread_t thread; 16668b8534bSLuigi Rizzo int affinity; 16768b8534bSLuigi Rizzo 16868b8534bSLuigi Rizzo uint8_t dst_mac[6]; 16968b8534bSLuigi Rizzo uint8_t src_mac[6]; 17068b8534bSLuigi Rizzo u_int dst_mac_range; 17168b8534bSLuigi Rizzo u_int src_mac_range; 17268b8534bSLuigi Rizzo uint32_t dst_ip; 17368b8534bSLuigi Rizzo uint32_t src_ip; 17468b8534bSLuigi Rizzo u_int dst_ip_range; 17568b8534bSLuigi Rizzo u_int src_ip_range; 17668b8534bSLuigi Rizzo 17768b8534bSLuigi Rizzo struct pkt pkt; 17868b8534bSLuigi Rizzo }; 17968b8534bSLuigi Rizzo 18068b8534bSLuigi Rizzo 18168b8534bSLuigi Rizzo static struct targ *targs; 18268b8534bSLuigi Rizzo static int global_nthreads; 18368b8534bSLuigi Rizzo 18468b8534bSLuigi Rizzo /* control-C handler */ 18568b8534bSLuigi Rizzo static void 18668b8534bSLuigi Rizzo sigint_h(__unused int sig) 18768b8534bSLuigi Rizzo { 18868b8534bSLuigi Rizzo for (int i = 0; i < global_nthreads; i++) { 18968b8534bSLuigi Rizzo /* cancel active threads. */ 19068b8534bSLuigi Rizzo if (targs[i].used == 0) 19168b8534bSLuigi Rizzo continue; 19268b8534bSLuigi Rizzo 19368b8534bSLuigi Rizzo D("Cancelling thread #%d\n", i); 19468b8534bSLuigi Rizzo pthread_cancel(targs[i].thread); 19568b8534bSLuigi Rizzo targs[i].used = 0; 19668b8534bSLuigi Rizzo } 19768b8534bSLuigi Rizzo 19868b8534bSLuigi Rizzo signal(SIGINT, SIG_DFL); 19968b8534bSLuigi Rizzo } 20068b8534bSLuigi Rizzo 20168b8534bSLuigi Rizzo 20268b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */ 20368b8534bSLuigi Rizzo static int 20468b8534bSLuigi Rizzo system_ncpus(void) 20568b8534bSLuigi Rizzo { 20668b8534bSLuigi Rizzo int mib[2], ncpus; 20768b8534bSLuigi Rizzo size_t len; 20868b8534bSLuigi Rizzo 20968b8534bSLuigi Rizzo mib[0] = CTL_HW; 21068b8534bSLuigi Rizzo mib[1] = HW_NCPU; 21168b8534bSLuigi Rizzo len = sizeof(mib); 21268b8534bSLuigi Rizzo sysctl(mib, 2, &ncpus, &len, NULL, 0); 21368b8534bSLuigi Rizzo 21468b8534bSLuigi Rizzo return (ncpus); 21568b8534bSLuigi Rizzo } 21668b8534bSLuigi Rizzo 21768b8534bSLuigi Rizzo /* 21868b8534bSLuigi Rizzo * locate the src mac address for our interface, put it 21968b8534bSLuigi Rizzo * into the user-supplied buffer. return 0 if ok, -1 on error. 22068b8534bSLuigi Rizzo */ 22168b8534bSLuigi Rizzo static int 22268b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf) 22368b8534bSLuigi Rizzo { 22468b8534bSLuigi Rizzo struct ifaddrs *ifaphead, *ifap; 22568b8534bSLuigi Rizzo int l = sizeof(ifap->ifa_name); 22668b8534bSLuigi Rizzo 22768b8534bSLuigi Rizzo if (getifaddrs(&ifaphead) != 0) { 22868b8534bSLuigi Rizzo D("getifaddrs %s failed", ifname); 22968b8534bSLuigi Rizzo return (-1); 23068b8534bSLuigi Rizzo } 23168b8534bSLuigi Rizzo 23268b8534bSLuigi Rizzo for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { 23368b8534bSLuigi Rizzo struct sockaddr_dl *sdl = 23468b8534bSLuigi Rizzo (struct sockaddr_dl *)ifap->ifa_addr; 23568b8534bSLuigi Rizzo uint8_t *mac; 23668b8534bSLuigi Rizzo 23768b8534bSLuigi Rizzo if (!sdl || sdl->sdl_family != AF_LINK) 23868b8534bSLuigi Rizzo continue; 23968b8534bSLuigi Rizzo if (strncmp(ifap->ifa_name, ifname, l) != 0) 24068b8534bSLuigi Rizzo continue; 24168b8534bSLuigi Rizzo mac = (uint8_t *)LLADDR(sdl); 24268b8534bSLuigi Rizzo sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 24368b8534bSLuigi Rizzo mac[0], mac[1], mac[2], 24468b8534bSLuigi Rizzo mac[3], mac[4], mac[5]); 24568b8534bSLuigi Rizzo if (verbose) 24668b8534bSLuigi Rizzo D("source hwaddr %s", buf); 24768b8534bSLuigi Rizzo break; 24868b8534bSLuigi Rizzo } 24968b8534bSLuigi Rizzo freeifaddrs(ifaphead); 25068b8534bSLuigi Rizzo return ifap ? 0 : 1; 25168b8534bSLuigi Rizzo } 25268b8534bSLuigi Rizzo 25368b8534bSLuigi Rizzo 25468b8534bSLuigi Rizzo /* set the thread affinity. */ 25568b8534bSLuigi Rizzo static int 25668b8534bSLuigi Rizzo setaffinity(pthread_t me, int i) 25768b8534bSLuigi Rizzo { 25868b8534bSLuigi Rizzo cpuset_t cpumask; 25968b8534bSLuigi Rizzo 26068b8534bSLuigi Rizzo if (i == -1) 26168b8534bSLuigi Rizzo return 0; 26268b8534bSLuigi Rizzo 26368b8534bSLuigi Rizzo /* Set thread affinity affinity.*/ 26468b8534bSLuigi Rizzo CPU_ZERO(&cpumask); 26568b8534bSLuigi Rizzo CPU_SET(i, &cpumask); 26668b8534bSLuigi Rizzo 26768b8534bSLuigi Rizzo if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { 26868b8534bSLuigi Rizzo D("Unable to set affinity"); 26968b8534bSLuigi Rizzo return 1; 27068b8534bSLuigi Rizzo } 27168b8534bSLuigi Rizzo return 0; 27268b8534bSLuigi Rizzo } 27368b8534bSLuigi Rizzo 27468b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */ 27568b8534bSLuigi Rizzo static uint16_t 27668b8534bSLuigi Rizzo checksum(const void *data, uint16_t len) 27768b8534bSLuigi Rizzo { 27868b8534bSLuigi Rizzo const uint8_t *addr = data; 27968b8534bSLuigi Rizzo uint32_t sum = 0; 28068b8534bSLuigi Rizzo 28168b8534bSLuigi Rizzo while (len > 1) { 28268b8534bSLuigi Rizzo sum += addr[0] * 256 + addr[1]; 28368b8534bSLuigi Rizzo addr += 2; 28468b8534bSLuigi Rizzo len -= 2; 28568b8534bSLuigi Rizzo } 28668b8534bSLuigi Rizzo 28768b8534bSLuigi Rizzo if (len == 1) 28868b8534bSLuigi Rizzo sum += *addr * 256; 28968b8534bSLuigi Rizzo 29068b8534bSLuigi Rizzo sum = (sum >> 16) + (sum & 0xffff); 29168b8534bSLuigi Rizzo sum += (sum >> 16); 29268b8534bSLuigi Rizzo 29368b8534bSLuigi Rizzo sum = htons(sum); 29468b8534bSLuigi Rizzo 29568b8534bSLuigi Rizzo return ~sum; 29668b8534bSLuigi Rizzo } 29768b8534bSLuigi Rizzo 29868b8534bSLuigi Rizzo /* 29968b8534bSLuigi Rizzo * Fill a packet with some payload. 30068b8534bSLuigi Rizzo */ 30168b8534bSLuigi Rizzo static void 30268b8534bSLuigi Rizzo initialize_packet(struct targ *targ) 30368b8534bSLuigi Rizzo { 30468b8534bSLuigi Rizzo struct pkt *pkt = &targ->pkt; 30568b8534bSLuigi Rizzo struct ether_header *eh; 30668b8534bSLuigi Rizzo struct ip *ip; 30768b8534bSLuigi Rizzo struct udphdr *udp; 30868b8534bSLuigi Rizzo uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip); 30968b8534bSLuigi Rizzo int i, l, l0 = strlen(default_payload); 31068b8534bSLuigi Rizzo char *p; 31168b8534bSLuigi Rizzo 31268b8534bSLuigi Rizzo for (i = 0; i < paylen;) { 31368b8534bSLuigi Rizzo l = min(l0, paylen - i); 31468b8534bSLuigi Rizzo bcopy(default_payload, pkt->body + i, l); 31568b8534bSLuigi Rizzo i += l; 31668b8534bSLuigi Rizzo } 31768b8534bSLuigi Rizzo pkt->body[i-1] = '\0'; 31868b8534bSLuigi Rizzo 31968b8534bSLuigi Rizzo udp = &pkt->udp; 32068b8534bSLuigi Rizzo udp->uh_sport = htons(1234); 32168b8534bSLuigi Rizzo udp->uh_dport = htons(4321); 32268b8534bSLuigi Rizzo udp->uh_ulen = htons(paylen); 32368b8534bSLuigi Rizzo udp->uh_sum = 0; // checksum(udp, sizeof(*udp)); 32468b8534bSLuigi Rizzo 32568b8534bSLuigi Rizzo ip = &pkt->ip; 32668b8534bSLuigi Rizzo ip->ip_v = IPVERSION; 32768b8534bSLuigi Rizzo ip->ip_hl = 5; 32868b8534bSLuigi Rizzo ip->ip_id = 0; 32968b8534bSLuigi Rizzo ip->ip_tos = IPTOS_LOWDELAY; 33068b8534bSLuigi Rizzo ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); 33168b8534bSLuigi Rizzo ip->ip_id = 0; 33268b8534bSLuigi Rizzo ip->ip_off = htons(IP_DF); /* Don't fragment */ 33368b8534bSLuigi Rizzo ip->ip_ttl = IPDEFTTL; 33468b8534bSLuigi Rizzo ip->ip_p = IPPROTO_UDP; 33568b8534bSLuigi Rizzo inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src); 33668b8534bSLuigi Rizzo inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst); 33768b8534bSLuigi Rizzo targ->dst_ip = ip->ip_dst.s_addr; 33868b8534bSLuigi Rizzo targ->src_ip = ip->ip_src.s_addr; 33968b8534bSLuigi Rizzo p = index(targ->g->src_ip, '-'); 34068b8534bSLuigi Rizzo if (p) { 34168b8534bSLuigi Rizzo targ->dst_ip_range = atoi(p+1); 34268b8534bSLuigi Rizzo D("dst-ip sweep %d addresses", targ->dst_ip_range); 34368b8534bSLuigi Rizzo } 34468b8534bSLuigi Rizzo ip->ip_sum = checksum(ip, sizeof(*ip)); 34568b8534bSLuigi Rizzo 34668b8534bSLuigi Rizzo eh = &pkt->eh; 34768b8534bSLuigi Rizzo bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6); 34868b8534bSLuigi Rizzo bcopy(targ->src_mac, eh->ether_shost, 6); 34968b8534bSLuigi Rizzo p = index(targ->g->src_mac, '-'); 35068b8534bSLuigi Rizzo if (p) 35168b8534bSLuigi Rizzo targ->src_mac_range = atoi(p+1); 35268b8534bSLuigi Rizzo 35368b8534bSLuigi Rizzo bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); 35468b8534bSLuigi Rizzo bcopy(targ->dst_mac, eh->ether_dhost, 6); 35568b8534bSLuigi Rizzo p = index(targ->g->dst_mac, '-'); 35668b8534bSLuigi Rizzo if (p) 35768b8534bSLuigi Rizzo targ->dst_mac_range = atoi(p+1); 35868b8534bSLuigi Rizzo eh->ether_type = htons(ETHERTYPE_IP); 35968b8534bSLuigi Rizzo } 36068b8534bSLuigi Rizzo 36168b8534bSLuigi Rizzo /* Check the payload of the packet for errors (use it for debug). 36268b8534bSLuigi Rizzo * Look for consecutive ascii representations of the size of the packet. 36368b8534bSLuigi Rizzo */ 36468b8534bSLuigi Rizzo static void 36568b8534bSLuigi Rizzo check_payload(char *p, int psize) 36668b8534bSLuigi Rizzo { 36768b8534bSLuigi Rizzo char temp[64]; 36868b8534bSLuigi Rizzo int n_read, size, sizelen; 36968b8534bSLuigi Rizzo 37068b8534bSLuigi Rizzo /* get the length in ASCII of the length of the packet. */ 37168b8534bSLuigi Rizzo sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace 37268b8534bSLuigi Rizzo 37368b8534bSLuigi Rizzo /* dummy payload. */ 37468b8534bSLuigi Rizzo p += 14; /* skip packet header. */ 37568b8534bSLuigi Rizzo n_read = 14; 37668b8534bSLuigi Rizzo while (psize - n_read >= sizelen) { 37768b8534bSLuigi Rizzo sscanf(p, "%d", &size); 37868b8534bSLuigi Rizzo if (size != psize) { 37968b8534bSLuigi Rizzo D("Read %d instead of %d", size, psize); 38068b8534bSLuigi Rizzo break; 38168b8534bSLuigi Rizzo } 38268b8534bSLuigi Rizzo 38368b8534bSLuigi Rizzo p += sizelen; 38468b8534bSLuigi Rizzo n_read += sizelen; 38568b8534bSLuigi Rizzo } 38668b8534bSLuigi Rizzo } 38768b8534bSLuigi Rizzo 38868b8534bSLuigi Rizzo 38968b8534bSLuigi Rizzo /* 39068b8534bSLuigi Rizzo * create and enqueue a batch of packets on a ring. 39168b8534bSLuigi Rizzo * On the last one set NS_REPORT to tell the driver to generate 39268b8534bSLuigi Rizzo * an interrupt when done. 39368b8534bSLuigi Rizzo */ 39468b8534bSLuigi Rizzo static int 39568b8534bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, 39668b8534bSLuigi Rizzo int size, u_int count, int fill_all) 39768b8534bSLuigi Rizzo { 39868b8534bSLuigi Rizzo u_int sent, cur = ring->cur; 39968b8534bSLuigi Rizzo 40068b8534bSLuigi Rizzo if (ring->avail < count) 40168b8534bSLuigi Rizzo count = ring->avail; 40268b8534bSLuigi Rizzo 40368b8534bSLuigi Rizzo for (sent = 0; sent < count; sent++) { 40468b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 40568b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 40668b8534bSLuigi Rizzo 40768b8534bSLuigi Rizzo if (fill_all) 40868b8534bSLuigi Rizzo memcpy(p, pkt, size); 40968b8534bSLuigi Rizzo 41068b8534bSLuigi Rizzo slot->len = size; 41168b8534bSLuigi Rizzo if (sent == count - 1) 41268b8534bSLuigi Rizzo slot->flags |= NS_REPORT; 41368b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 41468b8534bSLuigi Rizzo } 41568b8534bSLuigi Rizzo ring->avail -= sent; 41668b8534bSLuigi Rizzo ring->cur = cur; 41768b8534bSLuigi Rizzo 41868b8534bSLuigi Rizzo return (sent); 41968b8534bSLuigi Rizzo } 42068b8534bSLuigi Rizzo 42168b8534bSLuigi Rizzo static void * 42268b8534bSLuigi Rizzo sender_body(void *data) 42368b8534bSLuigi Rizzo { 42468b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 42568b8534bSLuigi Rizzo 42668b8534bSLuigi Rizzo struct pollfd fds[1]; 42768b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 42868b8534bSLuigi Rizzo struct netmap_ring *txring; 42968b8534bSLuigi Rizzo int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; 43068b8534bSLuigi Rizzo int fill_all = 1; 43168b8534bSLuigi Rizzo 43268b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 43368b8534bSLuigi Rizzo goto quit; 43468b8534bSLuigi Rizzo /* setup poll(2) machanism. */ 43568b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 43668b8534bSLuigi Rizzo fds[0].fd = targ->fd; 43768b8534bSLuigi Rizzo fds[0].events = (POLLOUT); 43868b8534bSLuigi Rizzo 43968b8534bSLuigi Rizzo /* main loop.*/ 44068b8534bSLuigi Rizzo gettimeofday(&targ->tic, NULL); 44168b8534bSLuigi Rizzo if (targ->g->use_pcap) { 44268b8534bSLuigi Rizzo int size = targ->g->pkt_size; 44368b8534bSLuigi Rizzo void *pkt = &targ->pkt; 44468b8534bSLuigi Rizzo pcap_t *p = targ->g->p; 44568b8534bSLuigi Rizzo 44668b8534bSLuigi Rizzo for (; sent < n; sent++) { 44768b8534bSLuigi Rizzo if (pcap_inject(p, pkt, size) == -1) 44868b8534bSLuigi Rizzo break; 44968b8534bSLuigi Rizzo } 45068b8534bSLuigi Rizzo } else { 45168b8534bSLuigi Rizzo while (sent < n) { 45268b8534bSLuigi Rizzo 45368b8534bSLuigi Rizzo /* 45468b8534bSLuigi Rizzo * wait for available room in the send queue(s) 45568b8534bSLuigi Rizzo */ 45668b8534bSLuigi Rizzo if (poll(fds, 1, 2000) <= 0) { 45768b8534bSLuigi Rizzo D("poll error/timeout on queue %d\n", targ->me); 45868b8534bSLuigi Rizzo goto quit; 45968b8534bSLuigi Rizzo } 46068b8534bSLuigi Rizzo /* 46168b8534bSLuigi Rizzo * scan our queues and send on those with room 46268b8534bSLuigi Rizzo */ 46368b8534bSLuigi Rizzo if (sent > 100000) 46468b8534bSLuigi Rizzo fill_all = 0; 46568b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 46668b8534bSLuigi Rizzo int m, limit = MIN(n - sent, targ->g->burst); 46768b8534bSLuigi Rizzo 46868b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 46968b8534bSLuigi Rizzo if (txring->avail == 0) 47068b8534bSLuigi Rizzo continue; 47168b8534bSLuigi Rizzo m = send_packets(txring, &targ->pkt, targ->g->pkt_size, 47268b8534bSLuigi Rizzo limit, fill_all); 47368b8534bSLuigi Rizzo sent += m; 47468b8534bSLuigi Rizzo targ->count = sent; 47568b8534bSLuigi Rizzo } 47668b8534bSLuigi Rizzo } 47768b8534bSLuigi Rizzo /* Tell the interface that we have new packets. */ 47868b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 47968b8534bSLuigi Rizzo 48068b8534bSLuigi Rizzo /* final part: wait all the TX queues to be empty. */ 48168b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 48268b8534bSLuigi Rizzo txring = NETMAP_TXRING(nifp, i); 48368b8534bSLuigi Rizzo while (!NETMAP_TX_RING_EMPTY(txring)) { 48468b8534bSLuigi Rizzo ioctl(fds[0].fd, NIOCTXSYNC, NULL); 48568b8534bSLuigi Rizzo usleep(1); /* wait 1 tick */ 48668b8534bSLuigi Rizzo } 48768b8534bSLuigi Rizzo } 48868b8534bSLuigi Rizzo } 48968b8534bSLuigi Rizzo 49068b8534bSLuigi Rizzo gettimeofday(&targ->toc, NULL); 49168b8534bSLuigi Rizzo targ->completed = 1; 49268b8534bSLuigi Rizzo targ->count = sent; 49368b8534bSLuigi Rizzo 49468b8534bSLuigi Rizzo quit: 49568b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 49668b8534bSLuigi Rizzo targ->used = 0; 49768b8534bSLuigi Rizzo 49868b8534bSLuigi Rizzo return (NULL); 49968b8534bSLuigi Rizzo } 50068b8534bSLuigi Rizzo 50168b8534bSLuigi Rizzo 50268b8534bSLuigi Rizzo static void 50368b8534bSLuigi Rizzo receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h, 50468b8534bSLuigi Rizzo __unused const u_char * bytes) 50568b8534bSLuigi Rizzo { 50668b8534bSLuigi Rizzo int *count = (int *)user; 50768b8534bSLuigi Rizzo (*count)++; 50868b8534bSLuigi Rizzo } 50968b8534bSLuigi Rizzo 51068b8534bSLuigi Rizzo static int 51168b8534bSLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload) 51268b8534bSLuigi Rizzo { 51368b8534bSLuigi Rizzo u_int cur, rx; 51468b8534bSLuigi Rizzo 51568b8534bSLuigi Rizzo cur = ring->cur; 51668b8534bSLuigi Rizzo if (ring->avail < limit) 51768b8534bSLuigi Rizzo limit = ring->avail; 51868b8534bSLuigi Rizzo for (rx = 0; rx < limit; rx++) { 51968b8534bSLuigi Rizzo struct netmap_slot *slot = &ring->slot[cur]; 52068b8534bSLuigi Rizzo char *p = NETMAP_BUF(ring, slot->buf_idx); 52168b8534bSLuigi Rizzo 52268b8534bSLuigi Rizzo if (!skip_payload) 52368b8534bSLuigi Rizzo check_payload(p, slot->len); 52468b8534bSLuigi Rizzo 52568b8534bSLuigi Rizzo cur = NETMAP_RING_NEXT(ring, cur); 52668b8534bSLuigi Rizzo } 52768b8534bSLuigi Rizzo ring->avail -= rx; 52868b8534bSLuigi Rizzo ring->cur = cur; 52968b8534bSLuigi Rizzo 53068b8534bSLuigi Rizzo return (rx); 53168b8534bSLuigi Rizzo } 53268b8534bSLuigi Rizzo 53368b8534bSLuigi Rizzo static void * 53468b8534bSLuigi Rizzo receiver_body(void *data) 53568b8534bSLuigi Rizzo { 53668b8534bSLuigi Rizzo struct targ *targ = (struct targ *) data; 53768b8534bSLuigi Rizzo struct pollfd fds[1]; 53868b8534bSLuigi Rizzo struct netmap_if *nifp = targ->nifp; 53968b8534bSLuigi Rizzo struct netmap_ring *rxring; 54068b8534bSLuigi Rizzo int i, received = 0; 54168b8534bSLuigi Rizzo 54268b8534bSLuigi Rizzo if (setaffinity(targ->thread, targ->affinity)) 54368b8534bSLuigi Rizzo goto quit; 54468b8534bSLuigi Rizzo 54568b8534bSLuigi Rizzo /* setup poll(2) machanism. */ 54668b8534bSLuigi Rizzo memset(fds, 0, sizeof(fds)); 54768b8534bSLuigi Rizzo fds[0].fd = targ->fd; 54868b8534bSLuigi Rizzo fds[0].events = (POLLIN); 54968b8534bSLuigi Rizzo 55068b8534bSLuigi Rizzo /* unbounded wait for the first packet. */ 55168b8534bSLuigi Rizzo for (;;) { 55268b8534bSLuigi Rizzo i = poll(fds, 1, 1000); 55368b8534bSLuigi Rizzo if (i > 0 && !(fds[0].revents & POLLERR)) 55468b8534bSLuigi Rizzo break; 55568b8534bSLuigi Rizzo D("waiting for initial packets, poll returns %d %d", i, fds[0].revents); 55668b8534bSLuigi Rizzo } 55768b8534bSLuigi Rizzo 55868b8534bSLuigi Rizzo /* main loop, exit after 1s silence */ 55968b8534bSLuigi Rizzo gettimeofday(&targ->tic, NULL); 56068b8534bSLuigi Rizzo if (targ->g->use_pcap) { 56168b8534bSLuigi Rizzo for (;;) { 56268b8534bSLuigi Rizzo pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL); 56368b8534bSLuigi Rizzo } 56468b8534bSLuigi Rizzo } else { 56568b8534bSLuigi Rizzo while (1) { 56668b8534bSLuigi Rizzo /* Once we started to receive packets, wait at most 1 seconds 56768b8534bSLuigi Rizzo before quitting. */ 56868b8534bSLuigi Rizzo if (poll(fds, 1, 1 * 1000) <= 0) { 56968b8534bSLuigi Rizzo gettimeofday(&targ->toc, NULL); 57068b8534bSLuigi Rizzo targ->toc.tv_sec -= 1; /* Substract timeout time. */ 57168b8534bSLuigi Rizzo break; 57268b8534bSLuigi Rizzo } 57368b8534bSLuigi Rizzo 57468b8534bSLuigi Rizzo for (i = targ->qfirst; i < targ->qlast; i++) { 57568b8534bSLuigi Rizzo int m; 57668b8534bSLuigi Rizzo 57768b8534bSLuigi Rizzo rxring = NETMAP_RXRING(nifp, i); 57868b8534bSLuigi Rizzo if (rxring->avail == 0) 57968b8534bSLuigi Rizzo continue; 58068b8534bSLuigi Rizzo 58168b8534bSLuigi Rizzo m = receive_packets(rxring, targ->g->burst, 58268b8534bSLuigi Rizzo SKIP_PAYLOAD); 58368b8534bSLuigi Rizzo received += m; 58468b8534bSLuigi Rizzo targ->count = received; 58568b8534bSLuigi Rizzo } 58668b8534bSLuigi Rizzo 58768b8534bSLuigi Rizzo // tell the card we have read the data 58868b8534bSLuigi Rizzo //ioctl(fds[0].fd, NIOCRXSYNC, NULL); 58968b8534bSLuigi Rizzo } 59068b8534bSLuigi Rizzo } 59168b8534bSLuigi Rizzo 59268b8534bSLuigi Rizzo targ->completed = 1; 59368b8534bSLuigi Rizzo targ->count = received; 59468b8534bSLuigi Rizzo 59568b8534bSLuigi Rizzo quit: 59668b8534bSLuigi Rizzo /* reset the ``used`` flag. */ 59768b8534bSLuigi Rizzo targ->used = 0; 59868b8534bSLuigi Rizzo 59968b8534bSLuigi Rizzo return (NULL); 60068b8534bSLuigi Rizzo } 60168b8534bSLuigi Rizzo 60268b8534bSLuigi Rizzo static void 60368b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta) 60468b8534bSLuigi Rizzo { 60568b8534bSLuigi Rizzo double amount = 8.0 * (1.0 * size * sent) / delta; 60668b8534bSLuigi Rizzo double pps = sent / delta; 60768b8534bSLuigi Rizzo char units[4] = { '\0', 'K', 'M', 'G' }; 60868b8534bSLuigi Rizzo int aunit = 0, punit = 0; 60968b8534bSLuigi Rizzo 61068b8534bSLuigi Rizzo while (amount >= 1000) { 61168b8534bSLuigi Rizzo amount /= 1000; 61268b8534bSLuigi Rizzo aunit += 1; 61368b8534bSLuigi Rizzo } 61468b8534bSLuigi Rizzo while (pps >= 1000) { 61568b8534bSLuigi Rizzo pps /= 1000; 61668b8534bSLuigi Rizzo punit += 1; 61768b8534bSLuigi Rizzo } 61868b8534bSLuigi Rizzo 61968b8534bSLuigi Rizzo printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", 62068b8534bSLuigi Rizzo sent, size, delta); 62168b8534bSLuigi Rizzo printf("Speed: %.2f%cpps. Bandwidth: %.2f%cbps.\n", 62268b8534bSLuigi Rizzo pps, units[punit], amount, units[aunit]); 62368b8534bSLuigi Rizzo } 62468b8534bSLuigi Rizzo 62568b8534bSLuigi Rizzo 62668b8534bSLuigi Rizzo static void 62768b8534bSLuigi Rizzo rx_output(uint64_t received, double delta) 62868b8534bSLuigi Rizzo { 62968b8534bSLuigi Rizzo 63068b8534bSLuigi Rizzo double pps = received / delta; 63168b8534bSLuigi Rizzo char units[4] = { '\0', 'K', 'M', 'G' }; 63268b8534bSLuigi Rizzo int punit = 0; 63368b8534bSLuigi Rizzo 63468b8534bSLuigi Rizzo while (pps >= 1000) { 63568b8534bSLuigi Rizzo pps /= 1000; 63668b8534bSLuigi Rizzo punit += 1; 63768b8534bSLuigi Rizzo } 63868b8534bSLuigi Rizzo 63968b8534bSLuigi Rizzo printf("Received %llu packets, in %.2f seconds.\n", received, delta); 64068b8534bSLuigi Rizzo printf("Speed: %.2f%cpps.\n", pps, units[punit]); 64168b8534bSLuigi Rizzo } 64268b8534bSLuigi Rizzo 64368b8534bSLuigi Rizzo static void 64468b8534bSLuigi Rizzo usage(void) 64568b8534bSLuigi Rizzo { 64668b8534bSLuigi Rizzo const char *cmd = "pkt-gen"; 64768b8534bSLuigi Rizzo fprintf(stderr, 64868b8534bSLuigi Rizzo "Usage:\n" 64968b8534bSLuigi Rizzo "%s arguments\n" 65068b8534bSLuigi Rizzo "\t-i interface interface name\n" 65168b8534bSLuigi Rizzo "\t-t pkts_to_send also forces send mode\n" 65268b8534bSLuigi Rizzo "\t-r pkts_to_receive also forces receive mode\n" 65368b8534bSLuigi Rizzo "\t-l pkts_size in bytes excluding CRC\n" 65468b8534bSLuigi Rizzo "\t-d dst-ip end with %%n to sweep n addresses\n" 65568b8534bSLuigi Rizzo "\t-s src-ip end with %%n to sweep n addresses\n" 65668b8534bSLuigi Rizzo "\t-D dst-mac end with %%n to sweep n addresses\n" 65768b8534bSLuigi Rizzo "\t-S src-mac end with %%n to sweep n addresses\n" 65868b8534bSLuigi Rizzo "\t-b burst size testing, mostly\n" 65968b8534bSLuigi Rizzo "\t-c cores cores to use\n" 66068b8534bSLuigi Rizzo "\t-p threads processes/threads to use\n" 66168b8534bSLuigi Rizzo "\t-T report_ms milliseconds between reports\n" 66268b8534bSLuigi Rizzo "\t-w wait_for_link_time in seconds\n" 66368b8534bSLuigi Rizzo "", 66468b8534bSLuigi Rizzo cmd); 66568b8534bSLuigi Rizzo 66668b8534bSLuigi Rizzo exit(0); 66768b8534bSLuigi Rizzo } 66868b8534bSLuigi Rizzo 66968b8534bSLuigi Rizzo 67068b8534bSLuigi Rizzo int 67168b8534bSLuigi Rizzo main(int arc, char **argv) 67268b8534bSLuigi Rizzo { 67368b8534bSLuigi Rizzo int i, fd; 67468b8534bSLuigi Rizzo 67568b8534bSLuigi Rizzo struct glob_arg g; 67668b8534bSLuigi Rizzo 67768b8534bSLuigi Rizzo struct nmreq nmr; 67868b8534bSLuigi Rizzo void *mmap_addr; /* the mmap address */ 67968b8534bSLuigi Rizzo void *(*td_body)(void *) = receiver_body; 68068b8534bSLuigi Rizzo int ch; 68168b8534bSLuigi Rizzo int report_interval = 1000; /* report interval */ 68268b8534bSLuigi Rizzo char *ifname = NULL; 68368b8534bSLuigi Rizzo int wait_link = 2; 68468b8534bSLuigi Rizzo int devqueues = 1; /* how many device queues */ 68568b8534bSLuigi Rizzo 68668b8534bSLuigi Rizzo bzero(&g, sizeof(g)); 68768b8534bSLuigi Rizzo 68868b8534bSLuigi Rizzo g.src_ip = "10.0.0.1"; 68968b8534bSLuigi Rizzo g.dst_ip = "10.1.0.1"; 69068b8534bSLuigi Rizzo g.dst_mac = "ff:ff:ff:ff:ff:ff"; 69168b8534bSLuigi Rizzo g.src_mac = NULL; 69268b8534bSLuigi Rizzo g.pkt_size = 60; 69368b8534bSLuigi Rizzo g.burst = 512; // default 69468b8534bSLuigi Rizzo g.nthreads = 1; 69568b8534bSLuigi Rizzo g.cpus = 1; 69668b8534bSLuigi Rizzo 69768b8534bSLuigi Rizzo while ( (ch = getopt(arc, argv, 69868b8534bSLuigi Rizzo "i:t:r:l:d:s:D:S:b:c:p:T:w:v")) != -1) { 69968b8534bSLuigi Rizzo switch(ch) { 70068b8534bSLuigi Rizzo default: 70168b8534bSLuigi Rizzo D("bad option %c %s", ch, optarg); 70268b8534bSLuigi Rizzo usage(); 70368b8534bSLuigi Rizzo break; 70468b8534bSLuigi Rizzo case 'i': /* interface */ 70568b8534bSLuigi Rizzo ifname = optarg; 70668b8534bSLuigi Rizzo break; 70768b8534bSLuigi Rizzo case 't': /* send */ 70868b8534bSLuigi Rizzo td_body = sender_body; 70968b8534bSLuigi Rizzo g.npackets = atoi(optarg); 71068b8534bSLuigi Rizzo break; 71168b8534bSLuigi Rizzo case 'r': /* receive */ 71268b8534bSLuigi Rizzo td_body = receiver_body; 71368b8534bSLuigi Rizzo g.npackets = atoi(optarg); 71468b8534bSLuigi Rizzo break; 71568b8534bSLuigi Rizzo case 'l': /* pkt_size */ 71668b8534bSLuigi Rizzo g.pkt_size = atoi(optarg); 71768b8534bSLuigi Rizzo break; 71868b8534bSLuigi Rizzo case 'd': 71968b8534bSLuigi Rizzo g.dst_ip = optarg; 72068b8534bSLuigi Rizzo break; 72168b8534bSLuigi Rizzo case 's': 72268b8534bSLuigi Rizzo g.src_ip = optarg; 72368b8534bSLuigi Rizzo break; 72468b8534bSLuigi Rizzo case 'T': /* report interval */ 72568b8534bSLuigi Rizzo report_interval = atoi(optarg); 72668b8534bSLuigi Rizzo break; 72768b8534bSLuigi Rizzo case 'w': 72868b8534bSLuigi Rizzo wait_link = atoi(optarg); 72968b8534bSLuigi Rizzo break; 73068b8534bSLuigi Rizzo case 'b': /* burst */ 73168b8534bSLuigi Rizzo g.burst = atoi(optarg); 73268b8534bSLuigi Rizzo break; 73368b8534bSLuigi Rizzo case 'c': 73468b8534bSLuigi Rizzo g.cpus = atoi(optarg); 73568b8534bSLuigi Rizzo break; 73668b8534bSLuigi Rizzo case 'p': 73768b8534bSLuigi Rizzo g.nthreads = atoi(optarg); 73868b8534bSLuigi Rizzo break; 73968b8534bSLuigi Rizzo 74068b8534bSLuigi Rizzo case 'P': 74168b8534bSLuigi Rizzo g.use_pcap = 1; 74268b8534bSLuigi Rizzo break; 74368b8534bSLuigi Rizzo 74468b8534bSLuigi Rizzo case 'D': /* destination mac */ 74568b8534bSLuigi Rizzo g.dst_mac = optarg; 74668b8534bSLuigi Rizzo { 74768b8534bSLuigi Rizzo struct ether_addr *mac = ether_aton(g.dst_mac); 74868b8534bSLuigi Rizzo D("ether_aton(%s) gives %p", g.dst_mac, mac); 74968b8534bSLuigi Rizzo } 75068b8534bSLuigi Rizzo break; 75168b8534bSLuigi Rizzo case 'S': /* source mac */ 75268b8534bSLuigi Rizzo g.src_mac = optarg; 75368b8534bSLuigi Rizzo break; 75468b8534bSLuigi Rizzo case 'v': 75568b8534bSLuigi Rizzo verbose++; 75668b8534bSLuigi Rizzo } 75768b8534bSLuigi Rizzo } 75868b8534bSLuigi Rizzo 75968b8534bSLuigi Rizzo if (ifname == NULL) { 76068b8534bSLuigi Rizzo D("missing ifname"); 76168b8534bSLuigi Rizzo usage(); 76268b8534bSLuigi Rizzo } 76368b8534bSLuigi Rizzo { 76468b8534bSLuigi Rizzo int n = system_ncpus(); 76568b8534bSLuigi Rizzo if (g.cpus < 0 || g.cpus > n) { 76668b8534bSLuigi Rizzo D("%d cpus is too high, have only %d cpus", g.cpus, n); 76768b8534bSLuigi Rizzo usage(); 76868b8534bSLuigi Rizzo } 76968b8534bSLuigi Rizzo if (g.cpus == 0) 77068b8534bSLuigi Rizzo g.cpus = n; 77168b8534bSLuigi Rizzo } 77268b8534bSLuigi Rizzo if (g.pkt_size < 16 || g.pkt_size > 1536) { 77368b8534bSLuigi Rizzo D("bad pktsize %d\n", g.pkt_size); 77468b8534bSLuigi Rizzo usage(); 77568b8534bSLuigi Rizzo } 77668b8534bSLuigi Rizzo 77768b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 77868b8534bSLuigi Rizzo /* 77968b8534bSLuigi Rizzo * Open the netmap device to fetch the number of queues of our 78068b8534bSLuigi Rizzo * interface. 78168b8534bSLuigi Rizzo * 78268b8534bSLuigi Rizzo * The first NIOCREGIF also detaches the card from the 78368b8534bSLuigi Rizzo * protocol stack and may cause a reset of the card, 78468b8534bSLuigi Rizzo * which in turn may take some time for the PHY to 78568b8534bSLuigi Rizzo * reconfigure. 78668b8534bSLuigi Rizzo */ 78768b8534bSLuigi Rizzo fd = open("/dev/netmap", O_RDWR); 78868b8534bSLuigi Rizzo if (fd == -1) { 78968b8534bSLuigi Rizzo D("Unable to open /dev/netmap"); 79068b8534bSLuigi Rizzo // fail later 79168b8534bSLuigi Rizzo } else { 79268b8534bSLuigi Rizzo if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 79368b8534bSLuigi Rizzo D("Unable to get if info without name"); 79468b8534bSLuigi Rizzo } else { 79568b8534bSLuigi Rizzo D("map size is %d Kb", nmr.nr_memsize >> 10); 79668b8534bSLuigi Rizzo } 79768b8534bSLuigi Rizzo bzero(&nmr, sizeof(nmr)); 79868b8534bSLuigi Rizzo strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name)); 79968b8534bSLuigi Rizzo if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) { 80068b8534bSLuigi Rizzo D("Unable to get if info for %s", ifname); 80168b8534bSLuigi Rizzo } 80268b8534bSLuigi Rizzo devqueues = nmr.nr_numrings; 80368b8534bSLuigi Rizzo } 80468b8534bSLuigi Rizzo 80568b8534bSLuigi Rizzo /* validate provided nthreads. */ 80668b8534bSLuigi Rizzo if (g.nthreads < 1 || g.nthreads > devqueues) { 80768b8534bSLuigi Rizzo D("bad nthreads %d, have %d queues", g.nthreads, devqueues); 80868b8534bSLuigi Rizzo // continue, fail later 80968b8534bSLuigi Rizzo } 81068b8534bSLuigi Rizzo 81168b8534bSLuigi Rizzo if (td_body == sender_body && g.src_mac == NULL) { 81268b8534bSLuigi Rizzo static char mybuf[20] = "ff:ff:ff:ff:ff:ff"; 81368b8534bSLuigi Rizzo /* retrieve source mac address. */ 81468b8534bSLuigi Rizzo if (source_hwaddr(ifname, mybuf) == -1) { 81568b8534bSLuigi Rizzo D("Unable to retrieve source mac"); 81668b8534bSLuigi Rizzo // continue, fail later 81768b8534bSLuigi Rizzo } 81868b8534bSLuigi Rizzo g.src_mac = mybuf; 81968b8534bSLuigi Rizzo } 82068b8534bSLuigi Rizzo 82168b8534bSLuigi Rizzo /* 82268b8534bSLuigi Rizzo * Map the netmap shared memory: instead of issuing mmap() 82368b8534bSLuigi Rizzo * inside the body of the threads, we prefer to keep this 82468b8534bSLuigi Rizzo * operation here to simplify the thread logic. 82568b8534bSLuigi Rizzo */ 82668b8534bSLuigi Rizzo D("mmapping %d Kbytes", nmr.nr_memsize>>10); 82768b8534bSLuigi Rizzo mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize, 82868b8534bSLuigi Rizzo PROT_WRITE | PROT_READ, 82968b8534bSLuigi Rizzo MAP_SHARED, fd, 0); 83068b8534bSLuigi Rizzo if (mmap_addr == MAP_FAILED) { 83168b8534bSLuigi Rizzo D("Unable to mmap %d KB", nmr.nr_memsize >> 10); 83268b8534bSLuigi Rizzo // continue, fail later 83368b8534bSLuigi Rizzo } 83468b8534bSLuigi Rizzo 83568b8534bSLuigi Rizzo /* 83668b8534bSLuigi Rizzo * Register the interface on the netmap device: from now on, 83768b8534bSLuigi Rizzo * we can operate on the network interface without any 83868b8534bSLuigi Rizzo * interference from the legacy network stack. 83968b8534bSLuigi Rizzo * 84068b8534bSLuigi Rizzo * We decide to put the first interface registration here to 84168b8534bSLuigi Rizzo * give time to cards that take a long time to reset the PHY. 84268b8534bSLuigi Rizzo */ 84368b8534bSLuigi Rizzo if (ioctl(fd, NIOCREGIF, &nmr) == -1) { 84468b8534bSLuigi Rizzo D("Unable to register interface %s", ifname); 84568b8534bSLuigi Rizzo //continue, fail later 84668b8534bSLuigi Rizzo } 84768b8534bSLuigi Rizzo 84868b8534bSLuigi Rizzo 84968b8534bSLuigi Rizzo /* Print some debug information. */ 85068b8534bSLuigi Rizzo fprintf(stdout, 85168b8534bSLuigi Rizzo "%s %s: %d queues, %d threads and %d cpus.\n", 85268b8534bSLuigi Rizzo (td_body == sender_body) ? "Sending on" : "Receiving from", 85368b8534bSLuigi Rizzo ifname, 85468b8534bSLuigi Rizzo devqueues, 85568b8534bSLuigi Rizzo g.nthreads, 85668b8534bSLuigi Rizzo g.cpus); 85768b8534bSLuigi Rizzo if (td_body == sender_body) { 85868b8534bSLuigi Rizzo fprintf(stdout, "%s -> %s (%s -> %s)\n", 85968b8534bSLuigi Rizzo g.src_ip, g.dst_ip, 86068b8534bSLuigi Rizzo g.src_mac, g.dst_mac); 86168b8534bSLuigi Rizzo } 86268b8534bSLuigi Rizzo 86368b8534bSLuigi Rizzo /* Exit if something went wrong. */ 86468b8534bSLuigi Rizzo if (fd < 0) { 86568b8534bSLuigi Rizzo D("aborting"); 86668b8534bSLuigi Rizzo usage(); 86768b8534bSLuigi Rizzo } 86868b8534bSLuigi Rizzo 86968b8534bSLuigi Rizzo 87068b8534bSLuigi Rizzo /* Wait for PHY reset. */ 87168b8534bSLuigi Rizzo D("Wait %d secs for phy reset", wait_link); 87268b8534bSLuigi Rizzo sleep(wait_link); 87368b8534bSLuigi Rizzo D("Ready..."); 87468b8534bSLuigi Rizzo 87568b8534bSLuigi Rizzo /* Install ^C handler. */ 87668b8534bSLuigi Rizzo global_nthreads = g.nthreads; 87768b8534bSLuigi Rizzo signal(SIGINT, sigint_h); 87868b8534bSLuigi Rizzo 87968b8534bSLuigi Rizzo if (g.use_pcap) { 88068b8534bSLuigi Rizzo // XXX g.p = pcap_open_live(..); 88168b8534bSLuigi Rizzo } 88268b8534bSLuigi Rizzo 88368b8534bSLuigi Rizzo targs = calloc(g.nthreads, sizeof(*targs)); 88468b8534bSLuigi Rizzo /* 88568b8534bSLuigi Rizzo * Now create the desired number of threads, each one 88668b8534bSLuigi Rizzo * using a single descriptor. 88768b8534bSLuigi Rizzo */ 88868b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 88968b8534bSLuigi Rizzo struct netmap_if *tnifp; 89068b8534bSLuigi Rizzo struct nmreq tifreq; 89168b8534bSLuigi Rizzo int tfd; 89268b8534bSLuigi Rizzo 89368b8534bSLuigi Rizzo if (g.use_pcap) { 89468b8534bSLuigi Rizzo tfd = -1; 89568b8534bSLuigi Rizzo tnifp = NULL; 89668b8534bSLuigi Rizzo } else { 89768b8534bSLuigi Rizzo /* register interface. */ 89868b8534bSLuigi Rizzo tfd = open("/dev/netmap", O_RDWR); 89968b8534bSLuigi Rizzo if (tfd == -1) { 90068b8534bSLuigi Rizzo D("Unable to open /dev/netmap"); 90168b8534bSLuigi Rizzo continue; 90268b8534bSLuigi Rizzo } 90368b8534bSLuigi Rizzo 90468b8534bSLuigi Rizzo bzero(&tifreq, sizeof(tifreq)); 90568b8534bSLuigi Rizzo strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name)); 90668b8534bSLuigi Rizzo tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0; 90768b8534bSLuigi Rizzo 90868b8534bSLuigi Rizzo /* 90968b8534bSLuigi Rizzo * if we are acting as a receiver only, do not touch the transmit ring. 91068b8534bSLuigi Rizzo * This is not the default because many apps may use the interface 91168b8534bSLuigi Rizzo * in both directions, but a pure receiver does not. 91268b8534bSLuigi Rizzo */ 91368b8534bSLuigi Rizzo if (td_body == receiver_body) { 91468b8534bSLuigi Rizzo tifreq.nr_ringid |= NETMAP_NO_TX_POLL; 91568b8534bSLuigi Rizzo } 91668b8534bSLuigi Rizzo 91768b8534bSLuigi Rizzo if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) { 91868b8534bSLuigi Rizzo D("Unable to register %s", ifname); 91968b8534bSLuigi Rizzo continue; 92068b8534bSLuigi Rizzo } 92168b8534bSLuigi Rizzo tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset); 92268b8534bSLuigi Rizzo } 92368b8534bSLuigi Rizzo /* start threads. */ 92468b8534bSLuigi Rizzo bzero(&targs[i], sizeof(targs[i])); 92568b8534bSLuigi Rizzo targs[i].g = &g; 92668b8534bSLuigi Rizzo targs[i].used = 1; 92768b8534bSLuigi Rizzo targs[i].completed = 0; 92868b8534bSLuigi Rizzo targs[i].fd = tfd; 92968b8534bSLuigi Rizzo targs[i].nmr = tifreq; 93068b8534bSLuigi Rizzo targs[i].nifp = tnifp; 93168b8534bSLuigi Rizzo targs[i].qfirst = (g.nthreads > 1) ? i : 0; 93268b8534bSLuigi Rizzo targs[i].qlast = (g.nthreads > 1) ? i+1 : tifreq.nr_numrings; 93368b8534bSLuigi Rizzo targs[i].me = i; 93468b8534bSLuigi Rizzo targs[i].affinity = g.cpus ? i % g.cpus : -1; 93568b8534bSLuigi Rizzo if (td_body == sender_body) { 93668b8534bSLuigi Rizzo /* initialize the packet to send. */ 93768b8534bSLuigi Rizzo initialize_packet(&targs[i]); 93868b8534bSLuigi Rizzo } 93968b8534bSLuigi Rizzo 94068b8534bSLuigi Rizzo if (pthread_create(&targs[i].thread, NULL, td_body, 94168b8534bSLuigi Rizzo &targs[i]) == -1) { 94268b8534bSLuigi Rizzo D("Unable to create thread %d", i); 94368b8534bSLuigi Rizzo targs[i].used = 0; 94468b8534bSLuigi Rizzo } 94568b8534bSLuigi Rizzo } 94668b8534bSLuigi Rizzo 94768b8534bSLuigi Rizzo { 94868b8534bSLuigi Rizzo uint64_t my_count = 0, prev = 0; 94968b8534bSLuigi Rizzo uint64_t count = 0; 95068b8534bSLuigi Rizzo double delta_t; 95168b8534bSLuigi Rizzo struct timeval tic, toc; 95268b8534bSLuigi Rizzo 95368b8534bSLuigi Rizzo gettimeofday(&toc, NULL); 95468b8534bSLuigi Rizzo for (;;) { 95568b8534bSLuigi Rizzo struct timeval now, delta; 95668b8534bSLuigi Rizzo uint64_t pps; 95768b8534bSLuigi Rizzo int done = 0; 95868b8534bSLuigi Rizzo 95968b8534bSLuigi Rizzo delta.tv_sec = report_interval/1000; 96068b8534bSLuigi Rizzo delta.tv_usec = (report_interval%1000)*1000; 96168b8534bSLuigi Rizzo select(0, NULL, NULL, NULL, &delta); 96268b8534bSLuigi Rizzo gettimeofday(&now, NULL); 96368b8534bSLuigi Rizzo timersub(&now, &toc, &toc); 96468b8534bSLuigi Rizzo my_count = 0; 96568b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 96668b8534bSLuigi Rizzo my_count += targs[i].count; 96768b8534bSLuigi Rizzo if (targs[i].used == 0) 96868b8534bSLuigi Rizzo done++; 96968b8534bSLuigi Rizzo } 97068b8534bSLuigi Rizzo pps = toc.tv_sec* 1000000 + toc.tv_usec; 97168b8534bSLuigi Rizzo if (pps < 10000) 97268b8534bSLuigi Rizzo continue; 97368b8534bSLuigi Rizzo pps = (my_count - prev)*1000000 / pps; 97468b8534bSLuigi Rizzo D("%llu pps", pps); 97568b8534bSLuigi Rizzo prev = my_count; 97668b8534bSLuigi Rizzo toc = now; 97768b8534bSLuigi Rizzo if (done == g.nthreads) 97868b8534bSLuigi Rizzo break; 97968b8534bSLuigi Rizzo } 98068b8534bSLuigi Rizzo 98168b8534bSLuigi Rizzo timerclear(&tic); 98268b8534bSLuigi Rizzo timerclear(&toc); 98368b8534bSLuigi Rizzo for (i = 0; i < g.nthreads; i++) { 98468b8534bSLuigi Rizzo /* 98568b8534bSLuigi Rizzo * Join active threads, unregister interfaces and close 98668b8534bSLuigi Rizzo * file descriptors. 98768b8534bSLuigi Rizzo */ 98868b8534bSLuigi Rizzo pthread_join(targs[i].thread, NULL); 98968b8534bSLuigi Rizzo ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr); 99068b8534bSLuigi Rizzo close(targs[i].fd); 99168b8534bSLuigi Rizzo 99268b8534bSLuigi Rizzo if (targs[i].completed == 0) 99368b8534bSLuigi Rizzo continue; 99468b8534bSLuigi Rizzo 99568b8534bSLuigi Rizzo /* 99668b8534bSLuigi Rizzo * Collect threads o1utput and extract information about 99768b8534bSLuigi Rizzo * how log it took to send all the packets. 99868b8534bSLuigi Rizzo */ 99968b8534bSLuigi Rizzo count += targs[i].count; 100068b8534bSLuigi Rizzo if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <)) 100168b8534bSLuigi Rizzo tic = targs[i].tic; 100268b8534bSLuigi Rizzo if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >)) 100368b8534bSLuigi Rizzo toc = targs[i].toc; 100468b8534bSLuigi Rizzo } 100568b8534bSLuigi Rizzo 100668b8534bSLuigi Rizzo /* print output. */ 100768b8534bSLuigi Rizzo timersub(&toc, &tic, &toc); 100868b8534bSLuigi Rizzo delta_t = toc.tv_sec + 1e-6* toc.tv_usec; 100968b8534bSLuigi Rizzo if (td_body == sender_body) 101068b8534bSLuigi Rizzo tx_output(count, g.pkt_size, delta_t); 101168b8534bSLuigi Rizzo else 101268b8534bSLuigi Rizzo rx_output(count, delta_t); 101368b8534bSLuigi Rizzo } 101468b8534bSLuigi Rizzo 101568b8534bSLuigi Rizzo ioctl(fd, NIOCUNREGIF, &nmr); 101668b8534bSLuigi Rizzo munmap(mmap_addr, nmr.nr_memsize); 101768b8534bSLuigi Rizzo close(fd); 101868b8534bSLuigi Rizzo 101968b8534bSLuigi Rizzo return (0); 102068b8534bSLuigi Rizzo } 102168b8534bSLuigi Rizzo /* end of file */ 1022