Skip to content

Commit 77abbcc

Browse files
polybassaNils WeissBenGardiner
authored
Introduce single-layer and compatibility modes for UDS, KWP, OBD, and GMLAN protocols; add documentation and tests. (secdev#4962)
Co-authored-by: Nils Weiss <nils.weiss@dissecto.com> Co-authored-by: BenGardiner <243321+BenGardiner@users.noreply.github.com>
1 parent 362eeeb commit 77abbcc

15 files changed

Lines changed: 1302 additions & 106 deletions

File tree

doc/scapy/layers/automotive.rst

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,98 @@ to the Scapy interpreter::
10811081

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

1084+
1085+
Single Layer Mode
1086+
-----------------
1087+
1088+
UDS, KWP, OBD, and GMLAN all support a *single layer mode* that makes each
1089+
service packet a standalone ``Packet`` rather than a nested sublayer.
1090+
1091+
**Default (multi-layer) mode**
1092+
1093+
.. code-block:: python
1094+
1095+
>>> pkt = UDS() / UDS_DSC(diagnosticSessionType=0x01)
1096+
>>> UDS(b'\x10\x01')
1097+
<UDS service=DiagnosticSessionControl |<UDS_DSC diagnosticSessionType=defaultSession |>>
1098+
1099+
**Single layer mode**
1100+
1101+
To enable before loading a module::
1102+
1103+
>>> conf.contribs['UDS'] = {'treat-response-pending-as-answer': False,
1104+
... 'single_layer_mode': True}
1105+
>>> load_contrib('automotive.uds')
1106+
1107+
To toggle at runtime after loading::
1108+
1109+
>>> conf.contribs['UDS']['single_layer_mode'] = True
1110+
>>> UDS(b'\x10\x01')
1111+
<UDS_DSC service=DiagnosticSessionControl diagnosticSessionType=defaultSession |>
1112+
>>> bytes(UDS_DSC(diagnosticSessionType=0x01))
1113+
b'\x10\x01'
1114+
>>> conf.contribs['UDS']['single_layer_mode'] = False # revert to multi-layer mode
1115+
1116+
The same ``single_layer_mode`` key works for all protocols: replace ``'UDS'``
1117+
with ``'KWP'``, ``'OBD'``, or ``'GMLAN'`` as appropriate.
1118+
1119+
Compatibility Mode
1120+
------------------
1121+
1122+
Scapy allows crafting packets freely, including stacking a service sub-packet
1123+
on top of the base protocol layer (e.g. ``UDS()/UDS_DSC()``). When both
1124+
``single_layer_mode`` *and* stacking are used together, the ``service`` byte
1125+
would normally appear twice in the resulting byte stream – once from the base
1126+
layer and once from the sub-packet's own ``service`` ConditionalField.
1127+
1128+
The **compatibility mode** flag (``compatibility_mode``, default ``True``)
1129+
addresses this: when it is enabled and ``single_layer_mode`` is active, the
1130+
sub-packet's ``service`` field is automatically **suppressed** whenever the
1131+
immediate underlayer is already the matching base-protocol packet.
1132+
1133+
.. list-table:: Behaviour matrix
1134+
:header-rows: 1
1135+
:widths: 25 25 50
1136+
1137+
* - ``single_layer_mode``
1138+
- ``compatibility_mode``
1139+
- ``UDS()/UDS_DSC()`` byte layout
1140+
* - ``False``
1141+
- any
1142+
- ``service`` (UDS) + ``diagnosticSessionType`` (UDS_DSC)
1143+
* - ``True``
1144+
- ``True`` *(default)*
1145+
- ``service`` (UDS) + ``diagnosticSessionType`` (UDS_DSC) — duplicate suppressed
1146+
* - ``True``
1147+
- ``False``
1148+
- ``service`` (UDS) + ``service`` (UDS_DSC) + ``diagnosticSessionType`` (UDS_DSC)
1149+
1150+
Example with compatibility mode on (default)::
1151+
1152+
>>> conf.contribs['UDS']['single_layer_mode'] = True
1153+
>>> conf.contribs['UDS']['compatibility_mode'] = True # already the default
1154+
1155+
>>> # Standalone sub-packet: service field IS present (no UDS underlayer)
1156+
>>> bytes(UDS_DSC(diagnosticSessionType=0x01))
1157+
b'\x10\x01'
1158+
1159+
>>> # Stacked: service field in UDS_DSC is suppressed (UDS is the underlayer)
1160+
>>> bytes(UDS() / UDS_DSC(diagnosticSessionType=0x01))
1161+
b'\x10\x01'
1162+
1163+
Example with compatibility mode off::
1164+
1165+
>>> conf.contribs['UDS']['compatibility_mode'] = False
1166+
1167+
>>> # Stacked: both UDS and UDS_DSC emit a service byte
1168+
>>> bytes(UDS() / UDS_DSC(diagnosticSessionType=0x01))
1169+
b'\x10\x10\x01'
1170+
1171+
>>> conf.contribs['UDS']['compatibility_mode'] = True # restore default
1172+
1173+
The same ``compatibility_mode`` key works for all protocols: replace ``'UDS'``
1174+
with ``'KWP'``, ``'OBD'``, or ``'GMLAN'`` as appropriate.
1175+
10841176
GMLAN
10851177
=====
10861178

0 commit comments

Comments
 (0)