DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Stephen Hemminger <stephen@networkplumber.org>,
	Thomas Monjalon <thomas@monjalon.net>,
	Reshma Pattan <reshma.pattan@intel.com>,
	Robin Jarry <rjarry@redhat.com>
Subject: [PATCH v2 4/4] usertools: add script for wireshark capture
Date: Mon, 27 Jul 2026 15:42:47 -0700	[thread overview]
Message-ID: <20260727224417.1419663-5-stephen@networkplumber.org> (raw)
In-Reply-To: <20260727224417.1419663-1-stephen@networkplumber.org>

Provide glue script that wireshark can use to access
telemetry based packet capture. It is dual licensed because
it maybe desirable to put this in wireshark repository.

See https://www.wireshark.org/docs/man-pages/extcap.html

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 MAINTAINERS                            |   2 +
 doc/guides/rel_notes/release_26_11.rst |   1 +
 doc/guides/tools/index.rst             |   1 +
 doc/guides/tools/wireshark_extcap.rst  | 144 +++++++++
 usertools/dpdk-wireshark-extcap.py     | 416 +++++++++++++++++++++++++
 usertools/meson.build                  |   1 +
 6 files changed, 565 insertions(+)
 create mode 100644 doc/guides/tools/wireshark_extcap.rst
 create mode 100755 usertools/dpdk-wireshark-extcap.py

diff --git a/MAINTAINERS b/MAINTAINERS
index 608cb4b77d..7f220cc2c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1732,6 +1732,8 @@ F: app/pdump/
 F: doc/guides/tools/pdump.rst
 F: app/dumpcap/
 F: doc/guides/tools/dumpcap.rst
+F: usertools/dpdk-wireshark-extcap.py
+F: doc/guides/tools/wireshark_extcap.rst
 
 
 Packet Framework
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 00acc02ac3..c79ffb38cf 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -58,6 +58,7 @@ New Features
 * **Added wireshark capture support.**
 
   * Added ``capture`` library for packet capture via telemetry API.
+  * Added python script for wireshark external capture integration.
 
 
 Removed Items
diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst
index 13f75a5bc6..01db27511a 100644
--- a/doc/guides/tools/index.rst
+++ b/doc/guides/tools/index.rst
@@ -14,6 +14,7 @@ DPDK Tools User Guides
     pmdinfo
     dumpcap
     pdump
+    wireshark_extcap
     telemetrywatcher
     dmaperf
     flow-perf
