Skip to content

Support multiple entries in IPOption_Timestamp - #5117

Open
itzzdev09 wants to merge 3 commits into
secdev:masterfrom
itzzdev09:ipoption-timestamp-multiple-entries
Open

itzzdev09 wants to merge 3 commits into
secdev:masterfrom
itzzdev09:ipoption-timestamp-multiple-entries

Conversation

@itzzdev09

@itzzdev09 itzzdev09 commented Aug 27, 2026

Copy link
Copy Markdown

What does this fix?

IPOption_Timestamp currently supports only a single address/timestamp entry, which makes it impossible to build a valid multi-entry timestamp option. RFC 791 requires the sender to reserve enough space for all the timestamps it expects to receive back.

This change makes the timestamp entries a list whose size is determined by the option length. With flg=0, the option uses timestamp, while flg=1 and flg=3 use ip_timestamp_pairs containing the address and timestamp.
The default pointer is also changed from 9 to 5.

This closes #4513.

One compatibility change is that the old internet_address and timestamp fields are removed. Existing code such as IPOption_Timestamp(internet_address=..., timestamp=...) will therefore no longer work, and the tests have been updated accordingly.

AI-Assisted: yes (Claude Sonnet 5), as noted in the commit.

@itzzdev09

Copy link
Copy Markdown
Author

Disclosure, per the "AI-assisted reports and PRs" section of CONTRIBUTING: this PR was drafted with AI assistance (Claude Code, Claude Opus 5). I should have said so in the original description — apologies for the omission.

Everything in it was verified by running it rather than assumed:

  • UTscapy -t test/scapy/layers/inet.uts passes 67/67, the same count as before the change.
  • The multi-entry build and dissection cases in the new assertions were checked against hand-written expected bytes.
  • flake8 scapy/layers/inet.py with the project config reports the same 15 pre-existing issues before and after — none introduced.
  • The two updated assertions keep their original expected bytes, and the checksum test keeps its original checksum by passing one explicit empty pair.

One thing worth calling out, since it drove a design decision: a non-empty PacketListField default is not safe here. Packet.do_init_cached_fields shallow-copies list defaults via value.copy(), so a Packet inside the default list ends up shared across instances. I hit that with a mutation test while developing, which is why the defaults are empty lists as in IPOption_RR.

Happy to adjust anything, including the field naming, if you'd prefer a different shape.

RFC 791 requires the originating host to compose the timestamp option with a
data area large enough to hold every timestamp it expects back, so that each
router on the path can append one. IPOption_Timestamp modelled only a single
entry, so a target host had no room to record anything and would reply with
the overflow flag set.

Model the data area as a list instead:

- flg 0 (timestamp_only) uses 'timestamps', a list of 32-bit timestamps.
- flg 1/3 use 'pairs', a list of IPOption_Timestamp_Pair, each an internet
  address followed by its timestamp.

Both are sized from the option length on dissection, mirroring IPOption_RR.
The defaults are empty lists, also as in IPOption_RR, so 'pointer' now
defaults to 5 (the RFC minimum, pointing at the first free octet) rather than
to 9, which assumed one entry was already present.

Note this replaces the 'internet_address' and 'timestamp' fields; existing
callers move to 'pairs=[IPOption_Timestamp_Pair(...)]' or 'timestamps=[...]'.

AI-Assisted: yes (Claude Sonnet 5)
@itzzdev09
itzzdev09 force-pushed the ipoption-timestamp-multiple-entries branch from f838267 to 24ee34b Compare August 28, 2026 16:30
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.07%. Comparing base (23808d9) to head (52d5ac0).
⚠️ Report is 107 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5117      +/-   ##
==========================================
- Coverage   80.63%   80.07%   -0.56%     
==========================================
  Files         390      375      -15     
  Lines       96895    97680     +785     
==========================================
+ Hits        78127    78219      +92     
- Misses      18768    19461     +693     
Files with missing lines Coverage Δ
scapy/layers/inet.py 72.08% <100.00%> (+0.14%) ⬆️

... and 315 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@itzzdev09
itzzdev09 force-pushed the ipoption-timestamp-multiple-entries branch from 98d0e99 to 24ee34b Compare September 15, 2026 14:24

@polybassa polybassa 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.

Thanks for your PR

Comment thread scapy/layers/inet.py
ConditionalField(IPField("internet_address", "0.0.0.0"),
lambda pkt: pkt.flg != 0),
IntField('timestamp', 0)]
ConditionalField(

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.

Please use a MultipleTypesField

Comment thread scapy/layers/inet.py Outdated
IntField('timestamp', 0)]
ConditionalField(
FieldListField(
"timestamps", [], IntField("", 0),

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.

Please don't change the field name and the original field type, except there are valid reasons to do so

Comment thread scapy/layers/inet.py Outdated
ConditionalField(
PacketListField(
"pairs", [], IPOption_Timestamp_Pair,
length_from=lambda pkt: max(0, (pkt.length or 4) - 4)), # noqa: E501

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.

Simplify the lambda expression

Address review: keep the original field name, timestamp, instead of the
separate timestamps/pairs fields, select its type from flg with a single
MultipleTypeField rather than two ConditionalFields, and simplify the
length_from lambda.

The field still holds a list, since RFC 791 has the originating host
reserve room for every entry it expects back; each address/timestamp pair
keeps the original internet_address and timestamp names and types.

AI-Assisted: yes (Claude Opus 5)
@itzzdev09

Copy link
Copy Markdown
Author

Thanks for the review! Done in fc08d0b: switched to a MultipleTypeField, kept the original timestamp name (each entry keeps internet_address / timestamp with their original types), and simplified the lambda. The one intentional type change is that timestamp is now a list, since RFC 791 has the sender reserve room for several entries — that's the point of the PR. Happy to adjust if you'd prefer a different shape. @polybassa

@itzzdev09
itzzdev09 requested a review from polybassa September 16, 2026 19:14
Comment thread scapy/layers/inet.py Outdated
lambda pkt: pkt.flg != 0),
IntField('timestamp', 0)]
MultipleTypeField(
[(FieldListField("timestamp", [], IntField("", 0),

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.

Why did this became a FieldListField?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Because the option can carry more than one timestamp. RFC 791 says the sender has to leave room for all the timestamps it wants routers to fill in, so with flg=0 you get a list of 32-bit values, not a single one. With just an IntField you could only ever read the first one. The size comes from length, same as IPOption_RR does for its addresses.

Comment thread scapy/layers/inet.py Outdated
[(FieldListField("timestamp", [], IntField("", 0),
length_from=lambda pkt: pkt.length - 4),
lambda pkt: pkt.flg == 0)],
PacketListField("timestamp", [], IPOption_Timestamp_Pair,

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.

Please choose a field name that indicates that this field holds pairs of IP and Timestamps

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Renamed it to ip_timestamp_pairs in 52d5ac0. The flg=0 list is still called timestamp.

Heads up, I had to drop the MultipleTypeField for this. It wants the same name for every variant, so with two different names I went back to two ConditionalFields. Let me know if you'd rather have it another way.

Address review: the address/timestamp pair list gets a name of its own,
ip_timestamp_pairs, so it no longer shares timestamp with the flg 0 list
of bare timestamps. A MultipleTypeField needs one name for all of its
variants, so the two lists are separate ConditionalFields selected by flg.

AI-Assisted: yes
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.

IPOptions_Timestamp does not support multiple IP Address and Timestamp fields

2 participants