Skip to content

Commit 45c216f

Browse files
polybassagpotter2
andauthored
Fix #4472: Add ability to parse multiple DoIP packets in one TCP pkt (#4515)
* Fix #4472: Add ability to parse multiple DoIP packets in one TCP packet via TCPSession * update * When not in app mode, tcp_reassemble sub-packets --------- Co-authored-by: gpotter2 <10530980+gpotter2@users.noreply.github.com>
1 parent 867f92a commit 45c216f

4 files changed

Lines changed: 35 additions & 8 deletions

File tree

scapy/contrib/automotive/doip.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,15 @@ def post_build(self, pkt, pay):
257257
def extract_padding(self, s):
258258
# type: (bytes) -> Tuple[bytes, Optional[bytes]]
259259
if self.payload_type == 0x8001:
260-
return s[:self.payload_length - 4], None
260+
return s[:self.payload_length - 4], s[self.payload_length - 4:]
261261
else:
262-
return b"", None
262+
return b"", s
263263

264264
@classmethod
265265
def tcp_reassemble(cls, data, metadata, session):
266266
# type: (bytes, Dict[str, Any], Dict[str, Any]) -> Optional[Packet]
267267
length = struct.unpack("!I", data[4:8])[0] + 8
268-
if len(data) == length:
268+
if len(data) >= length:
269269
return DoIP(data)
270270
return None
271271

scapy/sessions.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,22 @@ def process(self,
367367
metadata.clear()
368368
# Check for padding
369369
padding = self._strip_padding(packet)
370-
if padding:
370+
while padding:
371371
# There is remaining data for the next payload.
372372
full_length = data.content_len - len(padding)
373373
metadata["relative_seq"] = relative_seq + full_length
374374
data.shiftleft(full_length)
375+
# There might be a sub-payload hidden in the padding
376+
sub_packet = tcp_reassemble(
377+
bytes(data),
378+
metadata,
379+
tcp_session
380+
)
381+
if sub_packet:
382+
packet /= sub_packet
383+
padding = self._strip_padding(sub_packet)
384+
else:
385+
break
375386
else:
376387
# No padding (data) left. Clear
377388
data.clear()
@@ -397,10 +408,15 @@ def recv(self, sock: 'SuperSocket') -> Iterator[Packet]:
397408
"""
398409
pkt = sock.recv(stop_dissection_after=self.stop_dissection_after)
399410
# Now handle TCP reassembly
400-
while pkt is not None:
401-
pkt = self.process(pkt)
411+
if self.app:
412+
while pkt is not None:
413+
pkt = self.process(pkt)
414+
if pkt:
415+
yield pkt
416+
# keep calling process as there might be more
417+
pkt = b"" # type: ignore
418+
else:
419+
pkt = self.process(pkt) # type: ignore
402420
if pkt:
403421
yield pkt
404-
# keep calling process as there might be more
405-
pkt = b"" # type: ignore
406422
return None

test/contrib/automotive/doip.uts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,17 @@ pkts = sniff(offline=tmp_file, session=TCPSession)
394394
assert pkts[0].haslayer(UDS_TP)
395395
assert pkts[0].service == 0x3E
396396

397+
= TCPSession Test multiple DoIP messages
398+
399+
filename = scapy_path("/test/pcaps/multiple_doip_layers.pcap.gz")
400+
401+
pkts = sniff(offline=filename, session=TCPSession)
402+
print(repr(pkts[0]))
403+
print(repr(pkts[1]))
404+
assert len(pkts) == 2
405+
assert pkts[0][DoIP].payload_length == 2
406+
assert pkts[0][DoIP:2].payload_length == 7
407+
assert pkts[1][DoIP].payload_length == 103
397408

398409
+ DoIP Communication tests
399410

206 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)