Skip to content

Commit 3363981

Browse files
authored
Add version field for DoIP and DoIP sockets (#4828)
* Add version field for DoIP and DoIP sockets * Fix hashret of DoIP
1 parent 3449fb7 commit 3363981

File tree

2 files changed

+156
-10
lines changed

2 files changed

+156
-10
lines changed

scapy/contrib/automotive/doip.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,12 @@ class DoIP(Packet):
120120
0x8003: "Diagnostic message NACK"}
121121
name = 'DoIP'
122122
fields_desc = [
123-
XByteField("protocol_version", 0x02),
124-
XByteField("inverse_version", 0xFD),
123+
XByteEnumField("protocol_version", 0x02, {
124+
0x01: "ISO13400_2010", 0x02: "ISO13400_2012",
125+
0x03: "ISO13400_2019", 0x04: "ISO13400_2019_AMD1"}),
126+
XByteEnumField("inverse_version", 0xFD, {
127+
0xFE: "ISO13400_2010", 0xFD: "ISO13400_2012",
128+
0xFC: "ISO13400_2019", 0xFB: "ISO13400_2019_AMD1"}),
125129
XShortEnumField("payload_type", 0, payload_types),
126130
IntField("payload_length", None),
127131
ConditionalField(ByteEnumField("nack", 0, {
@@ -239,7 +243,26 @@ def answers(self, other):
239243

240244
def hashret(self):
241245
# type: () -> bytes
242-
return bytes(self)[:3]
246+
payload_type_mapping = {
247+
0x0000: b"\x01",
248+
0x0001: b"\x01",
249+
0x0002: b"\x01",
250+
0x0003: b"\x01",
251+
0x0004: b"\x01",
252+
0x0005: b"\x02",
253+
0x0006: b"\x02",
254+
0x0007: b"\x03",
255+
0x0008: b"\x03",
256+
0x4001: b"\x04",
257+
0x4002: b"\x04",
258+
0x4003: b"\x05",
259+
0x4004: b"\x05",
260+
0x8001: b"\x06",
261+
0x8002: b"\x06",
262+
0x8003: b"\x06",
263+
}
264+
265+
return payload_type_mapping.get(self.payload_type, b"\xff")
243266

244267
def post_build(self, pkt, pay):
245268
# type: (bytes, bytes) -> bytes
@@ -328,6 +351,10 @@ class DoIPSocket(DoIPSSLStreamSocket):
328351
connect via SSL/TLS
329352
:param context: Optional ssl.SSLContext object for initialization of ssl socket
330353
connections.
354+
:param doip_version: DoIP protocol version to use, default is 2 (ISO 13400-2012)
355+
:param enforce_doip_version: If true, the protocol_version field in each DoIP
356+
packet to be sent, is always set to the value of
357+
doip_version.
331358
332359
Example:
333360
>>> socket = DoIPSocket("169.254.0.131")
@@ -345,7 +372,9 @@ def __init__(self,
345372
activation_type=0, # type: int
346373
reserved_oem=b"", # type: bytes
347374
force_tls=False, # type: bool
348-
context=None # type: Optional[ssl.SSLContext]
375+
context=None, # type: Optional[ssl.SSLContext]
376+
doip_version=2, # type: int
377+
enforce_doip_version=False, # type: bool
349378
): # type: (...) -> None
350379
self.ip = ip
351380
self.port = port
@@ -357,6 +386,8 @@ def __init__(self,
357386
self.reserved_oem = reserved_oem
358387
self.force_tls = force_tls
359388
self.context = context
389+
self.doip_version = doip_version
390+
self.enforce_doip_version = enforce_doip_version
360391
try:
361392
self._init_socket()
362393
except Exception:
@@ -448,6 +479,12 @@ def _activate_routing(self): # type: (...) -> int
448479
else:
449480
return -1
450481

482+
def send(self, x): # type: (Packet) -> int
483+
if self.enforce_doip_version and isinstance(x, DoIP):
484+
x[DoIP].protocol_version = self.doip_version
485+
x[DoIP].inverse_version = 0xFF - self.doip_version
486+
return super().send(x)
487+
451488

452489
class UDS_DoIPSocket(DoIPSocket):
453490
"""

test/contrib/automotive/doip.uts

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,14 @@ def server():
484484
sock.bind(('127.0.0.1', 13400))
485485
sock.listen(1)
486486
server_up.set()
487-
connection, address = sock.accept()
488-
sniff_up.wait(timeout=1)
489-
for i in range(len(buffer)):
490-
connection.send(buffer[i:i+1])
491-
time.sleep(0.01)
492-
connection.close()
487+
try:
488+
connection, address = sock.accept()
489+
sniff_up.wait(timeout=1)
490+
for i in range(len(buffer)):
491+
connection.send(buffer[i:i+1])
492+
time.sleep(0.01)
493+
finally:
494+
connection.close()
493495
finally:
494496
sock.close()
495497

@@ -503,6 +505,49 @@ pkts = sock.sniff(timeout=1, count=2, started_callback=sniff_up.set)
503505
server_thread.join(timeout=1)
504506
assert len(pkts) == 2
505507

508+
= Test DoIPSocket 2 enforce protocol_version
509+
~ linux
510+
511+
server_up = threading.Event()
512+
sniff_up = threading.Event()
513+
def server():
514+
buffer = b'\x02\xfd\x80\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x02\xfd\x80\x01\x00\x00\x00\n\x10\x10\x0e\x80P\x03\x002\x01\xf4'
515+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
516+
try:
517+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
518+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
519+
sock.bind(('127.0.0.1', 13400))
520+
sock.listen(1)
521+
server_up.set()
522+
connection, address = sock.accept()
523+
try:
524+
sniff_up.wait(timeout=1)
525+
connection.send(buffer)
526+
doip_sock = DoIPSSLStreamSocket(connection)
527+
pkts = doip_sock.sniff(timeout=2, count=1)
528+
doip_sock.send(pkts[0])
529+
finally:
530+
connection.close()
531+
finally:
532+
sock.close()
533+
534+
535+
server_thread = threading.Thread(target=server)
536+
server_thread.start()
537+
server_up.wait(timeout=1)
538+
sock = DoIPSocket(activate_routing=False, doip_version=3, enforce_doip_version=True)
539+
540+
pkts = sock.sniff(timeout=1, count=2, started_callback=sniff_up.set)
541+
sock.send(DoIP(payload_type=0x8001, source_address=0xe80, target_address=0xe400) / UDS() / UDS_TP())
542+
pkts2 = sock.sniff(timeout=1, count=1)
543+
server_thread.join(timeout=1)
544+
assert len(pkts) == 2
545+
assert len(pkts2) == 1
546+
assert pkts2[0].protocol_version == 0x03
547+
assert pkts2[0].inverse_version == 0xfc
548+
assert pkts2[0].payload_type == 0x8001
549+
assert pkts2[0].service == 0x3E
550+
506551
= Test DoIPSocket 3
507552

508553
server_up = threading.Event()
@@ -813,3 +858,67 @@ pkts = sock.sniff(timeout=1, count=2, started_callback=sniff_up.set)
813858
server_tcp_thread.join(timeout=1)
814859
server_tls_thread.join(timeout=1)
815860
assert len(pkts) == 2
861+
862+
= Test UDS_DualDoIPSslSocket6 force version 3
863+
~ broken_windows not_pypy
864+
865+
server_tcp_up = threading.Event()
866+
server_tls_up = threading.Event()
867+
sniff_up = threading.Event()
868+
def server_tls():
869+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
870+
_load_certificate_chain(context)
871+
context.check_hostname = False
872+
context.verify_mode = ssl.CERT_NONE
873+
buffer = bytes.fromhex("03fc0006000000090e8011061000000000")
874+
buffer += b'\x03\xfc\x80\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x03\xfc\x80\x01\x00\x00\x00\n\x10\x10\x0e\x80P\x03\x002\x01\xf4'
875+
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
876+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
877+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
878+
ssock = context.wrap_socket(sock)
879+
try:
880+
ssock.bind(('::1', 3496))
881+
ssock.listen(1)
882+
server_tls_up.set()
883+
connection, address = ssock.accept()
884+
sniff_up.wait(timeout=1)
885+
connection.send(buffer)
886+
connection.close()
887+
finally:
888+
ssock.close()
889+
890+
def server_tcp():
891+
buffer = bytes.fromhex("03fc0006000000090e8011060700000000")
892+
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
893+
try:
894+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
895+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
896+
sock.bind(('::1', 13400))
897+
sock.listen(1)
898+
server_tcp_up.set()
899+
connection, address = sock.accept()
900+
connection.send(buffer)
901+
connection.shutdown(socket.SHUT_RDWR)
902+
connection.close()
903+
finally:
904+
sock.close()
905+
906+
907+
server_tcp_thread = threading.Thread(target=server_tcp)
908+
server_tcp_thread.start()
909+
server_tcp_up.wait(timeout=1)
910+
server_tls_thread = threading.Thread(target=server_tls)
911+
server_tls_thread.start()
912+
server_tls_up.wait(timeout=1)
913+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
914+
context.check_hostname = False
915+
context.verify_mode = ssl.CERT_NONE
916+
917+
conf.debug_dissector = True
918+
919+
sock = UDS_DoIPSocket(ip="::1", context=context, doip_version=3, enforce_doip_version=True)
920+
921+
pkts = sock.sniff(timeout=1, count=2, started_callback=sniff_up.set)
922+
server_tcp_thread.join(timeout=1)
923+
server_tls_thread.join(timeout=1)
924+
assert len(pkts) == 2

0 commit comments

Comments
 (0)