|WARNING| net/af_packet: add capture direction option

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Sun Oct 4 06:55:26 CEST 2026


Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/170525

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-10-04

# DPDK Patch Review: net/af_packet: add capture direction option

## Summary

This patch adds a `capture_dir` option to the AF_PACKET PMD to control packet filtering (ingress, egress, or both). The implementation is mostly correct but has one **Error**-level correctness bug and several style/documentation issues.

---

## Errors

### 1. Resource leak on early return paths in `eth_af_packet_rx()`

**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Lines:** 203-212

The `goto release_frame` paths skip allocation and jump directly to the frame release code. However, when VLAN stripping or timestamping is enabled, the existing frame release path expects an `mbuf` variable to be initialized. The label `release_frame` appears later in the function at a point where `mbuf` has been allocated. If `goto release_frame` is reached before `mbuf = rte_pktmbuf_alloc()`, and the code path attempts to free or manipulate `mbuf`, this is a use-after-free or null dereference.

**Review of the existing code context:** The original `eth_af_packet_rx()` function allocates `mbuf` before any `goto release_frame`. The new capture_dir check is placed **before** the allocation, so the `goto release_frame` jumps to cleanup code that may reference an uninitialized `mbuf`.

**Suggested fix:**

Move the capture_dir filtering to after the mbuf allocation, or ensure the `release_frame` label does not access `mbuf` when it is NULL. Preferred approach: move the filter check after allocation.

```c
/* allocate the next mbuf */
mbuf = rte_pktmbuf_alloc(pkt_q->mb_pool);
if (unlikely(mbuf == NULL)) {
	/* ... */
}

/* drop frames that do not match capture_dir */
if (pkt_q->capture_dir != RTE_AF_PACKET_CAPTURE_DIR_INOUT) {
	sll = (struct sockaddr_ll *)((char *)ppd + TPACKET_ALIGN(sizeof(*ppd)));
	if (sll->sll_pkttype == PACKET_OUTGOING) {
		if (pkt_q->capture_dir == RTE_AF_PACKET_CAPTURE_DIR_IN) {
			rte_pktmbuf_free(mbuf);
			goto release_frame;
		}
	} else {
		if (pkt_q->capture_dir == RTE_AF_PACKET_CAPTURE_DIR_OUT) {
			rte_pktmbuf_free(mbuf);
			goto release_frame;
		}
	}
}
```

---

## Warnings

### 1. Test leaks resources on early failure

**File:** `app/test/test_pmd_af_packet.c`  
**Lines:** 933-990

The test function `test_af_packet_capture_dir()` allocates TX port and RX ports, then has multiple early error paths (`goto out`). If an error occurs after `create_af_packet_port()` but before the port is added to the cleanup list, the port is not freed. The test attempts to track `n_rx` to limit cleanup, but if port creation succeeds and configuration fails, `n_rx` is incremented and cleanup will try to stop/close a port that was never successfully configured.

**Suggested fix:**

Initialize all port IDs to `RTE_MAX_ETHPORTS` (invalid). Only clean up ports whose IDs are valid. Set port ID to invalid after cleanup.

```c
for (m = 0; m < RTE_DIM(modes); m++)
	rx_ports[m] = RTE_MAX_ETHPORTS;
tx_port = RTE_MAX_ETHPORTS;

/* ... */

out:
	for (i = 0; i < RTE_DIM(modes); i++) {
		if (rx_ports[i] < RTE_MAX_ETHPORTS) {
			rte_eth_dev_stop(rx_ports[i]);
			rte_eth_dev_close(rx_ports[i]);
			rte_vdev_uninit(names[i]);
		}
	}
	if (tx_port < RTE_MAX_ETHPORTS) {
		rte_eth_dev_stop(tx_port);
		rte_eth_dev_close(tx_port);
		rte_vdev_uninit("net_af_packet_cap_tx");
	}
```

### 2. Test does not free allocated mbufs on failure

**File:** `app/test/test_pmd_af_packet.c`  
**Lines:** 933-990

When `alloc_tx_mbufs()` succeeds but TX fails or the test exits early, the allocated mbufs are not freed. After the `alloc_tx_mbufs(bufs, 4)` call, if an error path is taken, `bufs` array may contain allocated mbufs that are never transmitted or freed.

**Suggested fix:**

Track whether mbufs are allocated. Free them on error paths:

```c
if (allocated > 0 && nb_tx < allocated) {
	for (i = nb_tx; i < allocated; i++)
		rte_pktmbuf_free(bufs[i]);
}
```

### 3. Missing Doxygen comment for new enum

**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Lines:** 42-52

The new `enum rte_af_packet_capture_dir` is internal to the driver (not in a header), but DPDK style encourages documentation even for internal types when they represent user-visible options.

**Suggested fix:**

Add a comment block above the enum explaining what each value means.

```c
/**
 * Packet capture direction for AF_PACKET PMD.
 * Controls which packets are received on an AF_PACKET socket.
 */
enum rte_af_packet_capture_dir {
	RTE_AF_PACKET_CAPTURE_DIR_INVALID = -1,
	RTE_AF_PACKET_CAPTURE_DIR_IN,      /**< Ingress only */
	RTE_AF_PACKET_CAPTURE_DIR_OUT,     /**< Egress only */
	RTE_AF_PACKET_CAPTURE_DIR_INOUT,   /**< Both directions */
};
```

---

## Info

### 1. Implicit NULL comparison in `get_capture_dir()`

**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Line:** 896

```c
if (!capture_dir)
```

DPDK style requires explicit comparison:

```c
if (capture_dir == NULL)
```

### 2. Duplicate or missing blank line

**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Lines:** 889-891

There is no blank line between function `get_fanout()` and `get_capture_dir()`. DPDK style requires a blank line between function definitions.

**Suggested fix:**

```c
}

static enum rte_af_packet_capture_dir
```

### 3. Type size mismatch for enum storage

**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Line:** 70

```c
uint8_t capture_dir;
```

The enum `rte_af_packet_capture_dir` may be signed (contains `-1`), but it is stored in a `uint8_t`. This works but is semantically inconsistent.

**Suggested fix:**

```c
enum rte_af_packet_capture_dir capture_dir;
```

Or, if memory layout is critical:

```c
int8_t capture_dir;
```

### 4. Test could assert more specific expected counts

**File:** `app/test/test_pmd_af_packet.c`  
**Lines:** 992-994

The test asserts `rx[1] > 0` and `rx[2] > 0`, but `nb_tx` is known. Checking `rx[1] == nb_tx` would catch partial reception bugs.

**Suggested fix:**

```c
TEST_ASSERT(rx[1] == nb_tx, "Expected all %u packets with capture_dir=out, got %u", nb_tx, rx[1]);
TEST_ASSERT(rx[2] == nb_tx, "Expected all %u packets with capture_dir=inout, got %u", nb_tx, rx[2]);
```

---

## Positive Notes

- The feature is well-motivated and matches libpcap behavior.
- The test coverage is comprehensive (all three modes plus invalid input).
- Documentation is updated (user guide, release notes, param string).
- The approach (checking `sll_pkttype`) is correct per libpcap's `linux_check_direction()`.
- Release notes are properly placed in the current release file.

---

## Recommendations

1. **Fix the Error**: Move the capture_dir check after mbuf allocation to avoid potential use-after-free or null dereference.
2. **Fix test cleanup**: Ensure ports are only cleaned up if successfully created, and free allocated mbufs on error paths.
3. **Apply style fixes**: Explicit NULL comparison, blank line between functions, consistent enum storage type.


More information about the test-report mailing list