fields: fix i2len of 3-byte fields returning 4 instead of 3#5048
Open
chuenchen309 wants to merge 1 commit into
Open
fields: fix i2len of 3-byte fields returning 4 instead of 3#5048chuenchen309 wants to merge 1 commit into
chuenchen309 wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5048 +/- ##
=======================================
Coverage 80.22% 80.22%
=======================================
Files 388 388
Lines 96467 96469 +2
=======================================
+ Hits 77394 77397 +3
+ Misses 19073 19072 -1
🚀 New features to boost your workflow:
|
ThreeBytesField and LEThreeBytesField pass a 4-byte struct format
("!I"/"<I") to reuse struct for packing, then slice out 3 bytes in
addfield/getfield. But self.sz stayed 4 (struct.calcsize), so the
inherited Field.i2len returns 4 for a field that emits 3 bytes, and
randval() would draw from a 32-bit range for a 24-bit field. A
FieldLenField(length_of=<a 3-byte field>) therefore auto-computes a
length one byte too large.
Set self.sz = 3 so i2len (and randval) match the bytes actually
emitted. addfield/getfield use hardcoded 3-byte slices, so nothing else
reads sz and round-trip is unaffected.
There is one in-tree consumer: ISIS_TEDefaultMetricSubTlv wraps the
3-byte temetric field in a FieldLenField and, to work around the wrong
i2len, subtracted 1 in an adjust= lambda to land back on the correct
length of 3. With i2len now correct, drop that -1 so the sub-TLV length
stays 3 (test/contrib/isis.uts "LSP with Sub-TLVs" covers it end to end).
Note for out-of-tree layers: any code that similarly compensated for the
old i2len (e.g. an adjust= that subtracted 1) will now be off by one and
needs the same one-line cleanup.
AI-Assisted: yes (Claude Opus 4.8)
Co-authored-by AI: Claude (Claude Code).
Signed-off-by: Andrew Chen <48723787+chuenchen309@users.noreply.github.com>
chuenchen309
force-pushed
the
fix/threebytes-i2len
branch
from
July 19, 2026 06:12
d2abafd to
c8a4968
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The 3-byte field types (
ThreeBytesField,X3BytesField,LEThreeBytesField,XLE3BytesField,OUIField) report a length of 4 viai2len(), even though they serialize and consume exactly 3 bytes. So aFieldLenField(length_of=<a 3-byte field>)auto-computes a length one byte too large:Cause
ThreeBytesField/LEThreeBytesFieldpass a 4-byte struct format ("!I"/"<I") to reusestructfor packing, then slice out 3 bytes inaddfield/getfield. Butself.sz = struct.calcsize(fmt)stays 4, and the inheritedField.i2lenreturnsself.sz— soi2len(4) disagrees with the bytes actually emitted (3), andrandval()would also draw from a 32-bit range for a 24-bit field. The siblingNBytesFieldbuilds an exact-width format and is self-consistent.Fix
Set
self.sz = 3in the two base classes soi2len(andrandval) match the wire size.addfield/getfielduse hardcoded 3-byte slices, so nothing else readsszand build/dissect round-trip is unchanged (verified for all five classes).In-tree consumer (corrected)
There is one in-tree consumer, which my first push missed and CI caught:
ISIS_TEDefaultMetricSubTlvwraps the 3-bytetemetricfield in aFieldLenFieldand, to compensate for the wrongi2len, subtracted 1 in anadjust=lambda to land back on the correct length of 3:With
i2lennow correct, that-1makes the sub-TLV length come out as 2. This PR removes the workaround so the length stays 3.test/contrib/isis.uts(LSP with Sub-TLVs) exercises it end to end and passes again.Backward-compat note for out-of-tree layers: any user layer that similarly compensated for the old
i2len(anadjust=that subtracted 1 on a 3-byte field) will now be off by one and needs the same one-line cleanup. If you'd rather preserve the oldi2len == 4behavior, say so and I'll close this.Testing
test/fields.uts— addedi2len == emitted == 3assertions for all five classes plus aFieldLenField+OUIFieldTLV; red before, green after. PASSED=139 FAILED=0.test/contrib/isis.uts— PASSED=5 FAILED=0 (theLSP with Sub-TLVsroundtrip, which failed on the first push, now passes).scapy.fieldsimporting from the checkout.Disclosure: found, written and verified by an AI coding agent (Claude Code) on this account (per the
AI-Assistedcommit trailer) — it wrote the repro, the fix and this description from running the code. I review every change and am accountable for it. Happy to adjust anything.