diff mbox

[RFC] linux-generc: pktio: add pcap pktio type

Message ID 1438106069-14958-1-git-send-email-stuart.haslam@linaro.org
State New
Headers show

Commit Message

Stuart Haslam July 28, 2015, 5:54 p.m. UTC
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.haslam@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

Comments

Mike Holmes July 28, 2015, 7:43 p.m. UTC | #1
I like it.

Do we need a facility to compare output in some way to ensure correct
operation - even if it is just to test this interface and is not a bigger
part of regression ?
Maybe the test suite checks that pcap-diff [1] (or similar) results in zero
difference with a golden output file ?

For testing this interface or extending it to any test I assume we would
use the wrapper scripts to execute the test case passing in a pcap file and
the wrapper would check the result externally when the test case has
finished.

[1] https://github.com/isginf/pcap-diff

On 28 July 2015 at 13:54, Stuart Haslam <stuart.haslam@linaro.org> 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.haslam@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
>                                            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
> +};
> --
> 2.1.1
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
>
Bill Fischofer July 28, 2015, 9:06 p.m. UTC | #2
Agree.  Seems an elegant solution.  We should update the DEPENDENCIES files
if we're now going to require libpcap for a successful ODP build, as
currently we only call out libssl.

On Tue, Jul 28, 2015 at 2:43 PM, Mike Holmes <mike.holmes@linaro.org> wrote:

> I like it.
>
> Do we need a facility to compare output in some way to ensure correct
> operation - even if it is just to test this interface and is not a bigger
> part of regression ?
> Maybe the test suite checks that pcap-diff [1] (or similar) results in
> zero difference with a golden output file ?
>
> For testing this interface or extending it to any test I assume we would
> use the wrapper scripts to execute the test case passing in a pcap file and
> the wrapper would check the result externally when the test case has
> finished.
>
> [1] https://github.com/isginf/pcap-diff
>
> On 28 July 2015 at 13:54, Stuart Haslam <stuart.haslam@linaro.org> 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.haslam@linaro.org>
>
>
Reviewed-by: Bill Fischofer <bill.fischofer@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
>>                                            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
>> +};
>> --
>> 2.1.1
>>
>> _______________________________________________
>> lng-odp mailing list
>> lng-odp@lists.linaro.org
>> https://lists.linaro.org/mailman/listinfo/lng-odp
>>
>
>
>
> --
> Mike Holmes
> Technical Manager - Linaro Networking Group
> Linaro.org <http://www.linaro.org/> *│ *Open source software for ARM SoCs
>
>
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
>
>
Maxim Uvarov July 29, 2015, 9:58 a.m. UTC | #3
On 07/29/15 10:03, Nicolas Morey Chaisemartin wrote:
> Could we not have an optional dependency to pcap?
> OpenSSL is required for API reasons, but as this is a non standard 
> pktio, it seems a bit of a shame to force a dependency for this?
>
Nicolas, just to clarify. I think that libpcap exist almost everywhere 
and it's not difficult requirement to add libpcap.
If there is any platform where libpcap does not exist, then lets do it 
optional.

> Simply having a CONDITIONAL in configure/Makefile.am and adding 
> conditionnaly the pcap.c file. Plus small ifdef or weak trick for the 
> pktio ops structure.
>
> On a related note, there are several (OpenSSL, pthread and maybe pcap) 
> checks in the configure.ac. However these are very platform 
> (linux-generic) related.
> Would it not make sense to move all these checks in the 
> platform/<plat>/m4 directory?
>
Yes, all that checks should go to platform/linux-generic/m4 script.

> Nicolas
>
> ------------------------------------------------------------------------
>
>     *From: *"Bill Fischofer" <bill.fischofer@linaro.org>
>     *To: *"Mike Holmes" <mike.holmes@linaro.org>
>     *Cc: *"lng-odp" <lng-odp@lists.linaro.org>
>     *Sent: *Tuesday, 28 July, 2015 11:06:00 PM
>     *Subject: *Re: [lng-odp] [RFC PATCH] linux-generc: pktio: add pcap
>     pktio type
>
>     Agree.  Seems an elegant solution.  We should update the
>     DEPENDENCIES files if we're now going to require libpcap for a
>     successful ODP build, as currently we only call out libssl.
>
>     On Tue, Jul 28, 2015 at 2:43 PM, Mike Holmes
>     <mike.holmes@linaro.org <mailto:mike.holmes@linaro.org>> wrote:
>
>         I like it.
>
>         Do we need a facility to compare output in some way to ensure
>         correct operation - even if it is just to test this interface
>         and is not a bigger part of regression ?
>         Maybe the test suite checks that pcap-diff [1] (or similar)
>         results in zero difference with a golden output file ?
>
>         For testing this interface or extending it to any test I
>         assume we would use the wrapper scripts to execute the test
>         case passing in a pcap file and the wrapper would check the
>         result externally when the test case has finished.
>
>         [1] https://github.com/isginf/pcap-diff
>
>         On 28 July 2015 at 13:54, Stuart Haslam
>         <stuart.haslam@linaro.org <mailto:stuart.haslam@linaro.org>>
>         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.haslam@linaro.org
>             <mailto:stuart.haslam@linaro.org>>
>
>
>     Reviewed-by: Bill Fischofer <bill.fischofer@linaro.org
>     <mailto:bill.fischofer@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 <http://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 <http://configure.ac>
>             b/configure.ac <http://configure.ac>
>             index 2ea1368..2a58ba9 100644
>             --- a/configure.ac <http://configure.ac>
>             +++ b/configure.ac <http://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
>             <http://s.name>, IF_NAMESIZE, "%s", dev);
>             +               snprintf(pktio_entry->s.name
>             <http://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 <http://s.name>,
>             dev, IF_NAMESIZE) == 0)
>             +                   strncmp(entry->s.name <http://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
>             +};
>             --
>             2.1.1
>
>             _______________________________________________
>             lng-odp mailing list
>             lng-odp@lists.linaro.org <mailto:lng-odp@lists.linaro.org>
>             https://lists.linaro.org/mailman/listinfo/lng-odp
>
>
>
>
>         -- 
>         Mike Holmes
>         Technical Manager - Linaro Networking Group
>         Linaro.org <http://www.linaro.org/>***│ *Open source software
>         for ARM SoCs
>
>
>
>
>         _______________________________________________
>         lng-odp mailing list
>         lng-odp@lists.linaro.org <mailto:lng-odp@lists.linaro.org>
>         https://lists.linaro.org/mailman/listinfo/lng-odp
>
>
>
>     _______________________________________________
>     lng-odp mailing list
>     lng-odp@lists.linaro.org
>     https://lists.linaro.org/mailman/listinfo/lng-odp
>
>
>
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
Stuart Haslam July 29, 2015, 10:07 a.m. UTC | #4
On Wed, Jul 29, 2015 at 12:58:59PM +0300, Maxim Uvarov wrote:
> On 07/29/15 10:03, Nicolas Morey Chaisemartin wrote:
> >Could we not have an optional dependency to pcap?
> >OpenSSL is required for API reasons, but as this is a non standard
> >pktio, it seems a bit of a shame to force a dependency for this?
> >
> Nicolas, just to clarify. I think that libpcap exist almost
> everywhere and it's not difficult requirement to add libpcap.
> If there is any platform where libpcap does not exist, then lets do
> it optional.

It should not be difficult to make it conditional and I can't see any
downside, I'll do that before posting the non-RFC version.

> >Simply having a CONDITIONAL in configure/Makefile.am and adding
> >conditionnaly the pcap.c file. Plus small ifdef or weak trick for
> >the pktio ops structure.
> >
> >On a related note, there are several (OpenSSL, pthread and maybe
> >pcap) checks in the configure.ac. However these are very platform
> >(linux-generic) related.
> >Would it not make sense to move all these checks in the
> >platform/<plat>/m4 directory?
> >
> Yes, all that checks should go to platform/linux-generic/m4 script.
> 

I'll look at that.
Stuart Haslam July 29, 2015, 10:15 a.m. UTC | #5
On Tue, Jul 28, 2015 at 03:43:29PM -0400, Mike Holmes wrote:
> I like it.
> 
> Do we need a facility to compare output in some way to ensure correct
> operation - even if it is just to test this interface and is not a bigger
> part of regression ?
> Maybe the test suite checks that pcap-diff [1] (or similar) results in zero
> difference with a golden output file ?

Yeah that was the idea, I tested by comparing input and output files
using wireshark. Using the classifier example, if nothing in the trace
matches any of the rules then the output file is reported as being
identical (except for timestamps). If some of the packets in the input
trace match a rule, then they get reordered and wireshark reports this.

To compare in wireshark you load the first trace, then go to File->Merge
and load the second, then go to Statistics->Compare, then just click
Create Stat with the defaults. Obviously this can't be done from a
script.

I just tried pcap-diff but it crapped out as it didn't like the header
on the pcap file I'm using (one which wireshark is OK with), I tried it
on a different set of files with ~600k packets each and loading the
first one took 7G RAM so I killed it before it loaded the second.

I think it should be possible to do a simple missing / duplicate / reordered
check using the MD5 frame hash that you get from;

editcap -v -D 0 test.pcap /dev/null

> For testing this interface or extending it to any test I assume we would
> use the wrapper scripts to execute the test case passing in a pcap file and
> the wrapper would check the result externally when the test case has
> finished.

Yes something like that. In some cases it could be used directly in
place of an interface (as per the classifier example) but in others it
could perhaps just be used internally as a simple way of loading packets
from a pcap file (e.g. the generator could pre-charge a buffer pool).

--
Stuart.
Maxim Uvarov July 29, 2015, 10:23 a.m. UTC | #6
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.haslam@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
> +};
Stuart Haslam July 29, 2015, 11 a.m. UTC | #7
On Wed, Jul 29, 2015 at 01:23:20PM +0300, Maxim Uvarov wrote:
> 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.haslam@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.

As it is the generic code (odp_packet_io.c) uses PKTIO_NAME_LEN, the
socket code still uses IF_NAMESIZE and I checked that it does something
sane if passed a string that's > IF_NAMESIZE (it'll truncate using
snprintf then fail), so I think it's fine.

> And it will be good to send it in separate patch. Also

OK.

> 
> >  					   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.

This is linux-generic.. anyway I'll change it as below.

> 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
> 

Yeah I considered that and decided against, but in hindsight I agree it
seems better. It may also be useful to be able to specify only an output
file.

> 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?

I think just having it in the normal -help output is enough, which it's
missing from at the minute, but I'll add that.

> >+		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.

ACK

> >+
> >+	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

Yes. I didn't give any thought to the control funcs yet.

> make validation test passed.

It can't be used as a loopback device, I suppose it could be used if
the validation tests are run with a pair of interfaces defined as;

ODP_PKTIO_P0=pcap:test.pcap
ODP_PKTIO_P1=pcap:test_out.pcap

But I'd not really considered that, I'll give it a try (probably going
need some explicit flushing).

> >+
> >+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.

Yes.

> Also it will be good to code loop option to pcap interface.

I don't understand what you mean here.
Mike Holmes July 29, 2015, 11:01 a.m. UTC | #8
On 29 July 2015 at 06:47, Nicolas Morey Chaisemartin <nmorey@kalray.eu>
wrote:

> ----- Original Message -----
> > From: "Maxim Uvarov" <maxim.uvarov@linaro.org>
> > To: lng-odp@lists.linaro.org
> > Sent: Wednesday, 29 July, 2015 11:58:59 AM
> > Subject: Re: [lng-odp] [RFC PATCH] linux-generc: pktio: add pcap pktio
> type
> >
> > On 07/29/15 10:03, Nicolas Morey Chaisemartin wrote:
> > > Could we not have an optional dependency to pcap?
> > > OpenSSL is required for API reasons, but as this is a non standard
> > > pktio, it seems a bit of a shame to force a dependency for this?
> > >
> > Nicolas, just to clarify. I think that libpcap exist almost everywhere
> > and it's not difficult requirement to add libpcap.
> > If there is any platform where libpcap does not exist, then lets do it
> > optional.
> >
>
> It is very standard for linux running platform. But the configure file is
> reused by all platforms running ODP.
> As we don't run linux on most of our cores, we do not have pcap (nor
> socket) support for the moment.
>
> If the check is moved to the platform side, I guess it does not need to be
> conditional.
>

I think the interface needs to be optional at compile time.
But maybe we need to start thinking about the plugable interfaces/drivers
for the IO, then this becomes easier, we have had a few requests and use
cases show up for it now.



> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> https://lists.linaro.org/mailman/listinfo/lng-odp
>
Maxim Uvarov July 29, 2015, 11:14 a.m. UTC | #9
On 07/29/15 14:00, Stuart Haslam wrote:
> On Wed, Jul 29, 2015 at 01:23:20PM +0300, Maxim Uvarov wrote:
>> 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.haslam@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.
> As it is the generic code (odp_packet_io.c) uses PKTIO_NAME_LEN, the
> socket code still uses IF_NAMESIZE and I checked that it does something
> sane if passed a string that's > IF_NAMESIZE (it'll truncate using
> snprintf then fail), so I think it's fine.
>
>> And it will be good to send it in separate patch. Also
> OK.
>
>>>   					   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.
> This is linux-generic.. anyway I'll change it as below.
>
>> 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
>>
> Yeah I considered that and decided against, but in hindsight I agree it
> seems better. It may also be useful to be able to specify only an output
> file.
>
>> 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?
> I think just having it in the normal -help output is enough, which it's
> missing from at the minute, but I'll add that.
-help you can add only to specific example. But other examples will not 
support that -help output.
>>> +		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.
> ACK
>
>>> +
>>> +	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
> Yes. I didn't give any thought to the control funcs yet.
>
>> make validation test passed.
> It can't be used as a loopback device, I suppose it could be used if
> the validation tests are run with a pair of interfaces defined as;
>
> ODP_PKTIO_P0=pcap:test.pcap
> ODP_PKTIO_P1=pcap:test_out.pcap
>
> But I'd not really considered that, I'll give it a try (probably going
> need some explicit flushing).
>
>>> +
>>> +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.
> Yes.
>
>> Also it will be good to code loop option to pcap interface.
> I don't understand what you mean here.
>
you have 1Mb of pcap file. And you what it playback in the loop to 
reproduce some issue or load 20 workers
with that traffic.

Maxim.
Stuart Haslam July 29, 2015, 11:29 a.m. UTC | #10
On Wed, Jul 29, 2015 at 02:14:06PM +0300, Maxim Uvarov wrote:
> On 07/29/15 14:00, Stuart Haslam wrote:
> >On Wed, Jul 29, 2015 at 01:23:20PM +0300, Maxim Uvarov wrote:
> >>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.haslam@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.
> >As it is the generic code (odp_packet_io.c) uses PKTIO_NAME_LEN, the
> >socket code still uses IF_NAMESIZE and I checked that it does something
> >sane if passed a string that's > IF_NAMESIZE (it'll truncate using
> >snprintf then fail), so I think it's fine.
> >
> >>And it will be good to send it in separate patch. Also
> >OK.
> >
> >>>  					   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.
> >This is linux-generic.. anyway I'll change it as below.
> >
> >>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
> >>
> >Yeah I considered that and decided against, but in hindsight I agree it
> >seems better. It may also be useful to be able to specify only an output
> >file.
> >
> >>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?
> >I think just having it in the normal -help output is enough, which it's
> >missing from at the minute, but I'll add that.
> -help you can add only to specific example. But other examples will
> not support that -help output.

That's how it is at the minute, but we should really tidy all that mess
up as each of the examples duplicates a lot of argument parsing and help
printing.

> >>>+		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.
> >ACK
> >
> >>>+
> >>>+	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
> >Yes. I didn't give any thought to the control funcs yet.
> >
> >>make validation test passed.
> >It can't be used as a loopback device, I suppose it could be used if
> >the validation tests are run with a pair of interfaces defined as;
> >
> >ODP_PKTIO_P0=pcap:test.pcap
> >ODP_PKTIO_P1=pcap:test_out.pcap
> >
> >But I'd not really considered that, I'll give it a try (probably going
> >need some explicit flushing).
> >
> >>>+
> >>>+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.
> >Yes.
> >
> >>Also it will be good to code loop option to pcap interface.
> >I don't understand what you mean here.
> >
> you have 1Mb of pcap file. And you what it playback in the loop to
> reproduce some issue or load 20 workers
> with that traffic.

OK, I thought you were referring to loopback. Should be possible I
suppose.
diff mbox

Patch

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
+};