51 #define NETMAP_WITH_LIBS
53 #define DEBUG_NETMAP_USER
56 #include <net/netmap_user.h>
57 #include <libnetmap.h>
70 FatalError(
"Error creating thread %s: Netmap is not enabled. "
71 "Make sure to pass --enable-netmap to configure when building.",
96 #define POLL_TIMEOUT 100
98 #if defined(__linux__)
99 #define POLL_EVENTS (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL)
102 #define IFF_PPROMISC IFF_PROMISC
106 #define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
109 enum { NETMAP_FLAG_ZERO_COPY = 1, NETMAP_FLAG_EXCL_RING_ACCESS = 2 };
115 typedef struct NetmapDevice_
117 struct nmport_d *nmd;
134 typedef struct NetmapThreadVars_
142 struct bpf_program bpf_prog;
154 uint8_t *coalesce_buf;
155 uint32_t coalesce_size;
165 typedef TAILQ_HEAD(NetmapDeviceList_, NetmapDevice_) NetmapDeviceList;
175 struct nmreq_port_info_get req;
176 struct nmreq_header hdr;
180 char base_name[IFNAMSIZ];
181 strlcpy(base_name, ifname,
sizeof(base_name));
182 if (strlen(base_name) > 0 &&
183 (base_name[strlen(base_name) - 1] ==
'^' || base_name[strlen(base_name) - 1] ==
'*')) {
184 base_name[strlen(base_name) - 1] =
'\0';
190 int fd = open(
"/dev/netmap", O_RDWR);
192 SCLogError(
"%s: open netmap device failed: %s", ifname, strerror(errno));
197 memset(&req, 0,
sizeof(req));
198 memset(&hdr, 0,
sizeof(hdr));
199 hdr.nr_version = NETMAP_API;
200 hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
201 hdr.nr_body = (uintptr_t)&req;
202 strlcpy(hdr.nr_name, base_name,
sizeof(hdr.nr_name));
204 if (ioctl(fd, NIOCCTRL, &hdr) != 0) {
206 "Query of netmap HW rings count on %s failed; error: %s", ifname, strerror(errno));
211 if (req.nr_rx_rings == req.nr_tx_rings) {
212 rx_rings = req.nr_rx_rings;
222 static void NetmapDestroyDevice(NetmapDevice *pdev)
224 nmport_close(pdev->nmd);
234 static int NetmapClose(NetmapDevice *dev)
236 NetmapDevice *pdev, *tmp;
244 NetmapDestroyDevice(pdev);
258 static void NetmapCloseAll(
void)
260 NetmapDevice *pdev, *tmp;
265 NetmapDestroyDevice(pdev);
282 static int NetmapOpen(
NetmapIfaceSettings *ns, NetmapDevice **pdevice,
int verbose,
int read,
283 bool zerocopy,
bool soft)
288 char base_name[IFNAMSIZ];
290 if (strlen(base_name) > 0 &&
291 (base_name[strlen(base_name)-1] ==
'^' ||
292 base_name[strlen(base_name)-1] ==
'*'))
294 base_name[strlen(base_name)-1] =
'\0';
299 int if_flags = GetIfaceFlags(base_name);
300 if (if_flags == -1) {
302 SCLogError(
"%s: cannot access network interface: %s", base_name, ns->
iface);
308 if ((if_flags & IFF_UP) == 0) {
309 SCLogError(
"%s: interface is down", base_name);
313 if (ns->
promisc && (if_flags & (IFF_PROMISC|IFF_PPROMISC)) == 0) {
314 if_flags |= IFF_PPROMISC;
315 SetIfaceFlags(base_name, if_flags);
319 NetmapDevice *pdev = NULL, *spdev = NULL;
322 SCLogError(
"%s: memory allocation failed", base_name);
329 const int direction = (read != 1);
336 if (direction == spdev->direction && strcmp(ns->
iface, spdev->ifname) == 0) {
337 ring = spdev->ring + 1;
342 const char *opt_R =
"R";
343 const char *opt_T =
"T";
344 const char *opt_x =
"x";
345 const char *opt_z =
"z";
383 snprintf(optstr,
sizeof(optstr),
"%s%s%s", opt_z, opt_x, direction == 0 ? opt_R : opt_T);
386 if (strncmp(ns->
iface,
"netmap:", 7) == 0) {
387 snprintf(devname,
sizeof(devname),
"%s}%d%s%s",
388 ns->
iface, ring, strlen(optstr) ?
"/" :
"", optstr);
389 }
else if (strlen(ns->
iface) > 5 && strncmp(ns->
iface,
"vale", 4) == 0 && isdigit(ns->
iface[4])) {
390 snprintf(devname,
sizeof(devname),
"%s", ns->
iface);
391 }
else if (ring == 0 && ns->
threads == 1) {
393 snprintf(devname,
sizeof(devname),
"netmap:%s%s%s",
394 ns->
iface, strlen(optstr) ?
"/" :
"", optstr);
395 SCLogDebug(
"device with %s-ring enabled (devname): %s", soft ?
"SW" :
"HW", devname);
402 snprintf(devname,
sizeof(devname),
"netmap:%s%d%s%s@conf:host-rings=%d", ns->
iface,
403 ring, strlen(optstr) ?
"/" :
"", optstr, ns->
threads);
406 snprintf(devname,
sizeof(devname),
"netmap:%s%d%s%s", ns->
iface, ring,
407 strlen(optstr) ?
"/" :
"", optstr);
409 SCLogDebug(
"device with SW-ring enabled (devname): %s", devname);
410 }
else if (ring == 0 && soft) {
414 snprintf(devname,
sizeof(devname),
"netmap:%s-%d%s%s@conf:host-rings=%d", ns->
iface,
415 ring, strlen(optstr) ?
"/" :
"", optstr, ns->
threads);
416 SCLogDebug(
"device with HW-ring enabled (devname): %s", devname);
420 snprintf(devname,
sizeof(devname),
"netmap:%s-%d%s%s", ns->
iface, ring,
421 strlen(optstr) ?
"/" :
"", optstr);
422 SCLogDebug(
"device with HW-ring enabled (devname): %s", devname);
429 pdev->nmd = nmport_prepare(devname);
431 if (pdev->nmd != NULL) {
435 pdev->nmd->reg.nr_flags |= NR_NO_TX_POLL;
439 if (nmport_open_desc(pdev->nmd) < 0) {
441 nmport_close(pdev->nmd);
446 if (pdev->nmd == NULL) {
447 if (errno == EINVAL) {
448 if (opt_z[0] ==
'z') {
450 "%s: dev '%s' got EINVAL: going to retry without 'z'", base_name, devname);
453 }
else if (opt_x[0] ==
'x') {
455 "%s: dev '%s' got EINVAL: going to retry without 'x'", base_name, devname);
463 FatalError(
"opening devname %s failed: %s", devname, strerror(errno));
467 SCLogDebug(
"%s -- cur rings: [%d, %d] first rings: [%d, %d]", devname, pdev->nmd->cur_rx_ring,
468 pdev->nmd->cur_tx_ring, pdev->nmd->first_rx_ring, pdev->nmd->first_tx_ring);
469 pdev->nmd->cur_rx_ring = pdev->nmd->first_rx_ring;
470 pdev->nmd->cur_tx_ring = pdev->nmd->first_tx_ring;
472 SCLogInfo(
"%s: %s opened [fd: %d]", devname, ns->
iface, pdev->nmd->fd);
474 pdev->direction = direction;
491 static inline void NetmapDumpCounters(NetmapThreadVars *ntv)
512 if (initdata == NULL) {
517 NetmapThreadVars *ntv =
SCCalloc(1,
sizeof(*ntv));
524 if (ntv->livedev == NULL) {
535 ntv->coalesce_buf =
SCMalloc(ntv->coalesce_size);
536 if (
unlikely(ntv->coalesce_buf == NULL)) {
543 if (strcmp(
"workers", active_runmode) == 0) {
544 ntv->flags |= NETMAP_FLAG_ZERO_COPY;
546 }
else if (strcmp(
"autofp", active_runmode) == 0) {
547 ntv->flags |= NETMAP_FLAG_EXCL_RING_ACCESS;
552 if (NetmapOpen(&aconf->
in, &ntv->ifsrc, 1, 1, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
558 if (NetmapOpen(&aconf->
out, &ntv->ifdst, 1, 0, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
570 char errbuf[PCAP_ERRBUF_SIZE];
578 sizeof(errbuf)) == -1)
580 SCLogError(
"%s: failed to compile BPF \"%s\": %s", ntv->ifsrc->ifname,
596 NetmapClose(ntv->ifdst);
600 NetmapClose(ntv->ifsrc);
603 SCFree(ntv->coalesce_buf);
616 static TmEcode NetmapWritePacket(NetmapThreadVars *ntv,
Packet *
p)
626 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
637 (void)ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
640 if (write_tries < 3) {
645 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
648 SCLogDebug(
"failed to send %s -> %s", ntv->ifsrc->ifname, ntv->ifdst->ifname);
653 SCLogDebug(
"sent successfully: %s(%d)->%s(%d) (%u)", ntv->ifsrc->ifname, ntv->ifsrc->ring,
657 (void)ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
658 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
668 static void NetmapReleasePacket(
Packet *
p)
670 NetmapThreadVars *ntv = (NetmapThreadVars *)
p->netmap_v.ntv;
673 NetmapWritePacket(ntv,
p);
681 static void NetmapProcessPacket(NetmapThreadVars *ntv,
const struct nm_pkthdr *ph,
bool force_copy)
683 if (ntv->bpf_prog.bf_len) {
684 struct pcap_pkthdr pkthdr = { {0, 0}, ph->len, ph->len };
685 if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, ph->buf) == 0) {
700 ntv->bytes += ph->len;
702 if ((ntv->flags & NETMAP_FLAG_ZERO_COPY) && !force_copy) {
715 p->netmap_v.ntv = ntv;
717 SCLogDebug(
"pktlen: %" PRIu32
" (pkt %p, pkt data %p)",
720 (void)TmThreadsSlotProcessPkt(ntv->tv, ntv->slot,
p);
728 static bool NetmapReadOnePacket(
729 struct netmap_ring *ring, NetmapThreadVars *ntv,
struct nm_pkthdr *hdr,
bool *force_copy)
732 struct netmap_slot *slot = &ring->slot[i];
734 memset(hdr, 0,
sizeof(*hdr));
737 hdr->buf = (u_char *)NETMAP_BUF(ring, slot->buf_idx);
738 hdr->len = hdr->caplen = slot->len;
741 bool dropped =
false;
743 if (slot->flags & NS_MOREFRAG) {
745 uint32_t avail = nm_ring_space(ring);
748 bool malformed =
false;
753 uint32_t slen = slot->len;
755 if (slen > ntv->coalesce_size - total) {
759 memcpy(ntv->coalesce_buf + total, (u_char *)NETMAP_BUF(ring, slot->buf_idx),
766 if (!(slot->flags & NS_MOREFRAG))
768 if (nfrags >= avail) {
773 i = nm_ring_next(ring, i);
774 slot = &ring->slot[i];
781 hdr->buf = ntv->coalesce_buf;
782 hdr->len = hdr->caplen = total;
788 ring->head = ring->cur = nm_ring_next(ring, i);
798 static TmEcode NetmapReadPackets(
struct nmport_d *d,
int cnt, NetmapThreadVars *ntv)
800 int last_ring = d->last_rx_ring - d->first_rx_ring + 1;
801 int cur_ring, got = 0, cur_rx_ring = d->cur_rx_ring;
806 for (cur_ring = 0; cur_ring < last_ring &&
cnt != got; cur_ring++, cur_rx_ring++) {
807 struct netmap_ring *ring;
809 if (cur_rx_ring > d->last_rx_ring)
810 cur_rx_ring = d->first_rx_ring;
812 ring = NETMAP_RXRING(d->nifp, cur_rx_ring);
815 for (; !nm_ring_empty(ring) &&
cnt != got; got++) {
816 struct nm_pkthdr hdr;
819 d->cur_rx_ring = cur_rx_ring;
820 if (NetmapReadOnePacket(ring, ntv, &hdr, &force_copy)) {
821 NetmapProcessPacket(ntv, &hdr, force_copy);
837 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
841 fds.fd = ntv->ifsrc->nmd->fd;
863 SCLogError(
"%s: error polling netmap: %s", ntv->ifsrc->ifname, strerror(errno));
869 NetmapDumpCounters(ntv);
873 TmThreadsCaptureHandleTimeout(
tv, NULL);
877 if (
unlikely(fds.revents & POLL_EVENTS)) {
878 if (fds.revents & POLLERR) {
879 SCLogError(
"%s: error reading netmap data via polling: %s", ntv->ifsrc->ifname,
881 }
else if (fds.revents & POLLNVAL) {
882 SCLogError(
"%s: invalid polling request", ntv->ifsrc->ifname);
887 if (
likely(fds.revents & POLLIN)) {
889 NetmapReadPackets(ntv->ifsrc->nmd, -1, ntv);
892 NetmapDumpCounters(ntv);
896 NetmapDumpCounters(ntv);
906 static void ReceiveNetmapThreadExitStats(
ThreadVars *
tv,
void *data)
909 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
911 NetmapDumpCounters(ntv);
912 SCLogPerf(
"%s: (%s) packets %" PRIi64
", dropped %" PRIi64
", bytes %" PRIu64
"",
913 ntv->ifsrc->ifname,
tv->
name,
927 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
930 NetmapClose(ntv->ifsrc);
934 NetmapClose(ntv->ifdst);
937 if (ntv->bpf_prog.bf_insns) {
941 SCFree(ntv->coalesce_buf);