diff mbox series

[v3,1/1] odp: pktio: add pcapng capture capabilities

Message ID 1519912811-31335-2-git-send-email-odpbot@yandex.ru
State New
Headers show
Series [v3,1/1] odp: pktio: add pcapng capture capabilities | expand

Commit Message

Github ODP bot March 1, 2018, 2 p.m. UTC
From: Ilias Apalodimas <ilias.apalodimas@linaro.org>


Changes since RFC:
	- Addressed issues reported in RFC
	- rewrote some functions for easier understanding
	- added capability on all input queues

How to test:
sudo mkdir /var/run/odp/
start the ODP application
sudo dd if=/var/run/odp/"pid"-ethX-flow-"queue" of=~/t.pcap

changes since V1

	- Fixed documentation typo
	- Fixed documentation compilation
	- pcapng_prepare autocleans on error
	- renamed dump_pcapng_pkts to _odp_dump_pcapng_pkts
	- removed multiple AC_DEFINE from odp_pcapng.m4

changes since V2
	- renamed ODP_PCAPNG->_ODP_PCAPNG
	- fixed checkpatch error on commit message

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

---
/** Email created from pull request 488 (apalos:tcpdump_like)
 ** https://github.com/Linaro/odp/pull/488
 ** Patch: https://github.com/Linaro/odp/pull/488.patch
 ** Base sha: 284f52d72ec19df3774c7409780f1f9eea33b8e6
 ** Merge commit sha: 71675adb724439c8948f62e541e2341cb60c8ce0
 **/
 .travis.yml                                        |   1 +
 configure.ac                                       |   1 +
 doc/users-guide/Makefile.am                        |   3 +-
 .../users-guide-utilities-examples.adoc            |  17 +
 doc/users-guide/users-guide.adoc                   |   2 +
 platform/linux-generic/Makefile.am                 |   2 +
 platform/linux-generic/include/odp_internal.h      |   5 +
 .../linux-generic/include/odp_packet_io_internal.h |   9 +
 platform/linux-generic/include/odp_pcapng.h        |  56 +++
 platform/linux-generic/m4/configure.m4             |   1 +
 platform/linux-generic/m4/odp_pcapng.m4            |  18 +
 platform/linux-generic/odp_packet_io.c             |  54 ++-
 platform/linux-generic/odp_pcapng.c                | 434 +++++++++++++++++++++
 13 files changed, 597 insertions(+), 6 deletions(-)
 create mode 100644 doc/users-guide/users-guide-utilities-examples.adoc
 create mode 100644 platform/linux-generic/include/odp_pcapng.h
 create mode 100644 platform/linux-generic/m4/odp_pcapng.m4
 create mode 100644 platform/linux-generic/odp_pcapng.c
diff mbox series

Patch

diff --git a/.travis.yml b/.travis.yml
index 167c7319b..f7b0ae55e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -57,6 +57,7 @@  env:
         - CONF="--disable-static-applications"
         - CONF="--disable-host-optimization"
         - CONF="--disable-host-optimization --disable-abi-compat"
+        - CONF="--enable-pcapng-support"
         - DPDK_SHARED="y" CONF="--disable-static-applications"
         - DPDK_VERS="17.11" CONF=""
         - DPDK_VERS="17.11" DPDK_SHARED="y" CONF="--disable-static-applications"
diff --git a/configure.ac b/configure.ac
index 21718f07b..228856f2d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -419,4 +419,5 @@  AC_MSG_RESULT([
 	test_helper:		${test_helper}
 	test_example:		${test_example}
 	user_guides:		${user_guides}
+	pcapng:			${have_pcapng}
 ])
diff --git a/doc/users-guide/Makefile.am b/doc/users-guide/Makefile.am
index 7be32ddc1..6b2e818d0 100644
--- a/doc/users-guide/Makefile.am
+++ b/doc/users-guide/Makefile.am
@@ -7,7 +7,8 @@  SRC    = users-guide.adoc \
 	 users-guide-packet.adoc \
 	 users-guide-pktio.adoc \
 	 users-guide-timer.adoc \
