diff --git a/configure.ac b/configure.ac
index 2ea1368..2a58ba9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -265,6 +265,14 @@ AC_CHECK_HEADERS([openssl/des.h openssl/rand.h openssl/hmac.h openssl/evp.h], []
              [AC_MSG_ERROR([OpenSSL headers required])])
 
 ##########################################################################
+# Check for libpcap availability
+##########################################################################
+AC_CHECK_LIB([pcap], [pcap_open_offline], [],
+             [AC_MSG_FAILURE([libpcap libraries required])])
+AC_CHECK_HEADERS([pcap/pcap.h pcap/bpf.h], [],
+             [AC_MSG_ERROR([pcap headers required])])
+
+##########################################################################
 # Restore old saved variables
 ##########################################################################
 LDFLAGS=$OLD_LDFLAGS
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 4ee781c..4e702af 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -142,6 +142,7 @@ __LIB__libodp_la_SOURCES = \
 			   odp_packet_io.c \
 			   pktio/io_ops.c \
 			   pktio/loop.c \
+			   pktio/pcap.c \
 			   pktio/socket.c \
 			   pktio/socket_mmap.c \
 			   odp_pool.c \
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index f230936..5339606 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -28,6 +28,8 @@ extern "C" {
 #include <odp/hints.h>
 #include <net/if.h>
 
+#define PKTIO_NAME_LEN 64
+
 /* Forward declaration */
 struct pktio_if_ops;
 
@@ -36,6 +38,12 @@ typedef struct {
 	odp_bool_t promisc;		/**< promiscuous mode state */
 } pkt_loop_t;
 
+typedef struct {
+	odp_pool_t pool;
+	void *rx;
+	void *tx_dump;
+} pkt_pcap_t;
+
 struct pktio_entry {
 	const struct pktio_if_ops *ops; /**< Implementation specific methods */
 	odp_spinlock_t lock;		/**< entry spinlock */
@@ -49,9 +57,10 @@ struct pktio_entry {
 		pkt_sock_t pkt_sock;		/**< using socket API for IO */
 		pkt_sock_mmap_t pkt_sock_mmap;	/**< using socket mmap
 						 *   API for IO */
+		pkt_pcap_t pkt_pcap;		/**< Using pcap for IO */
 	};
 	classifier_t cls;		/**< classifier linked with this pktio*/
-	char name[IF_NAMESIZE];		/**< name of pktio provided to
+	char name[PKTIO_NAME_LEN];	/**< name of pktio provided to
 					   pktio_open() */
 };
 
@@ -106,6 +115,7 @@ extern const pktio_if_ops_t sock_basic_pktio_ops;
 extern const pktio_if_ops_t sock_mmsg_pktio_ops;
 extern const pktio_if_ops_t sock_mmap_pktio_ops;
 extern const pktio_if_ops_t loopback_pktio_ops;
+extern const pktio_if_ops_t pcap_pktio_ops;
 extern const pktio_if_ops_t * const pktio_if_ops[];
 
 #ifdef __cplusplus
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index c159baf..7eeadd5 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -187,10 +187,10 @@ static odp_pktio_t setup_pktio_entry(const char *dev, odp_pool_t pool)
 	int ret = -1;
 	int pktio_if;
 
-	if (strlen(dev) >= IF_NAMESIZE) {
+	if (strlen(dev) >= PKTIO_NAME_LEN) {
 		/* ioctl names limitation */
 		ODP_ERR("pktio name %s is too big, limit is %d bytes\n",
-			dev, IF_NAMESIZE);
+			dev, PKTIO_NAME_LEN);
 		return ODP_PKTIO_INVALID;
 	}
 
@@ -220,7 +220,7 @@ static odp_pktio_t setup_pktio_entry(const char *dev, odp_pool_t pool)
 		id = ODP_PKTIO_INVALID;
 		ODP_ERR("Unable to init any I/O type.\n");
 	} else {
-		snprintf(pktio_entry->s.name, IF_NAMESIZE, "%s", dev);
+		snprintf(pktio_entry->s.name, PKTIO_NAME_LEN, "%s", dev);
 		unlock_entry_classifier(pktio_entry);
 	}
 
@@ -285,7 +285,7 @@ odp_pktio_t odp_pktio_lookup(const char *dev)
 		lock_entry(entry);
 
 		if (!is_free(entry) &&
-		    strncmp(entry->s.name, dev, IF_NAMESIZE) == 0)
+		    strncmp(entry->s.name, dev, PKTIO_NAME_LEN) == 0)
 			id = _odp_cast_scalar(odp_pktio_t, i);
 
 		unlock_entry(entry);
diff --git a/platform/linux-generic/pktio/io_ops.c b/platform/linux-generic/pktio/io_ops.c
index 6cd3d00..ddcf39e 100644
--- a/platform/linux-generic/pktio/io_ops.c
+++ b/platform/linux-generic/pktio/io_ops.c
@@ -15,5 +15,6 @@ const pktio_if_ops_t * const pktio_if_ops[]  = {
 	&sock_mmap_pktio_ops,
 	&sock_mmsg_pktio_ops,
 	&sock_basic_pktio_ops,
+	&pcap_pktio_ops,
 	NULL
 };
