On 09/04/2015 03:20 PM, 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 use this interface the name passed to odp_pktio_open() must begin
> with "pcap:" and be in the format;
>
> pcap:in=test.pcap:out=test_out.pcap:loops=10
>
> in the name of the input pcap file. If no input file is given
> attempts to receive from the pktio will just return no
> packets.
> out the name of the output pcap file. If no output file is
> given any packets transmitted over the interface will just
> be freed.
> loops the number of times to iterate through the input file, set
> to 0 to loop indefinitely. The default value is 1.
>
> Signed-off-by: Stuart Haslam <[email protected]>
> ---
> platform/linux-generic/Makefile.am | 4 +
> .../linux-generic/include/odp_packet_io_internal.h | 21 ++
> platform/linux-generic/m4/configure.m4 | 16 +
> platform/linux-generic/pktio/io_ops.c | 3 +
> platform/linux-generic/pktio/pcap.c | 334
> +++++++++++++++++++++
> 5 files changed, 378 insertions(+)
> create mode 100644 platform/linux-generic/pktio/pcap.c
>
> diff --git a/platform/linux-generic/Makefile.am
> b/platform/linux-generic/Makefile.am
> index 4c79730..8d14477 100644
> --- a/platform/linux-generic/Makefile.am
> +++ b/platform/linux-generic/Makefile.am
> @@ -173,3 +173,7 @@ EXTRA_DIST = \
> arch/linux/odp_time_cycles.c \
> arch/mips64/odp_time_cycles.c \
> arch/x86/odp_time_cycles.c
> +
> +if HAVE_PCAP
> +__LIB__libodp_la_SOURCES += pktio/pcap.c
> +endif
> diff --git a/platform/linux-generic/include/odp_packet_io_internal.h
> b/platform/linux-generic/include/odp_packet_io_internal.h
> index bdffc15..7faa4f0 100644
> --- a/platform/linux-generic/include/odp_packet_io_internal.h
> +++ b/platform/linux-generic/include/odp_packet_io_internal.h
> @@ -38,6 +38,21 @@ typedef struct {
> odp_bool_t promisc; /**< promiscuous mode state */
> } pkt_loop_t;
>
> +#ifdef HAVE_PCAP
> +typedef struct {
> + char *fname_rx; /**< name of pcap file for rx */
> + char *fname_tx; /**< name of pcap file for tx */
> + void *rx; /**< rx pcap handle */
> + void *tx; /**< tx pcap handle */
> + void *tx_dump; /**< tx pcap dumper handle */
> + odp_pool_t pool; /**< rx pool */
> + unsigned char *buf; /**< per-pktio temp buffer */
> + odp_bool_t promisc; /**< promiscuous mode requested state */
> + int loops; /**< number of times to loop rx pcap */
> + int loop_cnt; /**< number of loops completed */
> +} pkt_pcap_t;
> +#endif
> +
> struct pktio_entry {
> const struct pktio_if_ops *ops; /**< Implementation specific methods */
> odp_spinlock_t lock; /**< entry spinlock */
> @@ -51,6 +66,9 @@ 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 */
> +#ifdef HAVE_PCAP
> + pkt_pcap_t pkt_pcap; /**< Using pcap for IO */
> +#endif
> };
> enum {
> STATE_START = 0,
> @@ -116,6 +134,9 @@ int pktin_poll(pktio_entry_t *entry);
> 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;
> +#ifdef HAVE_PCAP
> +extern const pktio_if_ops_t pcap_pktio_ops;
> +#endif
> extern const pktio_if_ops_t * const pktio_if_ops[];
>
> #ifdef __cplusplus
> diff --git a/platform/linux-generic/m4/configure.m4
> b/platform/linux-generic/m4/configure.m4
> index 9658274..4e4fa00 100644
> --- a/platform/linux-generic/m4/configure.m4
> +++ b/platform/linux-generic/m4/configure.m4
> @@ -22,3 +22,19 @@ m4_include([platform/linux-generic/m4/odp_openssl.m4])
> AC_CONFIG_FILES([platform/linux-generic/Makefile
> platform/linux-generic/test/Makefile
> platform/linux-generic/test/pktio/Makefile])
> +
> +#########################################################################
> +# Check for libpcap availability
> +#########################################################################
> +have_pcap=no
> +AC_CHECK_HEADER(pcap/pcap.h,
> + [AC_CHECK_HEADER(pcap/bpf.h,
> + [AC_CHECK_LIB(pcap, pcap_open_offline, have_pcap=yes, [])],
> + [])],
> +[])
> +
> +AM_CONDITIONAL([HAVE_PCAP], [test $have_pcap = yes])
> +if test $have_pcap == yes; then
> + AM_CFLAGS="$AM_CFLAGS -DHAVE_PCAP"
> + LIBS="$LIBS -lpcap"
> +fi
> diff --git a/platform/linux-generic/pktio/io_ops.c
> b/platform/linux-generic/pktio/io_ops.c
> index 1d47e74..bd53a5c 100644
> --- a/platform/linux-generic/pktio/io_ops.c
> +++ b/platform/linux-generic/pktio/io_ops.c
> @@ -12,6 +12,9 @@
> * Array must be NULL terminated */
> const pktio_if_ops_t * const pktio_if_ops[] = {
> &loopback_pktio_ops,
> +#ifdef HAVE_PCAP
> + &pcap_pktio_ops,
> +#endif
> &sock_mmap_pktio_ops,
> &sock_mmsg_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..d4173f9
> --- /dev/null
> +++ b/platform/linux-generic/pktio/pcap.c
> @@ -0,0 +1,334 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier: BSD-3-Clause
> + */
> +
> +/**
> + * @file
> + *
> + * PCAP pktio type
> + *
> + * This file provides a pktio interface that allows for reading from
> + * and writing to pcap capture files. It is intended to be used as
> + * simple way of injecting test packets into an application for the
> + * purpose of functional testing.
> + *
> + * To use this interface the name passed to odp_pktio_open() must begin
> + * with "pcap:" and be in the format;
> + *
> + * pcap:in=test.pcap:out=test_out.pcap:loops=10
> + *
> + * in the name of the input pcap file. If no input file is given
> + * attempts to receive from the pktio will just return no
> + * packets. If an input file is specified it must exist and be
> + * a readable pcap file with a link type of DLT_EN10MB.
> + * out the name of the output pcap file. If no output file is
> + * given any packets transmitted over the interface will just
> + * be freed. If an output file is specified and the file
> + * doesn't exist it will be created, if it does exist it will
> + * be overwritten.
> + * loops the number of times to iterate through the input file, set
> + * to 0 to loop indefinitely. The default value is 1.
> + *
> + * The total length of the string is limited by PKTIO_NAME_LEN.
> + */
> +
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif
> +
> +#include <odp.h>
> +#include <odp_packet_internal.h>
> +#include <odp_packet_io_internal.h>
> +
> +#include <odp/helper/eth.h>
> +
> +#include <pcap/pcap.h>
> +#include <pcap/bpf.h>
> +
> +#define PKTIO_PCAP_MTU (64 * 1024)
> +
> +static int _pcapif_parse_devname(pkt_pcap_t *pcap, const char *devname)
> +{
> + char *tok;
> + char in[PKTIO_NAME_LEN];
> +
> + if (strncmp(devname, "pcap:", 5) != 0)
> + return -1;
> +
> + snprintf(in, sizeof(in), "%s", devname);
> +
> + for (tok = strtok(in + 5, ":"); tok; tok = strtok(NULL, ":")) {
> + if (strncmp(tok, "in=", 3) == 0 && !pcap->fname_rx) {
> + tok += 3;
> + pcap->fname_rx = strdup(tok);
> + } else if (strncmp(tok, "out=", 4) == 0 && !pcap->fname_tx) {
> + tok += 4;
> + pcap->fname_tx = strdup(tok);
> + } else if (strncmp(tok, "loops=", 6) == 0) {
> + pcap->loops = atoi(tok + 6);
> + if (pcap->loops < 0) {
> + ODP_ERR("invalid loop count\n");
> + return -1;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int _pcapif_init_rx(pkt_pcap_t *pcap)
> +{
> + char errbuf[PCAP_ERRBUF_SIZE];
> + int linktype;
> +
> + pcap->rx = pcap_open_offline(pcap->fname_rx, errbuf);
> + if (!pcap->rx) {
> + ODP_ERR("failed to open pcap file %s (%s)\n",
> + pcap->fname_rx, errbuf);
> + return -1;
> + }
> +
> + linktype = pcap_datalink(pcap->rx);
> + if (linktype != DLT_EN10MB) {
> + ODP_ERR("unsupported datalink type: %d\n", linktype);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int _pcapif_init_tx(pkt_pcap_t *pcap)
> +{
> + pcap_t *tx = pcap->rx;
> +
> + if (!tx) {
> + /* if there is no rx pcap_t already open for rx, a dummy
> + * one needs to be opened for writing the dump */
> + tx = pcap_open_dead(DLT_EN10MB, PKTIO_PCAP_MTU);
> + if (!tx) {
> + ODP_ERR("failed to open TX dump\n");
> + return -1;
> + }
> +
> + pcap->tx = tx;
> + }
> +
> + pcap->buf = malloc(PKTIO_PCAP_MTU);
> + if (!pcap->buf) {
> + ODP_ERR("failed to malloc temp buffer\n");
> + return -1;
> + }
> +
> + pcap->tx_dump = pcap_dump_open(tx, pcap->fname_tx);
> + if (!pcap->tx_dump) {
> + ODP_ERR("failed to open dump file %s (%s)\n",
> + pcap->fname_tx, pcap_geterr(tx));
> + return -1;
> + }
> +
> + return pcap_dump_flush(pcap->tx_dump);
> +}
> +
> +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 *pcap = &pktio_entry->s.pkt_pcap;
> + int ret;
> +
> + memset(pcap, 0, sizeof(pkt_pcap_t));
> + pcap->loop_cnt = 1;
> + pcap->loops = 1;
> + pcap->pool = pool;
> +
> + ret = _pcapif_parse_devname(pcap, devname);
> +
> + if (ret == 0 && pcap->fname_rx)
> + ret = _pcapif_init_rx(pcap);
> +
> + if (ret == 0 && pcap->fname_tx)
> + ret = _pcapif_init_tx(pcap);
> +
> + if (ret == 0 && (!pcap->rx && !pcap->tx_dump))
> + ret = -1;
> +
> + return ret;
> +}
> +
> +static int pcapif_close(pktio_entry_t *pktio_entry)
> +{
> + pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
> +
> + if (pcap->tx_dump)
> + pcap_dump_close(pcap->tx_dump);
> +
> + if (pcap->tx)
> + pcap_close(pcap->tx);
> +
> + if (pcap->rx)
> + pcap_close(pcap->rx);
> +
> + free(pcap->buf);
> + free(pcap->fname_rx);
> + free(pcap->fname_tx);
> +
> + return 0;
> +}
> +
> +static int _pcapif_reopen(pkt_pcap_t *pcap)
> +{
> + char errbuf[PCAP_ERRBUF_SIZE];
> +
> + if (pcap->loops != 0 && ++pcap->loop_cnt >= pcap->loops)
> + return 1;
> +
> + if (pcap->rx)
> + pcap_close(pcap->rx);
> +
> + pcap->rx = pcap_open_offline(pcap->fname_rx, errbuf);
> + if (!pcap->rx) {
> + ODP_ERR("failed to reopen pcap file %s (%s)\n",
> + pcap->fname_rx, errbuf);
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, odp_packet_t pkts[],
> + unsigned len)
> +{
> + unsigned i;
> + struct pcap_pkthdr *hdr;
> + const u_char *data;
> + pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
> +
> + ODP_ASSERT(pktio_entry->s.state == STATE_START);
> +
> + if (!pcap->rx)
> + return 0;
> +
> + for (i = 0; i < len; ) {
> + int ret = pcap_next_ex(pcap->rx, &hdr, &data);
> +
> + /* end of file, attempt to reopen if within loop limit */
> + if (ret == -2 && _pcapif_reopen(pcap) == 0)
> + continue;
> +
> + if (ret != 1)
> + break;
> +
> + pkts[i] = odp_packet_alloc(pcap->pool, hdr->caplen);
> + if (odp_unlikely(pkts[i] == ODP_PACKET_INVALID))
> + break;
> +
> + if (odp_packet_copydata_in(pkts[i], 0, hdr->caplen, data) != 0)
> + break;
> +
> + _odp_packet_reset_parse(pkts[i]);
> +
> + i++;
> + }
> +
> + return i;
> +}
> +
> +static int _pcapif_dump_pkt(pkt_pcap_t *pcap, odp_packet_t pkt)
> +{
> + struct pcap_pkthdr hdr;
> +
> + if (!pcap->tx_dump)
> + return 0;
> +
> + hdr.caplen = odp_packet_len(pkt);
> + hdr.len = hdr.caplen;
> + (void)gettimeofday(&hdr.ts, NULL);
> +
> + if (odp_packet_copydata_out(pkt, 0, hdr.len, pcap->buf) != 0)
> + return -1;
> +
> + pcap_dump(pcap->tx_dump, &hdr, pcap->buf);
> + (void)pcap_dump_flush(pcap->tx_dump);
> +
> + return 0;
> +}
> +
> +static int pcapif_send_pkt(pktio_entry_t *pktio_entry, odp_packet_t pkts[],
> + unsigned len)
> +{
> + pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
> + unsigned i;
> +
> + ODP_ASSERT(pktio_entry->s.state == STATE_START);
> +
> + for (i = 0; i < len; ++i) {
> + if (odp_packet_len(pkts[i]) > PKTIO_PCAP_MTU) {
> + if (i == 0)
> + return -1;
> + break;
> + }
> +
> + if (_pcapif_dump_pkt(pcap, pkts[i]) != 0)
> + break;
> +
> + odp_packet_free(pkts[i]);
> + }
> +
> + return i;
> +}
> +
> +static int pcapif_mtu_get(pktio_entry_t *pktio_entry ODP_UNUSED)
> +{
> + return PKTIO_PCAP_MTU;
> +}
> +
> +static int pcapif_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
> + void *mac_addr)
> +{
> + /* this isn't used for anything, it's just here to keep callers of this
> + * API happy in cases where the pcap interface is used as a test mock */
> + static const char pcap_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x02};
> +
> + memcpy(mac_addr, pcap_mac, ODPH_ETHADDR_LEN);
> +
> + return ODPH_ETHADDR_LEN;
> +}
Isn't returning 0 the appropriate thing to do?
<0 is failure but 0 just means the mac address is NULL sized.
> +
> +static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry,
> + odp_bool_t enable)
> +{
> + pktio_entry->s.pkt_pcap.promisc = enable;
> +
> + return 0;
> +}
> +
> +static int pcapif_promisc_mode_get(pktio_entry_t *pktio_entry)
> +{
> + return pktio_entry->s.pkt_pcap.promisc;
> +}
> +
> +static int pcapif_start(pktio_entry_t *pktio_entry)
> +{
> + pktio_entry->s.state = STATE_START;
> + return 0;
> +}
> +
> +static int pcapif_stop(pktio_entry_t *pktio_entry)
> +{
> + pktio_entry->s.state = STATE_STOP;
> + return 0;
> +}
> +
Now that 45259bf4d4b0390d8baad03e942c0f49206643e6 (linux-generic: pktio: factor
state management into packet_io) made it in,
changing state inside the pktio implementation is not useful anymore. You can
even drop the start/stop function.
However it's not problematic so it could be done in a later patch.
> +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,
> + .start = pcapif_start,
> + .stop = pcapif_stop
> +};
_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp