Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pcapkit/protocols/internet/hopopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class HOPOPT(IPv6_Ext[Data_HOPOPT, Schema_HOPOPT],
* - :attr:`~pcapkit.const.ipv6.option.Option.Quick_Start`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._read_opt_qs`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._make_opt_qs`
* - :attr:`~pcapkit.const.ipv6.option.Option.RPL_Option_0x23`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._read_opt_rpl`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._make_opt_rpl`
* - :attr:`~pcapkit.const.ipv6.option.Option.RPL_Option_0x63`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._read_opt_rpl`
- :meth:`~pcapkit.protocols.internet.hopopt.HOPOPT._make_opt_rpl`
Expand Down Expand Up @@ -204,6 +207,7 @@ class HOPOPT(IPv6_Ext[Data_HOPOPT, Schema_HOPOPT],
Enum_Option.SMF_DPD: 'smf_dpd', # [RFC 6621]
Enum_Option.PDM: 'pdm', # [RFC 8250] 10
Enum_Option.Quick_Start: 'qs', # [RFC 4782][RFC Errata 2034] 6
Enum_Option.RPL_Option_0x23: 'rpl', # [RFC 9008]
Enum_Option.RPL_Option_0x63: 'rpl', # [RFC 6553]
Enum_Option.MPL_Option: 'mpl', # [RFC 7731]
Enum_Option.ILNP_Nonce: 'ilnp', # [RFC 6744]
Expand Down
4 changes: 4 additions & 0 deletions pcapkit/protocols/internet/ipv6_opts.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ class IPv6_Opts(IPv6_Ext[Data_IPv6_Opts, Schema_IPv6_Opts],
* - :attr:`~pcapkit.const.ipv6.option.Option.Quick_Start`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._read_opt_qs`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._make_opt_qs`
* - :attr:`~pcapkit.const.ipv6.option.Option.RPL_Option_0x23`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._read_opt_rpl`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._make_opt_rpl`
* - :attr:`~pcapkit.const.ipv6.option.Option.RPL_Option_0x63`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._read_opt_rpl`
- :meth:`~pcapkit.protocols.internet.ipv6_opts.IPv6_Opts._make_opt_rpl`
Expand Down Expand Up @@ -208,6 +211,7 @@ class IPv6_Opts(IPv6_Ext[Data_IPv6_Opts, Schema_IPv6_Opts],
Enum_Option.SMF_DPD: 'smf_dpd', # [RFC 6621]
Enum_Option.PDM: 'pdm', # [RFC 8250] 10
Enum_Option.Quick_Start: 'qs', # [RFC 4782][RFC Errata 2034] 6
Enum_Option.RPL_Option_0x23: 'rpl', # [RFC 9008]
Enum_Option.RPL_Option_0x63: 'rpl', # [RFC 6553]
Enum_Option.MPL_Option: 'mpl', # [RFC 7731]
Enum_Option.ILNP_Nonce: 'ilnp', # [RFC 6744]
Expand Down
74 changes: 74 additions & 0 deletions tests/protocols/internet/test_rpl_option_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
"""HOPOPT and IPv6-Opts read and rebuild the RPL option under type 0x23.

GitHub issue #1196: the ``__option__`` registries of :class:`HOPOPT` and
:class:`IPv6_Opts` mapped only ``RPL_Option_0x63`` to the RPL reader. Type
0x23, the RPL option code assigned by :rfc:`9008`, fell through to
``_read_opt_none``, which read ``schema.data`` off the RPL schema and raised
:exc:`AttributeError`.

Every case builds its own octets in memory and reads no capture. The protocol
classes are imported inside each test, after
:func:`~tests._support.reimport_once_per_class`, as in
:mod:`tests.protocols.link.test_ethernet_mac_roundtrip_unit`.

"""

import unittest

from tests._support import reimport_once_per_class

#: Extension headers carrying one RPL option [:rfc:`6553#section-3`], next
#: header 59: flags ``O``, ``R`` and ``F`` set, RPLInstanceID 0x2A, SenderRank
#: 0x0102. Type 0x63 is already routed to the RPL reader and is the control.
HEADERS = {
0x23: bytes.fromhex('3b002304e02a0102'),
0x63: bytes.fromhex('3b006304e02a0102'),
}


def _classes() -> 'dict[str, type]':
from pcapkit.protocols.internet.hopopt import HOPOPT
from pcapkit.protocols.internet.ipv6_opts import IPv6_Opts
return {'HOPOPT': HOPOPT, 'IPv6_Opts': IPv6_Opts}


class TestRPLOption(unittest.TestCase):
"""Pin the RPL option for both of its option types."""

def setUp(self) -> None:
reimport_once_per_class(self)

def test_rpl_option_is_read_by_the_rpl_reader(self) -> None:
for name, cls in _classes().items():
for code, octets in HEADERS.items():
with self.subTest(proto=name, type=hex(code)):
parsed = cls(octets, len(octets), extension=True)
opt = list(parsed.info.options.values())[0]
self.assertEqual(opt.type, code)
self.assertEqual(opt.length, 6)
self.assertEqual((opt.flags.down, opt.flags.rank_err, opt.flags.fwd_err),
(True, True, True))
self.assertEqual(opt.id, 0x2A)
self.assertEqual(opt.rank, 0x0102)

def test_rpl_option_rebuilds_byte_for_byte(self) -> None:
for name, cls in _classes().items():
for code, octets in HEADERS.items():
with self.subTest(proto=name, type=hex(code)):
parsed = cls(octets, len(octets), extension=True)
self.assertEqual(cls.from_data(parsed.info).data, octets)

def test_malformed_rpl_option_raises_protocol_error(self) -> None:
from pcapkit.utilities.exceptions import ProtocolError

# Opt Data Len 0, where the RPL option needs 4.
octets = bytes.fromhex('3b00230001020000')
for name, cls in _classes().items():
with self.subTest(proto=name):
with self.assertRaises(ProtocolError):
cls(octets, len(octets), extension=True)


if __name__ == '__main__':
unittest.main()
Loading