Skip to content

incremental checksums: NAT - #1809

Merged
daniel-noland merged 9 commits into
pr/daniel-noland/incremental-checksum-netfrom
pr/daniel-noland/incremental-nat-checksums
Sep 24, 2026
Merged

daniel-noland merged 9 commits into
pr/daniel-noland/incremental-checksum-netfrom
pr/daniel-noland/incremental-nat-checksums

Conversation

@daniel-noland

@daniel-noland daniel-noland commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

No description provided.

@daniel-noland daniel-noland added dont-merge Do not merge this Pull Request ci:+vlab Enable VLAB tests labels Sep 8, 2026
@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Essentials

Run ID: 4319de40-ccde-4f39-9d51-b808b50bd629

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Essentials

Run ID: 8f7b27df-7ea0-4084-b27d-0f864e8ef9a0

📥 Commits

Reviewing files that changed from the base of the PR and between aecbd87 and 5e9d719.

📒 Files selected for processing (12)
  • dataplane/src/packet_processor/ipforward.rs
  • nat/src/masquerade/icmp_handling.rs
  • nat/src/masquerade/nf.rs
  • nat/src/masquerade/packet.rs
  • nat/src/masquerade/test.rs
  • nat/src/portfw/icmp_handling.rs
  • nat/src/portfw/packet.rs
  • nat/src/static_nat/fuzz.rs
  • nat/src/static_nat/nf.rs
  • nat/src/static_nat/test.rs
  • nat/src/test.rs
  • net/src/packet/test_utils.rs

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.


📝 Walkthrough

Walkthrough

NAT translation paths now update transport checksums incrementally where possible and request full refreshes when needed. ICMP error handlers request refresh for outer checksums. VXLAN encapsulation handles IPv6 packets when checksum refresh is not requested.

Changes

NAT checksum handling

Layer / File(s) Summary
Masquerade checksum translation
nat/src/masquerade/packet.rs, nat/src/masquerade/icmp_handling.rs, nat/src/masquerade/nf.rs, nat/src/masquerade/test.rs
Masquerade SNAT and DNAT update checksums incrementally when possible. ICMP error handling requests refresh for the outer packet after inner-packet translation. Tests check translated TCP, UDP, and ICMP checksums.
Port-forwarding checksum translation
nat/src/portfw/packet.rs, nat/src/portfw/icmp_handling.rs
Port-forwarding SNAT and DNAT update address and transport checksums, and request full refreshes when incremental adjustment fails. Tests cover TCP and ICMPv4/v6 translations.
Static NAT checksum translation
nat/src/static_nat/nf.rs, nat/src/static_nat/test.rs
Static NAT updates address and port checksums incrementally. ICMP error translation requests refresh when the outer or quoted packet is modified. A TCP test checks the incremental checksum.
NAT checksum validation
net/src/packet/test_utils.rs, nat/src/static_nat/fuzz.rs, nat/src/masquerade/test.rs, nat/src/test.rs
A shared assertion checks transport checksum freshness unless refresh is requested. Fuzz and pipeline tests initialize checksums and validate translated packets.

IPv6 VXLAN encapsulation

Layer / File(s) Summary
VXLAN encapsulation without IPv4 checksum refresh
dataplane/src/packet_processor/ipforward.rs
Encapsulation no longer treats a missing IPv4 header as unreachable when refresh is not requested. A test checks IPv6 UDP packet state after VXLAN encapsulation.

Suggested reviewers: qmonnet

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 5e9d7

NAT now adjusts checksums incrementally instead of forcing a full recomputation for every translated packet. ICMP errors and cases that cannot be updated incrementally still request a full refresh. IPv4 header checksums are still recomputed when packets are sent, and IPv6 VXLAN encapsulation no longer hits an IPv4-only failure path. No outstanding correctness or availability risk was found, and the change looks ready to merge.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.70% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 47 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided, so its relevance to the changeset cannot be assessed. Add a concise description of the incremental checksum changes, fallback refresh behavior, ICMP error handling, and related tests.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: adding incremental checksum handling to NAT.

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

@daniel-noland daniel-noland changed the title perf(nat): update checksums incrementally instead of re-summing the payload incremental checksums Sep 8, 2026
@daniel-noland daniel-noland self-assigned this Sep 9, 2026
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from fb4d35b to f1b0b79 Compare September 23, 2026 02:05
@daniel-noland
daniel-noland changed the base branch from main to pr/daniel-noland/driven-clock September 23, 2026 02:05
@daniel-noland daniel-noland removed the dont-merge Do not merge this Pull Request label Sep 23, 2026
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from f1b0b79 to 4d48896 Compare September 23, 2026 02:30
@daniel-noland
daniel-noland changed the base branch from pr/daniel-noland/driven-clock to pr/daniel-noland/incremental-checksum-net September 23, 2026 02:30
@daniel-noland daniel-noland changed the title incremental checksums incremental checksums: NAT Sep 23, 2026
@daniel-noland
daniel-noland requested a lite review from Copilot September 23, 2026 02:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Critical checksum correctness issues remain for ICMP-error packets and IPv6 UDP packets with zero checksums.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 4 High severity

Open (4)
What changed in this PR

Adds RFC 1624 incremental checksum updates for masquerade, port forwarding, and static NAT to avoid unnecessary full payload recomputation.

Changes:

  • Updates NAT checksum handling and preserves recomputation for selected ICMP-error paths.
  • Adds checksum correctness, unit, and property-test coverage.
  • Removes redundant checksum refresh requests.
File Summary
nat/​src/​static_nat/​test.rs Adds end-to-end checksum validation.
nat/​src/​static_nat/​nf.rs Applies incremental static NAT checksum updates.
nat/​src/​static_nat/​fuzz.rs Validates checksum correctness in fuzz properties.
nat/​src/​portfw/​packet.rs Adds incremental port-forwarding checksum updates.
nat/​src/​masquerade/​test.rs Adds NF-level checksum assertions.
nat/​src/​masquerade/​packet.rs Adds incremental masquerade checksum updates.
nat/​src/​masquerade/​nf.rs Removes unconditional checksum refresh.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread nat/src/masquerade/packet.rs Outdated
if modified {
packet.meta_mut().src_natted(true);
packet.meta_mut().set_checksum_refresh(true);
packet.meta_mut().set_checksum_refresh(needs_full_recompute);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed. Headers::has_zero_ipv6_udp_checksum (in #1849, 227566f) flags the case, and masquerade seeds its recompute flag from it in 5e3843c; port forwarding does the same in a770b8a. IPv4 keeps "zero means disabled", as a_disabled_ipv4_udp_quote_checksum_stays_disabled already requires. Each NAT has a test that an IPv6 UDP datagram with a zero checksum comes out requesting a recompute.

Comment thread nat/src/masquerade/packet.rs Outdated
Comment thread nat/src/portfw/packet.rs Outdated
Comment thread nat/src/static_nat/nf.rs Outdated
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 4d48896 to 356a841 Compare September 23, 2026 03:03
daniel-noland added a commit that referenced this pull request Sep 23, 2026
The masquerade and port-forwarding ICMP error handlers rewrite the quoted packet, then translate
the outer header through the ordinary NAT path. That path used to request a full recompute for
every translation, which also covered the quote. With incremental updates it no longer does, so
nothing accounted for the quote in the outer ICMP checksum, which covers it.

The quote's own checksums are updated alongside it, and a field changed together with the
checksum covering it leaves the one's-complement sum unchanged, so often the outer checksum stays
right by accident. It does not when the covering checksum is missing: a TCP quote is usually cut
off before its checksum (an ICMPv4 error need only quote 8 bytes), and a UDP one may have none.
ICMP errors are rare, so both handlers now request a full recompute, as static NAT already did.

`assert_checksum_current_or_refresh_requested` is the oracle: either a recompute was requested, or
recomputing leaves the transport checksum unchanged. The masquerade ICMP error tests and the two
end-to-end ones in `nat/src/test.rs` now apply it. Only the masquerade TCP case exercises the fix;
the port-forwarding end-to-end test also runs static NAT, which requests the recompute on its own.

Reported in review of #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 356a841 to c7da5a5 Compare September 23, 2026 03:13
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-checksum-net branch 3 times, most recently from 15ec010 to 3afbdc4 Compare September 23, 2026 04:50
daniel-noland added a commit that referenced this pull request Sep 23, 2026
Add RFC 1624 helpers for port, address, and ICMP identifier changes.
Preserve disabled UDP checksums and store computed zeros as 0xFFFF.
Request full recomputation for IP version changes and ambiguous
ICMPv4 zero results.

Compare updates with full recomputation in Bolero properties and
test zero-checksum and IP version boundaries.
NAT integration follows in #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-checksum-net branch from 3afbdc4 to 36fabb5 Compare September 23, 2026 04:53
daniel-noland added a commit that referenced this pull request Sep 23, 2026
The masquerade and port-forwarding ICMP error handlers rewrite the quoted packet, then translate
the outer header through the ordinary NAT path. That path used to request a full recompute for
every translation, which also covered the quote. With incremental updates it no longer does, so
nothing accounted for the quote in the outer ICMP checksum, which covers it.

The quote's own checksums are updated alongside it, and a field changed together with the
checksum covering it leaves the one's-complement sum unchanged, so often the outer checksum stays
right by accident. It does not when the covering checksum is missing: a TCP quote is usually cut
off before its checksum (an ICMPv4 error need only quote 8 bytes), and a UDP one may have none.
ICMP errors are rare, so both handlers now request a full recompute, as static NAT already did.

`assert_checksum_current_or_refresh_requested` is the oracle: either a recompute was requested, or
recomputing leaves the transport checksum unchanged. The masquerade ICMP error tests and the two
end-to-end ones in `nat/src/test.rs` now apply it. Only the masquerade TCP case exercises the fix;
the port-forwarding end-to-end test also runs static NAT, which requests the recompute on its own.

Reported in review of #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch 2 times, most recently from ea7a148 to 96535d9 Compare September 23, 2026 05:05
daniel-noland added a commit that referenced this pull request Sep 23, 2026
Add RFC 1624 helpers for port, address, and ICMP identifier changes.
Preserve disabled UDP checksums and store computed zeros as 0xFFFF.
Request full recomputation for ambiguous ICMPv4 zero results.

The address helper is generic over `IpAddress`, so an IP version
change cannot be expressed and the update cannot fail. It folds the
whole address in one step. `Net::try_set_source_updating_checksum`
and `try_set_destination_updating_checksum` set an address and fold
it into the transport checksum under the version match `Net` already
does. `Headers::net_and_transport_mut` borrows both headers for callers
that do not match the header stack.

Compare updates with full recomputation in Bolero properties and
test the zero-checksum and version-mismatch boundaries.
NAT integration follows in #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-checksum-net branch from 36fabb5 to aecbd87 Compare September 23, 2026 05:24
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 96535d9 to 5e9d719 Compare September 23, 2026 05:24
@daniel-noland
daniel-noland marked this pull request as ready for review September 23, 2026 05:30
@daniel-noland
daniel-noland requested a review from a team as a code owner September 23, 2026 05:30
@daniel-noland
daniel-noland requested review from Fredi-raspall and removed request for a team September 23, 2026 05:30
@daniel-noland
daniel-noland added this pull request to stack #1850 September 23, 2026 05:30
@daniel-noland
daniel-noland requested a lite review from Copilot September 23, 2026 05:35
@daniel-noland
daniel-noland removed this pull request from stack #1850 September 23, 2026 05:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread nat/src/masquerade/packet.rs Outdated
) -> Result<(), NatPacketError> {
let mut modified = false;
// Request a full recomputation when a checksum delta cannot be applied.
let mut needs_full_recompute = false;
Comment thread nat/src/portfw/packet.rs
Comment on lines +80 to +82
if !tp.increment_checksum_for_u16(p.get(), new_src_port.get()) {
needs_full_recompute = true;
}
Comment thread nat/src/static_nat/nf.rs
let nfi = self.name();
debug!("{nfi}: Changing IP src: {} -> {target_src}", net.src_addr());
net.try_set_source(target_src)
net.try_set_source_updating_checksum(target_src, transport)
Comment on lines +896 to +898
if packet.meta().checksum_refresh() {
return;
}
daniel-noland added a commit that referenced this pull request Sep 23, 2026
Add RFC 1624 helpers for port, address, and ICMP identifier changes.
Preserve disabled IPv4 UDP checksums and store computed zeros as
0xFFFF. `Headers::has_zero_ipv6_udp_checksum` flags the IPv6 zero,
which only a full recompute can repair.
Request full recomputation for ambiguous ICMPv4 zero results.

The address helper is generic over `IpAddress`, so an IP version
change cannot be expressed and the update cannot fail. It folds the
whole address in one step. `Net::try_set_source_updating_checksum`
and `try_set_destination_updating_checksum` set an address and fold
it into the transport checksum under the version match `Net` already
does. `Headers::net_and_transport_mut` borrows both headers for callers
that do not match the header stack.

Compare updates with full recomputation in Bolero properties and
test the zero-checksum and version-mismatch boundaries.
NAT integration follows in #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-checksum-net branch from aecbd87 to 227566f Compare September 23, 2026 05:47
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 5e9d719 to 25889f1 Compare September 23, 2026 05:47
daniel-noland added a commit that referenced this pull request Sep 23, 2026
Add RFC 1624 helpers for port, address, and ICMP identifier changes.
Preserve disabled IPv4 UDP checksums and store computed zeros as
0xFFFF. `Headers::has_zero_ipv6_udp_checksum` flags the IPv6 zero,
which only a full recompute can repair.
Request full recomputation for ambiguous ICMPv4 zero results.

The address helper is generic over `IpAddress`, so an IP version
change cannot be expressed and the update cannot fail. It folds the
whole address in one step. `Net::try_set_source_updating_checksum`
and `try_set_destination_updating_checksum` set an address and fold
it into the transport checksum under the version match `Net` already
does.

Compare updates with full recomputation in Bolero properties. A
further property covers what a checksum-correct packet never reaches:
unchanged values, a missing transport, and disabled UDP checksums.
Test the zero-checksum and version-mismatch boundaries.
NAT integration follows in #1809.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-checksum-net branch from 227566f to 7d57087 Compare September 23, 2026 19:58
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 25889f1 to 75ba0b2 Compare September 23, 2026 19:58
@mvachhar
mvachhar self-requested a review September 24, 2026 02:04
daniel-noland and others added 9 commits September 23, 2026 22:03
Build ICMPv4 and ICMPv6 echo requests so NAT tests can exercise
identifier and checksum updates. Each builder rejects the other
family's ICMP.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Skip header checksum updates for IPv6, which has no header checksum.
Add a regression test for VXLAN encapsulation without a refresh request.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use `Transport` for ICMPv4 and ICMPv6 identifier updates so checksum
helpers can dispatch by version.
Identifier errors now use `TransportError::UnsupportedIdentifier`.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Apply address, port, and ICMP identifier deltas with the new `Transport`
helpers, requesting full recomputation when needed, including for an
IPv6 UDP checksum of zero, which cannot take a delta.
Compare SNAT and DNAT results with full recomputation for TCP and ICMP.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Let `snat` and `dnat` decide when a refresh is needed.
Check through the network function that translations produce correct
checksums without requesting full recomputation.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Apply address and port checksum deltas in both translation directions.
Keep ICMPv4 checksums unchanged for address-only updates and adjust
ICMPv6 pseudo-header checksums. Request full recomputation for an IPv6
UDP checksum of zero, which cannot take a delta.
Compare translated checksums with full recomputation in tests.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fold address changes as they are set, borrowing the network and
transport headers together through `Headers::net_and_transport_mut`.
Capture port deltas before translation and apply them after the
`TcpUdpMut` borrow ends.
Keep full recomputation for ICMP errors, whose checksums cover the quote,
and for an IPv6 UDP checksum of zero, which cannot take a delta.

Check checksum validity in the marking properties and add a test through
`StaticNat::process`.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Only set `checksum_refresh` when recomputation is needed; leave existing
requests intact when an update succeeds incrementally.
`Packet::update_checksums` clears the flag after recomputation.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Request full recomputation in masquerade and port-forwarding ICMP error
handlers because the outer checksum covers the modified quote.
A truncated TCP checksum or disabled UDP checksum can prevent inner
checksum updates from preserving the outer sum.

Add a shared assertion for current checksums or pending refresh requests
and apply it to ICMP error tests, including a port-forwarding test
through the ICMP error handler.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/incremental-nat-checksums branch from 75ba0b2 to a879a71 Compare September 24, 2026 04:04
@daniel-noland
daniel-noland merged commit 43db9d6 into pr/daniel-noland/incremental-checksum-net Sep 24, 2026
13 checks passed
@daniel-noland
daniel-noland deleted the pr/daniel-noland/incremental-nat-checksums branch September 24, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:+vlab Enable VLAB tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants