From patchwork Thu Mar 1 14:00:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Github ODP bot X-Patchwork-Id: 130230 Delivered-To: patch@linaro.org Received: by 10.80.172.228 with SMTP id x91csp2830863edc; Thu, 1 Mar 2018 06:00:44 -0800 (PST) X-Google-Smtp-Source: AG47ELuaCXCkmV+jpJB83ns85L6ovLVjmXJln1pAZ/8YliMOncQrrMRnlnZQqor7lpR/JLvSwRQx X-Received: by 10.129.109.133 with SMTP id i127mr1068033ywc.438.1519912844249; Thu, 01 Mar 2018 06:00:44 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519912844; cv=none; d=google.com; s=arc-20160816; b=mLcuHPWkGoHktQwU+1hpKdlJwRVDIbfgVNYZlY3K3mJ8Tigq5yIn4ehxCeYA3RdexL JLUVFhziGUnSdEBb7yNxToyVPrYRUKKn4G2iRJUCC5+VsAgCzuqF7bnZoSzGip4AC+lv TjB/m88Y27Tb798IFL3l5VuK/F5Bw8Xo5xeGGXS5QXRGJ12e9IRvzl16UP8ndXgEl4c8 H7BdXnYbilJOk3WiZ++UhyYtCx2RBvcchV5pDMvj2U9yTwG4EwW2aHtAb/d9T9gs4G6i zbQyDwJru6/OfNYNXi5e4jAmgK/Ut80lBiK8CgokMLfJVZ0ilHFx2ihTZBi8odDHAICq 7Xzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject:github-pr-num :references:in-reply-to:message-id:date:to:from:delivered-to :arc-authentication-results; bh=/g3HdCjTmfHKce9TKHTaHxm1JkRuSTzD70rfbrafD1A=; b=yow0pACYWvjY3MzsoT8QJI/FAb+45ceS0VUP36Mp1qZMHw5Lq+kMPMhctNg7EegQpQ DcY+asCHx2pJV904vv5xIK7RcVMufuyqAfU4Crb53CKHpQobrDM8FjDvraDiXchNx0mS DGbXpBVFJx1zyeVv/uEuqAXuVFtZaQu1WDYtBnNZJ/TomLGpitHiRGeW+4Z18wqGIMgn 9eVYvZahYddU5NYGPue7Ux42vvFUaaGBmq6SKgD8Rkb7l7rbGvC/ZXi2dvsmuF2ukbCe /lLv1w2RluJrJLgED2bX+PQqOKnZLzT7AnrP7OqrIGnqc6gOuUovvuSmVMbmNet9h4H9 jNyA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.197.127.237 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yandex.ru Return-Path: Received: from lists.linaro.org (ec2-54-197-127-237.compute-1.amazonaws.com. [54.197.127.237]) by mx.google.com with ESMTP id r13si4569541qtr.418.2018.03.01.06.00.43; Thu, 01 Mar 2018 06:00:44 -0800 (PST) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.197.127.237 as permitted sender) client-ip=54.197.127.237; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.197.127.237 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yandex.ru Received: by lists.linaro.org (Postfix, from userid 109) id 461A9614E1; Thu, 1 Mar 2018 14:00:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_LOW autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 41BDB6081D; Thu, 1 Mar 2018 14:00:24 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 32D0C60958; Thu, 1 Mar 2018 14:00:20 +0000 (UTC) Received: from forward101j.mail.yandex.net (forward101j.mail.yandex.net [5.45.198.241]) by lists.linaro.org (Postfix) with ESMTPS id 116F26081D for ; Thu, 1 Mar 2018 14:00:17 +0000 (UTC) Received: from mxback5o.mail.yandex.net (mxback5o.mail.yandex.net [IPv6:2a02:6b8:0:1a2d::1f]) by forward101j.mail.yandex.net (Yandex) with ESMTP id B7A081242C74 for ; Thu, 1 Mar 2018 17:00:14 +0300 (MSK) Received: from smtp4p.mail.yandex.net (smtp4p.mail.yandex.net [2a02:6b8:0:1402::15:6]) by mxback5o.mail.yandex.net (nwsmtp/Yandex) with ESMTP id s3OBoF4q9p-0EvCZNcf; Thu, 01 Mar 2018 17:00:14 +0300 Received: by smtp4p.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id agxPqZ0y2f-0DBSvJma; Thu, 01 Mar 2018 17:00:13 +0300 (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (Client certificate not present) From: Github ODP bot To: lng-odp@lists.linaro.org Date: Thu, 1 Mar 2018 17:00:11 +0300 Message-Id: <1519912811-31335-2-git-send-email-odpbot@yandex.ru> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1519912811-31335-1-git-send-email-odpbot@yandex.ru> References: <1519912811-31335-1-git-send-email-odpbot@yandex.ru> Github-pr-num: 488 Subject: [lng-odp] [PATCH v3 1/1] odp: pktio: add pcapng capture capabilities X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" From: Ilias Apalodimas Changes since RFC: - Addressed issues reported in RFC - rewrote some functions for easier understanding - added capability on all input queues How to test: sudo mkdir /var/run/odp/ start the ODP application sudo dd if=/var/run/odp/"pid"-ethX-flow-"queue" of=~/t.pcap changes since V1 - Fixed documentation typo - Fixed documentation compilation - pcapng_prepare autocleans on error - renamed dump_pcapng_pkts to _odp_dump_pcapng_pkts - removed multiple AC_DEFINE from odp_pcapng.m4 changes since V2 - renamed ODP_PCAPNG->_ODP_PCAPNG - fixed checkpatch error on commit message Signed-off-by: Ilias Apalodimas --- /** Email created from pull request 488 (apalos:tcpdump_like) ** https://github.com/Linaro/odp/pull/488 ** Patch: https://github.com/Linaro/odp/pull/488.patch ** Base sha: 284f52d72ec19df3774c7409780f1f9eea33b8e6 ** Merge commit sha: 71675adb724439c8948f62e541e2341cb60c8ce0 **/ .travis.yml | 1 + configure.ac | 1 + doc/users-guide/Makefile.am | 3 +- .../users-guide-utilities-examples.adoc | 17 + doc/users-guide/users-guide.adoc | 2 + platform/linux-generic/Makefile.am | 2 + platform/linux-generic/include/odp_internal.h | 5 + .../linux-generic/include/odp_packet_io_internal.h | 9 + platform/linux-generic/include/odp_pcapng.h | 56 +++ platform/linux-generic/m4/configure.m4 | 1 + platform/linux-generic/m4/odp_pcapng.m4 | 18 + platform/linux-generic/odp_packet_io.c | 54 ++- platform/linux-generic/odp_pcapng.c | 434 +++++++++++++++++++++ 13 files changed, 597 insertions(+), 6 deletions(-) create mode 100644 doc/users-guide/users-guide-utilities-examples.adoc create mode 100644 platform/linux-generic/include/odp_pcapng.h create mode 100644 platform/linux-generic/m4/odp_pcapng.m4 create mode 100644 platform/linux-generic/odp_pcapng.c diff --git a/.travis.yml b/.travis.yml index 167c7319b..f7b0ae55e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,6 +57,7 @@ env: - CONF="--disable-static-applications" - CONF="--disable-host-optimization" - CONF="--disable-host-optimization --disable-abi-compat" + - CONF="--enable-pcapng-support" - DPDK_SHARED="y" CONF="--disable-static-applications" - DPDK_VERS="17.11" CONF="" - DPDK_VERS="17.11" DPDK_SHARED="y" CONF="--disable-static-applications" diff --git a/configure.ac b/configure.ac index 21718f07b..228856f2d 100644 --- a/configure.ac +++ b/configure.ac @@ -419,4 +419,5 @@ AC_MSG_RESULT([ test_helper: ${test_helper} test_example: ${test_example} user_guides: ${user_guides} + pcapng: ${have_pcapng} ]) diff --git a/doc/users-guide/Makefile.am b/doc/users-guide/Makefile.am index 7be32ddc1..6b2e818d0 100644 --- a/doc/users-guide/Makefile.am +++ b/doc/users-guide/Makefile.am @@ -7,7 +7,8 @@ SRC = users-guide.adoc \ users-guide-packet.adoc \ users-guide-pktio.adoc \ users-guide-timer.adoc \ - users-guide-tm.adoc + users-guide-tm.adoc \ + users-guide-utilities-examples.adoc TARGET = users-guide.html IMAGES = $(IMAGES_DIR)/overview.svg \ $(IMAGES_DIR)/atomic_queue.svg \ diff --git a/doc/users-guide/users-guide-utilities-examples.adoc b/doc/users-guide/users-guide-utilities-examples.adoc new file mode 100644 index 000000000..433db9abd --- /dev/null +++ b/doc/users-guide/users-guide-utilities-examples.adoc @@ -0,0 +1,17 @@ +== Utilities and examples + +=== PcapNg capture +If compiled using `--enable-pcapng-support` ODP will offer packet capturing +functionality in PcapNg format. If /var/run/odp directory exists prior to +launching the application ODP will create as many fifos as the NIC queues. +Queue naming will be of the following format: *--flow-* + +. `./configure --enable-pcapng-support` +. `sudo mkdir /var/run/odp` +. `sudo ./example/generator/odp_generator -I enp2s0 -mu --dstmac +A0:F6:FD:AE:62:6C --dstip 192.168.49.20 --srcmac 2c:56:dc:9a:8f:06 --srcip +192.168.49.4 -i0 -w1` +. `sudo dd if=/var/run/odp/26737-enp2s0-flow-0 of=~/test.pcap` +. `ctrl^c` +. `wireshark ~/test.pcap` diff --git a/doc/users-guide/users-guide.adoc b/doc/users-guide/users-guide.adoc index 7f2ad69e2..397f222a6 100644 --- a/doc/users-guide/users-guide.adoc +++ b/doc/users-guide/users-guide.adoc @@ -1198,4 +1198,6 @@ include::users-guide-tm.adoc[] include::users-guide-cls.adoc[] +include::users-guide-utilities-examples.adoc[] + include::../glossary.adoc[] diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index c35c0bfe3..f3a538cb0 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -106,6 +106,7 @@ noinst_HEADERS = \ include/odp_packet_socket.h \ include/odp_packet_tap.h \ include/odp_packet_null.h \ + include/odp_pcapng.h \ include/odp_pkt_queue_internal.h \ include/odp_pool_internal.h \ include/odp_posix_extensions.h \ @@ -158,6 +159,7 @@ __LIB__libodp_linux_la_SOURCES = \ odp_packet.c \ odp_packet_flags.c \ odp_packet_io.c \ + odp_pcapng.c \ pktio/ethtool.c \ pktio/io_ops.c \ pktio/ipc.c \ diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h index c21228982..c6a2449d2 100644 --- a/platform/linux-generic/include/odp_internal.h +++ b/platform/linux-generic/include/odp_internal.h @@ -23,6 +23,7 @@ extern "C" { #include #include #include +#include #define MAX_CPU_NUMBER 128 #define UID_MAXLEN 30 @@ -55,6 +56,10 @@ struct odp_global_data_s { odp_cpumask_t control_cpus; odp_cpumask_t worker_cpus; int num_cpus_installed; + int inotify_pcapng_fd; + int inotify_watch_fd; + pthread_t inotify_thread; + int inotify_pcapng_is_running; }; enum init_stage { diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h index e4852cc9b..4be4b1513 100644 --- a/platform/linux-generic/include/odp_packet_io_internal.h +++ b/platform/linux-generic/include/odp_packet_io_internal.h @@ -182,6 +182,15 @@ struct pktio_entry { odp_queue_t queue; odp_pktout_queue_t pktout; } out_queue[PKTIO_MAX_QUEUES]; + + /**< inotify instance for pcapng fifos */ + struct { + enum { + PCAPNG_WR_STOP = 0, + PCAPNG_WR_PKT, + } state[PKTIO_MAX_QUEUES]; + int fd[PKTIO_MAX_QUEUES]; + } pcapng; }; typedef union { diff --git a/platform/linux-generic/include/odp_pcapng.h b/platform/linux-generic/include/odp_pcapng.h new file mode 100644 index 000000000..8e0e624e8 --- /dev/null +++ b/platform/linux-generic/include/odp_pcapng.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2018, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#define PCAPNG_BLOCK_TYPE_EPB 0x00000006UL +#define PCAPNG_BLOCK_TYPE_SHB 0x0A0D0D0AUL +#define PCAPNG_BLOCK_TYPE_IDB 0x00000001UL +#define PCAPNG_ENDIAN_MAGIC 0x1A2B3C4DUL +#define PCAPNG_DATA_ALIGN 4 +#define PCAPNG_LINKTYPE_ETHERNET 0x1 + +/* inotify */ +#define INOTIFY_BUF_LEN (16 * (sizeof(struct inotify_event))) +#define PCAPNG_WATCH_DIR "/var/run/odp/" + +/* pcapng: enhanced packet block file encoding */ +typedef struct ODP_PACKED pcapng_section_hdr_block_s { + uint32_t block_type; + uint32_t block_total_length; + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int64_t section_len; + uint32_t block_total_length2; +} pcapng_section_hdr_block_t; + +typedef struct pcapng_interface_description_block { + uint32_t block_type; + uint32_t block_total_length; + uint16_t linktype; + uint16_t reserved; + uint32_t snaplen; + uint32_t block_total_length2; +} pcapng_interface_description_block_t; + +typedef struct pcapng_enhanced_packet_block_s { + uint32_t block_type; + uint32_t block_total_length; + uint32_t interface_idx; + uint32_t timestamp_high; + uint32_t timestamp_low; + uint32_t captured_len; + uint32_t packet_len; +} pcapng_enhanced_packet_block_t; + +int pcapng_prepare(pktio_entry_t *entry); +void pcapng_destroy(pktio_entry_t *entry); +int write_pcapng_hdr(pktio_entry_t *entry, int qidx); +int write_pcapng_pkts(pktio_entry_t *entry, int qidx, + const odp_packet_t packets[], int num); diff --git a/platform/linux-generic/m4/configure.m4 b/platform/linux-generic/m4/configure.m4 index 7fa3652e2..1a45a6945 100644 --- a/platform/linux-generic/m4/configure.m4 +++ b/platform/linux-generic/m4/configure.m4 @@ -7,6 +7,7 @@ ODP_PTHREAD ODP_TIMER ODP_OPENSSL m4_include([platform/linux-generic/m4/odp_pcap.m4]) +m4_include([platform/linux-generic/m4/odp_pcapng.m4]) m4_include([platform/linux-generic/m4/odp_netmap.m4]) m4_include([platform/linux-generic/m4/odp_dpdk.m4]) m4_include([platform/linux-generic/m4/odp_schedule.m4]) diff --git a/platform/linux-generic/m4/odp_pcapng.m4 b/platform/linux-generic/m4/odp_pcapng.m4 new file mode 100644 index 000000000..44274c267 --- /dev/null +++ b/platform/linux-generic/m4/odp_pcapng.m4 @@ -0,0 +1,18 @@ +########################################################################## +# Enable PCAPNG support +########################################################################## +have_pcapng=no +pcapng_support=0 + +AC_ARG_ENABLE([pcapng-support], + [AS_HELP_STRING([--enable-pcapng-support], + [enable experimental tcpdump for pktios])], + have_pcapng=$enableval + [if test x$enableval = xyes; then + pcapng_support=1 + fi]) + +AC_DEFINE_UNQUOTED([_ODP_PCAPNG], [$pcapng_support], + [Define to 1 to enable pcapng support]) + +AM_CONDITIONAL([have_pcapng], [test x$have_pcapng = xyes]) diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c index ae8e390b1..45ffc5489 100644 --- a/platform/linux-generic/odp_packet_io.c +++ b/platform/linux-generic/odp_packet_io.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -488,6 +489,11 @@ int odp_pktio_start(odp_pktio_t hdl) sched_fn->pktio_start(_odp_pktio_index(hdl), num, index, odpq); } + if (_ODP_PCAPNG) { + if (pcapng_prepare(entry)) + ODP_ERR("pcap init failed, won't capture\n"); + } + return res; } @@ -510,6 +516,9 @@ static int _pktio_stop(pktio_entry_t *entry) else entry->s.state = PKTIO_STATE_STOPPED; + if (_ODP_PCAPNG) + pcapng_destroy(entry); + return res; } @@ -1653,10 +1662,18 @@ int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num) return num_queues; } +static inline void _odp_dump_pcapng_pkts(pktio_entry_t *entry, int qidx, + const odp_packet_t packets[], int num) +{ + if (odp_unlikely(entry->s.pcapng.state[qidx] == PCAPNG_WR_PKT)) + write_pcapng_pkts(entry, qidx, packets, num); +} + int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num) { pktio_entry_t *entry; odp_pktio_t pktio = queue.pktio; + int ret; entry = get_pktio_entry(pktio); if (entry == NULL) { @@ -1664,7 +1681,11 @@ int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num) return -1; } - return entry->s.ops->recv(entry, queue.index, packets, num); + ret = entry->s.ops->recv(entry, queue.index, packets, num); + if (_ODP_PCAPNG) + _odp_dump_pcapng_pkts(entry, queue.index, packets, ret); + + return ret; } int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num, @@ -1686,12 +1707,19 @@ int odp_pktin_recv_tmo(odp_pktin_queue_t queue, odp_packet_t packets[], int num, return -1; } - if (entry->s.ops->recv_tmo && wait != ODP_PKTIN_NO_WAIT) - return entry->s.ops->recv_tmo(entry, queue.index, packets, num, + if (entry->s.ops->recv_tmo && wait != ODP_PKTIN_NO_WAIT) { + ret = entry->s.ops->recv_tmo(entry, queue.index, packets, num, wait); + if (_ODP_PCAPNG) + _odp_dump_pcapng_pkts(entry, queue.index, packets, ret); + + return ret; + } while (1) { ret = entry->s.ops->recv(entry, queue.index, packets, num); + if (_ODP_PCAPNG) + _odp_dump_pcapng_pkts(entry, queue.index, packets, ret); if (ret != 0 || wait == 0) return ret; @@ -1734,6 +1762,7 @@ int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q, int started = 0; uint64_t sleep_round = 0; int trial_successful = 0; + unsigned lfrom = 0; for (i = 0; i < num_q; i++) { ret = odp_pktin_recv(queues[i], packets, num); @@ -1748,11 +1777,23 @@ int odp_pktin_recv_mq_tmo(const odp_pktin_queue_t queues[], unsigned num_q, if (wait == 0) return 0; - ret = sock_recv_mq_tmo_try_int_driven(queues, num_q, from, + ret = sock_recv_mq_tmo_try_int_driven(queues, num_q, &lfrom, packets, num, wait, &trial_successful); - if (trial_successful) + if (ret > 0 && from) + *from = lfrom; + if (trial_successful) { + if (_ODP_PCAPNG) { + pktio_entry_t *entry; + + entry = get_pktio_entry(queues[lfrom].pktio); + if (entry) + _odp_dump_pcapng_pkts(entry, lfrom, packets, + ret); + } + return ret; + } ts.tv_sec = 0; ts.tv_nsec = 1000 * SLEEP_USEC; @@ -1818,6 +1859,9 @@ int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], return -1; } + if (_ODP_PCAPNG) + _odp_dump_pcapng_pkts(entry, queue.index, packets, num); + return entry->s.ops->send(entry, queue.index, packets, num); } diff --git a/platform/linux-generic/odp_pcapng.c b/platform/linux-generic/odp_pcapng.c new file mode 100644 index 000000000..b8d29e5a8 --- /dev/null +++ b/platform/linux-generic/odp_pcapng.c @@ -0,0 +1,434 @@ +/* Copyright (c) 2018, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "config.h" + +#if defined(_ODP_PCAPNG) && _ODP_PCAPNG == 1 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pcapng_drain_fifo(int fd) +{ + char buffer[4096]; + ssize_t len; + + do { + len = read(fd, buffer, sizeof(buffer)); + } while (len > 0); +} + +static void inotify_event_handle(pktio_entry_t *entry, int qidx, + struct inotify_event *event) +{ + int mtu = MAX(odp_pktin_maxlen(entry->s.handle), + odp_pktout_maxlen(entry->s.handle)); + + if (event->mask & IN_OPEN) { + int ret; + + if (PIPE_BUF < mtu + sizeof(pcapng_enhanced_packet_block_t) + + sizeof(uint32_t)) { + ODP_ERR("PIPE_BUF:%d too small. Disabling pcap\n", + PIPE_BUF); + entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP; + + return; + } + + ret = write_pcapng_hdr(entry, qidx); + if (ret) { + entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP; + } else { + entry->s.pcapng.state[qidx] = PCAPNG_WR_PKT; + ODP_DBG("Open %s for pcap tracing\n", event->name); + } + } else if (event->mask & IN_CLOSE) { + int fd = entry->s.pcapng.fd[qidx]; + + pcapng_drain_fifo(fd); + entry->s.pcapng.state[qidx] = PCAPNG_WR_STOP; + ODP_DBG("Close %s for pcap tracing\n", event->name); + } else { + ODP_ERR("Unknown inotify event 0x%08x\n", event->mask); + } +} + +static void get_pcapng_fifo_name(char *pcapng_entry, size_t len, + char *pktio_name, int qidx) +{ + snprintf(pcapng_entry, len, "%d-%s-flow-%d", + odp_global_data.main_pid, pktio_name, qidx); + pcapng_entry[len - 1] = 0; +} + +static int get_qidx_from_fifo(pktio_entry_t *entry, char *name) +{ + unsigned int max_queue = + MAX(entry->s.num_in_queue, entry->s.num_out_queue); + unsigned int i; + + for (i = 0; i < max_queue; i++) { + char pcapng_entry[256]; + + get_pcapng_fifo_name(pcapng_entry, sizeof(pcapng_entry), + entry->s.name, i); + /* + * verify we still talk to a fifo before returning a valid + * queue number + */ + if (strcmp(name, pcapng_entry) == 0) { + struct stat fstat; + char pcapng_path[256]; + + snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s", + PCAPNG_WATCH_DIR, name); + stat(pcapng_path, &fstat); + + return S_ISFIFO(fstat.st_mode) ? (int)i : -1; + } + } + + return -1; +} + +static void *inotify_update(void *arg) +{ + pktio_entry_t *entry = (pktio_entry_t *)arg; + struct timeval time; + ssize_t rdlen; + int offset; + char buffer[INOTIFY_BUF_LEN]; + fd_set rfds; + + while (1) { + offset = 0; + FD_ZERO(&rfds); + FD_SET(odp_global_data.inotify_pcapng_fd, &rfds); + time.tv_sec = 2; + time.tv_usec = 0; + select(odp_global_data.inotify_pcapng_fd + 1, &rfds, NULL, + NULL, &time); + if (FD_ISSET(odp_global_data.inotify_pcapng_fd, &rfds)) { + rdlen = read(odp_global_data.inotify_pcapng_fd, + buffer, INOTIFY_BUF_LEN); + while (offset < rdlen) { + int qidx; + struct inotify_event *event = + (struct inotify_event *)(void *) + &buffer[offset]; + + qidx = get_qidx_from_fifo(entry, event->name); + if (qidx == -1) { + offset += sizeof(struct inotify_event) + + event->len; + continue; + } + + inotify_event_handle(entry, qidx, event); + offset += sizeof(struct inotify_event) + + event->len; + } + } + } + + return NULL; +} + +static int get_fifo_max_size(void) +{ + FILE *file; + char buf[128]; + int ret = -1; + + file = fopen("/proc/sys/fs/pipe-max-size", "r"); + if (file == NULL) + return ret; + + if (fgets(buf, sizeof(buf), file)) + ret = atoi(buf); + + fclose(file); + + return ret; +} + +int pcapng_prepare(pktio_entry_t *entry) +{ + int ret = -1, fd; + pthread_attr_t attr; + unsigned int i; + unsigned int max_queue = + MAX(entry->s.num_in_queue, entry->s.num_out_queue); + int fifo_sz; + + fifo_sz = get_fifo_max_size(); + if (fifo_sz < 0) + ODP_DBG("failed to read max fifo size\n"); + + for (i = 0; i < max_queue; i++) { + char pcapng_name[128]; + char pcapng_path[256]; + + entry->s.pcapng.fd[i] = -1; + entry->s.pcapng.state[i] = PCAPNG_WR_STOP; + + get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name), + entry->s.name, i); + snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s", + PCAPNG_WATCH_DIR, pcapng_name); + if (mkfifo(pcapng_path, O_RDWR)) { + ODP_ERR("pcap not available for %s %s\n", + pcapng_path, strerror(errno)); + continue; + } + + fd = open(pcapng_path, O_RDWR | O_NONBLOCK); + if (fd == -1) { + ODP_ERR("Fail to open fifo\n"); + entry->s.pcapng.state[i] = PCAPNG_WR_STOP; + if (remove(pcapng_path) == -1) + ODP_ERR("Can't remove fifo %s\n", pcapng_path); + continue; + } + + if (fifo_sz > 0) { + if (fcntl(fd, F_SETPIPE_SZ, fifo_sz) != fifo_sz) + ODP_DBG("Failed to set max fifo size\n"); + else + ODP_DBG("set pcap fifo size %i\n", fifo_sz); + } + + entry->s.pcapng.fd[i] = fd; + } + + /* already running from a previous pktio */ + if (odp_global_data.inotify_pcapng_is_running == 1) + return 0; + + odp_global_data.inotify_pcapng_fd = -1; + odp_global_data.inotify_watch_fd = -1; + + odp_global_data.inotify_pcapng_fd = inotify_init(); + if (odp_global_data.inotify_pcapng_fd == -1) { + ODP_ERR("can't init inotify. pcap disabled\n"); + goto out_destroy; + } + + odp_global_data.inotify_watch_fd = + inotify_add_watch(odp_global_data.inotify_pcapng_fd, + PCAPNG_WATCH_DIR, IN_CLOSE | IN_OPEN); + + if (odp_global_data.inotify_watch_fd == -1) { + ODP_ERR("can't register inotify for %s. pcap disabled\n", + strerror(errno)); + goto out_destroy; + } + + /* create a thread to poll inotify triggers */ + pthread_attr_init(&attr); + ret = pthread_create(&odp_global_data.inotify_thread, &attr, + inotify_update, entry); + if (ret) + ODP_ERR("can't start inotify thread. pcap disabled\n"); + else + odp_global_data.inotify_pcapng_is_running = 1; + + return ret; + +out_destroy: + pcapng_destroy(entry); + + return ret; +} + +void pcapng_destroy(pktio_entry_t *entry) +{ + int ret; + unsigned int i; + unsigned int max_queue = + MAX(entry->s.num_in_queue, entry->s.num_out_queue); + + if (odp_global_data.inotify_pcapng_is_running == 1) { + ret = pthread_cancel(odp_global_data.inotify_thread); + if (ret) + ODP_ERR("can't cancel inotify thread %s\n", + strerror(errno)); + } + + /* fd's will be -1 in case of any failure */ + ret = inotify_rm_watch(odp_global_data.inotify_pcapng_fd, + odp_global_data.inotify_watch_fd); + if (ret) + ODP_ERR("can't deregister inotify %s\n", strerror(errno)); + + if (odp_global_data.inotify_pcapng_fd != -1) + close(odp_global_data.inotify_pcapng_fd); + + if (odp_global_data.inotify_watch_fd != -1) + close(odp_global_data.inotify_watch_fd); + + for (i = 0; i < max_queue; i++) { + char pcapng_name[128]; + char pcapng_path[256]; + + entry->s.pcapng.state[i] = PCAPNG_WR_STOP; + close(entry->s.pcapng.fd[i]); + + get_pcapng_fifo_name(pcapng_name, sizeof(pcapng_name), + entry->s.name, i); + snprintf(pcapng_path, sizeof(pcapng_path), "%s/%s", + PCAPNG_WATCH_DIR, pcapng_name); + + if (remove(pcapng_path)) + ODP_ERR("can't delete fifo %s\n", pcapng_path); + } +} + +int write_pcapng_hdr(pktio_entry_t *entry, int qidx) +{ + size_t len; + pcapng_section_hdr_block_t shb; + pcapng_interface_description_block_t idb; + int fd = entry->s.pcapng.fd[qidx]; + + memset(&shb, 0, sizeof(shb)); + memset(&idb, 0, sizeof(idb)); + + shb.block_type = PCAPNG_BLOCK_TYPE_SHB; + shb.block_total_length = sizeof(shb); + shb.block_total_length2 = sizeof(shb); + shb.magic = PCAPNG_ENDIAN_MAGIC; + shb.version_major = 0x1; + shb.version_minor = 0x0; + shb.section_len = -1; + + len = write(fd, &shb, sizeof(shb)); + /* fail to write shb/idb means the pcapng is unreadable */ + if (len != sizeof(shb)) { + ODP_ERR("Failed to write pcapng section hdr\n"); + return -1; + } + fsync(fd); + + idb.block_type = PCAPNG_BLOCK_TYPE_IDB; + idb.block_total_length = sizeof(idb); + idb.block_total_length2 = sizeof(idb); + idb.linktype = PCAPNG_LINKTYPE_ETHERNET; + idb.snaplen = 0x0; /* unlimited */ + len = write(fd, &idb, sizeof(idb)); + if (len != sizeof(idb)) { + ODP_ERR("Failed to write pcapng interface description\n"); + return -1; + } + fsync(fd); + + return 0; +} + +/* + * make sure that each fifo write is less than PIPE_BUF + * this will make sure writes are atomic (on non blocking mode). + * writev() transfers all the data and returns the number of bytes requested or + * -EAGAIN + */ +static ssize_t write_fifo(int fd, struct iovec *iov, int iovcnt) +{ + ssize_t len = 0; + + len = writev(fd, iov, iovcnt); + /* + * we don't care if a writev fails, we asynchronously read the fifo + * so the next block of packets might be successful. This error only + * means that some packets failed to append on the pcap file + */ + if (len > 0) + fsync(fd); + + return len; +} + +int write_pcapng_pkts(pktio_entry_t *entry, int qidx, + const odp_packet_t packets[], int num) +{ + int i = 0; + struct iovec packet_iov[3 * num]; + pcapng_enhanced_packet_block_t epb[num]; + int iovcnt = 0; + ssize_t block_len = 0; + int fd = entry->s.pcapng.fd[qidx]; + ssize_t len = 0, wlen; + + for (i = 0; i < num; i++) { + odp_packet_hdr_t *pkt_hdr = packet_hdr(packets[i]); + uint32_t seg_len; + char *buf = (char *)odp_packet_offset(packets[i], 0, &seg_len, + NULL); + + if (block_len + sizeof(epb[i]) + + ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) + + sizeof(uint32_t) > PIPE_BUF) { + wlen = write_fifo(fd, packet_iov, iovcnt); + if (wlen > 0) { + len += wlen; + block_len = 0; + iovcnt = 0; + } + } + epb[i].block_type = PCAPNG_BLOCK_TYPE_EPB; + epb[i].block_total_length = sizeof(epb[i]) + + ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN) + + PCAPNG_DATA_ALIGN; + epb[i].interface_idx = 0; + epb[i].timestamp_high = + (uint32_t)(pkt_hdr->timestamp.u64 >> 32); + epb[i].timestamp_low = (uint32_t)(pkt_hdr->timestamp.u64); + epb[i].captured_len = seg_len; + epb[i].packet_len = seg_len; + + /* epb */ + packet_iov[iovcnt].iov_base = &epb[i]; + packet_iov[iovcnt].iov_len = sizeof(epb[i]); + block_len += packet_iov[iovcnt].iov_len; + iovcnt++; + + /* data */ + packet_iov[iovcnt].iov_base = buf; + packet_iov[iovcnt].iov_len = + ROUNDUP_ALIGN(seg_len, PCAPNG_DATA_ALIGN); + block_len += packet_iov[iovcnt].iov_len; + iovcnt++; + + /* trailing */ + packet_iov[iovcnt].iov_base = &epb[i].block_total_length; + packet_iov[iovcnt].iov_len = sizeof(uint32_t); + block_len += packet_iov[iovcnt].iov_len; + iovcnt++; + } + + if (iovcnt) { + wlen = write_fifo(fd, packet_iov, iovcnt); + if (wlen > 0) + len += wlen; + } + + return len; +} + +#endif /* _ODP_PCAPNG */