From patchwork Fri Feb 16 14:00:07 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: 128594 Delivered-To: patch@linaro.org Received: by 10.46.124.24 with SMTP id x24csp593044ljc; Fri, 16 Feb 2018 06:01:12 -0800 (PST) X-Google-Smtp-Source: AH8x226TacwL6gWvdn9uA59zRNqbWLnt3x+e+YlGBol5ZzjL9eg7Du6xdjqPtZgtKAkOy0kXq3s9 X-Received: by 10.55.142.71 with SMTP id q68mr9874911qkd.40.1518789672540; Fri, 16 Feb 2018 06:01:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518789672; cv=none; d=google.com; s=arc-20160816; b=Qo5E9EoQCnLoTKqrT5apGYKpJ0aCh5axxezvv8K5Zr7kgfOFsjQC3uSvZpX0n6Xcyn UxMSjR7maSnE4k4UoJn6S8UXCsu2tGwS2PMEdTj4CCMjamQvEn2d8zeCQ653eSH2eWFK j10pKxYKacKXqskQQbYpsBT03byKdTBCBdsUI0VcQMtxiIjsjDv1GyrfkPxdSIWpNkgk OwB0n9hus/mopNThFzp/QjGtqhpdfdbRDJ2TZDr5U905CflXLsW6Ag4yKJT/uE5BiwPF 1nMTz/fdm8CCxBUrMQR9aaRztKiMtk/Qnf6EcNKxJG99/d4eGjg+CdMX+YAtHfRJ0ne3 QvSg== 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=1tNXZ8rgP2n1pPkgVhbhV8Nvam9EFENMUj38HJKCoyA=; b=UKt1wqvoZpDgxbsQ6Dar9sYwxI8+9EX9c9+/9W1TsfxedRvVmLwkdMJ44DWdBHVGza R1p4LNejW0DrinlUQUI6VI2dLUVNH7SSeS0RouTwS+ZXCOEvatAGCZeU8mt/neSxUUkh +HCRZseL3y+jBRQvoykomKvTcKF2hQEHEtL/PHgyUOQcHUz13qV0ONUO5p7FiAhpVWXP zyV98Vlcgz7W/XI/JNw2/NVagY7OcXJCurTEx6S38CTyUF8eeiciUcmgmDLi90wj18m6 bZ492IY2Mks2J+qLOSWH6DHx6gAiXuMa0f/Pi2oQKNFV82j63plS4TSo3KHDgVQUuf0x 2zpg== 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 t185si4795063qkc.112.2018.02.16.06.01.12; Fri, 16 Feb 2018 06:01:12 -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 2DC3460847; Fri, 16 Feb 2018 14:01:12 +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, RCVD_IN_MSPIKE_H2 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 DEB42608EF; Fri, 16 Feb 2018 14:00:31 +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 5A64661706; Fri, 16 Feb 2018 14:00:20 +0000 (UTC) Received: from forward101o.mail.yandex.net (forward101o.mail.yandex.net [37.140.190.181]) by lists.linaro.org (Postfix) with ESMTPS id 2161561706 for ; Fri, 16 Feb 2018 14:00:11 +0000 (UTC) Received: from mxback11j.mail.yandex.net (mxback11j.mail.yandex.net [IPv6:2a02:6b8:0:1619::84]) by forward101o.mail.yandex.net (Yandex) with ESMTP id 4EBBD1344381 for ; Fri, 16 Feb 2018 17:00:09 +0300 (MSK) Received: from smtp1p.mail.yandex.net (smtp1p.mail.yandex.net [2a02:6b8:0:1472:2741:0:8b6:6]) by mxback11j.mail.yandex.net (nwsmtp/Yandex) with ESMTP id ce7ywkfZ33-09XWnHAo; Fri, 16 Feb 2018 17:00:09 +0300 Received: by smtp1p.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id MdDWaP934Z-08v4xVNw; Fri, 16 Feb 2018 17:00:08 +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: Fri, 16 Feb 2018 17:00:07 +0300 Message-Id: <1518789607-26194-2-git-send-email-odpbot@yandex.ru> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1518789607-26194-1-git-send-email-odpbot@yandex.ru> References: <1518789607-26194-1-git-send-email-odpbot@yandex.ru> Github-pr-num: 488 Subject: [lng-odp] [PATCH v1 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/"odp pid"-"inteface"-flow-"queue number" of=~/test.pcap 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: ea529e1c3312459b6722c8a175d7f03ad0251a0a ** Merge commit sha: 65f2433e63461bd9440df02bfe101e1b81c6f09d **/ .travis.yml | 1 + configure.ac | 1 + .../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 | 4 + .../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 | 19 + platform/linux-generic/odp_packet_io.c | 53 ++- platform/linux-generic/odp_pcapng.c | 429 +++++++++++++++++++++ 12 files changed, 589 insertions(+), 5 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 e36c7bcb2..3a3c755e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,6 +58,7 @@ env: - CONF="--enable-schedule-scalable" - CONF="--enable-dpdk-zero-copy" - CONF="--disable-static-applications" + - CONF="--enable-pcapng-support" - DPDK_SHARED="y" CONF="--disable-static-applications" compiler: diff --git a/configure.ac b/configure.ac index a44a2ec6f..f5a3b2375 100644 --- a/configure.ac +++ b/configure.ac @@ -410,4 +410,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/users-guide-utilities-examples.adoc b/doc/users-guide/users-guide-utilities-examples.adoc new file mode 100644 index 000000000..78b93a68c --- /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 9e82f2622..c1bd89c87 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -112,6 +112,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 \ @@ -162,6 +163,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 f85a2f538..9d962255a 100644 --- a/platform/linux-generic/include/odp_internal.h +++ b/platform/linux-generic/include/odp_internal.h @@ -55,6 +55,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 1de0cbf90..aaa5d553d 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..798d4b3cf --- /dev/null +++ b/platform/linux-generic/m4/odp_pcapng.m4 @@ -0,0 +1,19 @@ +########################################################################## +# Enable PCAPNG support +########################################################################## +have_pcapng=no +AC_ARG_ENABLE([pcapng-support], + [AS_HELP_STRING([--enable-pcapng-support], + [enable experimental tcpdump for pktios])], + have_pcapng=$enableval) + +if test x$have_pcapng = xyes +then + AC_DEFINE([ODP_PCAPNG], [1], + [Define to 1 to enable pcapng support]) +else + AC_DEFINE([ODP_PCAPNG], [0], + [Define to 0 to disable pcapng support]) +fi + +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 4eae4ed1e..ca55d0c5a 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 @@ -485,6 +486,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)) + pcapng_destroy(entry); + } + return res; } @@ -507,6 +513,9 @@ static int _pktio_stop(pktio_entry_t *entry) else entry->s.state = PKTIO_STATE_STOPPED; + if (ODP_PCAPNG) + pcapng_destroy(entry); + return res; } @@ -1650,10 +1659,18 @@ int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num) return num_queues; } +static inline void 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) { @@ -1661,7 +1678,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) + 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, @@ -1683,12 +1704,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) + 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) + dump_pcapng_pkts(entry, queue.index, packets, ret); if (ret != 0) return ret; @@ -1733,6 +1761,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); @@ -1747,11 +1776,22 @@ 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) + dump_pcapng_pkts(entry, lfrom, packets, ret); + } + return ret; + } ts.tv_sec = 0; ts.tv_nsec = 1000 * SLEEP_USEC; @@ -1816,6 +1856,9 @@ int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], return -1; } + if (ODP_PCAPNG) + 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..bffbfd996 --- /dev/null +++ b/platform/linux-generic/odp_pcapng.c @@ -0,0 +1,429 @@ +/* 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"); + return ret; + } + + 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)); + return ret; + } + + /* 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; +} + +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 */