Skip to content

Commit 49dc251

Browse files
baochunlicodex
andauthored
Reimplemented TCP and BBR with the correct packet timing and other fixes. Closes #34. (#35)
* chore(tcp): document transport timing invariants Clarify the packet timing and ownership contract that the TCP rewrite will follow. Document Packet.time as the original first-transmit timestamp used for sink-side latency accounting, and annotate the TCP/BBR sender state and receiver ACK frontier with the intended ownership and cleanup semantics. This keeps the rewrite boundaries explicit before the receiver and sender behavior changes land in later issue-scoped commits. Validation: - uv run python -m py_compile ns/packet/packet.py ns/packet/tcp_sink.py ns/packet/tcp_generator.py ns/packet/bbr_generator.py Refs: DT-262 Co-authored-by: Codex <codex@openai.com> * test(tcp): pin cumulative ACK semantics in sink red tests Add deterministic receiver-only tests for TCPSink cumulative ACK handling. The tests cover out-of-order arrival, duplicate buffered data, hole fill, and interval merging, and they fail against the current implementation as expected because TCPSink advances ACK from the first merged interval rather than the cumulative frontier. Validation: - uv run --with pytest python -m pytest -q tests/packet/test_tcp_sink.py Refs: DT-263 Co-authored-by: Codex <codex@openai.com> * test(tcp): pin sender retransmit and tail semantics Add deterministic classic TCP sender red tests for buffered sub-MSS sends, final tail segment sending, retransmit object freshness, partial ACK cleanup, and RTT suppression after retransmitted data is acknowledged. The tests fail against the current implementation because TCPPacketGenerator still assumes full-MSS sends, reuses Packet objects across retransmissions, cleans up by segment start instead of segment end, and updates RTT on ambiguous cumulative ACKs. Validation: - uv run --with pytest python -m pytest -q tests/packet/test_tcp_sink.py tests/packet/test_tcp_generator.py Refs: DT-265 Co-authored-by: Codex <codex@openai.com> * test(tcp): add red coverage for sender transport semantics Add deterministic sender-focused tests that pin down the classic TCP transport contract around retransmission object ownership, sink-observed latency, segment-end ACK cleanup, and sub-MSS/tail send behavior. These tests are intentionally red on the current implementation so the next rewrite step can drive the sender refactor with clear failures. Validation: - uv run python -m pytest -q tests/packet/test_tcp_generator.py (fails with 6 assertions, covering sub-MSS sends, tail sends, fresh packet allocation on fast/timeout retransmits, segment-end ACK cleanup, and RTT skip behavior for retransmitted data) Refs: DT-265 Co-authored-by: Codex <codex@openai.com> * fix(tcp): derive cumulative ACK from the receive frontier Update TCPSink to advance ACK from the current cumulative frontier across the merged receive intervals instead of blindly taking the first interval end. This preserves duplicate-ACK pinning on out-of-order arrivals and only advances once the hole is actually filled. Validation: - uv run --with pytest python -m pytest -q tests/packet/test_tcp_sink.py Refs: DT-264 Co-authored-by: Codex <codex@openai.com> * fix(tcp): move classic sender onto logical segment state Introduce sender-owned segment state for classic TCP so retransmissions emit fresh Packet objects with stable first-transmit timestamps and ACK cleanup uses segment-end semantics. Timeout and fast retransmit now build new packet attempts from durable segment metadata instead of mutating the original Packet object. This intentionally leaves sub-MSS/tail sending and RTT suppression for the later sender issues. Validation: - uv run --with pytest python -m pytest -q tests/packet/test_tcp_sink.py tests/packet/test_tcp_generator.py - Expected remaining failures: buffered sub-MSS send, final tail send, RTT suppression after retransmitted ACK Refs: DT-266 Co-authored-by: Codex <codex@openai.com> * fix(tcp): gate RTT samples on sender segment state Move classic TCP RTT and RTO sampling onto sender-owned segment metadata. The sender now accepts RTT updates only for exactly one un-retransmitted segment, keeps ACK advancement flowing through congestion control for ambiguous recovery ACKs, and reuses the last known RTT when no fresh sample is eligible. Validation: - uv run --with pytest python -m pytest -q tests/packet/test_tcp_sink.py tests/packet/test_tcp_generator.py Refs: DT-268 Co-authored-by: Codex <codex@openai.com> * docs(tcp): add timing contract note Add a short transport timing note that documents the sender/receiver contract used by the TCP rewrite. Link it from the TCP section of the README so it is easy to find from the main project entry point. Validation: - inspected docs/tcp_timing.md - verified README pointer Refs: DT-269 Co-authored-by: Codex <codex@openai.com> * test(tcp): add red coverage for BBR timing semantics Add deterministic packet-level BBR tests that pin down buffered sub-MSS and tail sending, fresh packet emission on retransmission, segment-end ACK cleanup, and rate-sample stability when retransmission timing changes. These tests intentionally fail against the current BBR implementation so the follow-on rewrite can be driven by explicit red state instead of assumptions. Validation: - uv run python -m pytest -q tests/packet/test_bbr_generator.py (fails with 6 assertions) Refs: DT-270 Co-authored-by: Codex <codex@openai.com> * fix(tcp): align BBR packet timing with sender state Refactor BBRPacketGenerator to keep outstanding-segment metadata in sender-owned state instead of mutating Packet objects in place. New sends and retransmissions now emit fresh Packet instances, preserve the original first-transmit timestamp for sink latency accounting, and reuse stable rate-sample metadata across retransmission attempts. The BBR sender also now handles buffered sub-MSS and tail sends, cleans up acked data with segment-end semantics, and restarts its retransmission timer from the oldest outstanding segment state. The packet-level BBR regression test was narrowed to compare the stable rate-sample outputs and final ACK-facing RTT input rather than the entire congestion-control call history, which necessarily differs when a timeout path runs. Validation: - uv run python -m pytest -q tests/packet/test_bbr_generator.py - uv run python -m pytest -q tests/flow/test_bbrv3.py tests/flow/test_bbrv3_integration.py Refs: DT-271 Co-authored-by: Codex <codex@openai.com> * fix(tcp): ignore dupacks beyond outstanding data Guard fast retransmit when a duplicate ACK no longer maps to a live outstanding segment. Validation exposed a KeyError in examples/tcp.py after a cumulative ACK advanced past the stored sender frontier while dupack handling still tried to resolve segment state for that ACK. Add a regression test that reproduces the stale-frontier dupack path and keeps the sender side effect free when no resendable segment remains. Validation: - uv run python -m pytest -q - uv run python examples/tcp.py - uv run python examples/bbr.py Refs: DT-272 Co-authored-by: Codex <codex@openai.com> * fix(bbr): preserve timeout order and ACK RTT sampling Keep the single retransmission timer anchored to the oldest outstanding segment instead of retargeting it to each newly sent packet. This restores loss-recovery ordering for multi-packet flights and prevents timeout retransmits from skipping the earliest loss. Also restore ACK RTT sampling to the acknowledged packet's transport timestamp. In this design `ack.time` carries that segment timestamp while `first_sent_time` remains the flight-level marker for delivery-rate sampling, so using `first_sent_time` inflated RTT and RTO on paced flights. Add regression coverage for both review findings and align the existing rate-sample stability fixture with the sender-owned timestamp contract. Validation: - uv run python -m pytest -q tests/packet/test_bbr_generator.py tests/flow/test_bbrv3.py tests/flow/test_bbrv3_integration.py - uv run python -m pytest -q - uv run python examples/bbr.py - uv run python examples/tcp.py Co-authored-by: Codex <codex@openai.com> * test(tcp): add end-to-end retransmit latency coverage Add a deterministic TCP integration test that drops the first transmission of a single segment, lets the timeout retransmission through, and asserts that the receiver-observed wait is still measured from the original first transmit time. This turns the issue #34 behavior into an end-to-end regression instead of an example-only check. Validation: - uv run --with pytest python -m pytest -q tests/flow/test_tcp_integration.py - uv run --with pytest python -m pytest -q tests/packet/test_tcp_generator.py tests/packet/test_tcp_sink.py tests/flow/test_tcp_integration.py - uv run --with pytest python -m pytest -q Refs: #34 Co-authored-by: Codex <codex@openai.com> * docs(test): add additional instructions in README about running tests. --------- Co-authored-by: Codex <codex@openai.com>
1 parent 5cf1e2c commit 49dc251

10 files changed

Lines changed: 1106 additions & 207 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ The network components that have already been implemented include:
3838
* `TracePacketGenerator`: generates packets according to a trace file, with each row in the trace file representing a packet.
3939

4040
* `TCPPacketGenerator`: generates packets using TCP as the transport protocol.
41+
See [`docs/tcp_timing.md`](docs/tcp_timing.md) for the sender/receiver
42+
timing contract used by the TCP rewrite.
4143

4244
* `ProxyPacketGenerator`: redirects real-world packets (with fixed packet sizes) into the simulation environment.
4345

@@ -286,3 +288,11 @@ self.deficit[flow_id] += self.quantum[flow_id]
286288
```
287289

288290
Most often, the mapping between flow IDs and per-flow parameters, such as weights in a Weighted Fair Queueing scheduler or priorities in a Static Priority scheduler, need to be stored in a dictionary, and then used to initialized these schedulers. An optional (but not recommended) style is to assign consecutive integers as flow IDs to the flows throughout the entire network, and then use simple lists of per-flow parameters to initialize the schedulers. In this case, flow IDs will be directly used as indices to look up these lists to find the parameter values.
291+
292+
## Running Tests
293+
294+
A few dozen tests have been included in the project. To run them, use the command:
295+
296+
```bash
297+
uv run --with pytest python -m pytest -q
298+
```

docs/tcp_timing.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# TCP Timing Contract
2+
3+
This note captures the transport semantics the TCP rewrite relies on.
4+
5+
- `TCPSink` tracks the cumulative ACK frontier, `RCV.NXT`, from the contiguous
6+
prefix only.
7+
- `TCPPacketGenerator` owns logical segment state keyed by segment start
8+
sequence number.
9+
- Every send or retransmit emits a fresh `Packet` object derived from that
10+
logical segment state.
11+
- `Packet.time` on TCP data packets is the original first-transmit timestamp
12+
used by sinks for end-to-end latency accounting.
13+
- RTT and RTO updates are sender-owned and conservative.
14+
- In this phase, the sender does not guess RTT from retransmitted or ambiguous
15+
ACKs.
16+
- The transport rewrite assumes no TCP timestamps, no SACK, and no delayed-ACK
17+
modeling.
18+
19+
## Why this exists
20+
21+
The classic TCP rewrite splits timing ownership between packet objects,
22+
receiver ACK logic, and sender metadata. This note gives a stable reference for
23+
the intended split so later changes can preserve the same contract.

0 commit comments

Comments
 (0)