diff --git a/doc/guides/tools/wireshark_extcap.rst b/doc/guides/tools/wireshark_extcap.rst
new file mode 100644
index 0000000000..99fb94df72
--- /dev/null
+++ b/doc/guides/tools/wireshark_extcap.rst
@@ -0,0 +1,144 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Stephen Hemminger
+
+Wireshark Extcap Plugin
+=======================
+
+The ``dpdk-wireshark-extcap.py`` script is an external capture (extcap)
+plugin that enables Wireshark capture of live traffic from
+the Ethernet ports of a DPDK application.
+Each DPDK port appears as a capture interface in the Wireshark interface list,
+alongside the host's own network interfaces.
+
+The plugin uses the DPDK telemetry API to query and start capture.
+It passes the path of the fifo file that Wireshark created to the DPDK application,
+which opens the file itself and writes pcapng packets straight into it;
+the plugin never touches packet data.
+Wireshark signals the plugin on exit and that results in closing
+the capture session.
+
+
+Requirements
+------------
+
+* A DPDK application built with the capture library and with telemetry enabled.
+  Telemetry is enabled by default.
+
+* Since the plugin is started by Wireshark, Wireshark and the DPDK application
+  must have the same permissions. See `Permissions`_.
+
+
+Installation
+------------
+
+For Wireshark to discover the plugin it must be present in an extcap
+directory. The configured locations are listed in Wireshark under
+*Help > About Wireshark > Folders*. Copy or symbolically link the script into
+the personal extcap directory, for example::
+
+    ln -s $RTE_SDK/usertools/dpdk-wireshark-extcap.py \
+        ~/.local/lib/wireshark/extcap/
+
+The DPDK ports then appear in the interface list the next time the capture
+options dialog is opened.
+
+
+Usage
+-----
+
+In normal use the plugin is not run by hand; Wireshark invokes it.
+The ports of a running DPDK application appear in the interface list as
+``DPDK <name>``, where ``<name>`` is the device name reported by
+DPDK ethdev, such as ``net_tap0``.
+
+The plugin can also be run directly, which is useful for confirming that a
+DPDK application is reachable::
+
+    $ usertools/dpdk-wireshark-extcap.py --extcap-interfaces
+    extcap {version=0.1}{display=DPDK telemetry capture}
+    interface {value=dpdk:0}{display=DPDK net_tap0}
+
+
+Capture options
+---------------
+
+The following options are offered in the Wireshark capture options dialog for
+a DPDK interface:
+
+Snapshot length
+    Number of bytes captured from each packet. ``0`` captures the whole
+    packet. The default is 262144.
+
+Queue
+    Capture a single hardware queue, or all queues (the default). The choices
+    are bounded to the port's configured queue count.
+
+Capture filter
+    A libpcap filter expression, applied by the DPDK application to the
+    captured traffic.
+
+
+Permissions
+-----------
+
+The DPDK runtime directory is created mode ``0700``, so only the user that
+started the DPDK application can reach its telemetry socket.
+Wireshark, and the plugin it launches, must run as that same user. If run as a
+different user, the interface list is simply empty; running the plugin directly
+with ``--extcap-interfaces`` prints a diagnostic to standard error explaining
+the permission failure.
+
+No privilege beyond access to the telemetry socket is required: if you can
+run ``dpdk-dumpcap`` against an application, you can capture from it with this
+plugin.
+
+
+Selecting a DPDK application
+----------------------------
+
+A host usually runs a single DPDK application, started with the default
+file-prefix (``rte``), and no configuration is needed: its ports appear
+automatically as ``DPDK <name>``.
+
+Running several DPDK applications on one host is also supported. In that case
+each application is started with a distinct ``--file-prefix`` so that its
+runtime state is kept separate. The plugin lists the ports of *all* running
+applications at once, so the application is chosen from the interface list
+rather than before Wireshark is launched. There is no environment variable to
+select one.
+
+Ports of the default prefix are shown plainly, as ``DPDK <name>`` with the
+interface value ``dpdk:<port>``. Ports of any other prefix are qualified with
+the prefix, shown as ``DPDK <name>@<prefix>`` with the interface value
+``dpdk:<prefix>:<port>``, so applications with colliding port numbers stay
+distinct in the list.
+
+
+Environment variables
+----------------------
+
+``DPDK_EXTCAP_PATH``
+    Overrides the base DPDK runtime directory that holds the per-prefix
+    subdirectories. Use it when the runtime directory is in a non-standard
+    location. It gives the base directory that holds the per-prefix
+    subdirectories, one of which is scanned for each running application.
+
+
+Troubleshooting
+---------------
+
+The DPDK ports do not appear in Wireshark
+    Confirm the application is running and was built with the capture library
+    and telemetry. Confirm Wireshark runs as the same user as the application;
+    see `Permissions`_. An application started with a non-default
+    ``--file-prefix`` is listed with its ports qualified as
+    ``DPDK <name>@<prefix>``; see `Selecting a DPDK application`_.
+
+    Running the plugin directly with ``--extcap-interfaces`` prints
+    diagnostics to standard error that the Wireshark GUI does not surface.
+
+A port is listed as ``portN`` instead of a device name
+    The port was reported by the application, but its details could not be
+    read, usually because the application stopped between listing and naming
+    its ports. A capture started against it will fail; restart the
+    application.
diff --git a/usertools/dpdk-wireshark-extcap.py b/usertools/dpdk-wireshark-extcap.py
new file mode 100755
index 0000000000..ba7323b857
--- /dev/null
+++ b/usertools/dpdk-wireshark-extcap.py
@@ -0,0 +1,416 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
+# Copyright(c) 2026 Stephen Hemminger
+
+"""
+Wireshark extcap plugin for live capture from DPDK ethdev ports.
+
+Capture path: Wireshark creates a FIFO and hands this plugin its path. The
+plugin opens the FIFO -- which rendezvous with Wireshark's read end -- and then
+passes the FIFO *path* to the DPDK primary process over the telemetry socket as
+the 'out=' parameter. DPDK opens the FIFO itself and writes pcapng straight into
+it; this plugin never touches packet data.
+
+Stopping: Wireshark stops a capture by closing the FIFO read end and/or sending
+SIGTERM. On either, this plugin asks DPDK to stop (/ethdev/capture/stop,<id>) so
+it can flush the trailing packets and the closing interface statistics block
+while a reader is still attached. If the read end is already gone, DPDK detects
+the same hangup and stops on its own; the explicit stop is then a harmless
+no-op. Note that when stopped from the Wireshark GUI the read-end close usually
+precedes the signal, so the statistics block is delivered reliably only for
+standalone (Ctrl+C) runs and for file output.
+
+Interface values: 'dpdk:<port>' for the default file-prefix ('rte'), and
+'dpdk:<prefix>:<port>' for any other primary. The default prefix is left
+implicit so the common single-instance case stays unadorned.
+"""
+
+import argparse
+import json
+import os
+import select
+import signal
+import socket
+import sys
+
+EXTCAP_VERSION = "0.1"
+TELEMETRY_SOCKET = "dpdk_telemetry.v2"
+CAPTURE_START_CMD = "/ethdev/capture/start"
+CAPTURE_STOP_CMD = "/ethdev/capture/stop"
+ETHDEV_LIST = "/ethdev/list"
+ETHDEV_INFO = "/ethdev/info"
+DEFAULT_SNAPLEN = 262144
+DEFAULT_PREFIX = "rte"  # EAL HUGEFILE_PREFIX_DEFAULT
+DLT_EN10MB = 1
+
+
+# --- DPDK runtime directory / socket discovery ---------------------------
+
+
+def dpdk_dir():
+    """Directory holding the per-file-prefix runtime subdirectories."""
+    override = os.environ.get("DPDK_EXTCAP_PATH")
+    if override:
+        return override
+    if os.geteuid() == 0:
+        base = "/var/run"
+    else:
+        base = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
+    return os.path.join(base, "dpdk")
+
+
+def socket_path(prefix):
+    return os.path.join(dpdk_dir(), prefix, TELEMETRY_SOCKET)
+
+
+def list_prefixes():
+    """Yield (prefix, path) for each DPDK telemetry socket found."""
+    root = dpdk_dir()
+    try:
+        entries = os.listdir(root)
+    except FileNotFoundError:
+        # No DPDK runtime dir is present.
+        sys.stderr.write(
+            f"no DPDK runtime directory {root}. Either no DPDK application "
+            "is running, or it runs as a different user\n"
+        )
+        return
+    except PermissionError:
+        # The runtime dir is mode 0700; a different user can see nothing.
+        sys.stderr.write(
+            f"cannot read {root}: permission denied. The DPDK runtime "
+            "directory is created mode 0700, so capture must run as the same "
+            "user as the DPDK application, or set DPDK_EXTCAP_PATH.\n"
+        )
+        return
+
+    found = False
+    for prefix in sorted(entries):
+        path = os.path.join(root, prefix, TELEMETRY_SOCKET)
+        if os.path.exists(path):
+            found = True
+            yield prefix, path
+
+    # Handle case where leftover unix domain socket
+    if not found:
+        sys.stderr.write(f"no DPDK telemetry socket under {root}\n")
+
+
+def iface_value(prefix, port):
+    """Interface value for a (prefix, port). The default prefix is left
+    implicit so a single default-prefix instance shows 'dpdk:<port>'; any
+    other primary is spelled out as 'dpdk:<prefix>:<port>'."""
+    if prefix == DEFAULT_PREFIX:
+        return f"dpdk:{port}"
+    return f"dpdk:{prefix}:{port}"
+
+
+# --- Telemetry transport -------------------------------------------------
+
+
+class Telemetry:
+    """Minimal client for the DPDK v2 telemetry socket (SOCK_SEQPACKET)."""
+
+    def __init__(self, path):
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+        self.sock.connect(path)
+        info = json.loads(self.sock.recv(1024).decode())
+        self.max_output_len = info.get("max_output_len", 16384)
+        self.pid = info.get("pid")
+        self.version = info.get("version")
+
+    def command(self, cmd):
+        """Send a command, return the decoded JSON reply, or None if empty."""
+        self.sock.send(cmd.encode())
+        reply = self.sock.recv(self.max_output_len)
+        if not reply:
+            return None
+        return json.loads(reply.decode())
+
+    def close(self):
+        self.sock.close()
+
+
+def port_queue_count(tel, port):
+    """Max of the rx/tx queue counts for a port, 0 if unknown."""
+    try:
+        reply = tel.command(f"{ETHDEV_INFO},{port}")
+    except OSError:
+        reply = None
+    info = (reply or {}).get(ETHDEV_INFO) or {}
+    return max(info.get("nb_rx_queues", 0), info.get("nb_tx_queues", 0))
+
+
+# --- extcap query operations --------------------------------------------
+
+
+def port_name(tel, port):
+    """Device name for a port via /ethdev/info, or 'port<N>' if unreadable.
+    For a physical port this is the PCI address (0000:c1:00.0); for a vdev it
+    is the vdev name (net_tap0). Reuses the caller's open connection."""
+    try:
+        reply = tel.command(f"{ETHDEV_INFO},{port}")
+    except OSError:
+        reply = None
+    info = (reply or {}).get(ETHDEV_INFO) or {}
+    return info.get("name") or f"port{port}"
+
+
+def cmd_interfaces():
+    print(f"extcap {{version={EXTCAP_VERSION}}}{{display=DPDK telemetry capture}}")
+    for prefix, path in list_prefixes():
+        try:
+            tel = Telemetry(path)
+        except OSError as e:
+            sys.stderr.write(f"cannot query {path}: {e}\n")
+            continue
+        # One connection per prefix for the whole enumeration: list the ports,
+        # then name each over the same socket (each connection costs the
+        # primary a handler thread).
+        try:
+            ports = (tel.command(ETHDEV_LIST) or {}).get(ETHDEV_LIST) or []
+            for port in ports:
+                name = port_name(tel, port)
+                # Name the prefix in the label only when it is not the default.
+                if prefix == DEFAULT_PREFIX:
+                    display = f"DPDK {name}"
+                else:
+                    display = f"DPDK {name}@{prefix}"
+                print(
+                    f"interface {{value={iface_value(prefix, port)}}}"
+                    f"{{display={display}}}"
+                )
+        except OSError as e:
+            sys.stderr.write(f"cannot query {path}: {e}\n")
+        finally:
+            tel.close()
+
+
+def cmd_dlts(_iface):
+    print(f"dlt {{number={DLT_EN10MB}}}{{name=EN10MB}}{{display=Ethernet}}")
+
+
+def cmd_config(iface):
+    print(
+        f"arg {{number=0}}{{call=--snaplen}}{{display=Snapshot length}}"
+        f"{{tooltip=Bytes captured per packet (0 = whole packet)}}"
+        f"{{type=integer}}{{range=0,{DEFAULT_SNAPLEN}}}"
+        f"{{default={DEFAULT_SNAPLEN}}}{{group=Capture}}"
+    )
+
+    # Bound the queue dropdown to the port's actual queue count. If the primary
+    # is unreachable, just omit the selector -- capture still defaults to all.
+    nqueues = 0
+    if iface:
+        prefix, port = parse_iface(iface)
+        try:
+            tel = Telemetry(socket_path(prefix))
+        except OSError:
+            tel = None
+        if tel is not None:
+            try:
+                nqueues = port_queue_count(tel, port)
+            finally:
+                tel.close()
+
+    if nqueues > 0:
+        print(
+            "arg {number=1}{call=--queue}{display=Queue}"
+            "{tooltip=Capture a single hardware queue}"
+            "{type=selector}{group=Capture}"
+        )
+        print("value {arg=1}{value=-1}{display=All queues}{default=true}")
+        for q in range(nqueues):
+            print(f"value {{arg=1}}{{value={q}}}{{display=Queue {q}}}")
+
+
+# --- capture -------------------------------------------------------------
+
+
+def comma_error(what):
+    """Telemetry command parameters are a comma separated list and there is no
+    escaping, so a comma inside a value is parsed as the start of the next
+    parameter. Reject it here rather than let the primary mis-parse it."""
+    return f"{what} must not contain a comma"
+
+
+def parse_iface(iface):
+    """Inverse of iface_value: accept 'dpdk:<port>' (default prefix) or
+    'dpdk:<prefix>:<port>'."""
+    parts = iface.split(":")
+    if parts[0] != "dpdk":
+        raise SystemExit(f"unsupported interface scheme in '{iface}'")
+    if len(parts) == 2:
+        prefix, port = DEFAULT_PREFIX, parts[1]
+    elif len(parts) == 3:
+        prefix, port = parts[1], parts[2]
+    else:
+        raise SystemExit(f"malformed interface '{iface}'")
+    try:
+        port = int(port)
+    except ValueError as e:
+        raise SystemExit(f"malformed interface '{iface}'") from e
+    return prefix, port
+
+
+def wait_for_stop(fifo_fd):
+    """Block until Wireshark stops us: either it closes the FIFO read end
+    (POLLERR on our write fd) or it sends SIGINT/SIGTERM. Watching the fd as
+    well as the signal matters because the signal may be missed -- Wireshark's
+    reliable stop is closing the pipe.
+
+    The handlers and the wakeup fd are restored before returning: the caller
+    still has telemetry work to do, and a second signal arriving during it
+    should terminate us the usual way rather than be swallowed."""
+    sigs = (signal.SIGINT, signal.SIGTERM)
+    old_handlers = {}
+    old_wakeup = None
+    rd, wr = os.pipe()
+    try:
+        os.set_blocking(wr, False)
+        old_wakeup = signal.set_wakeup_fd(wr)
+        for sig in sigs:
+            old_handlers[sig] = signal.signal(sig, lambda *_: None)
+
+        poller = select.poll()
+        poller.register(fifo_fd, select.POLLERR)
+        poller.register(rd, select.POLLIN)
+        poller.poll()
+    finally:
+        for sig, handler in old_handlers.items():
+            signal.signal(sig, handler)
+        if old_wakeup is not None:
+            signal.set_wakeup_fd(old_wakeup)
+        os.close(rd)
+        os.close(wr)
+
+
+def cmd_capture(iface, fifo, snaplen, queue, cfilter):
+    prefix, port = parse_iface(iface)
+    path = socket_path(prefix)
+
+    # Both are passed to the primary as telemetry parameters; check before
+    # opening anything so a bad one costs nothing.
+    if "," in fifo:
+        raise SystemExit(f"{comma_error('fifo path')}: {fifo}")
+    if cfilter and "," in cfilter:
+        raise SystemExit(comma_error("capture filter"))
+
+    # Blocking open of the FIFO Wireshark created. This rendezvous guarantees a
+    # reader is attached before we ask DPDK to open the write end (DPDK opens
+    # O_WRONLY|O_NONBLOCK and would fail with ENXIO if no reader were present).
+    # We then hold this fd for the whole session: it keeps the pipe from
+    # EOF-ing in the window before the DPDK capture thread opens its own write
+    # end, and it is our reliable stop signal -- POLLERR here means Wireshark
+    # closed the read end.
+    fifo_fd = os.open(fifo, os.O_WRONLY)
+
+    try:
+        tel = Telemetry(path)
+    except OSError as e:
+        os.close(fifo_fd)
+        raise SystemExit(f"cannot connect to DPDK telemetry at {path}: {e}") from e
+
+    params = [str(port), f"out={fifo}"]
+    if snaplen is not None:
+        params.append(f"snaplen={snaplen}")
+    if queue is not None and queue >= 0:
+        params.append(f"queue={queue}")
+    if cfilter:
+        params.append(f"filter={cfilter}")
+    cmd = CAPTURE_START_CMD + "," + ",".join(params)
+
+    try:
+        reply = tel.command(cmd)
+    except OSError as e:
+        os.close(fifo_fd)
+        tel.close()
+        raise SystemExit(f"capture start failed: {e}") from e
+
+    result = (reply or {}).get(CAPTURE_START_CMD) or {}
+    if "error" in result:
+        os.close(fifo_fd)
+        tel.close()
+        raise SystemExit(f"capture start failed: {result['error']}")
+
+    cap_id = result.get("id")
+
+    # Run until Wireshark stops us (signal or read-end close). DPDK now holds
+    # its own write end, so closing ours below won't EOF the reader prematurely.
+    wait_for_stop(fifo_fd)
+
+    # Ask DPDK to stop while we still hold the write end, so if the reader is
+    # still attached it drains the tail and writes the statistics block before
+    # the final EOF. If the read end is already gone DPDK has already stopped
+    # on the same hangup and this is a no-op.
+    if cap_id is not None:
+        try:
+            tel.command(f"{CAPTURE_STOP_CMD},{cap_id}")
+        except OSError:
+            pass
+
+    os.close(fifo_fd)
+    tel.close()
+
+
+# --- entry point ---------------------------------------------------------
+
+
+def main():
+    p = argparse.ArgumentParser(
+        prog="dpdk-wireshark-extcap.py",
+        allow_abbrev=False,
+        description="Wireshark extcap plugin for live packet capture from the "
+        "Ethernet ports of a running DPDK application. Normally "
+        "invoked by Wireshark; see the DPDK Wireshark extcap guide.",
+    )
+    p.add_argument("--version", action="version", version=f"%(prog)s {EXTCAP_VERSION}")
+
+    p.add_argument("--extcap-interfaces", action="store_true")
+    p.add_argument("--extcap-dlts", action="store_true")
+    p.add_argument("--extcap-config", action="store_true")
+    p.add_argument("--capture", action="store_true")
+    p.add_argument("--extcap-interface")
+    p.add_argument("--fifo")
+    p.add_argument("--extcap-capture-filter")
+    p.add_argument("--extcap-version", nargs="?")
+    p.add_argument("--snaplen", type=int)
+    p.add_argument("--queue", type=int)
+    args, _ = p.parse_known_args()
+
+    if args.extcap_interfaces:
+        cmd_interfaces()
+    elif args.extcap_dlts:
+        cmd_dlts(args.extcap_interface)
+    elif args.extcap_config:
+        cmd_config(args.extcap_interface)
+    elif args.capture:
+        if not args.extcap_interface or not args.fifo:
+            raise SystemExit("--capture requires --extcap-interface and --fifo")
+        cmd_capture(
+            args.extcap_interface,
+            args.fifo,
+            args.snaplen,
+            args.queue,
+            args.extcap_capture_filter,
+        )
+    elif args.extcap_capture_filter:
+        # Wireshark validates a capture filter by invoking the extcap with the
+        # filter but without --capture (see doc/extcap_example.py upstream).
+        # Wireshark already syntax-checks it with libpcap against our DLT
+        # (EN10MB) and DPDK compiles it again at capture start, so the only
+        # thing left to reject here is what the telemetry encoding cannot
+        # carry.
+        #
+        # No output means "accepted"; any output marks the filter invalid and
+        # its first line is shown to the user. The exit status must stay 0
+        # either way: Wireshark discards the output of a child that exited
+        # non-zero and reports the filter as unknown rather than invalid.
+        if "," in args.extcap_capture_filter:
+            print(comma_error("capture filter"))
+    else:
+        raise SystemExit("no extcap operation specified")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/usertools/meson.build b/usertools/meson.build
index 114d0a65b4..5debf73702 100644
--- a/usertools/meson.build
+++ b/usertools/meson.build
@@ -13,6 +13,7 @@ install_data([
             'dpdk-rss-flows.py',
             'dpdk-telemetry-exporter.py',
             'dpdk-telemetry-watcher.py',
+            'dpdk-wireshark-extcap.py',
         ],
         install_dir: 'bin')
 
-- 
2.53.0


      parent reply	other threads:[~2026-07-27 22:44 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24 21:11 [PATCH v6 0/5] Wireshark external capture for DPDK Stephen Hemminger
2026-07-24 21:11 ` [PATCH 1/5] pcapng: extend interface statistics Stephen Hemminger
2026-07-24 21:11 ` [PATCH 2/5] capture: infrastructure wireshark packet capture Stephen Hemminger
2026-07-24 21:11 ` [PATCH 3/5] test: add test for capture hooks Stephen Hemminger
2026-07-24 21:11 ` [PATCH 4/5] usertools/dpdk-wireshark-extcap.py: script for external capture Stephen Hemminger
2026-07-24 21:11 ` [PATCH 5/5] usertools/dpdk-dumpcap: add script for file capture Stephen Hemminger
2026-07-27 22:42 ` [PATCH v2 0/4] Wireshark external capture interface Stephen Hemminger
2026-07-27 22:42   ` [PATCH v2 1/4] pcapng: extend interface statistics Stephen Hemminger
2026-07-27 22:42   ` [PATCH v2 2/4] capture: infrastructure wireshark packet capture Stephen Hemminger
2026-07-27 22:42   ` [PATCH v2 3/4] test: add test for capture hooks Stephen Hemminger
2026-07-27 22:42   ` Stephen Hemminger [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260727224417.1419663-5-stephen@networkplumber.org \
    --to=stephen@networkplumber.org \
    --cc=dev@dpdk.org \
    --cc=reshma.pattan@intel.com \
    --cc=rjarry@redhat.com \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox