Date: Wed, 27 Jun 2018 16:17:22 GMT From: sduo@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r337265 - soc2018/sduo/netmap_utils Message-ID: <201806271617.w5RGHMEx083846@socsvn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sduo Date: Wed Jun 27 16:17:21 2018 New Revision: 337265 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=337265 Log: functional.c can request netmap interface file descriptors from fd_server. Added: soc2018/sduo/netmap_utils/functional.c Modified: soc2018/sduo/netmap_utils/fd_server.c soc2018/sduo/netmap_utils/fd_server.h Modified: soc2018/sduo/netmap_utils/fd_server.c ============================================================================== --- soc2018/sduo/netmap_utils/fd_server.c Wed Jun 27 15:52:47 2018 (r337264) +++ soc2018/sduo/netmap_utils/fd_server.c Wed Jun 27 16:17:21 2018 (r337265) @@ -1,5 +1,7 @@ #include <errno.h> #include <fcntl.h> +#include <net/if.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -10,17 +12,22 @@ #include <sys/un.h> #include <sys/wait.h> #include <syslog.h> -#include <unistd.h> + +#include <net/netmap.h> +#define NETMAP_WITH_LIBS +#include <net/netmap_user.h> #include "fd_server.h" struct nmd_entry { + char if_name[NETMAP_REQ_IFNAMSIZ]; struct nm_desc *nmd; uint8_t is_in_use; uint8_t is_open; }; -#define SOCKET_NAME "/tmp/my_unix_socket" +#define printf(format, ...) syslog(LOG_NOTICE, format, ##__VA_ARGS__) + #define MAX_OPEN_IF 128 struct nmd_entry entries[MAX_OPEN_IF]; int num_entries = 0; @@ -29,13 +36,13 @@ print_request(struct fd_request *req) { - syslog(LOG_NOTICE, "d action: %s, if_name: %s\n", + printf("action: %s, if_name: '%s'\n", req->action == FD_GET - ? "FD_GET" - : req->action == FD_RELEASE - ? "FD_RELEASE" - : req->action == FD_CLOSE ? "FD_CLOSE" - : "FD_STOP", + ? "FD_GET" + : req->action == FD_RELEASE + ? "FD_RELEASE" + : req->action == FD_CLOSE ? "FD_CLOSE" + : "FD_STOP", req->if_name); } @@ -44,32 +51,34 @@ { int i; + // printf("searching %s\n", if_name); for (i = 0; i < num_entries; ++i) { struct nmd_entry *entry = &entries[i]; - struct nm_desc *nmd = entry->nmd; + + // printf("i=%d, is_open=%d, is_in_use=%d, if_name=%s\n", + // i, entry->is_open, entry->is_in_use, entry->if_name); if (entry->is_open == 0) { continue; } - if (strcmp(nmd->req.nr_name, if_name) == 0) { + if (strncmp(entry->if_name, if_name, IFNAMSIZ) == 0) { + // printf("finished searching with a match\n"); return entry; } } + // printf("finished searching without a match\n"); return NULL; } struct nmd_entry * -get_free_des(int *ret) +get_free_des(void) { - if (num_entries == MAX_OPEN_IF) { - *ret = -1; return NULL; } - *ret = 0; return &entries[num_entries++]; } @@ -77,12 +86,11 @@ get_fd(const char *if_name, struct fd_response *res) { struct nmd_entry *entry; - int ret; entry = search_des(if_name); if (entry != NULL) { if (entry->is_in_use == 1) { - syslog(LOG_NOTICE, "if_name %s is in use\n", if_name); + printf("if_name %s is in use\n", if_name); res->result = EBUSY; return -1; } @@ -90,20 +98,21 @@ return entry->nmd->fd; } - entry = get_free_des(&ret); - if (ret == -1) { - syslog(LOG_NOTICE, "Out of memory\n"); + entry = get_free_des(); + if (entry == NULL) { + printf("Out of memory\n"); res->result = ENOMEM; return -1; } entry->nmd = nm_open(if_name, NULL, 0, NULL); if (entry->nmd == NULL) { - syslog(LOG_NOTICE, "Failed to nm_open(%s) with error %d\n", - if_name, errno); + printf("Failed to nm_open(%s) with error %d\n", if_name, errno); res->result = errno; return -1; } + strncpy(entry->if_name, if_name, sizeof(entry->if_name)); + entry->if_name[sizeof(entry->if_name) - 1] = '\0'; memcpy(&res->req, &entry->nmd->req, sizeof(entry->nmd->req)); entry->is_in_use = 1; @@ -118,7 +127,7 @@ entry = search_des(if_name); if (entry == NULL) { - syslog(LOG_NOTICE, "if_name %s isn't open\n", if_name); + printf("if_name %s isn't open\n", if_name); res->result = ENOENT; return; } @@ -132,7 +141,7 @@ struct nmd_entry *entry; int ret; - if (if_name == NULL || strnlen(if_name, IFNAMSIZ) == 0) { + if (if_name == NULL || strnlen(if_name, NETMAP_REQ_IFNAMSIZ) == 0) { res->result = EINVAL; return; } @@ -140,14 +149,14 @@ entry = search_des(if_name); if (entry == NULL) { res->result = ENOENT; - syslog(LOG_NOTICE, "if_name %s hasn't been opened\n", if_name); + printf("if_name %s hasn't been opened\n", if_name); return; } - ret = nm_close(entry->nmd); + ret = nm_close(entry->nmd); res->result = ret; if (ret != 0) { - syslog(LOG_NOTICE, "error while close interface %s\n", if_name); + printf("error while close interface %s\n", if_name); return; } entry->is_in_use = 0; @@ -164,10 +173,10 @@ struct cmsghdr *cmsg; struct iovec iov[1]; struct msghdr msg; + int ret; iov[0].iov_base = buf; iov[0].iov_len = buf_size; - memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iov = iov; msg.msg_iovlen = 1; @@ -176,17 +185,18 @@ /* We need the ancillary data only when we're sending a file * descriptor, and a file descriptor cannot be negative. */ - msg.msg_control = ancillary.buf; - msg.msg_controllen = sizeof(ancillary.buf); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + printf("sending a file descriptor\n"); + msg.msg_control = ancillary.buf; + msg.msg_controllen = sizeof(ancillary.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); *(int *)CMSG_DATA(cmsg) = fd; } - return sendmsg(socket, &msg, 0); + ret = sendmsg(socket, &msg, 0); + return ret; } int @@ -194,15 +204,14 @@ { struct fd_response res; struct fd_request req; - int amount; int fd = -1; + int amount; + int ret; - memset(&res, 0, sizeof(res)); memset(&req, 0, sizeof(req)); - amount = recv(socket, &req, sizeof(struct fd_request), 0); if (amount == -1) { - syslog(LOG_NOTICE, "error during recv()"); + printf("error while receiving the request\n"); return -1; } @@ -219,14 +228,18 @@ close_fd(req.if_name, &res); return 0; case FD_STOP: - syslog(LOG_NOTICE, "shutting down"); + printf("shutting down\n"); exit(EXIT_SUCCESS); break; default: res.result = EOPNOTSUPP; } - return send_fd(socket, fd, &res, sizeof(struct fd_response)); + ret = send_fd(socket, fd, &res, sizeof(struct fd_response)); + if (ret == -1) { + printf("error while sending the reponse\n"); + } + return ret; } void @@ -236,42 +249,48 @@ int socket_fd; int ret; + printf("starting up.\n"); + if (unlink(SOCKET_NAME) == -1 && errno != ENOENT) { + printf("error %d during unlink()", errno); + exit(EXIT_FAILURE); + } socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (socket_fd == -1) { - syslog(LOG_NOTICE, "error during socket()"); + printf("error during socket()\n"); exit(EXIT_FAILURE); } - unlink(SOCKET_NAME); memset(&name, 0, sizeof(struct sockaddr_un)); name.sun_family = AF_UNIX; strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1); ret = bind(socket_fd, (const struct sockaddr *)&name, - sizeof(struct sockaddr_un)); + sizeof(struct sockaddr_un)); if (ret == -1) { - syslog(LOG_NOTICE, "error during bind()"); + printf("error during bind()\n"); exit(EXIT_FAILURE); } ret = listen(socket_fd, 2); if (ret == -1) { - syslog(LOG_NOTICE, "error during listen()"); + printf("error during listen()"); exit(EXIT_FAILURE); } + printf("listening\n"); for (;;) { int conn_fd; int ret; conn_fd = accept(socket_fd, NULL, NULL); if (conn_fd == -1) { - syslog(LOG_NOTICE, - "error during accept(), shutting down"); + printf("error during accept(), shutting down\n"); exit(EXIT_FAILURE); } - syslog(LOG_NOTICE, "handling a request"); ret = handle_request(conn_fd); + if (ret == -1) { + printf("error while handling a request\n"); + } (void)ret; close(conn_fd); } @@ -291,7 +310,7 @@ exit(EXIT_SUCCESS); } - if (setsid() < 0) { + if (setsid() == -1) { exit(EXIT_FAILURE); } @@ -307,10 +326,11 @@ } umask(0); + if (chdir("/") == -1) { - perror("chdir()"); exit(EXIT_FAILURE); } + for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) { close(i); } @@ -321,7 +341,6 @@ int main() { - daemonize(); main_loop(); return 0; Modified: soc2018/sduo/netmap_utils/fd_server.h ============================================================================== --- soc2018/sduo/netmap_utils/fd_server.h Wed Jun 27 15:52:47 2018 (r337264) +++ soc2018/sduo/netmap_utils/fd_server.h Wed Jun 27 16:17:21 2018 (r337265) @@ -1,14 +1,7 @@ #ifndef FD_LIB_H #define FD_LIB_H -#include <errno.h> -#include <net/if.h> -#include <stdint.h> -#include <sys/socket.h> - -#include <net/netmap.h> -#define NETMAP_WITH_LIBS -#include <net/netmap_user.h> +#define SOCKET_NAME "/tmp/my_unix_socket" struct fd_request { #define FD_GET 1 @@ -16,7 +9,7 @@ #define FD_CLOSE 3 #define FD_STOP 4 uint8_t action; - char if_name[IFNAMSIZ]; + char if_name[NETMAP_REQ_IFNAMSIZ]; }; struct fd_response { Added: soc2018/sduo/netmap_utils/functional.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2018/sduo/netmap_utils/functional.c Wed Jun 27 16:17:21 2018 (r337265) @@ -0,0 +1,1245 @@ +/* + * A tool for functional testing netmap transmission and reception. + * + * Copyright (C) 2018 Vincenzo Maffione. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <assert.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <net/netmap.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#define NETMAP_WITH_LIBS +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <net/netmap_user.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <sys/wait.h> + +#include "fd_server.h" + +#define ETH_ADDR_LEN 6 + +struct Event { + unsigned evtype; +#define EVENT_TYPE_RX 0x1 +#define EVENT_TYPE_TX 0x2 +#define EVENT_TYPE_PAUSE 0x3 + unsigned num; /* > 1 if repeated event */ + + /* Tx and Rx event. */ + unsigned pkt_len; + char filler; + + /* Pause event. */ + unsigned long long usecs; +}; + +struct Global { + struct nm_desc nmd; + const char *ifname; + unsigned wait_link_secs; /* wait for link */ + unsigned timeout_secs; /* transmit/receive timeout */ + int ignore_if_not_matching; /* ignore certain received packets */ + int success_if_no_receive; /* exit status 0 if we receive no packets */ + int + sequential_fill; /* increment fill char for multi-packets + operations */ + int request_from_fd_server; + int verbose; + +#define MAX_PKT_SIZE 65536 + char pktm[MAX_PKT_SIZE]; /* packet model */ + unsigned pktm_len; /* packet model length */ + char pktr[MAX_PKT_SIZE]; /* packet received */ + unsigned pktr_len; /* length of received packet */ + unsigned max_frag_size; /* max bytes per netmap TX slot */ + + char src_mac[ETH_ADDR_LEN]; + char dst_mac[ETH_ADDR_LEN]; + uint32_t src_ip; + uint32_t dst_ip; + uint16_t src_port; + uint16_t dst_port; + char filler; + +#define MAX_EVENTS 64 + unsigned num_events; + struct Event events[MAX_EVENTS]; + unsigned num_loops; +}; + +void release_if_fd(struct Global *, const char *); + +void +clean_exit(struct Global *g) +{ + + release_if_fd(g, g->ifname); + exit(EXIT_FAILURE); +} + +static void +fill_packet_field(struct Global *g, unsigned offset, const char *content, + unsigned content_len) +{ + if (offset + content_len > sizeof(g->pktm)) { + printf("Packet layout overflow: %u + %u > %lu\n", offset, + content_len, sizeof(g->pktm)); + clean_exit(g); + } + + memcpy(g->pktm + offset, content, content_len); +} + +static void +fill_packet_8bit(struct Global *g, unsigned offset, uint8_t val) +{ + fill_packet_field(g, offset, (const char *)&val, sizeof(val)); +} + +static void +fill_packet_16bit(struct Global *g, unsigned offset, uint16_t val) +{ + val = htons(val); + fill_packet_field(g, offset, (const char *)&val, sizeof(val)); +} + +static void +fill_packet_32bit(struct Global *g, unsigned offset, uint32_t val) +{ + val = htonl(val); + fill_packet_field(g, offset, (const char *)&val, sizeof(val)); +} + +/* Compute the checksum of the given ip header. */ +static uint32_t +checksum(const void *data, uint16_t len, uint32_t sum /* host endianness */) +{ + 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; + } + } + /* + * If there's a single byte left over, checksum it, too. + * Network byte order is big-endian, so the remaining byte is + * the high byte. + */ + if (i < len) { + sum += addr[i] << 8; + if (sum > 0xFFFF) { + sum -= 0xFFFF; + } + } + return sum; +} + +static uint16_t +wrapsum(uint32_t sum /* host endianness */) +{ + sum = ~sum & 0xFFFF; + return sum; /* host endianness */ +} + +static void +build_packet(struct Global *g) +{ + unsigned ofs = 0; + unsigned ethofs; + unsigned ipofs; + unsigned udpofs; + unsigned pldofs; + + memset(g->pktm, 0, sizeof(g->pktm)); + if (g->verbose) { + printf("%s: starting at ofs %u\n", __func__, ofs); + } + + ethofs = ofs; + (void)ethofs; + /* Ethernet destination and source MAC address plus ethertype. */ + fill_packet_field(g, ofs, g->dst_mac, ETH_ADDR_LEN); + ofs += ETH_ADDR_LEN; + fill_packet_field(g, ofs, g->src_mac, ETH_ADDR_LEN); + ofs += ETH_ADDR_LEN; + fill_packet_16bit(g, ofs, ETHERTYPE_IP); + ofs += 2; + if (g->verbose) { + printf("%s: eth done, ofs %u\n", __func__, ofs); + } + + ipofs = ofs; + /* First byte of IP header. */ + fill_packet_8bit(g, ofs, + (IPVERSION << 4) | ((sizeof(struct iphdr)) >> 2)); + ofs += 1; + /* Skip QoS byte. */ + ofs += 1; + /* Total length. */ + fill_packet_16bit(g, ofs, g->pktm_len - ipofs); + ofs += 2; + /* Skip identification field. */ + ofs += 2; + /* Offset (and flags) field. */ + fill_packet_16bit(g, ofs, IP_DF); + ofs += 2; + /* TTL. */ + fill_packet_8bit(g, ofs, IPDEFTTL); + ofs += 1; + /* Protocol. */ + fill_packet_8bit(g, ofs, IPPROTO_UDP); + ofs += 1; + /* Skip checksum for now. */ + ofs += 2; + /* Source IP address. */ + fill_packet_32bit(g, ofs, g->src_ip); + ofs += 4; + /* Dst IP address. */ + fill_packet_32bit(g, ofs, g->dst_ip); + ofs += 4; + /* Now put the checksum. */ + fill_packet_16bit( + g, ipofs + 10, + wrapsum(checksum(g->pktm + ipofs, sizeof(struct iphdr), 0))); + if (g->verbose) { + printf("%s: ip done, ofs %u\n", __func__, ofs); + } + + udpofs = ofs; + /* UDP source port. */ + fill_packet_16bit(g, ofs, g->src_port); + ofs += 2; + /* UDP source port. */ + fill_packet_16bit(g, ofs, g->dst_port); + ofs += 2; + /* UDP length (UDP header + data). */ + fill_packet_16bit(g, ofs, g->pktm_len - udpofs); + ofs += 2; + /* Skip the UDP checksum for now. */ + ofs += 2; + if (g->verbose) { + printf("%s: udp done, ofs %u\n", __func__, ofs); + } + + /* Fill UDP payload. */ + pldofs = ofs; + for (; ofs < g->pktm_len; ofs++) { + fill_packet_8bit(g, ofs, g->filler); + } + if (g->verbose) { + printf("%s: payload done, ofs %u\n", __func__, ofs); + } + + /* Put the UDP checksum now. + * Magic: taken from sbin/dhclient/packet.c */ + fill_packet_16bit( + g, udpofs + 6, + wrapsum(checksum( + /* udp header */ g->pktm + udpofs, + sizeof(struct udphdr), + checksum(/* udp payload */ g->pktm + pldofs, + g->pktm_len - pldofs, + checksum(/* pseudo header */ g->pktm + ipofs + + 12, + 2 * sizeof(g->src_ip), + IPPROTO_UDP + (uint32_t)(g->pktm_len - + udpofs)))))); +} + +// static unsigned +// tx_bytes_avail(struct netmap_ring *ring, unsigned max_frag_size) +// { +// unsigned avail_per_slot = ring->nr_buf_size; + +// if (max_frag_size < avail_per_slot) { +// avail_per_slot = max_frag_size; +// } + +// return nm_ring_space(ring) * avail_per_slot; +// } + +static int +tx_flush(struct Global *g) +{ + struct nm_desc *nmd = &g->nmd; + unsigned elapsed_ms = 0; + unsigned wait_ms = 100; + int i; + + for (;;) { + int pending = 0; + for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) { + struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i); + + pending += nm_tx_pending(ring); + } + + if (!pending) { + return 0; + } + + if (elapsed_ms > g->timeout_secs * 1000) { + printf("%s: Timeout\n", __func__); + return -1; + } + + usleep(wait_ms * 1000); + elapsed_ms += wait_ms; + + ioctl(nmd->fd, NIOCTXSYNC, NULL); + } +} + +uint64_t +ring_avail_packets(struct netmap_ring *ring, unsigned pkt_len) +{ + uint64_t slot_per_packet; + + slot_per_packet = ceil((double)pkt_len / (double)ring->nr_buf_size); + return nm_ring_space(ring) / slot_per_packet; +} + +uint64_t +adapter_avail_sends(struct nm_desc *nmd, unsigned pkt_len) +{ + uint64_t sends_available = 0; + unsigned int i; + + for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) { + struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i); + + sends_available += ring_avail_packets(ring, pkt_len); + } + + return sends_available; +} + +void +put_one_packet(struct Global *g, struct netmap_ring *ring) +{ + unsigned head = ring->head; + unsigned frags = 0; + unsigned ofs = 0; + + for (;;) { + struct netmap_slot *slot = &ring->slot[head]; + char *buf = NETMAP_BUF(ring, slot->buf_idx); + unsigned copysize = g->pktm_len - ofs; + + if (copysize > ring->nr_buf_size) { + copysize = ring->nr_buf_size; + } + if (copysize > g->max_frag_size) { + copysize = g->max_frag_size; + } + + memcpy(buf, g->pktm + ofs, copysize); + ofs += copysize; + slot->len = copysize; + slot->flags = NS_MOREFRAG; + head = nm_ring_next(ring, head); + frags++; + if (ofs >= g->pktm_len) { + /* Last fragment. */ + assert(ofs == g->pktm_len); + slot->flags = NS_REPORT; + break; + } + } + + ring->head = ring->cur = head; + printf("packet (%u bytes, %u frags) placed to TX\n", g->pktm_len, + frags); +} + +char +next_fill(char cur_fill) +{ + if (cur_fill == 'z') + return 'a'; + if (cur_fill == 'Z') + return 'A'; + return ++cur_fill; +} + +/* Transmit packets_num packets using any combination of TX rings. */ +static int +tx(struct Global *g, unsigned packets_num) +{ + struct nm_desc *nmd = &g->nmd; + unsigned elapsed_ms = 0; + unsigned wait_ms = 100; + unsigned int i; + + /* We cycle here until either we timeout or we find enough space. */ + for (;;) { + if (adapter_avail_sends(nmd, g->pktm_len) >= packets_num) { + break; + } + + if (elapsed_ms > g->timeout_secs * 1000) { + printf("%s: Timeout\n", __func__); + return -1; + } + + /* Retry after a short while. */ + usleep(wait_ms * 1000); + elapsed_ms += wait_ms; + ioctl(nmd->fd, NIOCTXSYNC, NULL); + } + + /* Once we have enough space, we start filling slots. We might use + * multiple rings. + */ + for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) { + struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i); + uint64_t ring_sends_num; + + for (ring_sends_num = ring_avail_packets(ring, g->pktm_len); + ring_sends_num > 0 && packets_num > 0; + --ring_sends_num, --packets_num) { + put_one_packet(g, ring); + + if (g->sequential_fill == 1) { + g->filler = next_fill(g->filler); + build_packet(g); + } + } + + if (packets_num == 0) { + break; + } + } + + assert(packets_num == 0); + /* Once we're done we sync, sending all packets at once. */ + ioctl(nmd->fd, NIOCTXSYNC, NULL); + return 0; +} + +/* If -I option is specified, we want to ignore frames that don't match + * our expected ethernet header. + * This function currently assumes that Ethernet header starts from + * the beginning of the packet buffers. */ +static int +ignore_received_frame(struct Global *g) +{ + if (!g->ignore_if_not_matching) { + return 0; /* don't ignore */ + } + + if (g->pktr_len < 14 || memcmp(g->pktm, g->pktr, 14) != 0) { + return 1; /* ignore */ + } + + return 0; /* don't ignore */ +} + +uint64_t +adapter_avail_receives(struct nm_desc *nmd, unsigned pkt_len) +{ + uint64_t receives_available = 0; + unsigned int i; + + for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) { + struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i); + + receives_available += ring_avail_packets(ring, pkt_len); + } + + return receives_available; +} + +static int +rx_check(struct Global *g) +{ + unsigned i; + + if (g->pktr_len != g->pktm_len) { + printf("Received packet length (%u) different from " + "expected (%u bytes)\n", + g->pktr_len, g->pktm_len); + return -1; + } + + for (i = 0; i < g->pktr_len; i++) { + if (g->pktr[i] != g->pktm[i]) { + printf("Received packet differs from model at " + "offset %u (0x%02x!=0x%02x)\n", + i, g->pktr[i], (uint8_t)g->pktm[i]); + return -1; + } + } + + return 0; +} + +int +read_one_packet(struct Global *g, struct netmap_ring *ring) +{ + unsigned head = ring->head; + int frags = 0; + + g->pktr_len = 0; + for (;;) { + struct netmap_slot *slot = &ring->slot[head]; + char *buf = NETMAP_BUF(ring, slot->buf_idx); + + if (g->pktr_len + slot->len > sizeof(g->pktr)) { + /* Sanity check. */ + printf("Error: received packet too " + "large " + "(>= %u bytes) ", + g->pktr_len + slot->len); + clean_exit(g); + } + + memcpy(g->pktr + g->pktr_len, buf, slot->len); + g->pktr_len += slot->len; + head = nm_ring_next(ring, head); + frags++; + if (!(slot->flags & NS_MOREFRAG)) { + break; + } + + if (head == ring->tail) { + printf("warning: truncated packet " + "(len=%u)\n", + g->pktr_len); + frags = -1; + break; + } + } + + ring->head = ring->cur = head; + printf("packet (%u bytes, %d frags) received " + "from RX\n", + g->pktr_len, frags); + return frags; +} + +/* Receive packets_num packets from any combination of RX rings. */ +static int +rx(struct Global *g, unsigned packets_num) +{ + struct nm_desc *nmd = &g->nmd; + unsigned elapsed_ms = 0; + unsigned wait_ms = 100; + unsigned int i; + + /* We cycle here until either we timeout or we find enough space. */ + for (;;) { + again: + if (adapter_avail_receives(nmd, g->pktm_len) >= packets_num) { + break; + } + + if (elapsed_ms > g->timeout_secs * 1000) { + printf("%s: Timeout\n", __func__); + /* -n flag */ + return g->success_if_no_receive == 1 ? 0 : -1; + } + + /* Retry after a short while. */ + usleep(wait_ms * 1000); + elapsed_ms += wait_ms; + ioctl(nmd->fd, NIOCRXSYNC, NULL); + } + + /* Once we have enough space, we start reading packets. We might use + * multiple rings. + */ + for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) { + struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i); + uint64_t ring_receives_num; + + for (ring_receives_num = ring_avail_packets(ring, g->pktm_len); + ring_receives_num > 0 && packets_num > 0; + --ring_receives_num, --packets_num) { + int frags = 0; + + frags = read_one_packet(g, ring); + if (frags == -1) { + break; /* Truncated packet, skip this ring. */ + } + + if (ignore_received_frame(g)) { + if (g->verbose) { + printf("(ignoring packet with %u bytes " + "and " + "%d frags received from RX ring " + "#%d)\n", + g->pktr_len, frags, i); + } + elapsed_ms = 0; + /* We can go back there, because we're + * decrementing packets_num each time, therefore + * the we will wait only for the remaining + * packets. + */ + goto again; + } + + /* As soon as we find a packet wich doesn't match our + * packet model we exit with status EXIT_FAILURE. + */ + if (rx_check(g)) { + clean_exit(g); + } + + if (g->sequential_fill == 1) { + g->filler = next_fill(g->filler); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806271617.w5RGHMEx083846>