On 07/28/15 20:54, Stuart Haslam wrote:
Create a new pktio type that allows for reading from and writing to a
pcap capture file. This is intended to be used as a simple way of
injecting test packets into an application for functional testing and
can be used as it is with some of the existing example applications.
To open a pcap file the name passed to the odp_pktio_open() call needs
to be of the format "pcap:<filename>".
By default all packets transmitted to a pcap pktio are simply freed, but
if the environment variable ODP_PKTIO_PCAP_DUMP is set, transmitted
packets will instead be saved to a separate pcap dump file, the name of
which is generated from the input filename (test.pcap -> test_out.pcap).
MTU, MAC and promiscuous mode APIs aren't implemented.
Signed-off-by: Stuart Haslam <stuart.has...@linaro.org>
---
This is pretty handy for testing, for example with the classifier app;
sudo ./odp_classifier -ipcap:test.pcap -p
"ODP_PMR_SIP_ADDR:192.168.111.2:FFFFFFFF:queue1" -t 5
configure.ac | 8 +
platform/linux-generic/Makefile.am | 1 +
.../linux-generic/include/odp_packet_io_internal.h | 12 +-
platform/linux-generic/odp_packet_io.c | 8 +-
platform/linux-generic/pktio/io_ops.c | 1 +
platform/linux-generic/pktio/pcap.c | 191 +++++++++++++++++++++
6 files changed, 216 insertions(+), 5 deletions(-)
create mode 100644 platform/linux-generic/pktio/pcap.c
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
Needed to check if we have static assert to check that PKTIO_NAME_LEN <=
IF_NAMESIZE.
And it will be good to send it in separate patch. Also
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")) {
getenv might conflict with bare metal env. I think it's better to code
it pktio name if tx is implemented or not.
./odp_classifier -ipcap:test.pcap
./odp_classifier -ipcap:test.pcap:test_out.pcap
How about adding .help for pktio?
./odp_classifier -ipcap:help
can return all available options and use cases for that pcap pktio.
and
-ihelp can return all available prtios with it's options?
+ 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);
I guess Coverity will warn why you don't check buf != 0. It's better to
allocate some memory on init
for that temporary use.
+
+ 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;
+}
You can save any packets, right? Including jumbo. Setting it to jumbo
maximum will
make validation test passed.
+
+static int pcapif_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
+ void *mac_addr ODP_UNUSED)
+{
+ return 0;
+}
You can return some dummy mac addr to make validation test happy.
+
+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;
+}
Promisc looks like always 1, because it 'receives' all packets from pcap
not looking to any mac address.
Also it will be good to code loop option to pcap interface.
Thanks,
Maxim.
+
+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
+};
_______________________________________________
lng-odp mailing list
lng-odp@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/lng-odp