-	 users-guide-tm.adoc
+	 users-guide-tm.adoc \
+	 users-guide-utilities-examples.adoc
 TARGET = users-guide.html
 IMAGES = $(IMAGES_DIR)/overview.svg \
 	 $(IMAGES_DIR)/atomic_queue.svg \
diff --git a/doc/users-guide/users-guide-utilities-examples.adoc b/doc/users-guide/users-guide-utilities-examples.adoc
new file mode 100644
index 000000000..433db9abd
--- /dev/null
+++ b/doc/users-guide/users-guide-utilities-examples.adoc
@@ -0,0 +1,17 @@ 
+== Utilities and examples
+
+=== PcapNg capture
+If compiled using `--enable-pcapng-support` ODP will offer packet capturing
+functionality in PcapNg format. If /var/run/odp directory exists prior to
+launching the application ODP will create as many fifos as the NIC queues.
+Queue naming will be of the following format: *<odp global pid>-<NIC
+name>-flow-<queue number>*
+
+. `./configure --enable-pcapng-support`
+. `sudo mkdir /var/run/odp`
+. `sudo ./example/generator/odp_generator -I enp2s0 -mu --dstmac
+A0:F6:FD:AE:62:6C --dstip 192.168.49.20 --srcmac 2c:56:dc:9a:8f:06 --srcip
+192.168.49.4 -i0 -w1`
+. `sudo dd if=/var/run/odp/26737-enp2s0-flow-0 of=~/test.pcap`
+. `ctrl^c`
+. `wireshark ~/test.pcap`
diff --git a/doc/users-guide/users-guide.adoc b/doc/users-guide/users-guide.adoc
index 7f2ad69e2..397f222a6 100644
--- a/doc/users-guide/users-guide.adoc
+++ b/doc/users-guide/users-guide.adoc
@@ -1198,4 +1198,6 @@  include::users-guide-tm.adoc[]
 
 include::users-guide-cls.adoc[]
 
+include::users-guide-utilities-examples.adoc[]
+
 include::../glossary.adoc[]
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index c35c0bfe3..f3a538cb0 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -106,6 +106,7 @@  noinst_HEADERS = \
 		  include/odp_packet_socket.h \
 		  include/odp_packet_tap.h \
 		  include/odp_packet_null.h \
+		  include/odp_pcapng.h \
 		  include/odp_pkt_queue_internal.h \
 		  include/odp_pool_internal.h \
 		  include/odp_posix_extensions.h \
@@ -158,6 +159,7 @@  __LIB__libodp_linux_la_SOURCES = \
 			   odp_packet.c \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
+			   odp_pcapng.c \
 			   pktio/ethtool.c \
 			   pktio/io_ops.c \
 			   pktio/ipc.c \
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index c21228982..c6a2449d2 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -23,6 +23,7 @@  extern "C" {
 #include <odp_errno_define.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <pthread.h>
 
 #define MAX_CPU_NUMBER 128
 #define UID_MAXLEN 30
@@ -55,6 +56,10 @@  struct odp_global_data_s {
 	odp_cpumask_t control_cpus;
 	odp_cpumask_t worker_cpus;
 	int num_cpus_installed;
+	int inotify_pcapng_fd;
+	int inotify_watch_fd;
+	pthread_t inotify_thread;
+	int inotify_pcapng_is_running;
 };
 
 enum init_stage {
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index e4852cc9b..4be4b1513 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -182,6 +182,15 @@  struct pktio_entry {
 		odp_queue_t        queue;
 		odp_pktout_queue_t pktout;
 	} out_queue[PKTIO_MAX_QUEUES];
+
+	/**< inotify instance for pcapng fifos */
+	struct {
+		enum {
+			PCAPNG_WR_STOP = 0,
+			PCAPNG_WR_PKT,
+		} state[PKTIO_MAX_QUEUES];
+		int fd[PKTIO_MAX_QUEUES];
+	} pcapng;
 };
 
 typedef union {
diff --git a/platform/linux-generic/include/odp_pcapng.h b/platform/linux-generic/include/odp_pcapng.h
new file mode 100644
index 000000000..8e0e624e8
--- /dev/null
+++ b/platform/linux-generic/include/odp_pcapng.h
@@ -0,0 +1,56 @@ 
+/* Copyright (c) 2018, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <sys/types.h>
+
+#include <odp_packet_io_internal.h>
+
+#define PCAPNG_BLOCK_TYPE_EPB 0x00000006UL
+#define PCAPNG_BLOCK_TYPE_SHB 0x0A0D0D0AUL
+#define PCAPNG_BLOCK_TYPE_IDB 0x00000001UL
+#define PCAPNG_ENDIAN_MAGIC 0x1A2B3C4DUL
+#define PCAPNG_DATA_ALIGN 4
+#define PCAPNG_LINKTYPE_ETHERNET 0x1
+
+/* inotify */
+#define INOTIFY_BUF_LEN (16 * (sizeof(struct inotify_event)))
+#define PCAPNG_WATCH_DIR "/var/run/odp/"
+
+/* pcapng: enhanced packet block file encoding */
+typedef struct ODP_PACKED pcapng_section_hdr_block_s {
+	uint32_t block_type;
+	uint32_t block_total_length;
+	uint32_t magic;
+	uint16_t version_major;
+	uint16_t version_minor;
+	int64_t section_len;
+	uint32_t block_total_length2;
+} pcapng_section_hdr_block_t;
+
+typedef struct pcapng_interface_description_block {
+	uint32_t block_type;
+	uint32_t block_total_length;
+	uint16_t linktype;
+	uint16_t reserved;
+	uint32_t snaplen;
+	uint32_t block_total_length2;
+} pcapng_interface_description_block_t;
+
+typedef struct pcapng_enhanced_packet_block_s {
+	uint32_t block_type;
+	uint32_t block_total_length;
+	uint32_t interface_idx;
+	uint32_t timestamp_high;
+	uint32_t timestamp_low;
+	uint32_t captured_len;
+	uint32_t packet_len;
+} pcapng_enhanced_packet_block_t;
+
+int pcapng_prepare(pktio_entry_t *entry);
+void pcapng_destroy(pktio_entry_t *entry);
+int write_pcapng_hdr(pktio_entry_t *entry, int qidx);
+int write_pcapng_pkts(pktio_entry_t *entry, int qidx,
+		      const odp_packet_t packets[], int num);
diff --git a/platform/linux-generic/m4/configure.m4 b/platform/linux-generic/m4/configure.m4
index 7fa3652e2..1a45a6945 100644
--- a/platform/linux-generic/m4/configure.m4
+++ b/platform/linux-generic/m4/configure.m4
@@ -7,6 +7,7 @@  ODP_PTHREAD
 ODP_TIMER
 ODP_OPENSSL
 m4_include([platform/linux-generic/m4/odp_pcap.m4])
+m4_include([platform/linux-generic/m4/odp_pcapng.m4])
 m4_include([platform/linux-generic/m4/odp_netmap.m4])
 m4_include([platform/linux-generic/m4/odp_dpdk.m4])
 m4_include([platform/linux-generic/m4/odp_schedule.m4])
diff --git a/platform/linux-generic/m4/odp_pcapng.m4 b/platform/linux-generic/m4/odp_pcapng.m4
new file mode 100644
index 000000000..44274c267
--- /dev/null
+++ b/platform/linux-generic/m4/odp_pcapng.m4
@@ -0,0 +1,18 @@ 
+##########################################################################
+# Enable PCAPNG support
+##########################################################################
+have_pcapng=no
+pcapng_support=0
+
+AC_ARG_ENABLE([pcapng-support],
+	[AS_HELP_STRING([--enable-pcapng-support],
+	[enable experimental tcpdump for pktios])],
+	have_pcapng=$enableval
+    [if test x$enableval = xyes; then
+        pcapng_support=1
+    fi])
+
+AC_DEFINE_UNQUOTED([_ODP_PCAPNG], [$pcapng_support],
+	[Define to 1 to enable pcapng support])
+
+AM_CONDITIONAL([have_pcapng], [test x$have_pcapng = xyes])
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index ae8e390b1..45ffc5489 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -25,6 +25,7 @@ 
 #include <odp_debug_internal.h>
 #include <odp_packet_io_ipc_internal.h>
 #include <odp/api/time.h>
+#include <odp_pcapng.h>
 
 #include <string.h>
 #include <inttypes.h>
@@ -488,6 +489,11 @@  int odp_pktio_start(odp_pktio_t hdl)
 		sched_fn->pktio_start(_odp_pktio_index(hdl), num, index, odpq);
 	}
 
+	if (_ODP_PCAPNG) {
+		if (pcapng_prepare(entry))
+			ODP_ERR("pcap init failed, won't capture\n");
+	}
+
 	return res;
 }
 
