|WARNING| [v3] net/af_packet: add option to ignore outgoing packets

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Tue Oct 6 09:24:09 CEST 2026


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

_AI Code Review_

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

# DPDK Patch Review: net/af_packet: add option to ignore outgoing packets

## Summary
Overall this is a well-structured patch that adds a new devarg to the AF_PACKET PMD. The implementation is mostly correct, but there are several issues to address.

---

## Errors

### 1. Error path resource leak in `rte_pmd_init_internals()`
**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Location:** Lines 1031-1039

The new `setsockopt()` call for `PACKET_IGNORE_OUTGOING` jumps to the `error` label on failure, but this occurs inside the queue setup loop after `qsockfd` has been created via `socket()`. The `error` label does not close `qsockfd`, causing a file descriptor leak on this error path.

**Fix:** Ensure `qsockfd` is closed before jumping to `error`:
```c
if (ignore_outgoing) {
	rc = setsockopt(qsockfd, SOL_PACKET, PACKET_IGNORE_OUTGOING,
			&ignore_outgoing, sizeof(ignore_outgoing));
	if (rc == -1) {
		PMD_LOG_ERRNO(ERR,
			"%s: could not set PACKET_IGNORE_OUTGOING on AF_PACKET socket for %s",
			name, pair->value);
		close(qsockfd);  /* ADD THIS */
		goto error;
	}
}
```

**Note:** This same leak pattern exists for the `PACKET_QDISC_BYPASS` and `PACKET_FANOUT` `setsockopt()` calls earlier in the function (pre-existing bug, not introduced by this patch). Consider fixing all three in a follow-up patch.

---

### 2. Missing error check on `rte_vdev_init()` return value
**File:** `app/test/test_pmd_af_packet.c`  
**Location:** Lines 992-994

```c
ret = rte_vdev_init("net_af_packet_ign_bad",
		    "iface=" TAP_DEV_NAME ",ignore_outgoing=2");
TEST_ASSERT(ret != 0, "Expected failure with ignore_outgoing=2");
```

If `rte_vdev_init()` **succeeds** when it should have failed (returning 0), the test asserts failure correctly. However, if it succeeds, the created vdev is never cleaned up (`rte_vdev_uninit()` is never called), leaking the device and potentially affecting subsequent tests.

**Fix:**
```c
ret = rte_vdev_init("net_af_packet_ign_bad",
		    "iface=" TAP_DEV_NAME ",ignore_outgoing=2");
if (ret == 0) {
	/* Cleanup the device that shouldn't have been created */
	rte_vdev_uninit("net_af_packet_ign_bad");
}
TEST_ASSERT(ret != 0, "Expected failure with ignore_outgoing=2");
```

---

### 3. Test cleanup on assertion failure leaks devices
**File:** `app/test/test_pmd_af_packet.c`  
**Location:** Lines 988-991

The test uses `TEST_ASSERT()` after the `fail_rx_ign_on:` label:
```c
fail_rx_ign_on:
	TEST_ASSERT(err == NULL, "%s", err);
	TEST_ASSERT(rx_off > 0, "Expected packets with ignore_outgoing=0");
	TEST_ASSERT(rx_on == 0, "Expected no packets with ignore_outgoing=1");
```

If any early `goto fail_rx_ign_on` occurs (e.g., port creation failure on line 924), the cleanup for `rx_port_ign_out_on` never happens, but the subsequent `TEST_ASSERT(err == NULL, "%s", err)` will abort the test, skipping all later cleanup code (the `rte_vdev_init()` call and its cleanup).

This is not a resource leak in the traditional sense (the test exits), but it violates the test pattern where cleanup should happen before assertions.

**Fix:** Move all `TEST_ASSERT()` calls to after the cleanup cascade finishes, or structure the test so that cleanup always happens before any assertion that could abort.

---

## Warnings

### 1. Implicit comparison on error variable
**File:** `app/test/test_pmd_af_packet.c`  
**Location:** Line 988

```c
TEST_ASSERT(err == NULL, "%s", err);
```

This is correct (explicit comparison against `NULL`). No issue here.

### 2. Missing check for `parse_uint()` range enforcement
**File:** `drivers/net/af_packet/rte_eth_af_packet.c`  
**Location:** Lines 1290-1293

```c
if (strstr(pair->key, ETH_AF_PACKET_IGNORE_OUTGOING_ARG) != NULL) {
	if (parse_uint(pair->key, pair->value, &ignore_outgoing, 1) < 0)
		return -1;
	continue;
}
```

The `parse_uint()` call uses `1` as the `max` parameter, which should enforce `ignore_outgoing` is 0 or 1. However, you should verify that `parse_uint()` enforces this correctly. Looking at the test (lines 992-994), you test that `ignore_outgoing=2` fails, which is good.

**Recommendation:** Add a comment clarifying the range check:
```c
/* parse_uint enforces 0 or 1 via max parameter */
if (parse_uint(pair->key, pair->value, &ignore_outgoing, 1) < 0)
	return -1;
```

### 3. Test uses blocking delay in busy loop
**File:** `app/test/test_pmd_af_packet.c`  
**Location:** Lines 967-974

```c
while (elapsed < LOOPBACK_TIMEOUT_US) {
	rx_off += do_rx_burst(rx_port_ign_out_off, 0, bufs, BURST_SIZE);
	rx_on += do_rx_burst(rx_port_ign_out_on, 0, bufs, BURST_SIZE);
	if (rx_off >= nb_tx)
		break;
	rte_delay_us_block(STATS_POLL_INTERVAL_US);
	elapsed += STATS_POLL_INTERVAL_US;
}
```

This pattern is consistent with other tests in the file (e.g., `test_af_packet_qdisc_bypass()`), so it's acceptable. No change needed, but note that `rte_delay_us_block()` is a busy-wait and may waste CPU in test environments.

---

## Info / Style Notes

### 1. Test could check packet content
The test verifies that `ignore_outgoing=1` drops packets and `ignore_outgoing=0` receives them, but it does not verify that the **received packets are the transmitted ones**. For a more robust test, you could check packet payload or sequence numbers.

This is optional, but would improve test coverage.

### 2. Comment in test could clarify kernel behavior
**File:** `app/test/test_pmd_af_packet.c`  
**Location:** Lines 916-919

The comment is clear, but you could add a note that `PACKET_OUTGOING` is a kernel packet type (not a DPDK concept) to help future readers unfamiliar with AF_PACKET internals.

### 3. Release notes formatting
**File:** `doc/guides/rel_notes/release_26_11.rst`  
**Location:** Lines 67-71

The release notes entry is well-written and follows the existing format. No issues.

### 4. Documentation clarity
**File:** `doc/guides/nics/af_packet.rst`  
**Location:** Lines 28-30

The documentation clearly explains the new parameter. Consider adding a note about when this is useful (e.g., "Use this when the application only wants to receive external traffic, not packets sent by the host.").

Optional improvement.

---

## Positive Observations

1. **Test coverage:** The patch includes a comprehensive functional test that validates both `ignore_outgoing=0` and `ignore_outgoing=1`, plus bounds checking for invalid values.

2. **Documentation:** Release notes, user guide, and parameter help are all updated consistently.

3. **Backward compatibility:** Default is 0, preserving existing behavior.

4. **Code style:** The patch follows DPDK naming conventions and formatting standards.

5. **Error handling:** The `setsockopt()` call properly checks for errors and logs them.

---

## Recommendations

1. **High Priority:** Fix the file descriptor leak on the `PACKET_IGNORE_OUTGOING` error path (Error #1).

2. **High Priority:** Fix the vdev cleanup in the test when `rte_vdev_init()` unexpectedly succeeds (Error #2).

3. **Medium Priority:** Restructure test cleanup to ensure devices are always cleaned up before assertions (Error #3).

4. **Low Priority:** Add a comment documenting the `parse_uint()` range enforcement (Warning #2).

5. **Optional:** Consider a follow-up patch to fix the pre-existing fd leak for `PACKET_QDISC_BYPASS` and `PACKET_FANOUT` error paths (mentioned in Error #1 note).


More information about the test-report mailing list