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
14 changes: 9 additions & 5 deletions pcapkit/protocols/link/arp.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,14 @@ def make(self, *,
Constructed packet data.

Raises:
ProtocolError: If a packed hardware address is not ``hlen`` octets
long (with ``hlen`` not given and ``htype`` Ethernet, not 6
octets long), or a packed protocol address is not ``plen``
octets long.
FieldValueError: If ``spa`` or ``tpa`` is rejected by
:meth:`_make_proto_resolve`, e.g. a :obj:`bool`.
ProtocolError: If ``sha`` or ``tha`` is rejected by
:meth:`_make_addr_resolve`, or ``spa`` or ``tpa`` by
:meth:`_make_proto_resolve`; if a packed hardware address is
not ``hlen`` octets long (with ``hlen`` not given and
``htype`` Ethernet, not 6 octets long), or a packed protocol
address is not ``plen`` octets long.

"""
_htype = self._make_index(htype, htype_default, namespace=htype_namespace,
Expand Down Expand Up @@ -474,7 +478,7 @@ def _make_proto_resolve(self, addr: 'IPv4Address | IPv6Address | str | bytes', p
pack as ``00000001`` (IPv4) or ``::1`` (IPv6) with no exception and
no warning.

The description below uses :attr:`self.__class__.__name__
The messages are prefixed with :attr:`self.__class__.__name__
<type.__name__>` rather than :attr:`self.alias
<pcapkit.protocols.protocol.Protocol.alias>`, even though the
latter is this project's usual choice: :attr:`alias` here reads
Expand Down
4 changes: 4 additions & 0 deletions pcapkit/protocols/link/ethernet.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ def make(self,
Returns:
Constructed packet data.

Raises:
ProtocolError: If ``dst`` or ``src`` is rejected by
:meth:`_make_mac_addr`.

"""
_type = self._make_index(type, type_default, namespace=type_namespace,
reversed=type_reversed, pack=False)
Expand Down
9 changes: 6 additions & 3 deletions pcapkit/protocols/link/l2tp.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@
session header is, in :rfc:`3931` §4.1.1.1's own words, *"free of any restrictions
imposed by coexistence with L2TPv2 and L2F"* -- a data message opens with the raw
32-bit Session ID and carries **no version nibble at all**, so there is nothing a
v2 parser could test to recognise that the datagram is not its own. Measured,
v2 parser could test to recognise that the datagram is not its own. Without the
version check in :meth:`L2TPv2.read <pcapkit.protocols.link.l2tpv2.L2TPv2.read>`,
that binding reports ``version=4``, ``tunnelid=0x5678`` and ``sessionid=0xff03``
for a v3-over-IP datagram: a complete header assembled out of the top half of a
Session ID and the first two octets of the PPP frame behind it. 115 is a missing
for a v3-over-IP datagram: a complete header assembled out of a Session ID and
the first two octets of the PPP frame behind it. The check refuses that datagram
only because its Session ID does not put ``2`` in the nibble; one that does
parses as v2. 115 is a missing
*class*, not a missing registration, and until that class exists an undissected
payload is the honest answer.

Expand Down
7 changes: 5 additions & 2 deletions pcapkit/protocols/link/l2tpv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_L2TP':
Returns:
Parsed packet data.

Raises:
ProtocolError: If the version nibble is not ``2``.

"""
schema = self.__header__
_flag = schema.flags
Expand All @@ -199,8 +202,8 @@ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_L2TP':
# reports a tunnel and session ID assembled out of octets that are
# neither: an :rfc:`3931` §4.1.1 L2TPv3-over-IP datagram would otherwise
# yield ``version=4``, ``tunnelid=0x5678`` and ``sessionid=0xff03``, read
# out of the top half of a Session ID and the first two octets of the PPP
# frame behind it (#548). This is also what keeps the hard-coded
# out of the lower half of a Session ID and the first two octets of the
# PPP frame behind it (#548). This is also what keeps the hard-coded
# ``Literal[2]`` of ``version`` true, instead of disagreeing with
# ``info.version`` on the same datagram.
if _flag['version'] != 2:
Expand Down
5 changes: 3 additions & 2 deletions pcapkit/protocols/schema/transport/sctp.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ def padding_length(pkt: 'dict[str, Any]') -> 'int':

Returns:
Number of padding bytes, clamped to the number of bytes left in the
enclosing structure, since :rfc:`9260#section-3.2` allows the final
padding of a packet to be omitted. When packing, it is clamped as well
enclosing structure, so that a final chunk captured without (all of)
its padding still parses, although :rfc:`9260#section-3.2` has the
sender pad every chunk. When packing, it is clamped as well
to the ``__padding__`` width :func:`pre_pack_padding` hands over, so a
final chunk captured without (all of) its padding rebuilds without it
(:issue:`1474`).
Expand Down
17 changes: 10 additions & 7 deletions pcapkit/protocols/transport/sctp.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,9 @@ class SCTP(Transport[Data_SCTP, Schema_SCTP],
>>> SCTP.register(PayloadProtocolIdentifier.PayloadProtocolIdentifier_3GPP_NG_Application_Protocol, NGAP)
>>> SCTP.register(60, NGAP) # equivalent, PPID given as a plain integer

By default, PPIDs 60 (``NG_Application_Protocol``) and 66
(``NGAP_over_DTLS_over_SCTP``) are registered to
By default, PPIDs 60 (``PayloadProtocolIdentifier_3GPP_NG_Application_Protocol``)
and 66 (``PayloadProtocolIdentifier_3GPP_NGAP_over_DTLS_over_SCTP``) are
registered to
:class:`~pcapkit.protocols.application.ngap.NGAP`, and every other PPID
resolves to :class:`~pcapkit.protocols.misc.raw.Raw`.

Expand Down Expand Up @@ -1037,10 +1038,12 @@ def _make_sctp_chunks(self, chunks: 'list[Schema_Chunk | tuple[Enum_Chunk, dict[
No alignment fix-up happens here, unlike
:meth:`TCP._make_tcp_options
<pcapkit.protocols.transport.tcp.TCP._make_tcp_options>`: every
chunk schema carries its own trailing
:class:`~pcapkit.corekit.fields.strings.PaddingField`, computed from
the chunk's own ``length``, so a chunk pads itself to the four-byte
boundary required by :rfc:`9260#section-3.2`.
chunk pads itself to the four-byte boundary required by
:rfc:`9260#section-3.2`. A chunk without a nested list carries its
own trailing :class:`~pcapkit.corekit.fields.strings.PaddingField`,
computed from the chunk's own ``length``; one with a nested
parameter or error cause list folds its padding into that list, see
:func:`~pcapkit.protocols.schema.transport.sctp.nested_length`.

"""
chunks_list = [] # type: list[Schema_Chunk | bytes]
Expand Down Expand Up @@ -2068,7 +2071,7 @@ def _make_chunk_init_ack(self, code: 'Enum_Chunk', chunk: 'Optional[Data_INITACK
def _make_chunk_sack(self, code: 'Enum_Chunk', chunk: 'Optional[Data_SACKChunk]' = None, *,
flags: 'bytes' = b'\x00',
cum_tsn_ack: 'int' = 0,
a_rwnd: 'int' = 1500, # minimum permitted by [RFC 9260]
a_rwnd: 'int' = 1500, # the INIT minimum [RFC 9260]; a SACK may advertise less
gap_blocks: 'Optional[list[Schema_GapAckBlock | Data_GapAckBlock | tuple[int, int]]]' = None, # pylint: disable=line-too-long
dup_tsn: 'Optional[list[int]]' = None,
**kwargs: 'Any') -> 'Schema_SACKChunk':
Expand Down
10 changes: 7 additions & 3 deletions pcapkit/protocols/transport/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,9 @@ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_TCP':
# segment whose flags octet is all zero: the ``|=`` below is the only promotion and
# never runs, and ``Enum_Flags.SYN in self._flags`` would raise ``TypeError:
# argument of type 'int' is not a container or iterable``. A segment with any flag
# set would mask that. :class:`Enum_Flags` is an ``aenum.IntFlag`` with no
# ``_missing_`` of its own, so ``Enum_Flags(0)`` is a valid flagless member that
# compares equal to ``0`` and ORs as usual. This is the type :attr:`connection`
# set would mask that. :class:`Enum_Flags` is an ``aenum.IntFlag`` whose
# ``_missing_`` accepts any value from ``0`` to ``0xFFFF``, so ``Enum_Flags(0)`` is
# a valid flagless member that compares equal to ``0`` and ORs as usual. This is the type :attr:`connection`
# advertises.
_flag = Enum_Flags(0)
for key, val in schema.flags.items():
Expand Down Expand Up @@ -2163,6 +2163,8 @@ def _make_tcp_options(self, options: 'list[Schema_Option | tuple[Enum_Option, di
ProtocolNotImplemented: If an option argument given by name, an
MPTCP ``subtype`` or an alternate checksum ``algorithm``, is
not in its namespace and has no default.
FieldValueError: If an ADD_ADDR ``addr`` is a :obj:`bool` or not a
valid IP address.

Note:
The options are emitted in the given order, ``NOP`` and ``EOOL``
Expand Down Expand Up @@ -3245,6 +3247,8 @@ def _make_mptcp_addaddr(self, subtype: 'Enum_MPTCPOption', opt: 'Optional[Data_M
Raises:
ProtocolError: If ``hmac`` is not 8 octets, or is given together
with ``echo`` set.
FieldValueError: If ``addr`` is a :obj:`bool` or not a valid IP
address.

Note:
:rfc:`8684` section 3.4.1 allows an ADD_ADDR without the truncated
Expand Down
Loading