@@ -510,6 +516,9 @@  static int _pktio_stop(pktio_entry_t *entry)
 	else
 		entry->s.state = PKTIO_STATE_STOPPED;
 
+	if (_ODP_PCAPNG)
+		pcapng_destroy(entry);
+
 	return res;
 }
 
@@ -1653,10 +1662,18 @@  int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
 	return num_queues;
 }
 
+static inline void _odp_dump_pcapng_pkts(pktio_entry_t *entry, int qidx,
+					 const odp_packet_t packets[], int num)
+{
+	if (odp_unlikely(entry->s.pcapng.state[qidx] == PCAPNG_WR_PKT))
+		write_pcapng_pkts(entry, qidx, packets, num);
+}
+
 int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
 {
 	pktio_entry_t *entry;
 	odp_pktio_t pktio = queue.pktio;
+	int ret;
 
 	entry = get_pktio_entry(pktio);
 	if (entry == NULL) {
@@ -1664,7 +1681,11 @@  int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
 		return -1;
 	}
 
-	return entry->s.ops->recv(entry, queue.index, packets, num);
+	ret = entry->s.ops->recv(entry, queue.index, packets, num);
+	if (_ODP_PCAPNG)
+		_odp_dump_pcapng_pkts(entry, queue.index, packets, ret);
+
+	return ret;
 }
 
 int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num,
@@ -1686,12 +1707,19 @@  int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num,
 		return -1;
 	}
 
