From c8a4968219234f442271316563c899472fb5728d Mon Sep 17 00:00:00 2001 From: chuenchen309 <48723787+chuenchen309@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:12:27 +0800 Subject: [PATCH] fields: fix i2len of 3-byte fields returning 4 instead of 3 ThreeBytesField and LEThreeBytesField pass a 4-byte struct format ("!I"/") 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> --- scapy/contrib/isis.py | 2 +- scapy/fields.py | 2 ++ test/fields.uts | 12 ++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/scapy/contrib/isis.py b/scapy/contrib/isis.py index d277a27b568..6ed224835f0 100644 --- a/scapy/contrib/isis.py +++ b/scapy/contrib/isis.py @@ -350,7 +350,7 @@ class ISIS_UnreservedBandwidthSubTlv(ISIS_GenericSubTlv): class ISIS_TEDefaultMetricSubTlv(ISIS_GenericSubTlv): name = "TE Default Metric SubTLV" fields_desc = [ByteEnumField("type", 18, _isis_subtlv_names_1), - FieldLenField("len", None, length_of="temetric", adjust=lambda pkt, x:x - 1, fmt="B"), # noqa: E501 + FieldLenField("len", None, length_of="temetric", fmt="B"), ThreeBytesField("temetric", 1000)] diff --git a/scapy/fields.py b/scapy/fields.py index 23c8fa774c3..02ae4926b89 100644 --- a/scapy/fields.py +++ b/scapy/fields.py @@ -1072,6 +1072,7 @@ class ThreeBytesField(Field[int, int]): def __init__(self, name, default): # type: (str, int) -> None Field.__init__(self, name, default, "!I") + self.sz = 3 # emits/consumes 3 bytes; keep i2len consistent for FieldLenField def addfield(self, pkt, s, val): # type: (Packet, bytes, Optional[int]) -> bytes @@ -1092,6 +1093,7 @@ class LEThreeBytesField(ByteField): def __init__(self, name, default): # type: (str, Optional[int]) -> None Field.__init__(self, name, default, " bytes diff --git a/test/fields.uts b/test/fields.uts index 38263f2a49f..e2d1132d414 100644 --- a/test/fields.uts +++ b/test/fields.uts @@ -174,6 +174,18 @@ print(p.sprintf('%test1% %test2% %test3% %test4%')) assert p.sprintf('%test1% %test2% %test3% %test4%') == '0x123456 123456 0xfedbca 567890' assert repr(p.test1) == '1193046' +# i2len must match the 3 bytes actually emitted, so FieldLenField(length_of=) +# on a 3-byte field computes the right length. +for cls in [ThreeBytesField, X3BytesField, LEThreeBytesField, XLE3BytesField, OUIField]: + f = cls('x', 0) + assert f.i2len(None, 0x010203) == len(f.addfield(None, b'', 0x010203)) == 3, cls + +class ThreeBytesTLV(Packet): + fields_desc = [ FieldLenField('len', None, length_of='oui', fmt='B'), + OUIField('oui', 0x00000c) ] + +assert ThreeBytesTLV(raw(ThreeBytesTLV())).len == 3 + = NBytesField ~ field nbytesfield