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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Wave 2 of the roadmap, per the contributing docs' enhancement-proposal page: the protocol pairs that wanted a shared abstract base, and the two that simply had no dissector at all.

- **Added** -- `Netlink`, a dissector for the fixed 16-octet `nlmsghdr` framing of Linux's own kernel/userspace IPC transport (the kernel's own `netlink(7)` man page; [RFC 3549 Section 2.3.2](https://datatracker.ietf.org/doc/html/rfc3549#section-2.3.2)). `LinuxSLL`/ `LinuxSLL1`/ `LinuxSLL2` dispatch to it for the `ARPHRD_NETLINK` case instead of falling back to `Raw`. The byte order the header was written in is ambiguous -- Netlink is kernel/userspace IPC, not wire format -- and is guessed the way Wireshark's own netlink dissector does, by `nlmsg_byteorder()`. Per-family message payloads (routing table entries and the like) and multiple messages packed into one capture are out of scope for this first cut; every message's payload is kept as `Raw`, unconditionally. One of [#2460](https://github.com/JarryShaw/PyPCAPKit/issues/2460)'s five `arphrd_type` follow-ups to get a dissector (`FRAD` is tracked separately on [#2499](https://github.com/JarryShaw/PyPCAPKit/pull/2499)); `IPGRE`/`IP6GRE` and `IEEE80211_RADIOTAP` remain `Raw`.
- **Added** -- `FRAD`, a dissector for Frame Relay's multiprotocol encapsulation ([RFC 2427](https://datatracker.ietf.org/doc/html/rfc2427), obsoleting [RFC 1490](https://datatracker.ietf.org/doc/html/rfc1490)). `LinuxSLL`/ `LinuxSLL1`/ `LinuxSLL2` dispatch to it for the `ARPHRD_FRAD` case instead of falling back to `Raw`; the direct NLPID and SNAP-encapsulated forms both dispatch onward, to IPv4/IPv6 and (via the EtherType registry every Ethernet/VLAN/LinuxSLL frame shares) ARP among others. The first of [#2460](https://github.com/JarryShaw/PyPCAPKit/issues/2460)'s five `arphrd_type` follow-ups to get a dissector; the other four (`NETLINK`, `IPGRE`/`IP6GRE`, `IEEE80211_RADIOTAP`) remain `Raw`.

---
Expand Down
17 changes: 17 additions & 0 deletions docs/source/changelog/1.6.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ Wave 2 of the roadmap, per the contributing docs' enhancement-proposal page: the
protocol pairs that wanted a shared abstract base, and the two that simply had
no dissector at all.

* **Added** -- :class:`~pcapkit.protocols.link.netlink.Netlink`, a dissector for
the fixed 16-octet ``nlmsghdr`` framing of Linux's own kernel/userspace IPC
transport (the kernel's own ``netlink(7)`` man page; :rfc:`3549#section-2.3.2`).
:class:`~pcapkit.protocols.link.linux_sll.LinuxSLL`/
:class:`~pcapkit.protocols.link.linux_sll1.LinuxSLL1`/
:class:`~pcapkit.protocols.link.linux_sll2.LinuxSLL2` dispatch to it for the
``ARPHRD_NETLINK`` case instead of falling back to
:class:`~pcapkit.protocols.misc.raw.Raw`. The byte order the header was written
in is ambiguous -- Netlink is kernel/userspace IPC, not wire format -- and is
guessed the way Wireshark's own netlink dissector does, by
:func:`~pcapkit.protocols.link.netlink.nlmsg_byteorder`. Per-family message
payloads (routing table entries and the like) and multiple messages packed into
one capture are out of scope for this first cut; every message's payload is kept
as :class:`~pcapkit.protocols.misc.raw.Raw`, unconditionally. One of
:issue:`2460`'s five ``arphrd_type`` follow-ups to get a dissector (``FRAD`` is
tracked separately on :pr:`2499`); ``IPGRE``/``IP6GRE`` and
``IEEE80211_RADIOTAP`` remain :class:`~pcapkit.protocols.misc.raw.Raw`.
* **Added** -- :class:`~pcapkit.protocols.link.frad.FRAD`, a dissector for Frame
Relay's multiprotocol encapsulation (:rfc:`2427`, obsoleting :rfc:`1490`).
:class:`~pcapkit.protocols.link.linux_sll.LinuxSLL`/
Expand Down
1 change: 1 addition & 0 deletions docs/source/pcapkit/protocols/link/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ link layer, with detailed implementation and methods.
linux_sll1
linux_sll2
loopback
netlink
vlan
c_tag
s_tag
Expand Down
77 changes: 77 additions & 0 deletions docs/source/pcapkit/protocols/link/netlink.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
Netlink - Linux Netlink Socket Framing
=======================================

.. module:: pcapkit.protocols.link.netlink

:mod:`pcapkit.protocols.link.netlink` contains
:class:`~pcapkit.protocols.link.netlink.Netlink`
only, which implements the extractor for the ``struct nlmsghdr`` framing of
Linux's own kernel/user IPC transport, as seen on a
``LINKTYPE_LINUX_SLL``/``LINKTYPE_LINUX_SLL2`` capture whose ``arphrd_type``
is ``ARPHRD_NETLINK`` (an ``nlmon`` interface). Its structure is described as
below:

.. table::

====== ==== ======================= ================================
Octets Bits Name Description
====== ==== ======================= ================================
0 0 ``netlink.nlmsg_len`` Message Length (header included)
4 32 ``netlink.nlmsg_type`` Message Type
6 48 ``netlink.nlmsg_flags`` Message Flags
8 64 ``netlink.nlmsg_seq`` Sequence Number
12 96 ``netlink.nlmsg_pid`` Sender Port ID
====== ==== ======================= ================================

Only this fixed 16-octet header is dissected; per-family message payloads and
multiple messages packed into one capture are out of scope for this first
cut -- see the module docstring's *Scope* section. The byte order the header
was written in is ambiguous (Netlink is kernel/userspace IPC, not wire
format) and is guessed by :func:`nlmsg_byteorder`; either way it is kept as
``netlink.byteorder``.

.. autoclass:: pcapkit.protocols.link.netlink.Netlink
:no-members:
:show-inheritance:

.. autoproperty:: name
.. autoproperty:: alias
.. autoproperty:: length
.. autoproperty:: protocol

.. automethod:: id

.. automethod:: read
.. automethod:: make

.. automethod:: __length_hint__
.. automethod:: __index__

.. automethod:: _make_data

.. autoattribute:: __proto__
:no-value:

.. autofunction:: pcapkit.protocols.link.netlink.nlmsg_byteorder

Header Schemas
--------------

.. module:: pcapkit.protocols.schema.link.netlink

.. autoclass:: pcapkit.protocols.schema.link.netlink.Netlink
:members:
:show-inheritance:

Data Models
-----------

.. module:: pcapkit.protocols.data.link.netlink

.. autoclass:: pcapkit.protocols.data.link.netlink.Netlink
:members:
:show-inheritance:

.. rubric:: Footnotes

.. [*] :manpage:`netlink(7)`; :rfc:`3549#section-2.3.2`
2 changes: 1 addition & 1 deletion pcapkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
'Raw', # Raw Packet

'ARP', 'C_Tag', 'Ethernet', 'FRAD', 'InARP', 'L2TP', # Link Layer
'L2TPv2', 'LinuxSLL', 'LinuxSLL1', 'LinuxSLL2', 'Loopback', 'S_Tag', 'VLAN',
'L2TPv2', 'LinuxSLL', 'LinuxSLL1', 'LinuxSLL2', 'Loopback', 'Netlink', 'S_Tag', 'VLAN',

'AH', 'ESP', 'ICMP', 'ICMPv4', 'ICMPv6', # Internet Layer
'IGMP', 'IGMPv1', 'IGMPv2', 'IGMPv3', 'IP', 'IPsec', 'IPv4', 'IPv6', 'IPX',
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
'NoPayload', # No Payload
'Raw', # Raw Packet
'ARP', 'C_Tag', 'Ethernet', 'FRAD', 'InARP', 'L2TP', 'L2TPv2', 'LinuxSLL', 'LinuxSLL1',
'LinuxSLL2', 'Loopback', 'S_Tag', 'VLAN', # Link Layer
'LinuxSLL2', 'Loopback', 'Netlink', 'S_Tag', 'VLAN', # Link Layer
'AH', 'ESP', 'ICMP', 'ICMPv4', 'ICMPv6', 'IGMP', 'IGMPv1', 'IGMPv2', 'IGMPv3',
'IP', 'IPsec', 'IPv4', 'IPv6', 'IPX', # Internet Layer

Expand Down
3 changes: 2 additions & 1 deletion pcapkit/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@

# Link Layer
'ARP', 'C_Tag', 'Ethernet', 'FRAD', 'InARP', 'L2TP', 'L2TPv2', 'LinuxSLL', 'LinuxSLL1',
'LinuxSLL2', 'Loopback', 'S_Tag', 'VLAN',
'LinuxSLL2', 'Loopback', 'Netlink', 'S_Tag', 'VLAN',


# Internet Layer
'AH', 'ESP', 'ICMP', 'ICMPv4', 'ICMPv6', 'IGMP', 'IGMPv1', 'IGMPv2', 'IGMPv3',
Expand Down
3 changes: 3 additions & 0 deletions pcapkit/protocols/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
# BSD Loopback Encapsulation
'Loopback',

# Netlink Socket Framing
'Netlink',

# 802.1Q/802.1ad VLAN Tag Types
'VLAN', 'VLAN_TCI',

Expand Down
6 changes: 6 additions & 0 deletions pcapkit/protocols/data/link/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
# BSD Loopback Encapsulation
from pcapkit.protocols.data.link.loopback import Loopback

# Netlink Socket Framing
from pcapkit.protocols.data.link.netlink import Netlink

# 802.1Q/802.1ad VLAN Tag Types
from pcapkit.protocols.data.link.vlan import TCI as VLAN_TCI
from pcapkit.protocols.data.link.vlan import VLAN
Expand All @@ -46,6 +49,9 @@
# BSD Loopback Encapsulation
'Loopback',

# Netlink Socket Framing
'Netlink',

# 802.1Q/802.1ad VLAN Tag Types
'VLAN', 'VLAN_TCI',
]
40 changes: 40 additions & 0 deletions pcapkit/protocols/data/link/netlink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
"""data models for Linux Netlink socket framing"""

