From e5cdc74ac61e9e877cb09e636ad68c321bfa7b7f Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Tue, 6 Oct 2026 14:53:43 -0400 Subject: [PATCH] fix(ipv6): _read_ip_hextet slices include the version nibble The traffic class was read from hex[0:2] and the flow label from hex[2:], so both included the version nibble. Read them from hex[1:3] and hex[3:]. Correct the pinned values in test_ipv6_unit.py, and add test_ipv6_read_ip_hextet_unit.py, which also checks the helper against the schema's own packing. Closes #1094 --- pcapkit/protocols/internet/ipv6.py | 4 +- .../internet/test_ipv6_read_ip_hextet_unit.py | 49 +++++++++++++++++++ tests/protocols/internet/test_ipv6_unit.py | 2 +- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/protocols/internet/test_ipv6_read_ip_hextet_unit.py diff --git a/pcapkit/protocols/internet/ipv6.py b/pcapkit/protocols/internet/ipv6.py index 1cff0c112e..f41d1258f4 100644 --- a/pcapkit/protocols/internet/ipv6.py +++ b/pcapkit/protocols/internet/ipv6.py @@ -330,8 +330,8 @@ def _read_ip_hextet(self) -> 'tuple[int, int, int]': """ _htet = self._read_fileng(4).hex() _vers = int(_htet[0], base=16) # version number (6) - _tcls = int(_htet[0:2], base=16) # traffic class - _flow = int(_htet[2:], base=16) # flow label + _tcls = int(_htet[1:3], base=16) # traffic class + _flow = int(_htet[3:], base=16) # flow label return (_vers, _tcls, _flow) diff --git a/tests/protocols/internet/test_ipv6_read_ip_hextet_unit.py b/tests/protocols/internet/test_ipv6_read_ip_hextet_unit.py new file mode 100644 index 0000000000..6dc61e65af --- /dev/null +++ b/tests/protocols/internet/test_ipv6_read_ip_hextet_unit.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import importlib.util +import unittest +from unittest import mock + +from tests._support import reimport_once_per_class + +RUNTIME_DEPS = ('tbtrim', 'aenum', 'chardet', 'dictdumper') +HAS_RUNTIME = all(importlib.util.find_spec(name) is not None for name in RUNTIME_DEPS) + + +@unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed') +class IPv6ReadIPHextetUnitTests(unittest.TestCase): + """GitHub issue #1094: the traffic class and flow label exclude the version nibble.""" + + def setUp(self) -> None: + reimport_once_per_class(self) + + def test_read_ip_hextet_splits_version_class_and_label(self) -> None: + from pcapkit.protocols.internet.ipv6 import IPv6 + + proto = object.__new__(IPv6) + proto._read_fileng = mock.Mock(return_value=bytes.fromhex('6abcdef0')) + + self.assertEqual(proto._read_ip_hextet(), (6, 0xab, 0xcdef0)) + proto._read_fileng.assert_called_once_with(4) + + def test_read_ip_hextet_agrees_with_schema_packing(self) -> None: + from pcapkit.const.reg.transtype import TransType + from pcapkit.protocols.internet.ipv6 import IPv6 + + proto = object.__new__(IPv6) + header = proto.make( + traffic_class=0xff, + flow_label=0xfffff, + next=TransType.UDP, + hop_limit=64, + src='2001:db8::1', + dst='2001:db8::2', + payload=b'', + ).pack() + + proto._read_fileng = mock.Mock(return_value=header[:4]) + self.assertEqual(proto._read_ip_hextet(), (6, 0xff, 0xfffff)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/protocols/internet/test_ipv6_unit.py b/tests/protocols/internet/test_ipv6_unit.py index 264d490242..23dfd9711a 100644 --- a/tests/protocols/internet/test_ipv6_unit.py +++ b/tests/protocols/internet/test_ipv6_unit.py @@ -128,7 +128,7 @@ def test_ipv6_properties_read_and_low_level_field_readers(self) -> None: bytes.fromhex('6abcdef0'), ip_address('2001:db8::5').packed, ]) - self.assertEqual(proto._read_ip_hextet(), (6, 0x6a, 0xbcdef0)) + self.assertEqual(proto._read_ip_hextet(), (6, 0xab, 0xcdef0)) self.assertEqual(proto._read_ip_addr(), ip_address('2001:db8::5')) def test_ipv6_decode_next_layer_handles_extension_headers_and_fragments(self) -> None: