|WARNING| [v4,3/3] usertools: add script for wireshark capture
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Oct 13 12:44:06 CEST 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/171118
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-10-13
# DPDK Patch Review
## Summary
This patch series introduces a new packet capture library for DPDK that enables live traffic capture via telemetry API, along with Wireshark integration through an extcap plugin.
---
## ERRORS
### Patch 1/3: lib/capture/capture.c
**Error 1: Resource leak on error path after capture_link()**
*Line 380-384 in capture.c:*
```c
uint32_t idx = cap->idx;
capture_link(cap);
rte_thread_t thread_id;
int ret = rte_thread_create(&thread_id, NULL, capture_thread, cap);
if (ret != 0) {
capture_err(d, "thread start failed: %s", strerror(ret));
goto error_unlink;
}
```
After `capture_link(cap)` adds the capture to the global list, if `rte_thread_create()` fails, the code jumps to `error_unlink` which calls `capture_mp_broadcast(cap, CAPTURE_MP_STOP)`. If that broadcast fails (returns `-1`), the code logs an error but returns success (`return 0` at line 408), leaking the capture structure that remains linked in `capture_list`. The drain thread never ran to unlink it, and the cleanup path is skipped.
**Fix:** Change line 408 from `return 0;` to `goto error_callback_remove;` so that both the unlink and the full cleanup happen even when the broadcast fails:
```c
error_unlink:
if (capture_mp_broadcast(cap, CAPTURE_MP_STOP) < 0) {
CAPTURE_LOG(ERR,
"capture %u: secondary still attached, leaking ring and pool",
cap->idx);
capture_unlink(cap);
capture_remove_callbacks(cap);
/* fall through to free the capture struct */
} else {
capture_unlink(cap);
}
error_callback_remove:
capture_remove_callbacks(cap);
capture_free(cap);
return 0;
```
---
**Error 2: Statistics accumulation using `=` instead of `+=`**
*Line 371 in datapath.c:*
```c
static void
capture_sum_one(struct capture_total *t, const struct capture_stats *s)
{
t->accepted += s->accepted;
t->filtered += s->filtered;
t->nombuf += s->nombuf;
t->ringfull += s->ringfull;
}
```
This function correctly uses `+=`, but the pattern must be verified everywhere statistics are accumulated. After manual inspection of all call sites (`capture_sum_stats`, `capture_detach_queues`), the usage is correct. **Retracted** -- no error here.
---
**Error 3: MTU-related code in a capture library**
The capture library does not appear to handle MTU or scattered Rx, so the MTU-related rules from the guidelines do not apply here. **Not applicable.**
---
### Patch 1/3: lib/capture/output.c
**Error 4: File descriptor leak on capture_pcapng_open() failure path**
*Lines 92-110 in output.c:*
```c
if (fcntl(*fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
CAPTURE_LOG(ERR, "fcntl %s: %s", path, strerror(errno));
goto close_fd;
}
pcapng = rte_pcapng_fdopen(*fd, osname, NULL, appname, NULL);
if (pcapng == NULL) {
CAPTURE_LOG(ERR, "Add section block failed");
goto close_fd;
}
if (rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB, ifname, ifdescr, filter) < 0) {
CAPTURE_LOG(ERR, "Add interface for port %u:%s failed", port_id, ifname);
rte_pcapng_close(pcapng); /* closes fd */
pcapng = NULL;
}
goto cleanup;
close_fd:
close(*fd);
```
If `rte_pcapng_add_interface()` fails (line 104), the code calls `rte_pcapng_close(pcapng)` which closes `*fd`, then jumps to `cleanup` which frees `osname` and `ifdescr`. That path is correct. However, if `rte_pcapng_fdopen()` fails (line 99), the code jumps to `close_fd:` which calls `close(*fd)`, but `*fd` was already passed to `rte_pcapng_fdopen()`. If `rte_pcapng_fdopen()` takes ownership of the fd on entry (even on failure), this is a double-close. If it does not, the close is correct.
Inspection needed: does `rte_pcapng_fdopen()` take ownership of the fd on failure? Without reading `lib/pcapng/rte_pcapng.c`, this cannot be determined. **Flag as potential double-close** -- the caller should verify that `rte_pcapng_fdopen()` does not close the fd on failure, or adjust the error path.
**Likely correct** if `rte_pcapng_fdopen()` does not close on failure, but the pattern is fragile. The safe pattern is:
```c
pcapng = rte_pcapng_fdopen(*fd, osname, NULL, appname, NULL);
if (pcapng == NULL) {
CAPTURE_LOG(ERR, "Add section block failed");
goto close_fd; /* only if fdopen does NOT close on failure */
}
```
Or if `fdopen` closes on failure:
```c
pcapng = rte_pcapng_fdopen(*fd, osname, NULL, appname, NULL);
if (pcapng == NULL) {
CAPTURE_LOG(ERR, "Add section block failed");
*fd = -1; /* prevent double-close */
goto cleanup;
}
```
**Recommendation:** Document the ownership in a comment, or check the pcapng API.
---
### Patch 1/3: lib/capture/multiproc.c
**Error 5: Missing check on rte_mp_action_register() return**
*Lines 177-181 in multiproc.c:*
```c
void
capture_mp_register(void)
{
if (rte_mp_action_register(CAPTURE_MP, capture_mp_handler) < 0 &&
rte_errno != ENOTSUP)
CAPTURE_LOG(NOTICE, "Could not register ipc action: %s",
rte_strerror(rte_errno));
}
```
If `rte_mp_action_register()` fails with an error other than `ENOTSUP`, a log is emitted but the function returns successfully. Later, when `capture_mp_broadcast()` is called, it will fail because no handler is registered. The capture will proceed without multi-process support, which may be acceptable, but the error is silent.
**Not an error** -- this is defensive logging. The code assumes single-process is a valid fallback. The guidelines do not require failing here. **Retracted.**
---
### Patch 2/3: app/test/test_capture.c
**Error 6: Use of deprecated REGISTER_FAST_TEST macro**
*Line 688 in test_capture.c:*
```c
REGISTER_FAST_TEST(capture_autotest, NOHUGE_OK, ASAN_OK, test_capture);
```
The guidelines state:
> Do NOT flag `REGISTER_FAST_TEST` using `NOHUGE_OK`/`ASAN_OK` macros (this is the correct current format).
**Retracted** -- this is correct.
---
### Patch 3/3: usertools/dpdk-wireshark-extcap.c
**Error 7: Privilege not dropped before opening FIFO in cmd_capture()**
*Lines 930-937 in dpdk-wireshark-extcap.c:*
```c
fifo_fd = open(fifo, O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
if (fifo_fd < 0)
err(1, "cannot open fifo %s", fifo);
if (fstat(fifo_fd, &sb) != 0 || !S_ISFIFO(sb.st_mode))
errx(1, "%s is not a fifo", fifo);
if (tel_handshake(&t, fd) < 0)
err(1, "no banner from DPDK telemetry");
```
The FIFO open is the first thing `cmd_capture()` does, before `tel_handshake()`. The telemetry handshake reads the banner unprivileged (per the comment at line 363: "Runs unprivileged: this is the first point at which the primary gets to send us anything"). But the FIFO open in `cmd_capture()` happens *before* that, so if `cmd_capture()` is called while still privileged, the FIFO is opened privileged.
Looking at `main()`: `priv_drop()` is called at line 1107, then `cmd_capture()` is invoked at line 1121. So the FIFO open is **unprivileged**. **Retracted** -- the drop happens before capture.
---
### Patch 3/3: usertools/dpdk-wireshark-extcap.c
**Error 8: Missing validation of privileged_exec in cmd_capture()**
*Lines 917-925 in dpdk-wireshark-extcap.c:*
```c
if (o.capture) {
struct stat sb;
if (o.fifo == NULL)
errx(1, "--capture requires --fifo");
why = param_problem(o.fifo);
if (why != NULL)
errx(1, "fifo path %s", why);
```
The code comment at line 102 states:
> After a privileged start the plugin is more restrictive: the environment is ignored, only `/var/run/dpdk` is searched, and the output must be a FIFO.
However, the check `if (!S_ISFIFO(sb.st_mode))` in `cmd_capture()` at line 937 enforces the FIFO requirement unconditionally, not just after a privileged start. The guidelines do not require different behavior here, and the FIFO check is security-correct in both cases. **Not an error** -- the restriction is documented, not a bug.
---
## WARNINGS
### Patch 1/3: lib/capture/meson.build
**Warning 1: Missing release notes for internal API**
*Lines 19-23 in meson.build:*
```
if dpdk_conf.has('RTE_HAS_LIBPCAP')
sources += files('filter.c')
ext_deps += pcap_dep
else
warning('libpcap is missing, capture filtering will be disabled')
endif
```
The release notes in `doc/guides/rel_notes/release_26_11.rst` document the new library but do not mention the optional `libpcap` dependency or the reduced functionality when built without it. This is not a public API, but the operational difference is significant.
**Recommendation:** Add a note in the release notes or the library documentation about the `libpcap` dependency.
---
### Patch 1/3: lib/capture/datapath.c
**Warning 2: Static function pointer array not const**
*Lines 31-32 in private.h:*
```c
static struct capture_cb_list capture_cb_freelist =
TAILQ_HEAD_INITIALIZER(capture_cb_freelist);
```
This is a TAILQ head, not a function pointer array. **Not applicable.**
---
### Patch 1/3: Documentation
**Warning 3: RST documentation uses bullet lists instead of definition lists**
*Lines 55-64 in doc/guides/prog_guide/capture_lib.rst:*
```rst
``/ethdev/capture/start`` takes the port id as its first parameter, followed by
``name=value`` options separated by commas:
``out=<path>``
Required. Path of the output, which must already exist and be a FIFO.
``snaplen=<n>``
Number of bytes to capture from each packet. Default is 262144;
``snaplen=0`` means capture the whole packet.
```
This is already a definition list. **Not applicable.**
The documentation at lines 109-124 uses a definition list for the statistics fields. **Correct.**
---
## INFO
### Patch 1/3: Pre-existing code note
The capture library is entirely new code, so there is no pre-existing code to note.
---
### Patch 3/3: Standalone C program
**Info 1: dpdk-wireshark-extcap privilege model**
The extcap plugin uses a careful privilege drop model, similar to `dumpcap`. Privilege is used only for `connect()` to the telemetry socket, then dropped permanently before any reply is parsed. The drop is verified (lines 151-168) and includes capability clearing on Linux. This is a robust design for a setuid helper.
---
## Final Assessment
The patch series introduces a significant new feature with generally high code quality. The primary concern is **Error 1** (resource leak on thread creation failure), which must be fixed. The other errors are either retracted after inspection or require verification of external API behavior (pcapng fdopen ownership).
---
Review-Result: ERROR
More information about the test-report
mailing list