|WARNING| [v3] examples/rpcapd: demo version of packet capture daemon
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Sep 22 23:52:41 CEST 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/169946
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-09-22
# DPDK Patch Review: examples/rpcapd
## Summary
This patch adds a new example application implementing the rpcap protocol for remote packet capture. Overall, the code demonstrates good structure and follows many DPDK conventions. Below are findings organized by severity.
---
## Errors
### 1. Resource leak on error path in `handle_startcap()`
**Location:** `examples/rpcapd/main.c:665`
The `data_listen` socket is not closed if `rte_pdump_enable_bpf()` fails after the socket has been successfully opened.
```c
if (rte_pdump_enable_bpf(...) < 0) {
RPCAPD_LOG(ERR, "rte_pdump_enable_bpf port %u failed: %s",
s->port, rte_strerror(rte_errno));
close(data_listen); // <-- this is correctly placed
stop_capture(s);
return rpcap_send_error(fd, 0, "cannot enable capture");
}
```
Wait, reviewing more carefully - the code DOES close `data_listen` on the error path. However, there's a gap: if `rpcap_send_msg()` fails at line 689, the function returns -1 without closing `data_listen`.
**Fix:**
```c
if (rpcap_send_msg(fd, RPCAP_MSG_STARTCAP_REPLY, 0, &reply, sizeof(reply)) < 0) {
close(data_listen); // <-- add this
stop_capture(s);
return -1;
}
```
### 2. Resource leak in `dpdk_init()` on allocation failure
**Location:** `examples/rpcapd/main.c:1239-1251`
When `strdup()` calls fail in the EAL argv construction loop, the function returns -1 without freeing the previously allocated strings in `eal_argv`.
**Fix:** Add cleanup on all error paths:
```c
if (eal_argv[i] == NULL) {
for (unsigned int j = 0; j < i; j++)
free(eal_argv[j]);
free(eal_argv);
return -1;
}
```
### 3. `eal_argv` array never freed (memory leak)
**Location:** `examples/rpcapd/main.c:1230-1258`
After `rte_eal_init()` returns, the `eal_argv` array and all its string elements are never freed. This is a one-time leak at startup, but should be cleaned up.
**Fix:** After `rte_eal_init()` (line 1260), add:
```c
for (unsigned int j = 0; j < eal_argc; j++)
free(eal_argv[j]);
free(eal_argv);
```
### 4. Integer overflow risk in `handle_updatefilter()`
**Location:** `examples/rpcapd/main.c:575`
When checking filter instruction count, the multiplication `nitems * sizeof(winsn)` is performed on `uint32_t` values, which could overflow before comparison with `plen` (also `uint32_t`). For `MAX_FILTER_INSNS = 4096` this is safe, but the code does not enforce the relationship.
**Fix:** Perform the size check with widening:
```c
if (nitems > MAX_FILTER_INSNS ||
plen < (uint64_t)nitems * sizeof(winsn)) {
```
---
## Warnings
### 1. Missing release notes for experimental API usage
**Location:** `examples/rpcapd/main.c:38-39`
The example uses `rte_bpf_convert()` which is an experimental API (from `rte_bpf.h`). The release notes mention the example but do not call out that it relies on experimental APIs.
**Suggestion:** Add a note in `doc/guides/rel_notes/release_26_11.rst` that the example uses experimental `rte_bpf_convert()`.
### 2. Queue-related structure allocated with `malloc()` instead of `rte_zmalloc_socket()`
**Location:** `examples/rpcapd/main.c:105-107, 1082-1085`
The `session` structure is stack-allocated in `handle_client()`. This is acceptable for control plane state, but the structure contains pointers to rings and mempools that are shared-memory DPDK objects. The `session` structure itself does not need hugepage backing since it's process-local.
**Assessment:** Not a bug - the session structure is local control state, not a queue descriptor or shared structure. The ring and mempool it points to are correctly allocated with DPDK APIs. No action needed.
### 3. `rte_pktmbuf_free_bulk()` used in error path with potentially mixed pools
**Location:** `examples/rpcapd/main.c:867`
In `process_ring()` error handling, when `send_iov_full()` fails mid-burst, the remaining packets are freed with `rte_pktmbuf_free_bulk()`. Since these mbufs all came from the same capture mempool (`s->mp`), this is safe. However, the code could be clearer about this invariant.
**Assessment:** Actually correct - all packets in the ring came from the same mempool created in `handle_startcap()`. No issue.
### 4. `bool` not used for true/false variables
**Location:** Multiple locations
Several variables that are purely true/false are declared as `int` instead of `bool`:
- `main.c:1334`: `int srv_fd;` should be `int` (it's a file descriptor, not a boolean)
Actually, reviewing the code, most boolean variables ARE correctly declared as `bool` in the `session` structure (lines 87-89). This is good practice.
**Assessment:** Code already follows the guideline. No issue.
### 5. Statistics `svrcapt` uses `=` instead of `+=`
**Location:** `examples/rpcapd/main.c:976`
Wait, reviewing the code:
```c
struct rpcap_stats reply = {
.ifrecv = htonl((uint32_t)es.ipackets),
.ifdrop = htonl((uint32_t)es.ierrors),
.krnldrop = 0,
.svrcapt = htonl(s->npkt),
};
```
This is an assignment to a local structure being sent in a reply, not accumulation. And `s->npkt` is incremented with `s->npkt++` at line 821. This is correct.
**Assessment:** No issue.
---
## Info / Style
### 1. Explicit NULL comparisons
The code consistently uses explicit comparisons (`if (ptr == NULL)`, `if (fd < 0)`) which follows DPDK style. Good.
### 2. `rte_atomic_*` usage
The `quit_signal` variable correctly uses `rte_atomic_*_explicit()` with appropriate memory ordering (`relaxed` for a simple flag). Good.
### 3. Documentation matches implementation
The limitations section in `rpcapd.rst` accurately describes the single-client limitation, TCP-only transport, and lack of original length reporting. Good.
### 4. Release notes
The release notes entry is present and describes the feature at an appropriate level. Good.
### 5. Maintainer entry
MAINTAINERS file correctly updated with the new files. Good.
### 6. Hardcoded overhead constant
**Location:** `examples/rpcapd/main.c:65`
The code uses `DEFAULT_SNAPLEN = RTE_MBUF_DEFAULT_DATAROOM`, which is a reasonable default and not an Ethernet overhead calculation issue. The snaplen is provided by the client in the STARTCAP request. No issue.
---
## Summary of Required Fixes
### Errors (must fix):
1. Close `data_listen` socket when `rpcap_send_msg()` fails in `handle_startcap()`
2. Free allocated `eal_argv` strings on error in `dpdk_init()`
3. Free `eal_argv` array and strings after successful `rte_eal_init()`
4. Use widening cast in filter size check to prevent overflow
### Warnings (should fix):
1. Mention experimental API dependency in release notes
### Code Quality:
- Overall the code demonstrates good resource management patterns
- Atomics usage is correct
- Documentation is thorough and matches implementation
- Error handling is generally good with the exceptions noted above
The patch represents a substantial, well-structured example application. The resource leaks identified are the primary concerns that must be addressed before merging.
More information about the test-report
mailing list