Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
99ed189
feat(clock): Read the clock through a facade, and lint for it
daniel-noland Aug 18, 2026
d97c025
test(nat): Test flow expiry on a clock the test drives
daniel-noland Aug 18, 2026
c3e8a86
test(nat): Drive port forwarding with configuration-relative packets
daniel-noland Aug 18, 2026
256f1e8
test(net): Test the flow expiry state machine as an algebra
daniel-noland Aug 18, 2026
493cde4
fix(masquerade): Keep both halves of a flow pair alive
daniel-noland Aug 18, 2026
77dd7c6
test(stats): Test the exponentially weighted moving average
daniel-noland Aug 18, 2026
483ab8b
test(stats): Test the per-vpc statistics store
daniel-noland Aug 18, 2026
2b1e98e
test(stats): Test the time-slice apportioning in the collector
daniel-noland Aug 18, 2026
81105e9
test(routing): Test the stale window on a clock the test drives
daniel-noland Aug 18, 2026
eeb5494
test(routing): Drive the router IO loop through its own sockets
daniel-noland Aug 18, 2026
baaf9af
test(routing): Stand in for frr-agent and test the frrmi lifecycle
daniel-noland Aug 18, 2026
7edaa32
docs(routing): Record why cli_wake_on_writeable stays uncovered
daniel-noland Aug 18, 2026
17e44c0
test(routing): Follow a route from the socket to the fib
daniel-noland Aug 18, 2026
bc793c9
test(net): Close every mutant flow_info's properties were missing
daniel-noland Aug 18, 2026
1576fc2
test(masquerade): Pin the flow state machine, exhaustively
daniel-noland Aug 19, 2026
cd391d0
docs(testing): Record what mutation testing is for here, and what it …
daniel-noland Aug 19, 2026
8a5647e
fix(net): Validate the RFC 4884 original datagram field as the RFC says
daniel-noland Aug 19, 2026
8b92d3a
build(duvet): Track RFC 4884 compliance, and vendor the specification
daniel-noland Aug 19, 2026
a427e0c
build(duvet): Track RFC 5382, and record where masquerade departs fro…
daniel-noland Aug 19, 2026
ab5d086
docs(testing): Record what duvet can and cannot parse, and what is st…
daniel-noland Aug 19, 2026
16bd80c
build(duvet): Track RFC 4787, and record where masquerade departs fro…
daniel-noland Aug 19, 2026
8287e76
test(masquerade): State RFC 4787 endpoint independence as a property,…
daniel-noland Aug 19, 2026
da33f3a
test(masquerade): Assert RFC 4787 outbound refresh, and record where …
daniel-noland Aug 19, 2026
2653652
test(masquerade): Classify masquerade's filtering, and check RFC 4787…
daniel-noland Aug 19, 2026
74f81a4
docs(testing): Record what the RFC errata say, and where the corpus lies
daniel-noland Aug 19, 2026
29a7861
test(masquerade): State RFC 4787 REQ-12 as an executable contract
daniel-noland Aug 19, 2026
e7d0520
test(masquerade): Make a stale citation a build failure
daniel-noland Aug 19, 2026
faf1071
docs(forwarding): Record RFC 4787 REQ-13, and why it is one decision …
daniel-noland Aug 20, 2026
631488c
fix(duvet): Regenerate the snapshot the REQ-12 and REQ-13 commits lef…
daniel-noland Aug 20, 2026
13e981b
docs(testing): Record what the interlock found, and which specificati…
daniel-noland Aug 20, 2026
bbf5690
docs(testing): Record where a citation goes when the code is abstract
daniel-noland Aug 20, 2026
d75aaa7
test(net): Take the RFC 4884 minimum from both sides, in both families
daniel-noland Aug 21, 2026
56ced01
test(nat): Cite port overloading on the code that could commit it
daniel-noland Aug 21, 2026
1799836
test(nat): Cite the exhaustion walk alongside the stage property
daniel-noland Aug 21, 2026
88b7b64
docs(testing): Record the three tiers, and the two ways a citation can
daniel-noland Aug 21, 2026
fea7d1d
docs(nat): Drop the process narration from the citation comments
daniel-noland Aug 21, 2026
762b44a
fix(net): Backtick a doc identifier
daniel-noland Aug 22, 2026
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
35 changes: 35 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Configuration for cargo-mutants (https://mutants.rs).
#
# Mutation testing is a flashlight, not a gate: it is run by hand over a crate
# or a diff, and the product is the list of survivors rather than the score.
# Nothing in CI depends on this file.
#
# Two categories are excluded because a survivor there is noise rather than a
# finding. Both are excluded here rather than with `#[mutants::skip]` so that
# production crates take no dependency on the tool and the reasoning stays in
# one place.

exclude_re = [
# Printers. The standing rule is "don't test printers": asserting on
# rendered output is sisyphean, produces low signal, and breaks whenever
# anyone adjusts a column. A mutated `fmt` that no test notices is telling
# us the rule is being followed.
"<impl Display for",
"<impl Debug for",
"<impl std::fmt::Display for",
"<impl std::fmt::Debug for",

# Test-support code inside the crate under test. The workspace convention
# puts bolero generators in a `contract` module next to the type they
# generate, so cargo-mutants finds them and mutates the test harness at
# itself. A generator that draws a different distribution is not a defect
# in the code under test; the properties are what judge it.
"contract::",
]

exclude_globs = [
# Privileged sysfs manipulation. Exercising the write path needs root and a
# real sysfs, and the read path is thin enough that a property would assert
# little beyond what the type system already does.
"sysfs/**",
]
1 change: 1 addition & 0 deletions .duvet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
reports/
46 changes: 46 additions & 0 deletions .duvet/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# duvet: specification compliance coverage.
#
# `duvet report` matches citations in the source -- `//= <url>` followed by the requirement text --
# against the requirements duvet extracts from the specification, so a requirement with nothing
# implementing it, or an implementation with nothing testing it, is visible.
#
# Everything under .duvet/ is committed except `reports/`. That is not incidental: `duvet report`
# reads the specification from `.duvet/specifications/` and only reaches the network when it is
# missing, so vendoring the text is what lets the report run in a nix build sandbox at all. It also
# means an errata, a reformat, or a fetch that quietly returns something else arrives as a
# reviewable diff rather than as a change in results nobody can explain.
'$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"

# Every crate in the workspace. The generated default is `src/**/*.rs`, which matches nothing here.
[[source]]
pattern = "*/src/**/*.rs"

# RFC 4884 is the first specification tracked, because the code already cites it: the
# "original datagram" length checks in net/src/headers/embedded.rs were found to contradict it.
[[specification]]
source = "https://www.rfc-editor.org/rfc/rfc4884"

# RFC 5382 states 10 numbered requirements (REQ-1 .. REQ-10) for how a NAT must treat TCP. It
# constrains values this codebase already has and chose without reference to it -- notably the
# idle timeouts in nat/src/masquerade/nf.rs. Tracked second for that reason.
[[specification]]
source = "https://www.rfc-editor.org/rfc/rfc5382"

[report.html]
enabled = true

# The snapshot is the regression gate. It is line-oriented and diffs cleanly, and its unit is a
# sentence somebody else wrote -- which, unlike a mutant's file:line, does not move when a function
# is reformatted.
[report.snapshot]
enabled = true

# RFC 4787 is the UDP counterpart of RFC 5382 and states 14 numbered requirements. Tracked third
# because masquerade translates UDP on the same path it translates TCP, so its mapping and timeout
# requirements land on code that already carries RFC 5382 citations.
#
# Cited as the individual RFC, never as BCP 127. That composite concatenates RFC 4787, RFC 6888 and
# RFC 7857, whose section numbers collide, and duvet silently keeps only the last -- 42 requirements
# where the three separately yield 129. See development/code/spec-compliance.md.
[[specification]]
source = "https://www.rfc-editor.org/rfc/rfc4787"
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
target = "https://www.rfc-editor.org/rfc/rfc4787#section-10"

# Fragmentation of Outgoing Packets
#
# When the MTU of the adjacent link is too small, fragmentation of
# packets going from the internal side to the external side of the NAT
# may occur. This can occur if the NAT is doing Point-to-Point over
# Ethernet (PPPoE), or if the NAT has been configured with a small MTU
# to reduce serialization delay when sending large packets and small
# higher-priority packets, or for other reasons.
#
# It is worth noting that many IP stacks do not use Path MTU Discovery
# with UDP packets.
#
# The packet could have its Don't Fragment bit set to 1 (DF=1) or 0
# (DF=0).
#
# REQ-13: If the packet received on an internal IP address has DF=1,
# the NAT MUST send back an ICMP message "Fragmentation needed and
# DF set" to the host, as described in [RFC0792].
#
# a) If the packet has DF=0, the NAT MUST fragment the packet and
# SHOULD send the fragments in order.
#
# Justification: This is as per RFC 792.
#
# a) This is the same function a router performs in a similar
# situation [RFC1812].

[[spec]]
level = "MUST"
quote = '''
REQ-13: If the packet received on an internal IP address has DF=1,
the NAT MUST send back an ICMP message "Fragmentation needed and
DF set" to the host, as described in [RFC0792].
'''

[[spec]]
level = "MUST"
quote = '''
a) If the packet has DF=0, the NAT MUST fragment the packet and
SHOULD send the fragments in order.
'''

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
target = "https://www.rfc-editor.org/rfc/rfc4787#section-11"

# Receiving Fragmented Packets
#
# For a variety of reasons, a NAT may receive a fragmented packet. The
# IP packet containing the header could arrive in any fragment,
# depending on network conditions, packet ordering, and the
# implementation of the IP stack that generated the fragments.
#
# A NAT that is capable only of receiving fragments in order (that is,
# with the header in the first packet) and forwarding each of the
# fragments to the internal host is described as "Received Fragments
# Ordered".
#
# A NAT that is capable of receiving fragments in or out of order and
# forwarding the individual fragments (or a reassembled packet) to the
# internal host is referred to as "Receive Fragments Out of Order".
# See the Security Considerations section of this document for a
# discussion of this behavior.
#
# A NAT that is neither of these is referred to as "Receive Fragments
# None".
#
# REQ-14: A NAT MUST support receiving in-order and out-of-order
# fragments, so it MUST have "Received Fragment Out of Order"
# behavior.
#
# a) A NAT's out-of-order fragment processing mechanism MUST be
# designed so that fragmentation-based DoS attacks do not
# compromise the NAT's ability to process in-order and
# unfragmented IP packets.
#
# Justification: See Security Considerations.

[[spec]]
level = "MUST"
quote = '''
REQ-14: A NAT MUST support receiving in-order and out-of-order
fragments, so it MUST have "Received Fragment Out of Order"
behavior.
'''

[[spec]]
level = "MUST"
quote = '''
a) A NAT's out-of-order fragment processing mechanism MUST be
designed so that fragmentation-based DoS attacks do not
compromise the NAT's ability to process in-order and
unfragmented IP packets.
'''

Loading
Loading