49 #define NETMAP_WITH_LIBS
51 #define DEBUG_NETMAP_USER
54 #include <net/netmap_user.h>
55 #include <libnetmap.h>
69 "Error creating thread %s: Netmap is not enabled. "
70 "Make sure to pass --enable-netmap to configure when building.",
93 #define POLL_TIMEOUT 100
95 #if defined(__linux__)
96 #define POLL_EVENTS (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL)
99 #define IFF_PPROMISC IFF_PROMISC
103 #define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
106 enum { NETMAP_FLAG_ZERO_COPY = 1, NETMAP_FLAG_EXCL_RING_ACCESS = 2 };
112 typedef struct NetmapDevice_
114 struct nmport_d *nmd;
131 typedef struct NetmapThreadVars_
154 uint16_t capture_kernel_packets;
155 uint16_t capture_kernel_drops;
158 typedef TAILQ_HEAD(NetmapDeviceList_, NetmapDevice_) NetmapDeviceList;
168 struct nmreq_port_info_get req;
169 struct nmreq_header hdr;
173 char base_name[IFNAMSIZ];
174 strlcpy(base_name, ifname,
sizeof(base_name));
175 if (strlen(base_name) > 0 &&
176 (base_name[strlen(base_name) - 1] ==
'^' || base_name[strlen(base_name) - 1] ==
'*')) {
177 base_name[strlen(base_name) - 1] =
'\0';
183 int fd = open(
"/dev/netmap", O_RDWR);
186 "Couldn't open netmap device, error %s",
192 memset(&req, 0,
sizeof(req));
193 memset(&hdr, 0,
sizeof(hdr));
194 hdr.nr_version = NETMAP_API;
195 hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
196 hdr.nr_body = (uintptr_t)&req;
197 strlcpy(hdr.nr_name, base_name,
sizeof(hdr.nr_name));
199 if (ioctl(fd, NIOCCTRL, &hdr) != 0) {
201 ifname, strerror(errno));
206 if (req.nr_rx_rings == req.nr_tx_rings) {
207 rx_rings = req.nr_rx_rings;
217 static void NetmapDestroyDevice(NetmapDevice *pdev)
219 nmport_close(pdev->nmd);
229 static int NetmapClose(NetmapDevice *dev)
231 NetmapDevice *pdev, *tmp;
239 NetmapDestroyDevice(pdev);
253 static void NetmapCloseAll(
void)
255 NetmapDevice *pdev, *tmp;
260 NetmapDestroyDevice(pdev);
277 static int NetmapOpen(
NetmapIfaceSettings *ns, NetmapDevice **pdevice,
int verbose,
int read,
278 bool zerocopy,
bool soft)
283 char base_name[IFNAMSIZ];
285 if (strlen(base_name) > 0 &&
286 (base_name[strlen(base_name)-1] ==
'^' ||
287 base_name[strlen(base_name)-1] ==
'*'))
289 base_name[strlen(base_name)-1] =
'\0';
294 int if_flags = GetIfaceFlags(base_name);
295 if (if_flags == -1) {
298 base_name, ns->
iface);
304 if ((if_flags & IFF_UP) == 0) {
309 if (ns->
promisc && (if_flags & (IFF_PROMISC|IFF_PPROMISC)) == 0) {
310 if_flags |= IFF_PPROMISC;
311 SetIfaceFlags(base_name, if_flags);
315 NetmapDevice *pdev = NULL, *spdev = NULL;
325 const int direction = (read != 1);
332 if (direction == spdev->direction && strcmp(ns->
iface, spdev->ifname) == 0) {
333 ring = spdev->ring + 1;
338 const char *opt_R =
"R";
339 const char *opt_T =
"T";
340 const char *opt_x =
"x";
341 const char *opt_z =
"z";
379 snprintf(optstr,
sizeof(optstr),
"%s%s%s", opt_z, opt_x, direction == 0 ? opt_R : opt_T);
382 if (strncmp(ns->
iface,
"netmap:", 7) == 0) {
383 snprintf(devname,
sizeof(devname),
"%s}%d%s%s",
384 ns->
iface, ring, strlen(optstr) ?
"/" :
"", optstr);
385 }
else if (strlen(ns->
iface) > 5 && strncmp(ns->
iface,
"vale", 4) == 0 && isdigit(ns->
iface[4])) {
386 snprintf(devname,
sizeof(devname),
"%s", ns->
iface);
387 }
else if (ring == 0 && ns->
threads == 1) {
389 snprintf(devname,
sizeof(devname),
"netmap:%s%s%s",
390 ns->
iface, strlen(optstr) ?
"/" :
"", optstr);
391 SCLogDebug(
"device with %s-ring enabled (devname): %s", soft ?
"SW" :
"HW", devname);
398 snprintf(devname,
sizeof(devname),
"netmap:%s%d%s%s@conf:host-rings=%d", ns->
iface,
399 ring, strlen(optstr) ?
"/" :
"", optstr, ns->
threads);
402 snprintf(devname,
sizeof(devname),
"netmap:%s%d%s%s", ns->
iface, ring,
403 strlen(optstr) ?
"/" :
"", optstr);
405 SCLogDebug(
"device with SW-ring enabled (devname): %s", devname);
406 }
else if (ring == 0 && soft) {
410 snprintf(devname,
sizeof(devname),
"netmap:%s-%d%s%s@conf:host-rings=%d", ns->
iface,
411 ring, strlen(optstr) ?
"/" :
"", optstr, ns->
threads);
412 SCLogDebug(
"device with HW-ring enabled (devname): %s", devname);
416 snprintf(devname,
sizeof(devname),
"netmap:%s-%d%s%s", ns->
iface, ring,
417 strlen(optstr) ?
"/" :
"", optstr);
418 SCLogDebug(
"device with HW-ring enabled (devname): %s", devname);
425 pdev->nmd = nmport_prepare(devname);
427 if (pdev->nmd != NULL) {
431 pdev->nmd->reg.nr_flags |= NR_NO_TX_POLL;
435 if (nmport_open_desc(pdev->nmd) < 0) {
437 nmport_close(pdev->nmd);
442 if (pdev->nmd == NULL) {
443 if (errno == EINVAL) {
444 if (opt_z[0] ==
'z') {
445 SCLogNotice(
"got '%s' EINVAL: going to retry without 'z'", devname);
448 }
else if (opt_x[0] ==
'x') {
449 SCLogNotice(
"dev '%s' got EINVAL: going to retry without 'x'", devname);
460 SCLogDebug(
"%s -- cur rings: [%d, %d] first rings: [%d, %d]", devname, pdev->nmd->cur_rx_ring,
461 pdev->nmd->cur_tx_ring, pdev->nmd->first_rx_ring, pdev->nmd->first_tx_ring);
462 pdev->nmd->cur_rx_ring = pdev->nmd->first_rx_ring;
463 pdev->nmd->cur_tx_ring = pdev->nmd->first_tx_ring;
465 SCLogInfo(
"devname [fd: %d] %s %s opened", pdev->nmd->fd, devname, ns->
iface);
467 pdev->direction = direction;
484 static inline void NetmapDumpCounters(NetmapThreadVars *ntv)
486 StatsAddUI64(ntv->tv, ntv->capture_kernel_packets, ntv->pkts);
487 StatsAddUI64(ntv->tv, ntv->capture_kernel_drops, ntv->drops);
505 if (initdata == NULL) {
510 NetmapThreadVars *ntv =
SCCalloc(1,
sizeof(*ntv));
517 if (ntv->livedev == NULL) {
528 if (strcmp(
"workers", active_runmode) == 0) {
529 ntv->flags |= NETMAP_FLAG_ZERO_COPY;
531 }
else if (strcmp(
"autofp", active_runmode) == 0) {
532 ntv->flags |= NETMAP_FLAG_EXCL_RING_ACCESS;
537 if (NetmapOpen(&aconf->
in, &ntv->ifsrc, 1, 1, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
543 if (NetmapOpen(&aconf->
out, &ntv->ifdst, 1, 0, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
558 char errbuf[PCAP_ERRBUF_SIZE];
564 PCAP_NETMASK_UNKNOWN,
566 sizeof(errbuf)) == -1)
585 NetmapClose(ntv->ifdst);
589 NetmapClose(ntv->ifsrc);
604 static TmEcode NetmapWritePacket(NetmapThreadVars *ntv,
Packet *p)
614 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
620 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
623 SCLogDebug(
"failed to send %s -> %s", ntv->ifsrc->ifname, ntv->ifdst->ifname);
628 SCLogDebug(
"sent successfully: %s(%d)->%s(%d) (%u)", ntv->ifsrc->ifname, ntv->ifsrc->ring,
629 ntv->ifdst->ifname, ntv->ifdst->ring,
GET_PKT_LEN(p));
632 ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
633 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
643 static void NetmapReleasePacket(
Packet *p)
645 NetmapThreadVars *ntv = (NetmapThreadVars *)p->netmap_v.ntv;
648 NetmapWritePacket(ntv, p);
654 static void NetmapProcessPacket(NetmapThreadVars *ntv,
const struct nm_pkthdr *ph)
656 if (ntv->bpf_prog.bf_len) {
657 struct pcap_pkthdr pkthdr = { {0, 0}, ph->len, ph->len };
658 if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, ph->buf) == 0) {
673 ntv->bytes += ph->len;
675 if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
688 p->netmap_v.ntv = ntv;
690 SCLogDebug(
"pktlen: %" PRIu32
" (pkt %p, pkt data %p)",
693 (void)TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p);
702 static TmEcode NetmapReadPackets(
struct nmport_d *d,
int cnt, NetmapThreadVars *ntv)
704 struct nm_pkthdr hdr;
705 int last_ring = d->last_rx_ring - d->first_rx_ring + 1;
706 int cur_ring, got = 0, cur_rx_ring = d->cur_rx_ring;
708 memset(&hdr, 0,
sizeof(hdr));
709 hdr.flags = NM_MORE_PKTS;
714 for (cur_ring = 0; cur_ring < last_ring && cnt != got; cur_ring++, cur_rx_ring++) {
715 struct netmap_ring *ring;
717 if (cur_rx_ring > d->last_rx_ring)
718 cur_rx_ring = d->first_rx_ring;
720 ring = NETMAP_RXRING(d->nifp, cur_rx_ring);
723 for (; !nm_ring_empty(ring) && cnt != got; got++) {
726 struct netmap_slot *slot;
729 NetmapProcessPacket(ntv, &hdr);
733 slot = &ring->slot[i];
735 d->cur_rx_ring = cur_rx_ring;
737 oldbuf = hdr.buf = (u_char *)NETMAP_BUF(ring, idx);
738 hdr.len = hdr.caplen = slot->len;
741 while (slot->flags & NS_MOREFRAG) {
746 u_int oldlen = slot->len;
747 i = nm_ring_next(ring, i);
748 slot = &ring->slot[i];
749 hdr.len += slot->len;
750 nbuf = (u_char *)NETMAP_BUF(ring, slot->buf_idx);
752 if (oldbuf != NULL && nbuf - oldbuf == ring->nr_buf_size &&
753 oldlen == ring->nr_buf_size) {
754 hdr.caplen += slot->len;
762 ring->head = ring->cur = nm_ring_next(ring, i);
768 NetmapProcessPacket(ntv, &hdr);
781 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
785 fds.fd = ntv->ifsrc->nmd->fd;
804 "Error polling netmap from iface '%s': (%d" PRIu32
") %s",
805 ntv->ifsrc->ifname, errno, strerror(errno));
811 NetmapDumpCounters(ntv);
815 TmThreadsCaptureHandleTimeout(
tv, NULL);
819 if (
unlikely(fds.revents & POLL_EVENTS)) {
820 if (fds.revents & POLLERR) {
822 "Error reading netmap data via polling from iface '%s': (%d" PRIu32
") %s",
823 ntv->ifsrc->ifname, errno, strerror(errno));
824 }
else if (fds.revents & POLLNVAL) {
830 if (
likely(fds.revents & POLLIN)) {
832 NetmapReadPackets(ntv->ifsrc->nmd, -1, ntv);
835 NetmapDumpCounters(ntv);
839 NetmapDumpCounters(ntv);
849 static void ReceiveNetmapThreadExitStats(
ThreadVars *
tv,
void *data)
852 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
854 NetmapDumpCounters(ntv);
855 SCLogPerf(
"(%s) Kernel: Packets %" PRIu64
", dropped %" PRIu64
", bytes %" PRIu64
"",
871 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
874 NetmapClose(ntv->ifsrc);
878 NetmapClose(ntv->ifdst);
881 if (ntv->bpf_prog.bf_insns) {