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

Reply via email to