From 4c173302c307ba5b7c2c4368cd59f2d12866378b Mon Sep 17 00:00:00 2001 From: Intel Date: Wed, 18 Sep 2013 12:00:00 +0200 Subject: [PATCH] pcap: add new driver This PMD uses libpcap to send/receive packets to/from any NIC. It can also read/write to/from a file. Signed-off-by: Intel --- app/test-pmd/Makefile | 4 + config/defconfig_i686-default-linuxapp-gcc | 6 + config/defconfig_i686-default-linuxapp-icc | 5 + config/defconfig_x86_64-default-linuxapp-gcc | 5 + config/defconfig_x86_64-default-linuxapp-icc | 5 + lib/Makefile | 1 + .../common/eal_common_nonpci_devs.c | 9 + lib/librte_eal/common/eal_common_whitelist.c | 6 + lib/librte_eal/linuxapp/eal/Makefile | 1 + lib/librte_pmd_pcap/Makefile | 58 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 731 ++++++++++++++++++ lib/librte_pmd_pcap/rte_eth_pcap.h | 66 ++ lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.c | 255 ++++++ lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.h | 71 ++ mk/rte.app.mk | 4 + 15 files changed, 1227 insertions(+) create mode 100644 lib/librte_pmd_pcap/Makefile create mode 100644 lib/librte_pmd_pcap/rte_eth_pcap.c create mode 100644 lib/librte_pmd_pcap/rte_eth_pcap.h create mode 100644 lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.c create mode 100644 lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.h diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile index 9c61949454..8f7e579a85 100644 --- a/app/test-pmd/Makefile +++ b/app/test-pmd/Makefile @@ -39,6 +39,10 @@ APP = testpmd CFLAGS += -O3 CFLAGS += $(WERROR_FLAGS) +ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) +LDFLAGS += -lpcap +endif + # # all source are stored in SRCS-y # diff --git a/config/defconfig_i686-default-linuxapp-gcc b/config/defconfig_i686-default-linuxapp-gcc index 3d23f4c896..ebae2730a9 100644 --- a/config/defconfig_i686-default-linuxapp-gcc +++ b/config/defconfig_i686-default-linuxapp-gcc @@ -175,6 +175,12 @@ CONFIG_RTE_LIBRTE_PMD_RING=y CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# +# Compile software PMD backed by PCAP files +# +CONFIG_RTE_LIBRTE_PMD_PCAP=n + + # # Compile librte_ring # diff --git a/config/defconfig_i686-default-linuxapp-icc b/config/defconfig_i686-default-linuxapp-icc index 4fd26b71a0..bdbbb3e1b9 100644 --- a/config/defconfig_i686-default-linuxapp-icc +++ b/config/defconfig_i686-default-linuxapp-icc @@ -176,6 +176,11 @@ CONFIG_RTE_LIBRTE_PMD_RING=y CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# +# Compile software PMD backed by PCAP files +# +CONFIG_RTE_LIBRTE_PMD_PCAP=n + # # Compile librte_ring # diff --git a/config/defconfig_x86_64-default-linuxapp-gcc b/config/defconfig_x86_64-default-linuxapp-gcc index 077ad81a43..e0f4183b13 100644 --- a/config/defconfig_x86_64-default-linuxapp-gcc +++ b/config/defconfig_x86_64-default-linuxapp-gcc @@ -182,6 +182,11 @@ CONFIG_RTE_LIBRTE_PMD_RING=y CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# +# Compile software PMD backed by PCAP files +# +CONFIG_RTE_LIBRTE_PMD_PCAP=n + # # Do prefetch of packet data within PMD driver receive function # diff --git a/config/defconfig_x86_64-default-linuxapp-icc b/config/defconfig_x86_64-default-linuxapp-icc index 418f0ad1b4..f01eb0417d 100644 --- a/config/defconfig_x86_64-default-linuxapp-icc +++ b/config/defconfig_x86_64-default-linuxapp-icc @@ -171,6 +171,11 @@ CONFIG_RTE_LIBRTE_PMD_RING=y CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# +# Compile software PMD backed by PCAP files +# +CONFIG_RTE_LIBRTE_PMD_PCAP=n + # # Do prefetch of packet data within PMD driver receive function # diff --git a/lib/Makefile b/lib/Makefile index d0931e3f89..59f96c927d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -43,6 +43,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether DIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += librte_pmd_e1000 DIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += librte_pmd_ixgbe DIRS-$(CONFIG_RTE_LIBRTE_PMD_RING) += librte_pmd_ring +DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net diff --git a/lib/librte_eal/common/eal_common_nonpci_devs.c b/lib/librte_eal/common/eal_common_nonpci_devs.c index e4ef25be7b..b5867f4b26 100644 --- a/lib/librte_eal/common/eal_common_nonpci_devs.c +++ b/lib/librte_eal/common/eal_common_nonpci_devs.c @@ -36,6 +36,9 @@ #ifdef RTE_LIBRTE_PMD_RING #include #endif +#ifdef RTE_LIBRTE_PMD_PCAP +#include +#endif #include "eal_private.h" struct device_init { @@ -50,6 +53,12 @@ struct device_init dev_types[] = { .dev_prefix = RTE_ETH_RING_PARAM_NAME, .init_fn = rte_pmd_ring_init }, +#endif +#ifdef RTE_LIBRTE_PMD_PCAP + { + .dev_prefix = RTE_ETH_PCAP_PARAM_NAME, + .init_fn = rte_pmd_pcap_init + }, #endif { .dev_prefix = "-nodev-", diff --git a/lib/librte_eal/common/eal_common_whitelist.c b/lib/librte_eal/common/eal_common_whitelist.c index d5fdd3e4c2..ccfdad22d4 100644 --- a/lib/librte_eal/common/eal_common_whitelist.c +++ b/lib/librte_eal/common/eal_common_whitelist.c @@ -48,6 +48,9 @@ #ifdef RTE_LIBRTE_PMD_RING #include #endif +#ifdef RTE_LIBRTE_PMD_PCAP +#include +#endif #include "eal_private.h" static char dev_list_str[4096]; @@ -96,6 +99,9 @@ is_valid_wl_entry(const char *device_str, size_t dev_buf_len) static const char *non_pci_prefixes[] = { #ifdef RTE_LIBRTE_PMD_RING RTE_ETH_RING_PARAM_NAME, +#endif +#ifdef RTE_LIBRTE_PMD_PCAP + RTE_ETH_PCAP_PARAM_NAME, #endif "-nodev-" /* dummy value to prevent compiler warnings */ }; diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index b1f6e03381..9f9052eff3 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -42,6 +42,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_mempool CFLAGS += -I$(RTE_SDK)/lib/librte_malloc CFLAGS += -I$(RTE_SDK)/lib/librte_ether CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_ring +CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_pcap CFLAGS += $(WERROR_FLAGS) -O3 # specific to linuxapp exec-env diff --git a/lib/librte_pmd_pcap/Makefile b/lib/librte_pmd_pcap/Makefile new file mode 100644 index 0000000000..5e6088126d --- /dev/null +++ b/lib/librte_pmd_pcap/Makefile @@ -0,0 +1,58 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2013 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_pcap.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += rte_eth_pcap.c +SRCS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += rte_eth_pcap_arg_parser.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_pcap.h +SYMLINK-y-include += rte_eth_pcap_arg_parser.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mbuf lib/librte_ether + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c new file mode 100644 index 0000000000..50de8850ab --- /dev/null +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -0,0 +1,731 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rte_eth_pcap.h" +#include "rte_eth_pcap_arg_parser.h" + +#define RTE_ETH_PCAP_SNAPSHOT_LEN 65535 +#define RTE_ETH_PCAP_SNAPLEN 4096 +#define RTE_ETH_PCAP_PROMISC 1 +#define RTE_ETH_PCAP_TIMEOUT -1 +#define RTE_ETH_PCAP_MBUFS 64 +#define ETH_PCAP_RX_PCAP_ARG "rx_pcap" +#define ETH_PCAP_TX_PCAP_ARG "tx_pcap" +#define ETH_PCAP_RX_IFACE_ARG "rx_iface" +#define ETH_PCAP_TX_IFACE_ARG "tx_iface" +#define ETH_PCAP_IFACE_ARG "iface" + +static char errbuf[PCAP_ERRBUF_SIZE]; +static struct timeval start_time; +static uint64_t start_cycles; +static uint64_t hz; + +struct pcap_rx_queue { + pcap_t *pcap; + struct rte_mempool *mb_pool; + volatile unsigned long rx_pkts; + volatile unsigned long err_pkts; +}; + +struct pcap_tx_queue { + pcap_dumper_t *dumper; + pcap_t *pcap; + volatile unsigned long tx_pkts; + volatile unsigned long err_pkts; +}; + +struct rx_pcaps { + unsigned num_of_rx; + pcap_t *pcaps[RTE_PMD_RING_MAX_RX_RINGS]; +}; + +struct tx_pcaps { + unsigned num_of_tx; + pcap_dumper_t *dumpers[RTE_PMD_RING_MAX_TX_RINGS]; + pcap_t *pcaps[RTE_PMD_RING_MAX_RX_RINGS]; +}; + +struct pmd_internals { + unsigned nb_rx_queues; + unsigned nb_tx_queues; + + struct pcap_rx_queue rx_queue[RTE_PMD_RING_MAX_RX_RINGS]; + struct pcap_tx_queue tx_queue[RTE_PMD_RING_MAX_TX_RINGS]; +}; + +const char *valid_arguments[] = { + ETH_PCAP_RX_PCAP_ARG, + ETH_PCAP_TX_PCAP_ARG, + ETH_PCAP_RX_IFACE_ARG, + ETH_PCAP_TX_IFACE_ARG, + ETH_PCAP_IFACE_ARG, + NULL +}; + +static struct ether_addr eth_addr = { .addr_bytes = { 0, 0, 0, 0x1, 0x2, 0x3 } }; +static const char *drivername = "Pcap PMD"; +static struct rte_eth_link pmd_link = { + .link_speed = 10000, + .link_duplex = ETH_LINK_FULL_DUPLEX, + .link_status = 0 +}; + + +static uint16_t +eth_pcap_rx(void *queue, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + unsigned i; + struct pcap_pkthdr header; + const u_char *packet; + struct rte_mbuf *mbuf; + static struct rte_mbuf *mbufs[RTE_ETH_PCAP_MBUFS] = { 0 }; + struct pcap_rx_queue *pcap_q = queue; + uint16_t num_rx = 0; + + if (unlikely(pcap_q->pcap == NULL || nb_pkts == 0)) + return 0; + + if(unlikely(!mbufs[0])) + for (i = 0; i < RTE_ETH_PCAP_MBUFS; i++) + mbufs[i] = rte_pktmbuf_alloc(pcap_q->mb_pool); + + /* Reads the given number of packets from the pcap file one by one + * and copies the packet data into a newly allocated mbuf to return. + */ + for (i = 0; i < nb_pkts; i++) { + mbuf = mbufs[i % RTE_ETH_PCAP_MBUFS]; + packet = pcap_next(pcap_q->pcap, &header); + if (unlikely(packet == NULL)) + break; + if (unlikely(mbuf == NULL)) + break; + rte_memcpy(mbuf->pkt.data, packet, header.len); + mbuf->pkt.data_len = (uint16_t)header.len; + mbuf->pkt.pkt_len = mbuf->pkt.data_len; + bufs[i] = mbuf; + num_rx++; + } + pcap_q->rx_pkts += num_rx; + return num_rx; +} + +static inline void +calculate_timestamp(struct timeval *ts) { + uint64_t cycles; + struct timeval cur_time; + + cycles = rte_get_timer_cycles() - start_cycles; + cur_time.tv_sec = cycles / hz; + cur_time.tv_usec = (cycles % hz) * 10e6 / hz; + timeradd(&start_time, &cur_time, ts); +} + +/* + * Callback to handle writing packets to a pcap file. + */ +static uint16_t +eth_pcap_tx_dumper(void *queue, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + unsigned i; + struct rte_mbuf *mbuf; + struct pcap_tx_queue *dumper_q = queue; + uint16_t num_tx = 0; + struct pcap_pkthdr header; + + if (dumper_q->dumper == NULL || nb_pkts == 0) + return 0; + + /* writes the nb_pkts packets to the previously opened pcap file dumper */ + for (i = 0; i < nb_pkts; i++) { + mbuf = bufs[i]; + calculate_timestamp(&header.ts); + header.len = mbuf->pkt.data_len; + header.caplen = header.len; + pcap_dump((u_char*) dumper_q->dumper, &header, mbuf->pkt.data); + rte_pktmbuf_free(mbuf); + num_tx++; + } + + /* + * Since there's no place to hook a callback when the forwarding + * process stops and to make sure the pcap file is actually written, + * we flush the pcap dumper within each burst. + */ + pcap_dump_flush(dumper_q->dumper); + dumper_q->tx_pkts += num_tx; + dumper_q->err_pkts += nb_pkts - num_tx; + return num_tx; +} + +/* + * Callback to handle sending packets through a real NIC. + */ +static uint16_t +eth_pcap_tx(void *queue, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + unsigned i; + int ret; + struct rte_mbuf *mbuf; + struct pcap_tx_queue *tx_queue = queue; + uint16_t num_tx = 0; + + if (unlikely(nb_pkts == 0 || tx_queue->pcap == NULL)) + return 0; + + for (i = 0; i < nb_pkts; i++) { + mbuf = bufs[i]; + ret = pcap_sendpacket(tx_queue->pcap, (u_char*) mbuf->pkt.data, + mbuf->pkt.data_len); + if(likely(!ret)) + num_tx++; + rte_pktmbuf_free(mbuf); + } + + tx_queue->tx_pkts += num_tx; + tx_queue->err_pkts += nb_pkts - num_tx; + return num_tx; +} + +static int +eth_dev_start(struct rte_eth_dev *dev) +{ + dev->data->dev_link.link_status = 1; + return 0; +} + +/* + * This function gets called when the current port gets stopped. + * Is the only place for us to close all the tx streams dumpers. + * If not called the dumpers will be flushed within each tx burst. + */ +static void +eth_dev_stop(struct rte_eth_dev *dev) +{ + unsigned i; + pcap_dumper_t *dumper; + pcap_t *pcap; + struct pmd_internals *internals = dev->data->dev_private; + + for (i = 0; i < internals->nb_tx_queues; i++) { + dumper = internals->tx_queue[i].dumper; + if(dumper != NULL) + pcap_dump_close(dumper); + pcap = internals->tx_queue[i].pcap; + if(pcap != NULL) + pcap_close(pcap); + } + + dev->data->dev_link.link_status = 0; +} + +static int +eth_dev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +eth_dev_info(struct rte_eth_dev *dev, + struct rte_eth_dev_info *dev_info) +{ + struct pmd_internals *internals = dev->data->dev_private; + dev_info->driver_name = drivername; + dev_info->max_mac_addrs = 1; + dev_info->max_rx_pktlen = (uint32_t) -1; + dev_info->max_rx_queues = (uint16_t)internals->nb_rx_queues; + dev_info->max_tx_queues = (uint16_t)internals->nb_tx_queues; + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static void +eth_stats_get(struct rte_eth_dev *dev, + struct rte_eth_stats *igb_stats) +{ + unsigned i; + unsigned long rx_total = 0, tx_total = 0, tx_err_total = 0; + const struct pmd_internals *internal = dev->data->dev_private; + + memset(igb_stats, 0, sizeof(*igb_stats)); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS && i < internal->nb_rx_queues; + i++) { + igb_stats->q_ipackets[i] = internal->rx_queue[i].rx_pkts; + rx_total += igb_stats->q_ipackets[i]; + } + + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS && i < internal->nb_tx_queues; + i++) { + igb_stats->q_opackets[i] = internal->tx_queue[i].tx_pkts; + igb_stats->q_errors[i] = internal->tx_queue[i].err_pkts; + tx_total += igb_stats->q_opackets[i]; + tx_err_total += igb_stats->q_errors[i]; + } + + igb_stats->ipackets = rx_total; + igb_stats->opackets = tx_total; + igb_stats->oerrors = tx_err_total; +} + +static void +eth_stats_reset(struct rte_eth_dev *dev) +{ + unsigned i; + struct pmd_internals *internal = dev->data->dev_private; + for (i = 0; i < internal->nb_rx_queues; i++) + internal->rx_queue[i].rx_pkts = 0; + for (i = 0; i < internal->nb_tx_queues; i++) { + internal->tx_queue[i].tx_pkts = 0; + internal->tx_queue[i].err_pkts = 0; + } +} + +static void +eth_dev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static void +eth_queue_release(void *q __rte_unused) +{ +} + +static int +eth_link_update(struct rte_eth_dev *dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return 0; +} + +static int +eth_rx_queue_setup(struct rte_eth_dev *dev, + uint16_t rx_queue_id, + uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool) +{ + struct pmd_internals *internals = dev->data->dev_private; + struct pcap_rx_queue *pcap_q = &internals->rx_queue[rx_queue_id]; + pcap_q->mb_pool = mb_pool; + dev->data->rx_queues[rx_queue_id] = pcap_q; + return 0; +} + +static int +eth_tx_queue_setup(struct rte_eth_dev *dev, + uint16_t tx_queue_id, + uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + + struct pmd_internals *internals = dev->data->dev_private; + dev->data->tx_queues[tx_queue_id] = &internals->tx_queue[tx_queue_id]; + return 0; +} + +static struct eth_dev_ops ops = { + .dev_start = eth_dev_start, + .dev_stop = eth_dev_stop, + .dev_close = eth_dev_close, + .dev_configure = eth_dev_configure, + .dev_infos_get = eth_dev_info, + .rx_queue_setup = eth_rx_queue_setup, + .tx_queue_setup = eth_tx_queue_setup, + .rx_queue_release = eth_queue_release, + .tx_queue_release = eth_queue_release, + .link_update = eth_link_update, + .stats_get = eth_stats_get, + .stats_reset = eth_stats_reset, +}; + +/* + * Function handler that opens the pcap file for reading a stores a + * reference of it for use it later on. + */ +static int +open_rx_pcap(char *value, void *extra_args) +{ + unsigned i; + char *pcap_filename = value; + struct rx_pcaps *pcaps = extra_args; + pcap_t *rx_pcap; + + for (i = 0; i < pcaps->num_of_rx; i++) { + if ((rx_pcap = pcap_open_offline(pcap_filename, errbuf)) == NULL) { + RTE_LOG(ERR, PMD, "Couldn't open %s: %s\n", pcap_filename, errbuf); + return -1; + } + pcaps->pcaps[i] = rx_pcap; + } + + return 0; +} + +/* + * Opens a pcap file for writing and stores a reference to it + * for use it later on. + */ +static int +open_tx_pcap(char *value, void *extra_args) +{ + unsigned i; + char *pcap_filename = value; + struct tx_pcaps *dumpers = extra_args; + pcap_t *tx_pcap; + pcap_dumper_t *dumper; + + for (i = 0; i < dumpers->num_of_tx; i++) { + /* + * We need to create a dummy empty pcap_t to use it + * with pcap_dump_open(). We create big enough an Ethernet + * pcap holder. + */ + if ((tx_pcap = pcap_open_dead(DLT_EN10MB, RTE_ETH_PCAP_SNAPSHOT_LEN)) + == NULL) { + RTE_LOG(ERR, PMD, "Couldn't create dead pcap\n"); + return -1; + } + + /* The dumper is created using the previous pcap_t reference */ + if ((dumper = pcap_dump_open(tx_pcap, pcap_filename)) == NULL) { + RTE_LOG(ERR, PMD, "Couldn't open %s for writing.\n", pcap_filename); + return -1; + } + dumpers->dumpers[i] = dumper; + } + + return 0; +} + +/* + * pcap_open_live wrapper function + */ +static inline int +open_iface_live(const char *iface, pcap_t **pcap) { + *pcap = pcap_open_live(iface, RTE_ETH_PCAP_SNAPLEN, + RTE_ETH_PCAP_PROMISC, RTE_ETH_PCAP_TIMEOUT, errbuf); + + if (*pcap == NULL) { + RTE_LOG(ERR, PMD, "Couldn't open %s: %s\n", iface, errbuf); + return -1; + } + return 0; +} + +/* + * Opens an interface for reading and writing + */ +static inline int +open_rx_tx_iface(char *value, void *extra_args) +{ + const char *iface = value; + pcap_t **pcap = extra_args; + + if(open_iface_live(iface, pcap) < 0) + return -1; + return 0; +} + +/* + * Opens a NIC for reading packets from it + */ +static inline int +open_rx_iface(char *value, void *extra_args) +{ + unsigned i; + const char *iface = value; + struct rx_pcaps *pcaps = extra_args; + pcap_t *pcap = NULL; + + for (i = 0; i < pcaps->num_of_rx; i++) { + if(open_iface_live(iface, &pcap) < 0) + return -1; + pcaps->pcaps[i] = pcap; + } + + return 0; +} + +/* + * Opens a NIC for writing packets to it + */ +static inline int +open_tx_iface(char *value, void *extra_args) +{ + unsigned i; + const char *iface = value; + struct tx_pcaps *pcaps = extra_args; + pcap_t *pcap; + + for (i = 0; i < pcaps->num_of_tx; i++) { + if(open_iface_live(iface, &pcap) < 0) + return -1; + pcaps->pcaps[i] = pcap; + } + + return 0; +} + + +static int +rte_pmd_init_internals(const unsigned nb_rx_queues, + const unsigned nb_tx_queues, + const unsigned numa_node, + struct pmd_internals **internals, + struct rte_eth_dev **eth_dev) +{ + struct rte_eth_dev_data *data = NULL; + struct rte_pci_device *pci_dev = NULL; + + RTE_LOG(INFO, PMD, + "Creating pcap-backed ethdev on numa socket %u\n", numa_node); + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + if (data == NULL) + goto error; + + pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + if (pci_dev == NULL) + goto error; + + *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + if (*internals == NULL) + goto error; + + /* reserve an ethdev entry */ + *eth_dev = rte_eth_dev_allocate(); + if (*eth_dev == NULL) + goto error; + + /* now put it all together + * - store queue data in internals, + * - store numa_node info in pci_driver + * - point eth_dev_data to internals and pci_driver + * - and point eth_dev structure to new eth_dev_data structure + */ + /* NOTE: we'll replace the data element, of originally allocated eth_dev + * so the rings are local per-process */ + + (*internals)->nb_rx_queues = nb_rx_queues; + (*internals)->nb_tx_queues = nb_tx_queues; + + pci_dev->numa_node = numa_node; + + data->dev_private = *internals; + data->port_id = (*eth_dev)->data->port_id; + data->nb_rx_queues = (uint16_t)nb_rx_queues; + data->nb_tx_queues = (uint16_t)nb_tx_queues; + data->dev_link = pmd_link; + data->mac_addrs = ð_addr; + + (*eth_dev)->data = data; + (*eth_dev)->dev_ops = &ops; + (*eth_dev)->pci_dev = pci_dev; + + return 0; + + error: if (data) + rte_free(data); + if (pci_dev) + rte_free(pci_dev); + if (*internals) + rte_free(*internals); + return -1; +} + +int +rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], + const unsigned nb_rx_queues, + pcap_dumper_t * const tx_queues[], + const unsigned nb_tx_queues, + const unsigned numa_node) +{ + struct pmd_internals *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + unsigned i; + + /* do some parameter checking */ + if (rx_queues == NULL && nb_rx_queues > 0) + return -1; + if (tx_queues == NULL && nb_tx_queues > 0) + return -1; + + if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + &internals, ð_dev) < 0) + return -1; + + for (i = 0; i < nb_rx_queues; i++) { + internals->rx_queue->pcap = rx_queues[i]; + } + for (i = 0; i < nb_tx_queues; i++) { + internals->tx_queue->dumper = tx_queues[i]; + } + + eth_dev->rx_pkt_burst = eth_pcap_rx; + eth_dev->tx_pkt_burst = eth_pcap_tx_dumper; + + return 0; +} + +int +rte_eth_from_pcaps(pcap_t * const rx_queues[], + const unsigned nb_rx_queues, + pcap_t * const tx_queues[], + const unsigned nb_tx_queues, + const unsigned numa_node) +{ + struct pmd_internals *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + unsigned i; + + /* do some parameter checking */ + if (rx_queues == NULL && nb_rx_queues > 0) + return -1; + if (tx_queues == NULL && nb_tx_queues > 0) + return -1; + + if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + &internals, ð_dev) < 0) + return -1; + + for (i = 0; i < nb_rx_queues; i++) { + internals->rx_queue->pcap = rx_queues[i]; + } + for (i = 0; i < nb_tx_queues; i++) { + internals->tx_queue->pcap = tx_queues[i]; + } + + eth_dev->rx_pkt_burst = eth_pcap_rx; + eth_dev->tx_pkt_burst = eth_pcap_tx; + + return 0; +} + + +int +rte_pmd_pcap_init(const char *name, const char *params) +{ + unsigned numa_node, using_dumpers = 0; + int ret; + struct args_dict dict; + struct rx_pcaps pcaps; + struct tx_pcaps dumpers; + + rte_eth_pcap_init_args_dict(&dict); + + numa_node = rte_socket_id(); + + gettimeofday(&start_time, NULL); + start_cycles = rte_get_timer_cycles(); + hz = rte_get_timer_hz(); + + if (rte_eth_pcap_parse_args(&dict, name, params, valid_arguments) < 0) + return -1; + + /* + * If iface argument is passed we open the NICs and use them for + * reading / writing + */ + if (rte_eth_pcap_num_of_args(&dict, ETH_PCAP_IFACE_ARG) == 1) { + + ret = rte_eth_pcap_post_process_arguments(&dict, ETH_PCAP_IFACE_ARG, + &open_rx_tx_iface, &pcaps.pcaps[0]); + if (ret < 0) + return -1; + + return rte_eth_from_pcaps(pcaps.pcaps, 1, pcaps.pcaps, 1, numa_node); + } + + /* + * We check whether we want to open a RX stream from a real NIC or a + * pcap file + */ + if ((pcaps.num_of_rx = rte_eth_pcap_num_of_args(&dict, ETH_PCAP_RX_PCAP_ARG))) { + ret = rte_eth_pcap_post_process_arguments(&dict, ETH_PCAP_RX_PCAP_ARG, + &open_rx_pcap, &pcaps); + } else { + pcaps.num_of_rx = rte_eth_pcap_num_of_args(&dict, + ETH_PCAP_RX_IFACE_ARG); + ret = rte_eth_pcap_post_process_arguments(&dict, ETH_PCAP_RX_IFACE_ARG, + &open_rx_iface, &pcaps); + } + + if (ret < 0) + return -1; + + /* + * We check whether we want to open a TX stream to a real NIC or a + * pcap file + */ + if ((dumpers.num_of_tx = rte_eth_pcap_num_of_args(&dict, + ETH_PCAP_TX_PCAP_ARG))) { + ret = rte_eth_pcap_post_process_arguments(&dict, ETH_PCAP_TX_PCAP_ARG, + &open_tx_pcap, &dumpers); + using_dumpers = 1; + } else { + dumpers.num_of_tx = rte_eth_pcap_num_of_args(&dict, + ETH_PCAP_TX_IFACE_ARG); + ret = rte_eth_pcap_post_process_arguments(&dict, ETH_PCAP_TX_IFACE_ARG, + &open_tx_iface, &dumpers); + } + + if (ret < 0) + return -1; + + if (using_dumpers) + return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + dumpers.dumpers, dumpers.num_of_tx, numa_node); + + return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + dumpers.num_of_tx, numa_node); + +} + diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.h b/lib/librte_pmd_pcap/rte_eth_pcap.h new file mode 100644 index 0000000000..7ed68b18a1 --- /dev/null +++ b/lib/librte_pmd_pcap/rte_eth_pcap.h @@ -0,0 +1,66 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_PCAP_H_ +#define _RTE_ETH_PCAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#define RTE_ETH_PCAP_PARAM_NAME "eth_pcap" + +int rte_eth_from_pcaps(pcap_t * const rx_queues[], + const unsigned nb_rx_queues, + pcap_t * const tx_queues[], + const unsigned nb_tx_queues, + const unsigned numa_node); + +int rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], + const unsigned nb_rx_queues, + pcap_dumper_t * const tx_queues[], + const unsigned nb_tx_queues, + const unsigned numa_node); + +/** + * For use by the EAL only. Called as part of EAL init to set up any dummy NICs + * configured on command line. + */ +int rte_pmd_pcap_init(const char *name, const char *params); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.c b/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.c new file mode 100644 index 0000000000..c881f3b2b3 --- /dev/null +++ b/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.c @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include + +#include +#include +#include + +#include "rte_eth_pcap_arg_parser.h" + +/* + * Initializes a non NULL dictionary reference to be used later on. + */ +inline int +rte_eth_pcap_init_args_dict(struct args_dict *dict) +{ + dict->index = 0; + dict->size = RTE_ETH_PCAP_ARG_PARSER_MAX_ARGS; + memset(dict->pairs, 0, dict->size); + return 0; +} + +/* + * Adds a key-value pair to a given non-NULL dictionary reference. + * The final key will be the name+key. + * Returns error in case the dictionary is full or if the key is duplicated. + */ +inline int +rte_eth_pcap_add_pair_to_dict(struct args_dict *dict, + char *key, + char *val) +{ + unsigned i; + struct key_value* entry; + + /* is the dictionary full? */ + if (dict->index >= dict->size) { + RTE_LOG(ERR, PMD, "Couldn't add %s, dictionary is full\n", key); + return -1; + } + + /* Check if the key is duplicated */ + for (i = 0; i < dict->index; i++) { + entry = &dict->pairs[i]; + if (strcmp(entry->key, key) == 0) { + RTE_LOG(ERR, PMD, "Couldn't add %s, duplicated key\n", key); + return -1; + } + } + + entry = &dict->pairs[dict->index]; + entry->key = key; + entry->value = val; + dict->index++; + return 0; + +} + +#define RTE_ETH_PCAP_PAIRS_DELIM ';' +#define RTE_ETH_PCAP_KEY_VALUE_DELIM '=' +/* + * Receives a string with a list of arguments following the pattern + * key=value;key=value;... and inserts them into the non NULL dictionary. + * strtok is used so the params string will be copied to be modified. + */ +inline int +rte_eth_pcap_tokenize_args(struct args_dict *dict, + const char *name, + const char *params) +{ + int i; + char *args; + char *pairs[RTE_ETH_PCAP_ARG_PARSER_MAX_ARGS]; + char *pair[2]; + int num_of_pairs; + + /* If params are empty, nothing to do */ + if (params == NULL || params[0] == 0) { + RTE_LOG(ERR, PMD, "Couldn't parse %s device, empty arguments\n", name); + return -1; + } + + /* Copy the const char *params to a modifiable string + * to pass to rte_strsplit + */ + args = strdup(params); + if(args == NULL){ + RTE_LOG(ERR, PMD, "Couldn't parse %s device \n", name); + return -1; + } + + num_of_pairs = rte_strsplit(args, strnlen(args, MAX_ARG_STRLEN), pairs, + RTE_ETH_PCAP_ARG_PARSER_MAX_ARGS, RTE_ETH_PCAP_PAIRS_DELIM); + + for (i = 0; i < num_of_pairs; i++) { + pair[0] = NULL; + pair[1] = NULL; + + rte_strsplit(pairs[i], strnlen(pairs[i], MAX_ARG_STRLEN), pair, 2, + RTE_ETH_PCAP_KEY_VALUE_DELIM); + + if (pair[0] == NULL || pair[1] == NULL || pair[0][0] == 0 + || pair[1][0] == 0) { + RTE_LOG(ERR, PMD, + "Couldn't parse %s device, wrong key or value \n", name); + goto error; + } + + if (rte_eth_pcap_add_pair_to_dict(dict, pair[0], pair[1]) < 0) + goto error; + } + return 0; + +error: + rte_free(args); + return -1; +} + +/* + * Determines whether a key is valid or not by looking + * into a list of valid keys. + */ +static inline int +is_valid_key(const char *valid[], + struct key_value *pair) +{ + const char **valid_ptr; + + for (valid_ptr = valid; *valid_ptr != NULL; valid_ptr++) + if (strstr(pair->key, *valid_ptr) != NULL) + return 1; + return 0; +} + +/* + * Determines whether all keys are valid or not by looking + * into a list of valid keys. + */ +static inline int +check_for_valid_keys(struct args_dict *dict, + const char *valid[]) +{ + unsigned k_index, ret; + struct key_value *pair; + + for (k_index = 0; k_index < dict->index; k_index++) { + pair = &dict->pairs[k_index]; + ret = is_valid_key(valid, pair); + if (!ret) { + RTE_LOG(ERR, PMD, + "Error parsing device, invalid key %s\n", pair->key); + return -1; + } + } + return 0; +} + +/* + * Returns the number of times a given arg_name exists on a dictionary. + * E.g. given a dict = { rx0 = 0, rx1 = 1, tx0 = 2 } the number of args for + * arg "rx" will be 2. + */ +inline unsigned +rte_eth_pcap_num_of_args(struct args_dict *dict, const char *arg_name) +{ + unsigned k_index; + struct key_value *pair; + unsigned num_of_keys; + + num_of_keys = 0; + for (k_index = 0; k_index < dict->index; k_index++) { + pair = &dict->pairs[k_index]; + if (strcmp(pair->key, arg_name) == 0) + num_of_keys++; + } + + return num_of_keys; +} + +/* + * Calls the handler function for a given arg_name passing the + * value on the dictionary for that key and a given extra argument. + */ +inline int +rte_eth_pcap_post_process_arguments(struct args_dict *dict, + const char *arg_name, + arg_handler_t handler, + void *extra_args) +{ + unsigned k_index; + struct key_value *pair; + + for (k_index = 0; k_index < dict->index; k_index++) { + pair = &dict->pairs[k_index]; + if (strstr(pair->key, arg_name) != NULL) { + if ((*handler)(pair->value, extra_args) < 0) + return -1; + } + } + return 0; +} + +/* + * Parses the arguments "key=value;key=value;..." string and returns + * a simple dictionary implementation containing these pairs. It also + * checks if only valid keys were used. + */ +inline int +rte_eth_pcap_parse_args(struct args_dict *dict, + const char *name, + const char *args, + const char *valids[]) +{ + + int ret; + + ret = rte_eth_pcap_tokenize_args(dict, name, args); + if (ret < 0) + return ret; + + return check_for_valid_keys(dict, valids); +} + diff --git a/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.h b/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.h new file mode 100644 index 0000000000..47eb3a0508 --- /dev/null +++ b/lib/librte_pmd_pcap/rte_eth_pcap_arg_parser.h @@ -0,0 +1,71 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_ARG_PARSER_H_ +#define _RTE_ETH_ARG_PARSER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTE_ETH_PCAP_ARG_PARSER_MAX_ARGS 32 + +typedef int (*arg_handler_t)(char*, void*); + +struct key_value { + char *key; + char *value; +}; + +struct args_dict { + unsigned index; + size_t size; + struct key_value pairs[RTE_ETH_PCAP_ARG_PARSER_MAX_ARGS]; +}; + +int rte_eth_pcap_tokenize_args(struct args_dict *dict, const char *name, + const char *args); +int rte_eth_pcap_init_args_dict(struct args_dict *dict); +int rte_eth_pcap_add_pair_to_dict(struct args_dict *dict, char *key, char *val); +int rte_eth_pcap_parse_args(struct args_dict *dict, const char* name, + const char *args, const char *valids[]); +int rte_eth_pcap_post_process_arguments(struct args_dict *dict, + const char *arg_name, arg_handler_t handler, void *extra_args); +unsigned rte_eth_pcap_num_of_args(struct args_dict *dict, const char *key); +void rte_eth_pcap_free_dict(struct args_dict *dict); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index ea861cf934..324021f17b 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -145,6 +145,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_EAL),y) LDLIBS += -lrte_eal endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) +LDLIBS += -lrte_pmd_pcap -lpcap +endif + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 2.20.1