From 9991f7b1817401445349792bfb29ceabef24ad44 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Sun, 18 Feb 2024 18:14:53 +0100 Subject: [PATCH] Port netmap code to Linux Restructure netmap code into generic code and OS-specific support code. Port OS-specific support code to Linux. Linux port tested and seems to work well on Ubuntu 23.10 with netmap kernel modules and drivers added, on arm64 with UTM and vmxnet3. Results for e1000 and e1000e varied due to bad Linux drivers. Untested on bare metal or fast link speeds so far. --- README.netmap.md | 6 +- src/CMakeLists.txt | 7 ++ src/if-netmap-bsd.c | 141 ++++++++++++++++++++++++++ src/if-netmap-linux.c | 223 ++++++++++++++++++++++++++++++++++++++++++ src/if-netmap.h | 57 +++++++++++ src/recv-netmap.c | 145 ++++++++------------------- src/send-netmap.c | 4 +- src/socket-netmap.c | 8 +- src/zmap.c | 31 ++---- 9 files changed, 486 insertions(+), 136 deletions(-) create mode 100644 src/if-netmap-bsd.c create mode 100644 src/if-netmap-linux.c create mode 100644 src/if-netmap.h diff --git a/README.netmap.md b/README.netmap.md index 8fd98af59..ef2edae85 100644 --- a/README.netmap.md +++ b/README.netmap.md @@ -8,9 +8,9 @@ information on netmap. Netmap is available by default on FreeBSD on many architectures, including amd64 and arm64, and is easy to add to the kernel config on architectures where -it is not built by default. While netmap has been ported to Linux, ZMap's -netmap mode currently only supports FreeBSD and will require porting to build -and run on Linux. +it is not built by default. On Linux, netmap itself and netmap-aware drivers +can be installed by following the instructions in +[netmap/LINUX/README.md](https://github.com/luigirizzo/netmap/blob/master/LINUX/README.md). ### Prerequisites diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e77ef9075..e6b52a5f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,13 @@ if(WITH_PFRING) elseif(WITH_NETMAP) set(SOURCES ${SOURCES} socket-netmap.c send-netmap.c) set(ZTESTSOURCES ${ZTESTSOURCES} socket-netmap.c send-netmap.c) + if(APPLE OR BSD) + set(SOURCES ${SOURCES} if-netmap-bsd.c) + set(ZTESTSOURCES ${ZTESTSOURCES} if-netmap-bsd.c) + else() + set(SOURCES ${SOURCES} if-netmap-linux.c) + set(ZTESTSOURCES ${ZTESTSOURCES} if-netmap-linux.c) + endif() elseif (APPLE OR BSD) set(SOURCES ${SOURCES} socket-bsd.c send-bsd.c) set(ZTESTSOURCES ${ZTESTSOURCES} socket-bsd.c send-bsd.c) diff --git a/src/if-netmap-bsd.c b/src/if-netmap-bsd.c new file mode 100644 index 000000000..278cbc45c --- /dev/null +++ b/src/if-netmap-bsd.c @@ -0,0 +1,141 @@ +/* + * ZMap Copyright 2013 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef __FreeBSD__ +#error "NETMAP requires FreeBSD or Linux" +#endif + +#include "if-netmap.h" + +#include "../lib/includes.h" +#include "../lib/logger.h" + +#include +#include +#include +#include +#include +#include +#include + +static void +fetch_if_data(struct if_data *ifd, char const *ifname, int fd) +{ + struct ifreq ifr; + bzero(&ifr, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (caddr_t)ifd; + if (ioctl(fd, SIOCGIFDATA, &ifr) == -1) { + log_fatal("if-netmap-bsd", "unable to retrieve if_data: %d: %s", + errno, strerror(errno)); + } +} + +void +if_wait_for_phy_reset(char const *ifname, int fd) +{ + struct if_data ifd; + bzero(&ifd, sizeof(ifd)); + for (size_t i = 0; i < 40 /* 10s */; i++) { + fetch_if_data(&ifd, ifname, fd); + if (ifd.ifi_link_state == LINK_STATE_UP) { + return; + } + usleep(250000); + } + log_fatal("if-netmap-bsd", "timeout waiting for PHY reset to complete"); +} + +size_t +if_get_data_link_size(char const *ifname, int fd) +{ + struct if_data ifd; + bzero(&ifd, sizeof(ifd)); + fetch_if_data(&ifd, ifname, fd); + + switch (ifd.ifi_type) { + case IFT_ETHER: + log_debug("if-netmap-bsd", "IFT_ETHER"); + return sizeof(struct ether_header); + default: + log_fatal("if-netmap-bsd", "Unsupported if type %u", ifd.ifi_type); + } +} + +// Notes on if counters: +// On interfaces without hardware counters (HWSTATS), ipackets misses +// packets that we do not forward to the host ring pair. +// oqdrops counts packets the host OS could not send due to netmap mode. + +struct if_stats_ctx { + char *ifname; // owned + int fd; // borrowed + bool hwstats; + uint64_t ifi_ipackets; + uint64_t ifi_iqdrops; + uint64_t ifi_ierrors; + uint64_t ifi_oerrors; +}; + +if_stats_ctx_t * +if_stats_init(char const *ifname, int fd) +{ + if_stats_ctx_t *ctx = malloc(sizeof(struct if_stats_ctx)); + bzero(ctx, sizeof(struct if_stats_ctx)); + + ctx->ifname = strdup(ifname); + ctx->fd = fd; + + struct if_data ifd; + bzero(&ifd, sizeof(ifd)); + fetch_if_data(&ifd, ctx->ifname, ctx->fd); + + ctx->hwstats = (ifd.ifi_hwassist & IFCAP_HWSTATS) != 0; + if (ctx->hwstats) { + ctx->ifi_ipackets = ifd.ifi_ipackets; + } else { + ctx->ifi_ipackets = 0; + } + ctx->ifi_iqdrops = ifd.ifi_iqdrops; + ctx->ifi_ierrors = ifd.ifi_ierrors; + ctx->ifi_oerrors = ifd.ifi_oerrors; + return ctx; +} + +void +if_stats_fini(if_stats_ctx_t *ctx) +{ + free(ctx->ifname); + free(ctx); +} + +bool +if_stats_have_recv_ctr(if_stats_ctx_t *ctx) +{ + return ctx->hwstats; +} + +int +if_stats_get(if_stats_ctx_t *ctx, uint32_t *ps_recv, uint32_t *ps_drop, uint32_t *ps_ifdrop) +{ + struct if_data ifd; + bzero(&ifd, sizeof(ifd)); + fetch_if_data(&ifd, ctx->ifname, ctx->fd); + + if (ctx->hwstats) { + *ps_recv = (uint32_t)(ifd.ifi_ipackets - ctx->ifi_ipackets); + } + *ps_drop = (uint32_t)(ifd.ifi_iqdrops - ctx->ifi_iqdrops); + *ps_ifdrop = (uint32_t)(ifd.ifi_ierrors - ctx->ifi_ierrors + + ifd.ifi_oerrors - ctx->ifi_oerrors); + return 0; +} + + + + diff --git a/src/if-netmap-linux.c b/src/if-netmap-linux.c new file mode 100644 index 000000000..4af87f169 --- /dev/null +++ b/src/if-netmap-linux.c @@ -0,0 +1,223 @@ +/* + * ZMap Copyright 2013 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include "if-netmap.h" + +#include "../lib/includes.h" +#include "../lib/logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +nlrt_socket(void) +{ + int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd == -1) { + log_fatal("if-netmap-linux", "socket(NETLINK_ROUTE): %d: %s", errno, strerror(errno)); + } + + int one = 1; + (void)setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &one, sizeof(one)); + + struct sockaddr_nl sanl; + memset(&sanl, 0, sizeof(sanl)); + sanl.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *)&sanl, sizeof(sanl)) == -1) { + log_fatal("if-netmap-linux", "bind(AF_NETLINK): %d: %s", errno, strerror(errno)); + } + + return fd; +} + +static void +fetch_stats64(struct rtnl_link_stats64 *rtlstats64, char const *ifname, int nlrtfd) +{ + struct { + struct nlmsghdr nlh; + struct if_stats_msg ifsm; + } nlreq; + memset(&nlreq, 0, sizeof(nlreq)); + nlreq.nlh.nlmsg_len = sizeof(nlreq); + nlreq.nlh.nlmsg_type = RTM_GETSTATS; + nlreq.nlh.nlmsg_flags = NLM_F_REQUEST; + nlreq.ifsm.ifindex = if_nametoindex(ifname); + nlreq.ifsm.filter_mask = IFLA_STATS_LINK_64; + + struct iovec iov[2]; + memset(&iov[0], 0, sizeof(iov[0])); + iov[0].iov_base = (void *)&nlreq; + iov[0].iov_len = sizeof(nlreq); + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov[0]; + msg.msg_iovlen = 1; + + if (sendmsg(nlrtfd, &msg, 0) == -1) { + log_fatal("if-netmap-linux", "sendmsg(RTM_GETSTATS): %d: %s", errno, strerror(errno)); + } + + struct nlresp { + struct nlmsghdr nlh; + union { + struct { + struct rtmsg rth; + struct rtnl_link_stats64 rtlstats64; + } ans; + struct { + struct nlmsgerr nlerr; + } err; + }; + } nlresp; + _Static_assert(sizeof(nlresp.ans) >= sizeof(nlresp.err)); + static const size_t ans_size = offsetof(struct nlresp, ans) + sizeof(nlresp.ans); + static const size_t err_size = offsetof(struct nlresp, err) + sizeof(nlresp.err); + + memset(iov, 0, sizeof(iov)); + iov[0].iov_base = (void *)&nlresp; + iov[0].iov_len = offsetof(struct nlresp, ans.rtlstats64); + iov[1].iov_base = (void *)rtlstats64; // caller-provided + iov[1].iov_len = sizeof(struct rtnl_link_stats64); + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + ssize_t n = recvmsg(nlrtfd, &msg, 0); + if (n == -1) { + log_fatal("if-netmap-linux", "recvmsg(RTM_GETSTATS): %d: %s", errno, strerror(errno)); + } + if ((size_t)n < err_size) { + log_fatal("if-netmap-linux", "received %zu expected %zu or larger", (size_t)n, err_size); + } + + if (nlresp.nlh.nlmsg_type == NLMSG_ERROR) { + // copy second iov into ans in first iov to get contiguous struct nlmsgerr + nlresp.ans.rtlstats64 = *rtlstats64; + assert(nlresp.err.nlerr.error < 0); + errno = -nlresp.err.nlerr.error; + log_fatal("if-netmap-linux", "received NLMSG_ERROR: %d: %s", errno, strerror(errno)); + } + if (nlresp.nlh.nlmsg_type != RTM_NEWSTATS) { + log_fatal("if-netmap-linux", "received unexpected nlmsg_type %u", nlresp.nlh.nlmsg_type); + } + if ((size_t)n != ans_size) { + log_fatal("if-netmap-linux", "received %zu expected %zu", (size_t)n, ans_size); + } +} + +void +if_wait_for_phy_reset(char const *ifname, int fd) +{ + // clobber deliberately + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd == -1) { + log_fatal("if-netmap-linux", "socket(AF_INET): %d: %s", errno, strerror(errno)); + } + + for (size_t i = 0; i < 40 /* 10s */; i++) { + struct ifreq ifr; + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + struct ethtool_value etv; + etv.cmd = ETHTOOL_GLINK; + ifr.ifr_data = (void *)&etv; + if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) { + log_fatal("if-netmap-linux", "ioctl(SIOCETHTOOL): %d: %s", errno, strerror(errno)); + } + if (etv.data != 0 /* carrier */) { + close(fd); + return; + } + usleep(250000); + } + log_fatal("if-netmap-linux", "timeout waiting for PHY reset to complete"); +} + +size_t +if_get_data_link_size(UNUSED char const *ifname, UNUSED int fd) +{ + // Assuming Ethernet is not entirely unreasonable, as that's + // the only thing we support on the send path anyway. + // TODO figure out actual link type or link header size + return sizeof(struct ether_header); +} + +struct if_stats_ctx { + char *ifname; // owned + int nlrtfd; // owned + // uint64_t rx_packets; + uint64_t rx_dropped; + uint64_t rx_errors; + uint64_t tx_errors; +}; + +if_stats_ctx_t * +if_stats_init(char const *ifname, UNUSED int fd) +{ + if_stats_ctx_t *ctx = malloc(sizeof(struct if_stats_ctx)); + memset(ctx, 0, sizeof(struct if_stats_ctx)); + + ctx->ifname = strdup(ifname); + ctx->nlrtfd = nlrt_socket(); + + struct rtnl_link_stats64 rtlstats64; + memset(&rtlstats64, 0, sizeof(rtlstats64)); + fetch_stats64(&rtlstats64, ctx->ifname, ctx->nlrtfd); + + //ctx->rx_packets = rtlstats64.rx_packets; + ctx->rx_dropped = rtlstats64.rx_dropped; + ctx->rx_errors = rtlstats64.rx_errors; + ctx->tx_errors = rtlstats64.tx_errors; + return ctx; +} + +void +if_stats_fini(if_stats_ctx_t *ctx) +{ + free(ctx->ifname); + close(ctx->nlrtfd); + free(ctx); +} + +bool +if_stats_have_recv_ctr(UNUSED if_stats_ctx_t *ctx) +{ + return false; +} + +int +if_stats_get(if_stats_ctx_t *ctx, UNUSED uint32_t *ps_recv, uint32_t *ps_drop, uint32_t *ps_ifdrop) +{ + struct rtnl_link_stats64 rtlstats64; + memset(&rtlstats64, 0, sizeof(rtlstats64)); + fetch_stats64(&rtlstats64, ctx->ifname, ctx->nlrtfd); + + //*ps_recv = (uint32_t)(rtlstats64.rx_packets - ctx->rx_packets); + *ps_drop = (uint32_t)(rtlstats64.rx_dropped - ctx->rx_dropped); + *ps_ifdrop = (uint32_t)(rtlstats64.rx_errors - ctx->rx_errors + + rtlstats64.tx_errors - ctx->tx_errors); + return 0; +} diff --git a/src/if-netmap.h b/src/if-netmap.h new file mode 100644 index 000000000..fc200e24b --- /dev/null +++ b/src/if-netmap.h @@ -0,0 +1,57 @@ +/* + * ZMap Copyright 2013 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef ZMAP_IF_NETMAP_H +#define ZMAP_IF_NETMAP_H + +// Platform-specific functionality required for NETMAP. + +#include +#include +#include + +// Wait until a NICs PHY has reset and the interface is ready for sending +// packets again. Must be called after the reset has begun. Upon return, the +// interface is ready for sending packets. Exits on timeout. +// +// *ifname* is the name of the interface. +// *fd* is the file descriptor to the main netmap socket. +void if_wait_for_phy_reset(char const *ifname, int fd); + +// Get the size of the link layer header for the interface. +// +// *ifname* is the name of the interface. +// *fd* is the file descriptor to the main netmap socket. +size_t if_get_data_link_size(char const *ifname, int fd); + +// Opaque context for the if_stats_* set of functions. +struct if_stats_ctx; +typedef struct if_stats_ctx if_stats_ctx_t; + +// Initialise interface statistics. +// +// *ifname* is the name of the interface. +// *fd* is the file descriptor to the netmap socket used for recv. +if_stats_ctx_t * if_stats_init(char const *ifname, int fd); + +// Returns true if the count of received packets is available +// through if_stats_get(), false otherwise. If this returns +// false, the caller will need to count received packets. +bool if_stats_have_recv_ctr(if_stats_ctx_t *ctx); + +// Get recv, drop and ifdrop counters. +// Some interfaces do not report any received packets while in +// netmap mode. In that case, *ps_recv* will not be set. +// Check if_stats_have_recv_ctr() for whether the interface +// supports received packet count in netmap mode. +int if_stats_get(if_stats_ctx_t *ctx, uint32_t *ps_recv, uint32_t *ps_drop, uint32_t *ps_ifdrop); + +// Clean up and invalidate the if_stats_* context. +void if_stats_fini(if_stats_ctx_t *ctx); + +#endif /* ZMAP_IF_NETMAP_H */ diff --git a/src/recv-netmap.c b/src/recv-netmap.c index d4b097bce..830bb0f18 100644 --- a/src/recv-netmap.c +++ b/src/recv-netmap.c @@ -6,8 +6,8 @@ * of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -#ifndef __FreeBSD__ -#error "NETMAP is only currently supported on FreeBSD" +#if !(defined(__FreeBSD__) || defined(__linux__)) +#error "NETMAP requires FreeBSD or Linux" #endif #include "recv.h" @@ -16,12 +16,13 @@ #include "send.h" #include "send-internal.h" #include "probe_modules/packet.h" +#include "if-netmap.h" +#include "state.h" #include "../lib/includes.h" #include "../lib/logger.h" #include -#include #include #include #include @@ -32,94 +33,13 @@ #include #include -#include "state.h" - -static void -fetch_if_data(struct if_data *ifd) -{ - struct ifreq ifr; - bzero(&ifr, sizeof(ifr)); - strlcpy(ifr.ifr_name, zconf.iface, sizeof(ifr.ifr_name)); - ifr.ifr_data = (caddr_t)ifd; - if (ioctl(zconf.nm.nm_fd, SIOCGIFDATA, &ifr) == -1) { - log_fatal("recv-netmap", "unable to retrieve if_data: %d: %s", - errno, strerror(errno)); - } -} - -static size_t -data_link_size_from_if_type(unsigned char if_type) -{ - switch (if_type) { - case IFT_ETHER: - log_debug("recv-netmap", "IFT_ETHER"); - return sizeof(struct ether_header); - case IFT_LOOP: - log_debug("recv-netmap", "IFT_LOOP"); - return 4; - default: - log_fatal("recv-netmap", "Unknown if_type: %u", if_type); - } -} - -static size_t -fetch_data_link_size(void) -{ - struct if_data ifd; - bzero(&ifd, sizeof(ifd)); - fetch_if_data(&ifd); - return data_link_size_from_if_type(ifd.ifi_type); -} - -static struct { - bool initialized; - bool hwstats; - uint64_t ifi_ipackets; - uint64_t ifi_iqdrops; - uint64_t ifi_ierrors; - uint64_t ifi_oerrors; -} stats; - -static int -fetch_stats(uint32_t *ps_recv, uint32_t *ps_drop, uint32_t *ps_ifdrop) -{ - // Notes on if counters: - // On interfaces without hardware counters (HWSTATS), ipackets misses - // packets that we do not forward to the host ring pair. - // oqdrops counts packets the host OS could not send due to netmap mode. - struct if_data ifd; - bzero(&ifd, sizeof(ifd)); - fetch_if_data(&ifd); - - if (!stats.initialized) { - assert(!ps_recv && !ps_drop && !ps_ifdrop); - stats.initialized = true; - stats.hwstats = (ifd.ifi_hwassist & IFCAP_HWSTATS) != 0; - if (stats.hwstats) { - stats.ifi_ipackets = ifd.ifi_ipackets; - } else { - stats.ifi_ipackets = 0; - } - stats.ifi_iqdrops = ifd.ifi_iqdrops; - stats.ifi_ierrors = ifd.ifi_ierrors; - stats.ifi_oerrors = ifd.ifi_oerrors; - } else { - if (stats.hwstats) { - *ps_recv = (uint32_t)(ifd.ifi_ipackets - stats.ifi_ipackets); - } else { - *ps_recv = (uint32_t)stats.ifi_ipackets; - } - *ps_drop = (uint32_t)(ifd.ifi_iqdrops - stats.ifi_iqdrops); - *ps_ifdrop = (uint32_t)(ifd.ifi_ierrors - stats.ifi_ierrors + - ifd.ifi_oerrors - stats.ifi_oerrors); - } - return 0; -} - static struct pollfd fds; static struct netmap_if *nm_if; static bool *in_multi_seg_packet; static void (*handle_packet_func)(uint32_t buflen, const uint8_t *bytes, const struct timespec ts); +static if_stats_ctx_t *stats_ctx; +static bool need_recv_counter; +static uint64_t recv_counter; static void handle_packet_wait_ping(uint32_t buflen, const uint8_t *bytes, UNUSED const struct timespec ts) @@ -260,7 +180,7 @@ void recv_init(void) in_multi_seg_packet[ri] = false; } - zconf.data_link_size = fetch_data_link_size(); + zconf.data_link_size = if_get_data_link_size(zconf.iface, zconf.nm.nm_fd); log_debug("recv-netmap", "data_link_size %d", zconf.data_link_size); if (zconf.nm.wait_ping_dstip != 0) { @@ -270,27 +190,43 @@ void recv_init(void) handle_packet_func = handle_packet; } - if (fetch_stats(NULL, NULL, NULL) == -1) { - log_fatal("recv-netmap", "Failed to fetch initial interface counters"); + stats_ctx = if_stats_init(zconf.iface, zconf.nm.nm_fd); + assert(stats_ctx); + need_recv_counter = !if_stats_have_recv_ctr(stats_ctx); + if (need_recv_counter) { + recv_counter = 0; } } -void recv_cleanup(void) +void +recv_cleanup(void) { + if_stats_fini(stats_ctx); + stats_ctx = NULL; free(in_multi_seg_packet); in_multi_seg_packet = NULL; nm_if = NULL; } -void recv_packets(void) +void +recv_packets(void) { - int ret = poll(&fds, 1, 100 /* ms */); - if (ret == 0) { - return; - } - else if (ret == -1) { - log_error("recv-netmap", "poll(POLLIN) failed: %d: %s", errno, strerror(errno)); - return; + // On Linux, EINTR seems to happen here once at startup. + // Haven't seen any EINTR on FreeBSD. Retry is not wrong + // and making the total delay longer should not hurt. + // We may want to look into the root cause some time tho. + for (ssize_t retry = 5; retry >= 0; retry--) { + int ret = poll(&fds, 1, 100 /* ms */); + if (ret > 0) { + break; + } else if (ret == 0) { + return; + } else if (errno != EINTR || retry == 0) { + log_error("recv-netmap", "poll(POLLIN) failed: %d: %s", errno, strerror(errno)); + return; + } else { + log_debug("recv-netmap", "poll(POLLIN) failed: %d: %s (retrying)", errno, strerror(errno)); + } } for (unsigned int ri = 0; ri < nm_if->ni_rx_rings; ri++) { @@ -337,8 +273,8 @@ void recv_packets(void) struct timespec ts; ts.tv_sec = rxring->ts.tv_sec; ts.tv_nsec = rxring->ts.tv_usec * 1000; - if (!stats.hwstats) { - stats.ifi_ipackets++; + if (need_recv_counter) { + recv_counter++; } handle_packet_func(slot->len, (uint8_t *)buf, ts); } @@ -361,12 +297,15 @@ void recv_packets(void) int recv_update_stats(void) { - if (!stats.initialized) { + if (!stats_ctx) { return EXIT_FAILURE; } - if (fetch_stats(&zrecv.pcap_recv, &zrecv.pcap_drop, &zrecv.pcap_ifdrop) == -1) { + if (if_stats_get(stats_ctx, &zrecv.pcap_recv, &zrecv.pcap_drop, &zrecv.pcap_ifdrop) == -1) { return EXIT_FAILURE; } + if (need_recv_counter) { + zrecv.pcap_recv = (uint32_t)recv_counter; + } return EXIT_SUCCESS; } diff --git a/src/send-netmap.c b/src/send-netmap.c index 409e9253a..b101ad9a6 100644 --- a/src/send-netmap.c +++ b/src/send-netmap.c @@ -6,8 +6,8 @@ * of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -#ifndef __FreeBSD__ -#error "NETMAP is only currently supported on FreeBSD" +#if !(defined(__FreeBSD__) || defined(__linux__)) +#error "NETMAP requires FreeBSD or Linux" #endif #include "send.h" diff --git a/src/socket-netmap.c b/src/socket-netmap.c index ae62993df..85c48212c 100644 --- a/src/socket-netmap.c +++ b/src/socket-netmap.c @@ -6,8 +6,8 @@ * of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -#ifndef __FreeBSD__ -#error "NETMAP is only currently supported on FreeBSD" +#if !(defined(__FreeBSD__) || defined(__linux__)) +#error "NETMAP requires FreeBSD or Linux" #endif #include "socket.h" @@ -38,12 +38,12 @@ sock_t get_socket(uint32_t id) } struct nmreq_register nmrreg; - bzero(&nmrreg, sizeof(nmrreg)); + memset(&nmrreg, 0, sizeof(nmrreg)); nmrreg.nr_ringid = sock.nm.tx_ring_idx; nmrreg.nr_mode = NR_REG_ONE_NIC; nmrreg.nr_flags = NR_TX_RINGS_ONLY | NR_NO_TX_POLL; struct nmreq_header nmrhdr; - bzero(&nmrhdr, sizeof(nmrhdr)); + memset(&nmrhdr, 0, sizeof(nmrhdr)); nmrhdr.nr_version = NETMAP_API; nmrhdr.nr_reqtype = NETMAP_REQ_REGISTER; strlcpy(nmrhdr.nr_name, zconf.iface, sizeof(nmrhdr.nr_name)); diff --git a/src/zmap.c b/src/zmap.c index 167cd98fe..4f0fca402 100644 --- a/src/zmap.c +++ b/src/zmap.c @@ -60,9 +60,10 @@ static int32_t distrib_func(pfring_zc_pkt_buff *pkt, pfring_zc_queue *in_queue, #endif #ifdef NETMAP -#ifndef __FreeBSD__ -#error "NETMAP is only currently supported on FreeBSD" +#if !(defined(__FreeBSD__) || defined(__linux__)) +#error "NETMAP requires FreeBSD or Linux" #endif +#include "if-netmap.h" #include #include #include @@ -985,11 +986,11 @@ int main(int argc, char *argv[]) } struct nmreq_register nmrreg; - bzero(&nmrreg, sizeof(nmrreg)); + memset(&nmrreg, 0, sizeof(nmrreg)); nmrreg.nr_mode = NR_REG_ALL_NIC; nmrreg.nr_flags = NR_NO_TX_POLL; struct nmreq_header nmrhdr; - bzero(&nmrhdr, sizeof(nmrhdr)); + memset(&nmrhdr, 0, sizeof(nmrhdr)); nmrhdr.nr_version = NETMAP_API; nmrhdr.nr_reqtype = NETMAP_REQ_REGISTER; strlcpy(nmrhdr.nr_name, zconf.iface, sizeof(nmrhdr.nr_name)); @@ -1026,26 +1027,8 @@ int main(int argc, char *argv[]) // on physical NICs can take multiple seconds to complete. // To avoid dropping packets while the reset is ongoing, // wait for the interface to come back up here. - log_debug("zmap", "waiting max 10s for PHY reset to complete"); - struct if_data ifd; - bzero(&ifd, sizeof(ifd)); - struct ifreq ifr; - bzero(&ifr, sizeof(ifr)); - strlcpy(ifr.ifr_name, zconf.iface, sizeof(ifr.ifr_name)); - ifr.ifr_data = (caddr_t)&ifd; - for (size_t i = 0; i < 40; i++) { - if (ioctl(zconf.nm.nm_fd, SIOCGIFDATA, &ifr) == -1) { - log_fatal("zmap", "unable to retrieve if_data: %d: %s", - errno, strerror(errno)); - } - if (ifd.ifi_link_state == LINK_STATE_UP) { - break; - } - usleep(250000); - } - if (ifd.ifi_link_state != LINK_STATE_UP) { - log_fatal("zmap", "timeout waiting for PHY reset to complete"); - } + log_debug("zmap", "waiting for PHY reset to complete"); + if_wait_for_phy_reset(zconf.iface, zconf.nm.nm_fd); log_debug("zmap", "PHY reset is complete, link state is up"); if (args.netmap_wait_ping_arg != NULL) {