diff --git a/platform/linux-generic/pktio/pcap.c b/platform/linux-generic/pktio/pcap.c
new file mode 100644
index 0000000..58dcd6f
--- /dev/null
+++ b/platform/linux-generic/pktio/pcap.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <odp.h>
+#include <odp_packet_internal.h>
+#include <odp_packet_io_internal.h>
+
+#include <pcap/pcap.h>
+#include <pcap/bpf.h>
+
+#define PCAP_DUMPFILE_SUFFIX "_out"
+
+static int _pcapif_output_fname(char *out, const char *in, size_t len)
+{
+	char tmp[len];
+	int ret, i;
+
+	strncpy(tmp, in, sizeof(tmp));
+
+	i = strlen(tmp);
+	while (i && tmp[i] != '.')
+		i--;
+
+	if (i) {
+		tmp[i] = '\0';
+		ret = snprintf(out, len, "%s%s.%s", tmp,
+			       PCAP_DUMPFILE_SUFFIX, tmp + i + 1);
+	} else {
+		ret = snprintf(out, len, "%s%s", tmp, PCAP_DUMPFILE_SUFFIX);
+	}
+
+	return ret;
+}
+
+static int pcapif_init(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
+		       const char *devname, odp_pool_t pool)
+{
+	pkt_pcap_t *pkt_pcap = &pktio_entry->s.pkt_pcap;
+	const char *fname_rx;
+	char errbuf[PCAP_ERRBUF_SIZE];
+	int linktype;
+
+	if (strncmp(devname, "pcap:", 5))
+		return -1;
+
+	fname_rx = devname + 5;
+
+	pkt_pcap->rx = pcap_open_offline(fname_rx, errbuf);
+	if (!pkt_pcap->rx) {
+		ODP_ERR("PCAP open failure: %s\n", errbuf);
+		return -1;
+	}
+
+	pkt_pcap->pool = pool;
+
+	linktype = pcap_datalink(pkt_pcap->rx);
+	if (linktype != DLT_EN10MB) {
+		ODP_ERR("Datalink type not supported: %d\n", linktype);
+		return -1;
+	}
+
+	/* By default all packets sent to the pktio will just be freed, but if
+	 * the ODP_PKTIO_PCAP_DUMP environment variable is set sent packets will
+	 * instead be saved to a separate output pcap file. */
+	if (getenv("ODP_PKTIO_PCAP_DUMP")) {
+		char fname_tx[PKTIO_NAME_LEN + sizeof(PCAP_DUMPFILE_SUFFIX)];
+
+		_pcapif_output_fname(fname_tx, fname_rx, sizeof(fname_tx));
+
+		pkt_pcap->tx_dump = pcap_dump_open(pkt_pcap->rx, fname_tx);
+		if (!pkt_pcap->tx_dump) {
+			ODP_ERR("pcap_dump_open failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int pcapif_close(pktio_entry_t *pktio_entry)
+{
+	pkt_pcap_t *pkt_pcap = &pktio_entry->s.pkt_pcap;
+
+	if (pkt_pcap->rx)
+		pcap_close(pkt_pcap->rx);
+
+	if (pkt_pcap->tx_dump)
+		pcap_dump_close(pkt_pcap->tx_dump);
+
+	return 0;
+}
+
+static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, odp_packet_t pkts[],
+			   unsigned len)
+{
+	unsigned i;
+	struct pcap_pkthdr *pkt_hdr;
+	const u_char *pkt_data;
+	pkt_pcap_t *pkt_pcap = &pktio_entry->s.pkt_pcap;
+	int nbr = 0;
+
+	for (i = 0; i < len; ++i) {
+		if (pcap_next_ex(pkt_pcap->rx, &pkt_hdr, &pkt_data) != 1)
+			break;
+
+		pkts[i] = odp_packet_alloc(pkt_pcap->pool, pkt_hdr->caplen);
+		if (odp_unlikely(pkts[i] == ODP_PACKET_INVALID))
+			break;
+
+		odp_packet_copydata_in(pkts[i], 0, pkt_hdr->caplen, pkt_data);
+		_odp_packet_reset_parse(pkts[i]);
+
+		nbr++;
+	}
+
+	return nbr;
+}
+
+static void pcapif_dump_pkt(pkt_pcap_t *pkt_pcap, odp_packet_t pkt)
+{
+	unsigned char *buf;
+	struct pcap_pkthdr hdr;
+
+	if (!pkt_pcap->tx_dump)
+		return;
+
+	hdr.caplen = odp_packet_len(pkt);
+	hdr.len = hdr.caplen;
+	gettimeofday(&hdr.ts, NULL);
+
+	buf = malloc(hdr.len);
+
+	if (odp_packet_copydata_out(pkt, 0, hdr.len, buf) == 0)
+		pcap_dump(pkt_pcap->tx_dump, &hdr, buf);
+
+	free(buf);
+}
+
+static int pcapif_send_pkt(pktio_entry_t *pktio_entry, odp_packet_t pkts[],
+			   unsigned len)
+{
+	pkt_pcap_t *pkt_pcap = &pktio_entry->s.pkt_pcap;
+	unsigned i;
+
+	for (i = 0; i < len; ++i) {
+		pcapif_dump_pkt(pkt_pcap, pkts[i]);
+		odp_packet_free(pkts[i]);
+	}
+
+	return len;
+}
+
+static int pcapif_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+	return 0;
+}
+
+static int pcapif_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
+			       void *mac_addr ODP_UNUSED)
+{
+	return 0;
+}
+
+static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry ODP_UNUSED,
+				   odp_bool_t enable ODP_UNUSED)
+{
+	return 0;
+}
+
+static int pcapif_promisc_mode_get(pktio_entry_t *pktio_entry ODP_UNUSED)
+{
+	return 0;
+}
+
+const pktio_if_ops_t pcap_pktio_ops = {
+	.open = pcapif_init,
+	.close = pcapif_close,
+	.recv = pcapif_recv_pkt,
+	.send = pcapif_send_pkt,
+	.mtu_get = pcapif_mtu_get,
+	.promisc_mode_set = pcapif_promisc_mode_set,
+	.promisc_mode_get = pcapif_promisc_mode_get,
+	.mac_get = pcapif_mac_addr_get
+};
