Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ Each bullet below is a standalone, one-PR-sized deliverable unless noted otherwi
- [ ] **3. `SocketAddrV6` through `UdpSocket`** — `bind` / `send_to` / `recv_from` / `connect` / `local_addr` / `peer_addr` accept and return IPv6 addresses. `set_only_v6` / `only_v6` socket option. `AddressFamily` state on the socket so the send/recv paths pick the right wire format.
- [x] **4. IPv6 hardware offload flags** — TX: set `RTE_MBUF_F_TX_IPV6` + `RTE_MBUF_F_TX_UDP_CKSUM` with the IPv6 pseudo-header checksum in the UDP field. RX: validate IPv6 UDP checksums (honor `PKT_RX_L4_CKSUM_GOOD`). Software fallback on NICs without support. `has_tx_ipv6_cksum_offload()` accessor. *(PR [#55](https://github.com/gspivey/dpdk-stdlib-rust/pull/55), 8 tests)*
- [x] **5. Link-local / scope IDs / solicited-node multicast MAC** — `fe80::/10` handling, `%ifindex` scope parsing, `33:33:ff:XX:XX:XX` MAC derivation from the low 24 bits of the target IPv6 address. Prereq for task 6 (NDP).
- [ ] **6. NDP (Neighbor Discovery Protocol)** — `NdpHandler` mirroring `ArpHandler`: Neighbor Solicitation and Neighbor Advertisement message types, atomic NDP cache with fast-path lookup, auto-resolution on send, gratuitous NA on bind (parallel to our Gratuitous ARP feature), and seeding the cache from `/proc/net/ipv6_neigh` on Linux.
- [x] **6. NDP (Neighbor Discovery Protocol)** — `NdpHandler` mirroring `ArpHandler`: Neighbor Solicitation and Neighbor Advertisement message types, atomic NDP cache with fast-path lookup, auto-resolution on send, gratuitous NA on bind (parallel to our Gratuitous ARP feature), and seeding the cache from `/proc/net/ipv6_neigh` on Linux. *(PR [#59](https://github.com/gspivey/dpdk-stdlib-rust/pull/59), 32 tests)*
- [x] **7. ICMPv6 echo reply** — auto-respond to `ping6`, parallel to our existing IPv4 ICMP echo reply.
- [x] **8. ICMPv6 error handling** — Destination Unreachable, Packet Too Big (with Next-Hop MTU), Time Exceeded, and Parameter Problem parsed and matched back to the originating socket. Plugs into the existing per-socket error queue (introduced for IPv4 ICMP errors) so `take_error()` works for IPv6 destinations too. *(PR [#58](https://github.com/gspivey/dpdk-stdlib-rust/pull/58), 24 tests)*
- [ ] **9. Performance tests** — TRex PPS run at 64 / 512 / 1400B, plus the synthetic CPU-only benchmark, compared against the IPv4 baseline. Results posted to `docs/perf-test-log.md`. No PPS regression vs IPv4 required to cross off the IPv6 feature.
Expand Down
76 changes: 76 additions & 0 deletions docs/perf-test-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,82 @@ Each entry captures the git context, test configuration, results, and analysis.

---

## Run #18: NDP (Neighbor Discovery Protocol) — No Regression

| Field | Value |
|-------|-------|
| **Date** | 2026-05-20 |
| **Git Hash** | `1dd5643` |
| **Branch** | `agent/ndp` |
| **PR** | [#59](https://github.com/gspivey/dpdk-stdlib-rust/pull/59) |
| **GH Actions Run** | [26163492881](https://github.com/gspivey/dpdk-stdlib-rust/actions/runs/26163492881) |
| **Instance Type** | c6in.xlarge (4 vCPU, 6.25 Gbps baseline / 30 Gbps burst) |
| **Traffic Generator** | TRex |

### Changes Since Run #17

1. **`1dd5643` — NDP (Neighbor Discovery Protocol) module.** New `dpdk-udp/src/ndp.rs` implementing RFC 4861 Neighbor Solicitation/Advertisement: parse/build NS and NA frames, NdpCache with atomic fast-path for single-peer steady state, NdpHandler mirroring ArpHandler, gratuitous NA on bind, and `/proc/net/ipv6_neigh` cache seeding. 32 unit tests. This is a new module — zero changes to the existing RX/TX hot path.

### Results: Hardware (TRex)

#### 64-byte packets

| Target PPS | rust-dpdk RX | Drop | Kernel RX | Drop | native-dpdk RX | Drop |
|-----------|-------------|------|----------|------|---------------|------|
| 70,000 | 69,000 | 1.4% | 69,000 | 1.4% | 70,000 | 0.0% |
| 140,000 | 139,000 | 0.7% | 139,000 | 0.7% | 140,000 | 0.0% |
| 350,000 | 348,997 | 0.3% | 348,979 | 0.3% | 350,000 | 0.0% |
| 700,000 | 697,788 | 0.3% | 382,398 | 45.4% | 699,686 | 0.04% |

#### 512-byte packets

| Target PPS | rust-dpdk RX | Drop | Kernel RX | Drop | native-dpdk RX | Drop |
|-----------|-------------|------|----------|------|---------------|------|
| 70,000 | 69,000 | 1.4% | 69,000 | 1.4% | 70,000 | 0.0% |
| 140,000 | 139,000 | 0.7% | 139,000 | 0.7% | 140,000 | 0.0% |
| 350,000 | 349,000 | 0.3% | 348,916 | 0.3% | 350,000 | 0.0% |
| 700,000 | 696,778 | 0.5% | 431,308 | 38.4% | 694,499 | 0.8% |

#### 1400-byte packets (near MTU)

| Target PPS | rust-dpdk RX | Drop | Kernel RX | Drop | native-dpdk RX | Drop |
|-----------|-------------|------|----------|------|---------------|------|
| 70,000 | 69,000 | 1.4% | 69,000 | 1.4% | 70,000 | 0.0% |
| 140,000 | 139,000 | 0.7% | 139,000 | 0.7% | 140,000 | 0.0% |
| 350,000 | 349,000 | 0.3% | 348,651 | 0.4% | 350,000 | 0.0% |
| 700,000 | 475,607 | 0.2% | 437,947 | 8.1% | 457,658 | 4.0% |

#### 8500-byte packets (jumbo)

| Target PPS | rust-dpdk RX | Drop | Kernel RX | Drop | native-dpdk RX | Drop |
|-----------|-------------|------|----------|------|---------------|------|
| 70,000 | 69,000 | 1.4% | 36,107 | 48.4% | 70,000 | 0.0% |
| 140,000 | 76,140 | 2.7% | 77,729 | 0.8% | 76,031 | 2.9% |
| 350,000 | 77,675 | 0.8% | 77,893 | 0.5% | 77,976 | 0.4% |

#### tokio-dpdk (async compat layer)

| Target PPS | tokio-dpdk RX | Drop |
|-----------|--------------|------|
| 70,000 | 69,000 | 1.4% |
| 140,000 | 139,000 | 0.7% |
| 350,000 | 306,839 | 12.3% |
| 700,000 | 307,556 | 56.1% |

### Analysis

**No performance regression from NDP module.** NDP is a standalone new module (`ndp.rs`) that adds no code to the existing UDP RX/TX hot path. The benchmark results confirm zero measurable impact:

- **rust-dpdk at 700K PPS, 64B**: 697,788 RX (0.3% drop) — matches Run #15's 665K and exceeds it, within normal variance
- **rust-dpdk at 700K PPS, 512B**: 696,778 RX (0.5% drop) — consistent with Run #15's 657K
- **rust-dpdk at 700K PPS, 1400B**: 475,607 RX (0.2% drop) — matches Run #15's 470K

**rust-dpdk vs native-dpdk parity**: At 700K PPS with 64B packets, Rust delivers 697,788 vs native C's 699,686 — within 0.3%. At 1400B near-MTU, Rust actually beats native C (475,607 vs 457,658) due to the line-rate cap.

**tokio-dpdk**: Caps at ~307K PPS at 350K+ target — improved from Run #15's ~37K cap, likely due to recent tokio compat layer improvements.

---

## Run #17: ICMPv6 Error Handling — No Regression

| Field | Value |
Expand Down
9 changes: 9 additions & 0 deletions dpdk-udp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub mod geneve;
pub mod gue;
pub mod ipv6;
pub mod ipv6_addr;
pub mod ndp;
pub mod vxlan;

pub use arp::{ArpCache, ArpHandler, ArpPacket};
Expand Down Expand Up @@ -95,6 +96,14 @@ pub use ipv6_addr::{
is_link_local, parse_scoped_address, solicited_node_multicast_addr,
solicited_node_multicast_mac, ipv6_multicast_mac, ScopedIpv6Addr,
};
pub use ndp::{
NdpCache, NdpHandler, NdpPacket, ProcNeighEntry,
build_neighbor_solicitation, build_neighbor_advertisement, build_gratuitous_na,
parse_ndp_packet, parse_proc_ipv6_neigh, seed_cache_from_proc,
ICMPV6_TYPE_NEIGHBOR_SOLICITATION, ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT,
NA_FLAG_ROUTER, NA_FLAG_SOLICITED, NA_FLAG_OVERRIDE,
NDP_HOP_LIMIT, NDP_NS_FRAME_LEN, NDP_NA_FRAME_LEN,
};

// ============================================================================
// Error Types
Expand Down
Loading
Loading