From owner-svn-src-all@freebsd.org Mon Nov 19 08:55:27 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 4F6F51139650; Mon, 19 Nov 2018 08:55:27 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id F09636C2B0; Mon, 19 Nov 2018 08:55:26 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id B8B0B1FE03; Mon, 19 Nov 2018 08:55:26 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id wAJ8tQIV097818; Mon, 19 Nov 2018 08:55:26 GMT (envelope-from vmaffione@FreeBSD.org) Received: (from vmaffione@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id wAJ8tQMG097816; Mon, 19 Nov 2018 08:55:26 GMT (envelope-from vmaffione@FreeBSD.org) Message-Id: <201811190855.wAJ8tQMG097816@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: vmaffione set sender to vmaffione@FreeBSD.org using -f From: Vincenzo Maffione Date: Mon, 19 Nov 2018 08:55:26 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r340619 - stable/12/tools/tools/netmap X-SVN-Group: stable-12 X-SVN-Commit-Author: vmaffione X-SVN-Commit-Paths: stable/12/tools/tools/netmap X-SVN-Commit-Revision: 340619 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: F09636C2B0 X-Spamd-Result: default: False [0.44 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_SPAM_MEDIUM(0.15)[0.148,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_SPAM_SHORT(0.29)[0.290,0] X-Rspamd-Server: mx1.freebsd.org X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 19 Nov 2018 08:55:27 -0000 Author: vmaffione Date: Mon Nov 19 08:55:26 2018 New Revision: 340619 URL: https://svnweb.freebsd.org/changeset/base/340619 Log: MFC r340325 netmap: pkt-gen: several updates from upstream Various improvements to the netmap pkt-gen program: - indentation fixes - support for IPV6 - fixes to checksum computation - support for NS_MOREFRAG - rate limiting in ping mode Differential Revision: https://reviews.freebsd.org/D17698 Modified: stable/12/tools/tools/netmap/pkt-gen.8 stable/12/tools/tools/netmap/pkt-gen.c Directory Properties: stable/12/ (props changed) Modified: stable/12/tools/tools/netmap/pkt-gen.8 ============================================================================== --- stable/12/tools/tools/netmap/pkt-gen.8 Mon Nov 19 08:53:52 2018 (r340618) +++ stable/12/tools/tools/netmap/pkt-gen.8 Mon Nov 19 08:55:26 2018 (r340619) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 23, 2018 +.Dd October 31, 2018 .Dt PKT-GEN 8 .Os .Sh NAME @@ -36,96 +36,215 @@ .Bl -item -compact .It .Nm +.Op Fl h46XzZNIWvrAB .Op Fl i Ar interface .Op Fl f Ar function .Op Fl n Ar count -.Op Fl t Ar pkts_to_send -.Op Fl r Ar pkts_to_receive .Op Fl l Ar pkt_size +.Op Fl b Ar burst_size .Op Fl d Ar dst_ip[:port[-dst_ip:port]] .Op Fl s Ar src_ip[:port[-src_ip:port]] -.Op Fl D Ar dst-mac -.Op Fl S Ar src-mac +.Op Fl D Ar dst_mac +.Op Fl S Ar src_mac .Op Fl a Ar cpu_id -.Op Fl b Ar burst size -.Op Fl c Ar cores +.Op Fl c Ar cpus .Op Fl p Ar threads .Op Fl T Ar report_ms -.Op Fl P +.Op Fl P Ar file .Op Fl w Ar wait_for_link_time .Op Fl R Ar rate -.Op Fl X .Op Fl H Ar len -.Op Fl P Ar xfile -.Op Fl z -.Op Fl Z +.Op Fl F Ar num_frags +.Op Fl M Ar frag_size +.Op Fl C Ar port_config +.El .Sh DESCRIPTION .Nm -generates and receives raw network packets using -.Xr netmap 4 . +leverages +.Xr netmap 4 +to generate and receive raw network packets in batches. The arguments are as follows: -.Pp .Bl -tag -width Ds +.It Fl h +Show program usage and exit. .It Fl i Ar interface -Network interface name. -.It Fl f Ar function tx rx ping pong -Set the function to transmit, receive of ping/pong. -.It Fl n count -Number of iterations (can be 0). -.It Fl t pkts_to_send -Number of packets to send. Also forces transmit mode. -.It Fl r Ar pkts_to_receive -Number of packets to receive. Also forces rx mode. +Name of the network interface that +.Nm +operates on. +It can be a system network interface (e.g., em0), +the name of a +.Xr vale 4 +port (e.g., valeSSS:PPP), the name of a netmap pipe or monitor, +or any valid netmap port name accepted by the +.Ar nm_open +library function, as documented in +.Xr netmap 4 +(NIOCREGIF section). +.It Fl f Ar function +The function to be executed by +.Nm . +Specify +.Cm tx +for transmission, +.Cm rx +for reception, +.Cm ping +for client-side ping-pong operation, and +.Cm pong +for server-side ping-pong operation. +.It Fl n Ar count +Number of iterations of the +.Nm +function, with 0 meaning infinite). +In case of +.Cm tx +or +.Cm rx , +.Ar count +is the number of packets to receive or transmit. +In case of +.Cm ping +or +.Cm pong , +.Ar count +is the number of ping-pong transactions. .It Fl l Ar pkt_size Packet size in bytes excluding CRC. +If passed a second time, use random sizes larger or equal than the +second one and lower than the first one. +.It Fl b Ar burst_size +Transmit or receive up to +.Ar burst_size +packets at a time. +.It Fl 4 +Use IPv4 addresses. +.It Fl 6 +Use IPv6 addresses. .It Fl d Ar dst_ip[:port[-dst_ip:port]] -Destination IPv4 address and port, single or range. +Destination IPv4/IPv6 address and port, single or range. .It Fl s Ar src_ip[:port[-src_ip:port]] -Source IPv4 address and port, single or range. -.It Fl D Ar dst-mac -Destination MAC address in colon notation. -.It Fl S Ar src-mac +Source IPv4/IPv6 address and port, single or range. +.It Fl D Ar dst_mac +Destination MAC address in colon notation (e.g., aa:bb:cc:dd:ee:00). +.It Fl S Ar src_mac Source MAC address in colon notation. .It Fl a Ar cpu_id -Tie +Pin the first thread of .Nm -to a particular CPU core using -.Xr setaffinity 2. -.It Fl b Ar burst size -Set the size of a burst of packets. -.It Fl c Ar cores -Number of cores to use. +to a particular CPU using +.Xr pthread_setaffinity_np 3 . +If more threads are used, they are pinned to the subsequent CPUs, +one per thread. +.It Fl c Ar cpus +Maximum number of CPUs to use (0 means to use all the available ones). .It Fl p Ar threads Number of threads to use. +By default, only a single thread is used +to handle all the netmap rings. +If +.Ar threads +is larger than one, each thread handles a single TX ring (in +.Cm tx +mode), a single RX ring (in +.Cm rx +mode), or a TX/RX ring couple. +The number of +.Ar threads +must be less or equal than the number of TX (or RX) ring available +in the device specified by +.Ar interface . .It Fl T Ar report_ms Number of milliseconds between reports. -.It Fl P -Use libpcap instead of netmap for reading or writing. .It Fl w Ar wait_for_link_time -Number of seconds to wait to make sure that the network link is up. A -network device driver may take some time to create a new -transmit/receive ring pair when +Number of seconds to wait before starting the +.Nm +function, useuful to make sure that the network link is up. +A network device driver may take some time to enter netmap mode, or +to create a new transmit/receive ring pair when .Xr netmap 4 requests one. .It Fl R Ar rate -Packet transmission rate. Not setting the packet transmission rate tells +Packet transmission rate. +Not setting the packet transmission rate tells .Nm -to transmit packets as quickly as possible. On servers from 2010 on-wards +to transmit packets as quickly as possible. +On servers from 2010 on-wards .Xr netmap 4 is able to completely use all of the bandwidth of a 10 or 40Gbps link, so this option should be used unless your intention is to saturate the link. .It Fl X -Dump payload transmitted or received. +Dump payload of each packet transmitted or received. .It Fl H Ar len -Add empty virtio-net-header with size 'len'. This option is only use -with Virtual Machine technologies that use virtio as a network interface. +Add empty virtio-net-header with size +.Ar len . +Valid sizes are 0, 10 and 12. +This option is only used with Virtual Machine technologies that use virtio +as a network interface. .It Fl P Ar file -Load the packet from a pcap file rather than constructing it inside of -.Nm +Load the packet to be transmitted from a pcap file rather than constructing +it within +.Nm . .It Fl z -Use random IPv4 src address/port +Use random IPv4/IPv6 src address/port. .It Fl Z -Use random IPv4 dst address/port +Use random IPv4/IPv6 dst address/port. +.It Fl N +Do not normalize units (i.e., use bps, pps instead of Mbps, Kpps, etc.). +.It Fl F Ar num_frags +Send multi-slot packets, each one with +.Ar num_frags +fragments. +A multi-slot packet is represented by two or more consecutive netmap slots +with the +.Ar NS_MOREFRAG +flag set (except for the last slot). +This is useful to transmit or receive packets larger than the netmap +buffer size. +.It Fl M Ar frag_size +In multi-slot mode, +.Ar frag_size +specifies the size of each fragment, if smaller than the packet length +divided by +.Ar num_frags . +.It Fl I +Use indirect buffers. +It is only valid for transmitting on VALE ports, +and it is implemented by setting the +.Ar NS_INDIRECT +flag in the netmap slots. +.It Fl W +Exit immediately if all the RX rings are empty the first time they are +examined. +.It Fl v +Increase the verbosity level. +.It Fl r +In +.Cm tx +mode, do not initialize packets, but send whatever the content of +the uninitialized netmap buffers is (rubbish mode). +.It Fl A +Compute mean and standard deviation (over a sliding window) for the +transmit or receive rate. +.It Fl B +Take Ethernet framing and CRC into account when computing the average bps. +This adds 4 bytes of CRC and 20 bytes of framing to each packet. +.It Fl C Ar tx_slots Ns Oo Cm \&, Ns Ar rx_slots Ns Oo Cm \&, Ns Ar tx_rings Ns Oo Cm \&, Ns Ar rx_rings Oc Oc Oc +Configuration in terms of number of rings and slots to be used when +opening the netmap port. +Such configuration has effect on software ports +created on the fly, such as VALE ports and netmap pipes. +The configuration may consist of 1 to 4 numbers separated by commas: +.Dq tx_slots,rx_slots,tx_rings,rx_rings . +Missing numbers or zeroes stand for default values. +As an additional convenience, if exactly one number is specified, +then this is assigned to both +.Ar tx_slots +and +.Ar rx_slots . +If there is no fourth number, then the third one is assigned to both +.Ar tx_rings +and +.Ar rx_rings . .El .Pp .Nm @@ -133,7 +252,7 @@ is a raw packet generator that can utilize either .Xr netmap 4 or .Xr bpf 4 -but which is most often uses with +but which is most often used with .Xr netmap 4 . The .Ar interface name @@ -146,7 +265,8 @@ system to balance traffic across the interface. .Nm can peel off one or more of the transmit or receive rings for its own use without interfering with packets that might otherwise be destined -for the host. For example on a system with a Chelsio Network +for the host. +For example on a system with a Chelsio Network Interface Card (NIC) the interface specification of .Ar -i netmap:ncxl0 gives @@ -156,20 +276,20 @@ the more commonly known cxl0 interface, which is used system's TCP/IP stack. .Sh EXAMPLES Capture and count all packets arriving on the operating system's cxl0 -interface. Using this will block packets from reaching the operating +interface. +Using this will block packets from reaching the operating system's network stack. -.Dl +.Bd -literal -offset indent +pkt-gen -i cxl0 -f rx +.Ed .Pp -.Nm --i cxl0 -f rx -.Pp Send a stream of fake DNS packets between two hosts with a packet -length of 128 bytes. You must set the destination MAC address for +length of 128 bytes. +You must set the destination MAC address for packets to be received by the target host. -.Pp -.Dl -.Nm --i netmap:ncxl0 -f tx -s 172.16.0.1:53 -d 172.16.1.3:53 -D 00:07:43:29:2a:e0 +.Bd -literal -offset intent +pkt-gen -i netmap:ncxl0 -f tx -s 172.16.0.1:53 -d 172.16.1.3:53 -D 00:07:43:29:2a:e0 +.Ed .Sh SEE ALSO .Xr netmap 4 , .Xr bridge 8 Modified: stable/12/tools/tools/netmap/pkt-gen.c ============================================================================== --- stable/12/tools/tools/netmap/pkt-gen.c Mon Nov 19 08:53:52 2018 (r340618) +++ stable/12/tools/tools/netmap/pkt-gen.c Mon Nov 19 08:55:26 2018 (r340619) @@ -55,6 +55,11 @@ #include #include #include +#include +#ifdef linux +#define IPV6_VERSION 0x60 +#define IPV6_DEFHLIM 64 +#endif #include #include @@ -66,16 +71,18 @@ #include "ctrs.h" +static void usage(int); + #ifdef _WIN32 #define cpuset_t DWORD_PTR //uint64_t static inline void CPU_ZERO(cpuset_t *p) { - *p = 0; + *p = 0; } static inline void CPU_SET(uint32_t i, cpuset_t *p) { - *p |= 1<< (i & 0x3f); + *p |= 1<< (i & 0x3f); } #define pthread_setaffinity_np(a, b, c) !SetThreadAffinityMask(a, *c) //((void)a, 0) @@ -155,12 +162,12 @@ ether_ntoa(const struct ether_addr *n) #define cpuset_t uint64_t // XXX static inline void CPU_ZERO(cpuset_t *p) { - *p = 0; + *p = 0; } static inline void CPU_SET(uint32_t i, cpuset_t *p) { - *p |= 1<< (i & 0x3f); + *p |= 1<< (i & 0x3f); } #define pthread_setaffinity_np(a, b, c) ((void)a, 0) @@ -169,7 +176,7 @@ static inline void CPU_SET(uint32_t i, cpuset_t *p) #define IFF_PPROMISC IFF_PROMISC #include /* LLADDR */ #define clock_gettime(a,b) \ - do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) + do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) #endif /* __APPLE__ */ const char *default_payload="netmap pkt-gen DIRECT payload\n" @@ -179,10 +186,8 @@ const char *indirect_payload="netmap pkt-gen indirect "http://info.iet.unipi.it/~luigi/netmap/ "; int verbose = 0; +int normalize = 1; -#define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */ - - #define VIRT_HDR_1 10 /* length of a base vnet-hdr */ #define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ #define VIRT_HDR_MAX VIRT_HDR_2 @@ -195,14 +200,34 @@ struct virt_header { struct pkt { struct virt_header vh; struct ether_header eh; - struct ip ip; - struct udphdr udp; - uint8_t body[MAX_BODYSIZE]; // XXX hardwired + union { + struct { + struct ip ip; + struct udphdr udp; + uint8_t body[MAX_BODYSIZE]; /* hardwired */ + } ipv4; + struct { + struct ip6_hdr ip; + struct udphdr udp; + uint8_t body[MAX_BODYSIZE]; /* hardwired */ + } ipv6; + }; } __attribute__((__packed__)); +#define PKT(p, f, af) \ + ((af) == AF_INET ? (p)->ipv4.f: (p)->ipv6.f) + struct ip_range { char *name; - uint32_t start, end; /* same as struct in_addr */ + union { + struct { + uint32_t start, end; /* same as struct in_addr */ + } ipv4; + struct { + struct in6_addr start, end; + uint8_t sgroup, egroup; + } ipv6; + }; uint16_t port0, port1; }; @@ -227,15 +252,18 @@ struct tstamp { */ struct glob_arg { + int af; /* address family AF_INET/AF_INET6 */ struct ip_range src_ip; struct ip_range dst_ip; struct mac_range dst_mac; struct mac_range src_mac; int pkt_size; + int pkt_min_size; int burst; int forever; uint64_t npackets; /* total packets to send */ - int frags; /* fragments per packet */ + int frags; /* fragments per packet */ + u_int mtu; /* size of each fragment */ int nthreads; int cpus; /* cpus used for running */ int system_cpus; /* cpus on the system */ @@ -271,12 +299,12 @@ struct glob_arg { char *nmr_config; int dummy_send; int virt_header; /* send also the virt_header */ - int extra_bufs; /* goes in nr_arg3 */ - int extra_pipes; /* goes in nr_arg1 */ char *packet_file; /* -P option */ #define STATS_WIN 15 int win_idx; int64_t win[STATS_WIN]; + int wait_link; + int framing; /* #bits of framing (for bw output) */ }; enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; @@ -304,79 +332,166 @@ struct targ { struct pkt pkt; void *frame; + uint16_t seed[3]; + u_int frags; + u_int frag_size; }; +static __inline uint16_t +cksum_add(uint16_t sum, uint16_t a) +{ + uint16_t res; + res = sum + a; + return (res + (res < a)); +} + +static void +extract_ipv4_addr(char *name, uint32_t *addr, uint16_t *port) +{ + struct in_addr a; + char *pp; + + pp = strchr(name, ':'); + if (pp != NULL) { /* do we have ports ? */ + *pp++ = '\0'; + *port = (uint16_t)strtol(pp, NULL, 0); + } + + inet_pton(AF_INET, name, &a); + *addr = ntohl(a.s_addr); +} + +static void +extract_ipv6_addr(char *name, struct in6_addr *addr, uint16_t *port, + uint8_t *group) +{ + char *pp; + + /* + * We accept IPv6 address in the following form: + * group@[2001:DB8::1001]:port (w/ brackets and port) + * group@[2001:DB8::1] (w/ brackets and w/o port) + * group@2001:DB8::1234 (w/o brackets and w/o port) + */ + pp = strchr(name, '@'); + if (pp != NULL) { + *pp++ = '\0'; + *group = (uint8_t)strtol(name, NULL, 0); + if (*group > 7) + *group = 7; + name = pp; + } + if (name[0] == '[') + name++; + pp = strchr(name, ']'); + if (pp != NULL) + *pp++ = '\0'; + if (pp != NULL && *pp != ':') + pp = NULL; + if (pp != NULL) { /* do we have ports ? */ + *pp++ = '\0'; + *port = (uint16_t)strtol(pp, NULL, 0); + } + inet_pton(AF_INET6, name, addr); +} /* * extract the extremes from a range of ipv4 addresses. * addr_lo[-addr_hi][:port_lo[-port_hi]] */ -static void -extract_ip_range(struct ip_range *r) +static int +extract_ip_range(struct ip_range *r, int af) { - char *ap, *pp; + char *name, *ap, start[INET6_ADDRSTRLEN]; + char end[INET6_ADDRSTRLEN]; struct in_addr a; + uint32_t tmp; if (verbose) D("extract IP range from %s", r->name); - r->port0 = r->port1 = 0; - r->start = r->end = 0; + name = strdup(r->name); + if (name == NULL) { + D("strdup failed"); + usage(-1); + } /* the first - splits start/end of range */ - ap = index(r->name, '-'); /* do we have ports ? */ - if (ap) { + ap = strchr(name, '-'); + if (ap != NULL) *ap++ = '\0'; - } - /* grab the initial values (mandatory) */ - pp = index(r->name, ':'); - if (pp) { - *pp++ = '\0'; - r->port0 = r->port1 = strtol(pp, NULL, 0); - }; - inet_aton(r->name, &a); - r->start = r->end = ntohl(a.s_addr); - if (ap) { - pp = index(ap, ':'); - if (pp) { - *pp++ = '\0'; - if (*pp) - r->port1 = strtol(pp, NULL, 0); + r->port0 = 1234; /* default port */ + if (af == AF_INET6) { + r->ipv6.sgroup = 7; /* default group */ + extract_ipv6_addr(name, &r->ipv6.start, &r->port0, + &r->ipv6.sgroup); + } else + extract_ipv4_addr(name, &r->ipv4.start, &r->port0); + + r->port1 = r->port0; + if (af == AF_INET6) { + if (ap != NULL) { + r->ipv6.egroup = r->ipv6.sgroup; + extract_ipv6_addr(ap, &r->ipv6.end, &r->port1, + &r->ipv6.egroup); + } else { + r->ipv6.end = r->ipv6.start; + r->ipv6.egroup = r->ipv6.sgroup; } - if (*ap) { - inet_aton(ap, &a); - r->end = ntohl(a.s_addr); - } + } else { + if (ap != NULL) { + extract_ipv4_addr(ap, &r->ipv4.end, &r->port1); + if (r->ipv4.start > r->ipv4.end) { + tmp = r->ipv4.end; + r->ipv4.end = r->ipv4.start; + r->ipv4.start = tmp; + } + } else + r->ipv4.end = r->ipv4.start; } + if (r->port0 > r->port1) { - uint16_t tmp = r->port0; + tmp = r->port0; r->port0 = r->port1; r->port1 = tmp; } - if (r->start > r->end) { - uint32_t tmp = r->start; - r->start = r->end; - r->end = tmp; + if (af == AF_INET) { + a.s_addr = htonl(r->ipv4.start); + inet_ntop(af, &a, start, sizeof(start)); + a.s_addr = htonl(r->ipv4.end); + inet_ntop(af, &a, end, sizeof(end)); + } else { + inet_ntop(af, &r->ipv6.start, start, sizeof(start)); + inet_ntop(af, &r->ipv6.end, end, sizeof(end)); } - { - struct in_addr a; - char buf1[16]; // one ip address + if (af == AF_INET) + D("range is %s:%d to %s:%d", start, r->port0, end, r->port1); + else + D("range is %d@[%s]:%d to %d@[%s]:%d", r->ipv6.sgroup, + start, r->port0, r->ipv6.egroup, end, r->port1); - a.s_addr = htonl(r->end); - strncpy(buf1, inet_ntoa(a), sizeof(buf1)); - a.s_addr = htonl(r->start); - if (1) - D("range is %s:%d to %s:%d", - inet_ntoa(a), r->port0, buf1, r->port1); - } + free(name); + if (r->port0 != r->port1 || + (af == AF_INET && r->ipv4.start != r->ipv4.end) || + (af == AF_INET6 && + !IN6_ARE_ADDR_EQUAL(&r->ipv6.start, &r->ipv6.end))) + return (OPT_COPY); + return (0); } -static void +static int extract_mac_range(struct mac_range *r) { + struct ether_addr *e; if (verbose) D("extract MAC range from %s", r->name); - bcopy(ether_aton(r->name), &r->start, 6); - bcopy(ether_aton(r->name), &r->end, 6); + + e = ether_aton(r->name); + if (e == NULL) { + D("invalid MAC address '%s'", r->name); + return 1; + } + bcopy(e, &r->start, 6); + bcopy(e, &r->end, 6); #if 0 bcopy(targ->src_mac, eh->ether_shost, 6); p = index(targ->g->src_mac, '-'); @@ -391,6 +506,7 @@ extract_mac_range(struct mac_range *r) #endif if (verbose) D("%s starts at %s", r->name, ether_ntoa(&r->start)); + return 0; } static struct targ *targs; @@ -456,7 +572,7 @@ system_ncpus(void) /* * parse the vale configuration in conf and put it in nmr. * Return the flag set if necessary. - * The configuration may consist of 0 to 4 numbers separated + * The configuration may consist of 1 to 4 numbers separated * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. * Missing numbers or zeroes stand for default values. * As an additional convenience, if exactly one number @@ -500,7 +616,7 @@ parse_nmr_config(const char* conf, struct nmreq *nmr) nmr->nr_rx_rings, nmr->nr_rx_slots); free(w); return (nmr->nr_tx_rings || nmr->nr_tx_slots || - nmr->nr_rx_rings || nmr->nr_rx_slots) ? + nmr->nr_rx_rings || nmr->nr_rx_slots) ? NM_OPEN_RING_CFG : 0; } @@ -513,7 +629,6 @@ static int source_hwaddr(const char *ifname, char *buf) { struct ifaddrs *ifaphead, *ifap; - int l = sizeof(ifap->ifa_name); if (getifaddrs(&ifaphead) != 0) { D("getifaddrs %s failed", ifname); @@ -527,7 +642,7 @@ source_hwaddr(const char *ifname, char *buf) if (!sdl || sdl->sdl_family != AF_LINK) continue; - if (strncmp(ifap->ifa_name, ifname, l) != 0) + if (strncmp(ifap->ifa_name, ifname, IFNAMSIZ) != 0) continue; mac = (uint8_t *)LLADDR(sdl); sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", @@ -562,19 +677,20 @@ setaffinity(pthread_t me, int i) return 0; } + /* Compute the checksum of the given ip header. */ -static uint16_t +static uint32_t checksum(const void *data, uint16_t len, uint32_t sum) { - const uint8_t *addr = data; + const uint8_t *addr = data; uint32_t i; - /* Checksum all the pairs of bytes first... */ - for (i = 0; i < (len & ~1U); i += 2) { - sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); - if (sum > 0xFFFF) - sum -= 0xFFFF; - } + /* Checksum all the pairs of bytes first... */ + for (i = 0; i < (len & ~1U); i += 2) { + sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } /* * If there's a single byte left over, checksum it, too. * Network byte order is big-endian, so the remaining byte is @@ -588,8 +704,8 @@ checksum(const void *data, uint16_t len, uint32_t sum) return sum; } -static u_int16_t -wrapsum(u_int32_t sum) +static uint16_t +wrapsum(uint32_t sum) { sum = ~sum & 0xFFFF; return (htons(sum)); @@ -637,64 +753,198 @@ dump_payload(const char *_p, int len, struct netmap_ri #define uh_sum check #endif /* linux */ -/* - * increment the addressed in the packet, - * starting from the least significant field. - * DST_IP DST_PORT SRC_IP SRC_PORT - */ static void -update_addresses(struct pkt *pkt, struct glob_arg *g) +update_ip(struct pkt *pkt, struct targ *t) { - uint32_t a; - uint16_t p; - struct ip *ip = &pkt->ip; - struct udphdr *udp = &pkt->udp; + struct glob_arg *g = t->g; + struct ip ip; + struct udphdr udp; + uint32_t oaddr, naddr; + uint16_t oport, nport; + uint16_t ip_sum, udp_sum; - do { - /* XXX for now it doesn't handle non-random src, random dst */ - if (g->options & OPT_RANDOM_SRC) { - udp->uh_sport = random(); - ip->ip_src.s_addr = random(); - } else { - p = ntohs(udp->uh_sport); - if (p < g->src_ip.port1) { /* just inc, no wrap */ - udp->uh_sport = htons(p + 1); + memcpy(&ip, &pkt->ipv4.ip, sizeof(ip)); + memcpy(&udp, &pkt->ipv4.udp, sizeof(udp)); + do { + ip_sum = udp_sum = 0; + naddr = oaddr = ntohl(ip.ip_src.s_addr); + nport = oport = ntohs(udp.uh_sport); + if (g->options & OPT_RANDOM_SRC) { + ip.ip_src.s_addr = nrand48(t->seed); + udp.uh_sport = nrand48(t->seed); + naddr = ntohl(ip.ip_src.s_addr); + nport = ntohs(udp.uh_sport); break; } - udp->uh_sport = htons(g->src_ip.port0); - - a = ntohl(ip->ip_src.s_addr); - if (a < g->src_ip.end) { /* just inc, no wrap */ - ip->ip_src.s_addr = htonl(a + 1); + if (oport < g->src_ip.port1) { + nport = oport + 1; + udp.uh_sport = htons(nport); break; } - ip->ip_src.s_addr = htonl(g->src_ip.start); - - udp->uh_sport = htons(g->src_ip.port0); + nport = g->src_ip.port0; + udp.uh_sport = htons(nport); + if (oaddr < g->src_ip.ipv4.end) { + naddr = oaddr + 1; + ip.ip_src.s_addr = htonl(naddr); + break; + } + naddr = g->src_ip.ipv4.start; + ip.ip_src.s_addr = htonl(naddr); + } while (0); + /* update checksums if needed */ + if (oaddr != naddr) { + ip_sum = cksum_add(ip_sum, ~oaddr >> 16); + ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff); + ip_sum = cksum_add(ip_sum, naddr >> 16); + ip_sum = cksum_add(ip_sum, naddr & 0xffff); } - - if (g->options & OPT_RANDOM_DST) { - udp->uh_dport = random(); - ip->ip_dst.s_addr = random(); - } else { - p = ntohs(udp->uh_dport); - if (p < g->dst_ip.port1) { /* just inc, no wrap */ - udp->uh_dport = htons(p + 1); + if (oport != nport) { + udp_sum = cksum_add(udp_sum, ~oport); + udp_sum = cksum_add(udp_sum, nport); + } + do { + naddr = oaddr = ntohl(ip.ip_dst.s_addr); + nport = oport = ntohs(udp.uh_dport); + if (g->options & OPT_RANDOM_DST) { + ip.ip_dst.s_addr = nrand48(t->seed); + udp.uh_dport = nrand48(t->seed); + naddr = ntohl(ip.ip_dst.s_addr); + nport = ntohs(udp.uh_dport); break; } - udp->uh_dport = htons(g->dst_ip.port0); - - a = ntohl(ip->ip_dst.s_addr); - if (a < g->dst_ip.end) { /* just inc, no wrap */ - ip->ip_dst.s_addr = htonl(a + 1); + if (oport < g->dst_ip.port1) { + nport = oport + 1; + udp.uh_dport = htons(nport); break; } + nport = g->dst_ip.port0; + udp.uh_dport = htons(nport); + if (oaddr < g->dst_ip.ipv4.end) { + naddr = oaddr + 1; + ip.ip_dst.s_addr = htonl(naddr); + break; + } + naddr = g->dst_ip.ipv4.start; + ip.ip_dst.s_addr = htonl(naddr); + } while (0); + /* update checksums */ + if (oaddr != naddr) { + ip_sum = cksum_add(ip_sum, ~oaddr >> 16); + ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff); + ip_sum = cksum_add(ip_sum, naddr >> 16); + ip_sum = cksum_add(ip_sum, naddr & 0xffff); } - ip->ip_dst.s_addr = htonl(g->dst_ip.start); - } while (0); - // update checksum + if (oport != nport) { + udp_sum = cksum_add(udp_sum, ~oport); + udp_sum = cksum_add(udp_sum, nport); + } + if (udp_sum != 0) + udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(udp_sum)); + if (ip_sum != 0) { + ip.ip_sum = ~cksum_add(~ip.ip_sum, htons(ip_sum)); + udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(ip_sum)); + } + memcpy(&pkt->ipv4.ip, &ip, sizeof(ip)); + memcpy(&pkt->ipv4.udp, &udp, sizeof(udp)); } +#ifndef s6_addr16 +#define s6_addr16 __u6_addr.__u6_addr16 +#endif +static void +update_ip6(struct pkt *pkt, struct targ *t) +{ + struct glob_arg *g = t->g; + struct ip6_hdr ip6; + struct udphdr udp; + uint16_t udp_sum; + uint16_t oaddr, naddr; + uint16_t oport, nport; + uint8_t group; + + memcpy(&ip6, &pkt->ipv6.ip, sizeof(ip6)); + memcpy(&udp, &pkt->ipv6.udp, sizeof(udp)); + do { + udp_sum = 0; + group = g->src_ip.ipv6.sgroup; + naddr = oaddr = ntohs(ip6.ip6_src.s6_addr16[group]); + nport = oport = ntohs(udp.uh_sport); + if (g->options & OPT_RANDOM_SRC) { + ip6.ip6_src.s6_addr16[group] = nrand48(t->seed); + udp.uh_sport = nrand48(t->seed); + naddr = ntohs(ip6.ip6_src.s6_addr16[group]); + nport = ntohs(udp.uh_sport); + break; + } + if (oport < g->src_ip.port1) { + nport = oport + 1; + udp.uh_sport = htons(nport); + break; + } + nport = g->src_ip.port0; + udp.uh_sport = htons(nport); + if (oaddr < ntohs(g->src_ip.ipv6.end.s6_addr16[group])) { + naddr = oaddr + 1; + ip6.ip6_src.s6_addr16[group] = htons(naddr); + break; + } + naddr = ntohs(g->src_ip.ipv6.start.s6_addr16[group]); + ip6.ip6_src.s6_addr16[group] = htons(naddr); + } while (0); + /* update checksums if needed */ + if (oaddr != naddr) + udp_sum = cksum_add(~oaddr, naddr); + if (oport != nport) + udp_sum = cksum_add(udp_sum, + cksum_add(~oport, nport)); + do { + group = g->dst_ip.ipv6.egroup; + naddr = oaddr = ntohs(ip6.ip6_dst.s6_addr16[group]); + nport = oport = ntohs(udp.uh_dport); + if (g->options & OPT_RANDOM_DST) { + ip6.ip6_dst.s6_addr16[group] = nrand48(t->seed); + udp.uh_dport = nrand48(t->seed); + naddr = ntohs(ip6.ip6_dst.s6_addr16[group]); + nport = ntohs(udp.uh_dport); + break; + } + if (oport < g->dst_ip.port1) { + nport = oport + 1; + udp.uh_dport = htons(nport); + break; + } + nport = g->dst_ip.port0; + udp.uh_dport = htons(nport); + if (oaddr < ntohs(g->dst_ip.ipv6.end.s6_addr16[group])) { + naddr = oaddr + 1; + ip6.ip6_dst.s6_addr16[group] = htons(naddr); + break; + } + naddr = ntohs(g->dst_ip.ipv6.start.s6_addr16[group]); + ip6.ip6_dst.s6_addr16[group] = htons(naddr); + } while (0); + /* update checksums */ + if (oaddr != naddr) + udp_sum = cksum_add(udp_sum, + cksum_add(~oaddr, naddr)); + if (oport != nport) + udp_sum = cksum_add(udp_sum, + cksum_add(~oport, nport)); + if (udp_sum != 0) + udp.uh_sum = ~cksum_add(~udp.uh_sum, udp_sum); + memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6)); + memcpy(&pkt->ipv6.udp, &udp, sizeof(udp)); +} + +static void +update_addresses(struct pkt *pkt, struct targ *t) +{ + + if (t->g->af == AF_INET) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***