@@ -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 = 0x 01 )
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+
10841176GMLAN
10851177=====
10861178
0 commit comments