Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
9 changed files
with
486 additions
and
136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <sys/ioctl.h> | ||
#include <net/if_types.h> | ||
#include <unistd.h> | ||
#include <stdlib.h> | ||
#include <strings.h> | ||
#include <string.h> | ||
#include <errno.h> | ||
|
||
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; | ||
} | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <linux/netlink.h> | ||
#include <linux/rtnetlink.h> | ||
#include <linux/ethtool.h> | ||
#include <linux/sockios.h> | ||
#include <sys/socket.h> | ||
#include <sys/ioctl.h> | ||
#include <sys/socket.h> | ||
#include <netinet/in.h> | ||
#include <net/if.h> | ||
|
||
#include <unistd.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <stddef.h> | ||
#include <string.h> | ||
#include <stdint.h> | ||
#include <inttypes.h> | ||
#include <assert.h> | ||
#include <errno.h> | ||
|
||
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; | ||
} |
Oops, something went wrong.