-	if (entry->s.ops->recv_tmo && wait != ODP_PKTIN_NO_WAIT)
-		return entry->s.ops->recv_tmo(entry, queue.index, packets, num,
+	if (entry->s.ops->recv_tmo && wait != ODP_PKTIN_NO_WAIT) {
+		ret = entry->s.ops->recv_tmo(entry, queue.index, packets, num,
 					      wait);
+		if (_ODP_PCAPNG)
+			_odp_dump_pcapng_pkts(entry, queue.index, packets, ret);
+
+		return ret;
+	}
 
 	while (1) {
 		ret = entry->s.ops->recv(entry, queue.index, packets, num);
+		if (_ODP_PCAPNG)
+			_odp_dump_pcapng_pkts(entry, queue.index, packets, ret);
 
 		if (ret != 0 || wait == 0)
 			return ret;
@@ -1734,6 +1762,7 @@  int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q,
 	int started = 0;
 	uint64_t sleep_round = 0;
 	int trial_successful = 0;
+	unsigned lfrom = 0;
 
 	for (i = 0; i < num_q; i++) {
 		ret = odp_pktin_recv(queues[i], packets, num);
@@ -1748,11 +1777,23 @@  int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q,
 	if (wait == 0)
 		return 0;
 
-	ret = sock_recv_mq_tmo_try_int_driven(queues, num_q, from,
+	ret = sock_recv_mq_tmo_try_int_driven(queues, num_q, &lfrom,
 					      packets, num, wait,
 					      &trial_successful);
-	if (trial_successful)
+	if (ret > 0 && from)
+		*from = lfrom;
+	if (trial_successful) {
+		if (_ODP_PCAPNG) {
+			pktio_entry_t *entry;
+
+			entry = get_pktio_entry(queues[lfrom].pktio);
+			if (entry)
+				_odp_dump_pcapng_pkts(entry, lfrom, packets,
+						      ret);
+		}
+
 		return ret;
+	}
 
 	ts.tv_sec  = 0;
 	ts.tv_nsec = 1000 * SLEEP_USEC;
@@ -1818,6 +1859,9 @@  int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[],
 		return -1;
 	}
 
+	if (_ODP_PCAPNG)
+		_odp_dump_pcapng_pkts(entry, queue.index, packets, num);
+
 	return entry->s.ops->send(entry, queue.index, packets, num);
 }
 
diff --git a/platform/linux-generic/odp_pcapng.c b/platform/linux-generic/odp_pcapng.c
new file mode 100644
index 000000000..b8d29e5a8
--- /dev/null
+++ b/platform/linux-generic/odp_pcapng.c
@@ -0,0 +1,434 @@ 
+/* Copyright (c) 2018, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include "config.h"
+
+#if defined(_ODP_PCAPNG) && _ODP_PCAPNG == 1
+
+#include <odp_macros_internal.h>
+#include <odp_packet_io_internal.h>
+#include <odp/api/plat/packet_inlines.h>
+#include <odp_posix_extensions.h>
+#include <odp_pcapng.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
+
+static void pcapng_drain_fifo(int fd)
+{
+	char buffer[4096];
+	ssize_t len;
+
+	do {
+		len = read(fd, buffer, sizeof(buffer));
+	} while (len > 0);
+}
+
+static void inotify_event_handle(pktio_entry_t *entry, int qidx,
+				 struct inotify_event *event)
+{
+	int mtu = MAX(odp_pktin_maxlen(entry->s.handle),
+		      odp_pktout_maxlen(entry->s.handle));
+
+	if (event->mask & IN_OPEN) {
+		int ret;
+
+		if (PIPE_BUF < mtu + sizeof(pcapng_enhanced_packet_block_t) +
+		    sizeof(uint32_t)) {
+			ODP_ERR("PIPE_BUF:%d too small. Disabling pcap\n",
+				PIPE_BUF);
+			entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP;
+
+			return;
+		}
+
+		ret = write_pcapng_hdr(entry, qidx);
+		if (ret) {
+			entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP;
+		} else {
+			entry->s.pcapng.state[qidx] = PCAPNG_WR_PKT;
+			ODP_DBG("Open %s for pcap tracing\n", event->name);
+		}
+	} else if (event->mask & IN_CLOSE) {
+		int fd = entry->s.pcapng.fd[qidx];
+
+		pcapng_drain_fifo(fd);
+		entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP;
+		ODP_DBG("Close %s for pcap tracing\n", event->name);
+	} else {
+		ODP_ERR("Unknown inotify event 0x%08x\n", event->mask);
+	}
+}
+
+static void get_pcapng_fifo_name(char *pcapng_entry, size_t len,
+				 char *pktio_name, int qidx)
+{
+	snprintf(pcapng_entry, len, "%d-%s-flow-%d",
+		 odp_global_data.main_pid, pktio_name, qidx);
+	pcapng_entry[len - 1] = 0;
+}
+
+static int get_qidx_from_fifo(pktio_entry_t *entry, char *name)
+{
+	unsigned int max_queue =
+		MAX(entry->s.num_in_queue, entry->s.num_out_queue);
+	unsigned int i;
+
+	for (i = 0; i < max_queue; i++) {
+		char pcapng_entry[256];
+
+		get_pcapng_fifo_name(pcapng_entry, sizeof(pcapng_entry),
+				     entry->s.name, i);
+		/*
+		 * verify we still talk to a fifo before returning a valid
+		 * queue number
+		 */
+		if (strcmp(name, pcapng_entry) == 0) {
+			struct stat fstat;
+			char pcapng_path[256];
+
+			snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+				 PCAPNG_WATCH_DIR, name);
+			stat(pcapng_path, &fstat);
+
+			return S_ISFIFO(fstat.st_mode) ? (int)i : -1;
+		}
+	}
+
+	return -1;
+}
+
+static void *inotify_update(void *arg)
+{
+	pktio_entry_t *entry = (pktio_entry_t *)arg;
+	struct timeval time;
+	ssize_t rdlen;
+	int offset;
+	char buffer[INOTIFY_BUF_LEN];
+	fd_set rfds;
+
+	while (1) {
+		offset = 0;
+		FD_ZERO(&rfds);
+		FD_SET(odp_global_data.inotify_pcapng_fd, &rfds);
+		time.tv_sec = 2;
+		time.tv_usec = 0;
+		select(odp_global_data.inotify_pcapng_fd + 1, &rfds, NULL,
+		       NULL, &time);
+		if (FD_ISSET(odp_global_data.inotify_pcapng_fd, &rfds)) {
+			rdlen = read(odp_global_data.inotify_pcapng_fd,
+				     buffer, INOTIFY_BUF_LEN);
+			while (offset < rdlen) {
+				int qidx;
+				struct inotify_event *event =
+					(struct inotify_event *)(void *)
+					 &buffer[offset];
+
+				qidx = get_qidx_from_fifo(entry, event->name);
+				if (qidx == -1) {
+					offset += sizeof(struct inotify_event) +
+						event->len;
+					continue;
+				}
+
+				inotify_event_handle(entry, qidx, event);
+				offset += sizeof(struct inotify_event) +
+						 event->len;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static int get_fifo_max_size(void)
+{
+	FILE *file;
+	char buf[128];
+	int ret = -1;
+
+	file = fopen("/proc/sys/fs/pipe-max-size", "r");
+	if (file == NULL)
+		return ret;
+
+	if (fgets(buf, sizeof(buf), file))
+		ret = atoi(buf);
+
+	fclose(file);
+
+	return ret;
+}
+
+int pcapng_prepare(pktio_entry_t *entry)
+{
+	int ret = -1, fd;
+	pthread_attr_t attr;
+	unsigned int i;
+	unsigned int max_queue =
+		MAX(entry->s.num_in_queue, entry->s.num_out_queue);
+	int fifo_sz;
+
+	fifo_sz = get_fifo_max_size();
+	if (fifo_sz < 0)
+		ODP_DBG("failed to read max fifo size\n");
+
+	for (i = 0; i < max_queue; i++) {
+		char pcapng_name[128];
+		char pcapng_path[256];
+
+		entry->s.pcapng.fd[i] = -1;
+		entry->s.pcapng.state[i] = PCAPNG_WR_STOP;
+
+		get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name),
+				     entry->s.name, i);
+		snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+			 PCAPNG_WATCH_DIR, pcapng_name);
+		if (mkfifo(pcapng_path, O_RDWR)) {
+			ODP_ERR("pcap not available for %s %s\n",
+				pcapng_path, strerror(errno));
+			continue;
+		}
+
+		fd = open(pcapng_path, O_RDWR | O_NONBLOCK);
+		if (fd == -1) {
+			ODP_ERR("Fail to open fifo\n");
+			entry->s.pcapng.state[i] = PCAPNG_WR_STOP;
+			if (remove(pcapng_path) == -1)
+				ODP_ERR("Can't remove fifo %s\n", pcapng_path);
+			continue;
+		}
+
+		if (fifo_sz > 0) {
+			if (fcntl(fd, F_SETPIPE_SZ, fifo_sz) != fifo_sz)
+				ODP_DBG("Failed to set max fifo size\n");
+			else
+				ODP_DBG("set pcap fifo size %i\n", fifo_sz);
+		}
+
+		entry->s.pcapng.fd[i] = fd;
+	}
+
+	/* already running from a previous pktio */
+	if (odp_global_data.inotify_pcapng_is_running == 1)
+		return 0;
+
+	odp_global_data.inotify_pcapng_fd = -1;
+	odp_global_data.inotify_watch_fd = -1;
+
+	odp_global_data.inotify_pcapng_fd = inotify_init();
+	if (odp_global_data.inotify_pcapng_fd == -1) {
+		ODP_ERR("can't init inotify. pcap disabled\n");
+		goto out_destroy;
+	}
+
+	odp_global_data.inotify_watch_fd =
+		inotify_add_watch(odp_global_data.inotify_pcapng_fd,
+				  PCAPNG_WATCH_DIR, IN_CLOSE | IN_OPEN);
+
+	if (odp_global_data.inotify_watch_fd == -1) {
+		ODP_ERR("can't register inotify for %s. pcap disabled\n",
+			strerror(errno));
+		goto out_destroy;
+	}
+
+	/* create a thread to poll inotify triggers */
+	pthread_attr_init(&attr);
+	ret = pthread_create(&odp_global_data.inotify_thread, &attr,
+			     inotify_update, entry);
+	if (ret)
+		ODP_ERR("can't start inotify thread. pcap disabled\n");
+	else
+		odp_global_data.inotify_pcapng_is_running = 1;
+
+	return ret;
+
+out_destroy:
+	pcapng_destroy(entry);
+
+	return ret;
+}
+
+void pcapng_destroy(pktio_entry_t *entry)
+{
+	int ret;
+	unsigned int i;
+	unsigned int max_queue =
+		MAX(entry->s.num_in_queue, entry->s.num_out_queue);
+
+	if (odp_global_data.inotify_pcapng_is_running == 1) {
+		ret = pthread_cancel(odp_global_data.inotify_thread);
+		if (ret)
+			ODP_ERR("can't cancel inotify thread %s\n",
+				strerror(errno));
+	}
+
+	/* fd's will be -1 in case of any failure */
+	ret = inotify_rm_watch(odp_global_data.inotify_pcapng_fd,
+			       odp_global_data.inotify_watch_fd);
+	if (ret)
+		ODP_ERR("can't deregister inotify %s\n", strerror(errno));
+
+	if (odp_global_data.inotify_pcapng_fd != -1)
+		close(odp_global_data.inotify_pcapng_fd);
+
+	if (odp_global_data.inotify_watch_fd != -1)
+		close(odp_global_data.inotify_watch_fd);
+
+	for (i = 0; i < max_queue; i++) {
+		char pcapng_name[128];
+		char pcapng_path[256];
+
+		entry->s.pcapng.state[i] = PCAPNG_WR_STOP;
+		close(entry->s.pcapng.fd[i]);
+
+		get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name),
+				     entry->s.name, i);
+		snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s",
+			 PCAPNG_WATCH_DIR, pcapng_name);
+
+		if (remove(pcapng_path))
+			ODP_ERR("can't delete fifo %s\n", pcapng_path);
+	}
+}
+
+int write_pcapng_hdr(pktio_entry_t *entry, int qidx)
+{
+	size_t len;
+	pcapng_section_hdr_block_t shb;
+	pcapng_interface_description_block_t idb;
+	int fd = entry->s.pcapng.fd[qidx];
+
+	memset(&shb, 0, sizeof(shb));
+	memset(&idb, 0, sizeof(idb));
+
+	shb.block_type = PCAPNG_BLOCK_TYPE_SHB;
+	shb.block_total_length = sizeof(shb);
+	shb.block_total_length2 = sizeof(shb);
+	shb.magic = PCAPNG_ENDIAN_MAGIC;
+	shb.version_major = 0x1;
+	shb.version_minor = 0x0;
+	shb.section_len = -1;
+
+	len = write(fd, &shb, sizeof(shb));
+	/* fail to write shb/idb means the pcapng is unreadable */
+	if (len != sizeof(shb)) {
+		ODP_ERR("Failed to write pcapng section hdr\n");
+		return -1;
+	}
+	fsync(fd);
+
+	idb.block_type = PCAPNG_BLOCK_TYPE_IDB;
+	idb.block_total_length = sizeof(idb);
+	idb.block_total_length2 = sizeof(idb);
+	idb.linktype = PCAPNG_LINKTYPE_ETHERNET;
+	idb.snaplen = 0x0; /* unlimited */
+	len = write(fd, &idb, sizeof(idb));
+	if (len != sizeof(idb)) {
+		ODP_ERR("Failed to write pcapng interface description\n");
+		return -1;
+	}
+	fsync(fd);
+
+	return 0;
+}
+
+/*
+ * make sure that each fifo write is less than PIPE_BUF
+ * this will make sure writes are atomic (on non blocking mode).
+ * writev() transfers all the data and returns the number of bytes requested or
+ * -EAGAIN
+ */
+static ssize_t write_fifo(int fd, struct iovec *iov, int iovcnt)
+{
+	ssize_t len = 0;
+
+	len = writev(fd, iov, iovcnt);
+	/*
+	 * we don't care if a writev fails, we asynchronously read the fifo
+	 * so the next block of packets might be successful. This error only
+	 * means that some packets failed to append on the pcap file
+	 */
+	if (len > 0)
+		fsync(fd);
+
+	return len;
+}
+
+int write_pcapng_pkts(pktio_entry_t *entry, int qidx,
+		      const odp_packet_t packets[], int num)
+{
+	int i = 0;
+	struct iovec packet_iov[3 * num];
+	pcapng_enhanced_packet_block_t epb[num];
+	int iovcnt = 0;
+	ssize_t block_len = 0;
+	int fd = entry->s.pcapng.fd[qidx];
+	ssize_t len = 0, wlen;
+
+	for (i = 0; i < num; i++) {
+		odp_packet_hdr_t *pkt_hdr = packet_hdr(packets[i]);
+		uint32_t seg_len;
+		char *buf = (char *)odp_packet_offset(packets[i], 0, &seg_len,
+						      NULL);
+
+		if (block_len + sizeof(epb[i]) +
+		    ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) +
+		    sizeof(uint32_t) > PIPE_BUF) {
+			wlen = write_fifo(fd, packet_iov, iovcnt);
+			if (wlen > 0) {
+				len += wlen;
+				block_len = 0;
+				iovcnt = 0;
+			}
+		}
+		epb[i].block_type = PCAPNG_BLOCK_TYPE_EPB;
+		epb[i].block_total_length = sizeof(epb[i]) +
+			ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) +
+			PCAPNG_DATA_ALIGN;
+		epb[i].interface_idx = 0;
+		epb[i].timestamp_high =
+			(uint32_t)(pkt_hdr->timestamp.u64 >> 32);
+		epb[i].timestamp_low = (uint32_t)(pkt_hdr->timestamp.u64);
+		epb[i].captured_len = seg_len;
+		epb[i].packet_len = seg_len;
+
+		/* epb */
+		packet_iov[iovcnt].iov_base = &epb[i];
+		packet_iov[iovcnt].iov_len = sizeof(epb[i]);
+		block_len += packet_iov[iovcnt].iov_len;
+		iovcnt++;
+
+		/* data */
+		packet_iov[iovcnt].iov_base = buf;
+		packet_iov[iovcnt].iov_len =
+			ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN);
+		block_len += packet_iov[iovcnt].iov_len;
+		iovcnt++;
+
+		/* trailing */
+		packet_iov[iovcnt].iov_base = &epb[i].block_total_length;
+		packet_iov[iovcnt].iov_len = sizeof(uint32_t);
+		block_len += packet_iov[iovcnt].iov_len;
+		iovcnt++;
+	}
+
+	if (iovcnt) {
+		wlen = write_fifo(fd, packet_iov, iovcnt);
+		if (wlen > 0)
+			len += wlen;
+	}
+
+	return len;
+}
+
+#endif /* _ODP_PCAPNG */