from typing import TYPE_CHECKING

from pcapkit.corekit.infoclass import info_final
from pcapkit.protocols.data.protocol import Protocol

if TYPE_CHECKING:
from typing_extensions import Literal

__all__ = ['Netlink']


@info_final
class Netlink(Protocol):
"""Data model for Linux Netlink socket framing (``nlmsghdr``)."""

#: Message length, in octets, including this 16-octet header -- as
#: written, in whichever byte order :attr:`byteorder` records.
nlmsg_len: 'int'
#: Message type; ``1``-``4`` are the generic control values
#: (``NLMSG_NOOP``/``NLMSG_ERROR``/``NLMSG_DONE``/``NLMSG_OVERRUN``)
#: shared by every Netlink family, ``16`` and above are family-specific
#: and not interpreted by this class.
nlmsg_type: 'int'
#: Message flags; their bit meanings depend on ``nlmsg_type`` and on
#: whether the message is a request, so they are not decoded further.
nlmsg_flags: 'int'
#: Sequence number.
nlmsg_seq: 'int'
#: Sender port ID; ``0`` when sent by the kernel.
nlmsg_pid: 'int'
#: Byte order the header was written in.
byteorder: "Literal['big', 'little']"

if TYPE_CHECKING:
def __init__(self, nlmsg_len: 'int', nlmsg_type: 'int', nlmsg_flags: 'int', # pylint: disable=unused-argument,super-init-not-called,multiple-statements
nlmsg_seq: 'int', nlmsg_pid: 'int', # pylint: disable=unused-argument
byteorder: "Literal['big', 'little']") -> 'None': ... # pylint: disable=unused-argument
4 changes: 3 additions & 1 deletion pcapkit/protocols/link/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pcapkit.protocols.link.ethernet import Ethernet
from pcapkit.protocols.link.frad import FRAD
from pcapkit.protocols.link.loopback import Loopback
from pcapkit.protocols.link.netlink import Netlink

