Skip to content

IPv4Option: decode Record Route, Timestamp and Router Alert values - #138

Merged
EONRaider merged 1 commit into
masterfrom
claude/decoder-depth-polish-8m54to
Sep 4, 2026
Merged

IPv4Option: decode Record Route, Timestamp and Router Alert values#138
EONRaider merged 1 commit into
masterfrom
claude/decoder-depth-polish-8m54to

Conversation

@EONRaider

Copy link
Copy Markdown
Owner

Summary

Part of #96 (the IPv4Option piece — item 3). IPv4Option has kind_name and no value. Record Route (7), Timestamp (68) and Router Alert (148) are named, and their contents left raw — unlike TCPOption, which does decode its values.

What's included

  • IPv4Option.value — a new property, mirroring TCPOption.value's "decode the well-known kinds, degrade to None for the rest or on malformed data" contract.
  • Record Route (7)tuple[ipaddress.IPv4Address, ...]. RFC 791 §3.1's pointer byte says how many address slots are actually filled, which is not the same as the option's total declared capacity — .value honors that and returns only the recorded addresses. Returns () (not None) when the pointer is legally at its minimum (4) with nothing recorded yet; None for a pointer below 4, misaligned, or pointing past the available route data (malformed, degrades rather than raises).
  • Timestamp (68)tuple[int, ...] of plain millisecond timestamps (flag 0) or tuple[tuple[ipaddress.IPv4Address, int], ...] of (address, timestamp) pairs (flag 1 or 3 — the variant that also identifies which IP module recorded the entry). The overflow counter and pointer are not decoded here — read data raw for those. None for an unrecognized flag value or malformed entry length.
  • Router Alert (148) → the 2-byte value as int (RFC 2113 §2; 0 is the only value currently defined).
  • Full test coverage: happy paths for all three kinds, every degrade-to-None malformed case, direct construction.

Design note on Timestamp's shape: rather than inventing a new NamedTuple, the (address, timestamp) pair mirrors TCPOption.value's existing tuple[tuple[int, int], ...] shape for SACK — a plain nested tuple already expresses this correctly, and it's the precedent already established in this exact file's sibling module.

No breaking change: .value is a new property; kind/data/kind_name are unchanged.

Verification

  • uv run --frozen ruff check . and uv run --frozen ruff format --check . are clean
  • uv run --frozen mypy is clean (strict, src/ only)
  • uv run --frozen pytest passes locally (full suite)
  • uv run --frozen python scripts/benchmark.py --check --threshold 15 — 124,772 f/s, +11.0% vs. baseline, within threshold
  • CHANGELOG.md has an entry under ## [Unreleased]

New protocol or dispatch change — also:

Not applicable — no new protocol, no dispatch change. Deleted this block's checklist since it doesn't apply.

Notes

Part of #96 (four independent PRs by the issue's own instruction); this is the third of four — only the IPv6Routing/IPv6Option piece remains. #96 itself stays open until all four land.

Part of #96.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CP7X7H4k3pBWoiAkBdxATM


Generated by Claude Code

IPv4Option had kind_name and no .value -- the three common kinds (RFC
791 section 3.1, RFC 2113) were named but their contents left raw,
unlike TCPOption, which already decodes its values.

Record Route: the option's own pointer byte says how many address
slots are actually filled (RFC 791: pointer 4 means none yet), which
is not generally the same as how much route-data capacity the option
declares. .value honors that -- it returns only the recorded
addresses, not the full capacity, and returns () rather than None when
the pointer is legally at its minimum with nothing recorded.

Timestamp: the low nibble of the second data byte is a flag that
changes the entry shape -- plain millisecond timestamps for flag 0, or
(address, timestamp) pairs for flag 1/3, since the address-carrying
variant exists precisely so a timestamp option can identify which IP
module returned each entry. The overflow counter and pointer stay
undecoded (data raw covers them); mirrors TCPOption's tuple-of-pairs
shape for SACK rather than inventing a NamedTuple for something a
plain nested tuple already expresses.

Router Alert: the 2-byte value as an int (RFC 2113 section 2; 0 is the
only value currently defined).

Everything else, and malformed data on one of these three (bad
pointer, unrecognized flag, wrong length), degrades to None -- same
contract as every other .value accessor in this tier.

Part of #96.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CP7X7H4k3pBWoiAkBdxATM
@EONRaider
EONRaider merged commit 1f8a55e into master Sep 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants