Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ff9a9a4
Add single_layer_UDS config option to UDS contrib
Copilot Apr 5, 2026
f28bd73
Address code review: explicit length check and closure comment
Copilot Apr 5, 2026
e9467d8
Add single layer UDS config, tests, and documentation
Copilot Apr 5, 2026
4354e39
Fix uds_single_layer_mode idempotency and clarify test naming
Copilot Apr 5, 2026
db73b4a
Make service decorator and single layer mode generic for KWP, OBD, GMLAN
Copilot Apr 5, 2026
7ea604d
Fix flake8 and mypy issues introduced by generic service decorator ch…
Copilot Apr 5, 2026
a7517ff
Fix remaining flake8 and mypy issues in automotive protocol files
Copilot Apr 5, 2026
0b07906
Remove _make_single_layer_mode helper; rename config key to single_la…
Copilot Apr 6, 2026
58162cf
Update documentation to reflect single_layer_mode rename and removal …
Copilot Apr 6, 2026
3fc4bd5
Remove _make_service_decorator; add ConditionalField explicitly in ev…
Copilot Apr 6, 2026
a8f05e9
Make OBD service ConditionalField modifications explicit per-class in…
Copilot Apr 6, 2026
c137bec
Address PR review: delete utils.py, fix type annotations, named slm f…
Copilot Apr 6, 2026
45e6653
feat: add compatibility_mode flag to UDS, KWP, GMLAN, OBD single-laye…
Copilot Apr 7, 2026
242ab0d
fix: use Packet type annotation in _kwp_slm; simplify OBD assertion i…
Copilot Apr 7, 2026
ea02c8f
fix: use valid 7-byte test data for GMLAN_RFRDPR sub-subpacket test i…
Copilot Apr 7, 2026
b8ed598
fix: resolve flake8 E302/E501/F401 errors in changed automotive files
Copilot Apr 7, 2026
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
92 changes: 92 additions & 0 deletions doc/scapy/layers/automotive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,98 @@ to the Scapy interpreter::

.. image:: ../graphics/animations/animation-scapy-uds3.svg


Single Layer Mode
-----------------

UDS, KWP, OBD, and GMLAN all support a *single layer mode* that makes each
service packet a standalone ``Packet`` rather than a nested sublayer.

**Default (multi-layer) mode**

.. code-block:: python

>>> pkt = UDS() / UDS_DSC(diagnosticSessionType=0x01)
>>> UDS(b'\x10\x01')
<UDS service=DiagnosticSessionControl |<UDS_DSC diagnosticSessionType=defaultSession |>>

**Single layer mode**

To enable before loading a module::

>>> conf.contribs['UDS'] = {'treat-response-pending-as-answer': False,
... 'single_layer_mode': True}
>>> load_contrib('automotive.uds')

To toggle at runtime after loading::

>>> conf.contribs['UDS']['single_layer_mode'] = True
>>> UDS(b'\x10\x01')
<UDS_DSC service=DiagnosticSessionControl diagnosticSessionType=defaultSession |>
>>> bytes(UDS_DSC(diagnosticSessionType=0x01))
b'\x10\x01'
>>> conf.contribs['UDS']['single_layer_mode'] = False # revert to multi-layer mode

The same ``single_layer_mode`` key works for all protocols: replace ``'UDS'``
with ``'KWP'``, ``'OBD'``, or ``'GMLAN'`` as appropriate.

Compatibility Mode
------------------

Scapy allows crafting packets freely, including stacking a service sub-packet
on top of the base protocol layer (e.g. ``UDS()/UDS_DSC()``). When both
``single_layer_mode`` *and* stacking are used together, the ``service`` byte
would normally appear twice in the resulting byte stream – once from the base
layer and once from the sub-packet's own ``service`` ConditionalField.

The **compatibility mode** flag (``compatibility_mode``, default ``True``)
addresses this: when it is enabled and ``single_layer_mode`` is active, the
sub-packet's ``service`` field is automatically **suppressed** whenever the
immediate underlayer is already the matching base-protocol packet.

.. list-table:: Behaviour matrix
:header-rows: 1
:widths: 25 25 50

* - ``single_layer_mode``
- ``compatibility_mode``
- ``UDS()/UDS_DSC()`` byte layout
* - ``False``
- any
- ``service`` (UDS) + ``diagnosticSessionType`` (UDS_DSC)
* - ``True``
- ``True`` *(default)*
- ``service`` (UDS) + ``diagnosticSessionType`` (UDS_DSC) — duplicate suppressed
* - ``True``
- ``False``
- ``service`` (UDS) + ``service`` (UDS_DSC) + ``diagnosticSessionType`` (UDS_DSC)

Example with compatibility mode on (default)::

>>> conf.contribs['UDS']['single_layer_mode'] = True
>>> conf.contribs['UDS']['compatibility_mode'] = True # already the default

>>> # Standalone sub-packet: service field IS present (no UDS underlayer)
>>> bytes(UDS_DSC(diagnosticSessionType=0x01))
b'\x10\x01'

>>> # Stacked: service field in UDS_DSC is suppressed (UDS is the underlayer)
>>> bytes(UDS() / UDS_DSC(diagnosticSessionType=0x01))
b'\x10\x01'

Example with compatibility mode off::

>>> conf.contribs['UDS']['compatibility_mode'] = False

>>> # Stacked: both UDS and UDS_DSC emit a service byte
>>> bytes(UDS() / UDS_DSC(diagnosticSessionType=0x01))
b'\x10\x10\x01'

>>> conf.contribs['UDS']['compatibility_mode'] = True # restore default

The same ``compatibility_mode`` key works for all protocols: replace ``'UDS'``
with ``'KWP'``, ``'OBD'``, or ``'GMLAN'`` as appropriate.

GMLAN
=====

Expand Down
Loading
Loading