# Linux "Cooked" Capture Encapsulation Family
from pcapkit.protocols.link.linux_sll import LinuxSLL
Expand All @@ -43,5 +44,6 @@

# Link Layer Protocols
'ARP', 'C_Tag', 'Ethernet', 'FRAD', 'InARP', 'L2TP', 'L2TPv2', 'LinuxSLL', 'LinuxSLL1',
'LinuxSLL2', 'Loopback', 'S_Tag', 'VLAN',
'LinuxSLL2', 'Loopback', 'Netlink', 'S_Tag', 'VLAN',

]
13 changes: 11 additions & 2 deletions pcapkit/protocols/link/linux_sll.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,11 @@ def _import_next_layer(self, proto: 'int', length: 'Optional[int]' = None, *, #
<pcapkit.protocols.protocol.Protocol.unpack>`)

Returns:
Instance of next layer: :class:`~pcapkit.protocols.link.frad.FRAD`
when ``arphrd_type`` is :attr:`ARPHRD.FRAD <pcapkit.const.linux.arphrd.ARPHRD.FRAD>`;
Instance of next layer: :class:`~pcapkit.protocols.link.netlink.Netlink`
when ``arphrd_type`` is :attr:`ARPHRD.NETLINK
<pcapkit.const.linux.arphrd.ARPHRD.NETLINK>`; or
:class:`~pcapkit.protocols.link.frad.FRAD` when ``arphrd_type`` is
:attr:`ARPHRD.FRAD <pcapkit.const.linux.arphrd.ARPHRD.FRAD>`;
otherwise :class:`~pcapkit.protocols.misc.raw.Raw`, recording
``proto``, for every other ``arphrd_type`` in
:data:`_ARPHRD_NOT_ETHERTYPE`.
Expand All @@ -164,6 +167,12 @@ def _import_next_layer(self, proto: 'int', length: 'Optional[int]' = None, *, #
if not min(length, len(file_)):
return super()._import_next_layer(proto, length, packet=packet) # type: ignore[misc,call-arg]

if arphrd_type == ARPHRD.NETLINK:
from pcapkit.protocols.link.netlink import Netlink # pylint: disable=import-outside-toplevel # isort: skip
return self._parse_next_layer(Netlink, file_, length, alias=proto, packet=packet,
layer=self._exlayer, protocol=self._exproto,
__context__=self._exctx)

if arphrd_type == ARPHRD.FRAD:
from pcapkit.protocols.link.frad import FRAD # pylint: disable=import-outside-toplevel
return self._parse_next_layer(FRAD, file_, length, alias=proto, packet=packet,
Expand Down
Loading
Loading