IPv4 Datagram Reassembly¶
pcapkit.foundation.reassembly.ipv4 contains
IPv4
only, which reconstructs fragmented IPv4 packets back to
origin. Please refer to Base Class for more information.
- class pcapkit.foundation.reassembly.ipv4.IPv4(*, strict=True, store=True, timeout=None)[source]¶
Bases:
IPReassembly for IPv4 payload.
- Parameters:
strict (
bool) – ifTrue, report a datagram that is not completely reassembled as the tuple of its received runs; otherwise as one contiguous payload, its holes zero-filled – or, while the total length is unknown, only the prefix up to the first holestore (
bool) – if store reassembled datagram in memory, i.e.,self._dtgram(if not, datagram will be discarded after callback)timeout (
float|None) – reassembly timeout in seconds, on the capture’s own clock;Noneselects__timeout__
Example
>>> from pcapkit.foundation.reassembly import IPv4 # Initialise instance: >>> ipv4_reassembly = IPv4() # Call reassembly: >>> ipv4_reassembly(packet_dict) # Fetch result: >>> result = ipv4_reassembly.datagram
- __protocol_type__: Type[ProtocolBase] = <class 'pcapkit.protocols.internet.ipv4.IPv4'>¶
Protocol of current reassembly object.
Terminology¶
- reasm.ipv4.packet¶
Data structure for IPv4 datagram reassembly (
IPv4.reassembly) is as following, withipv4the protocol instance (frame['IPv4']) andipv4_infoitsinfo– the header fields come off the latter, the raw octets off the former:packet_dict = dict( bufid = ( ipv4_info.src, # source IP address ipv4_info.dst, # destination IP address ipv4_info.id, # identification ipv4_info.protocol, # payload protocol type ), num = frame.info.number, # original packet range number fo = ipv4_info.offset, # fragment offset, in octets ihl = ipv4_info.hdr_len, # internet header length mf = ipv4_info.flags.mf, # more fragment flag tl = ipv4_info.len, # total length, header includes header = ipv4.packet.header, # raw bytes type header payload = bytearray( ipv4.packet.payload), # raw bytearray type payload timestamp = float( frame.info.time_epoch), # capture timestamp )
- reasm.ipv4.datagram¶
Data structure for reassembled IPv4 datagram (element from
IPv4.datagramtuple) is as following:(tuple) datagram |--> (Info) data | |--> 'completed' : (Completion) COMPLETE --> reassembled in whole | |--> 'id' : (Info) original packet identifier | | |--> 'src' --> (IPv4Address) ipv4.src | | |--> 'dst' --> (IPv4Address) ipv4.dst | | |--> 'id' --> (int) ipv4.id | | |--> 'proto' --> (TransType) ipv4.protocol | |--> 'index' : (tuple) packet numbers | | |--> (int) original packet range number | |--> 'header' : (bytes) IPv4 header | |--> 'payload' : (bytes) reassembled IPv4 payload | |--> 'packet' : (Protocol) parsed reassembled payload | |--> 'conflict' : (tuple) octet ranges on which two fragments disagreed | | |--> (tuple) (first, last), absolute and inclusive | | |--> ... |--> (Info) data | |--> 'completed' : (Completion) PARTIAL or TIMEOUT --> incomplete | |--> 'id' : (Info) original packet identifier | | |--> 'src' --> (IPv4Address) ipv4.src | | |--> 'dst' --> (IPv4Address) ipv4.dst | | |--> 'id' --> (int) ipv4.id | | |--> 'proto' --> (TransType) ipv4.protocol | |--> 'index' : (tuple) packet numbers | | |--> (int) original packet range number | |--> 'header' : (bytes) IPv4 header | |--> 'payload' : (tuple) partially reassembled IPv4 payload | | |--> (bytes) IPv4 payload fragment | | |--> ... | |--> 'packet' : (None) | |--> 'conflict' : (tuple) octet ranges on which two fragments disagreed | | |--> (tuple) (first, last), absolute and inclusive | | |--> ... |--> (Info) data ...
Note
packetis analysed on the first read, not when the datagram is submitted. A datagram is submitted for every frame – an unfragmented one included, since nothing upstream filters it out – and the analysis is a second full parse of the payload, so running it eagerly would charge every caller for a result most never read. Reading the attribute, or any mapping view of it (datagram['packet'],to_dict(),items(),repr()), runs it and keeps the result; seeDeferred.Note
completedandconflictare independent signals: a datagram can beCOMPLETEand still carry a non-emptyconflict. RFC 791 resolves an overlapping fragment’s disagreement itself – “this procedure will use the more recently arrived copy in the data buffer” – the opposite resolution from TCP’s first-write-wins (RFC 9293 Section 3.10) – so a contested range never leaves a hole on its own, andconflictis what lets a caller tell a clean datagram from a contested one.- reasm.ipv4.buffer¶
Data structure for internal buffering when performing reassembly algorithms (
IPv4._buffer) is as following:(dict) buffer --> memory buffer for reassembly |--> (tuple) BUFID : (dict) | |--> ipv4.src | | |--> ipv4.dst | | |--> ipv4.id | | |--> ipv4.protocol | | |--> 'TDL' : (int) total data length | |--> 'RCVBT' : (bytearray) fragment received bit table | | |--> (bytes) b'\\x00' -> not received | | |--> (bytes) b'\\x01' -> received | | |--> (bytes) ... | |--> 'index' : (list) list of reassembled packets | | |--> (int) packet range number | |--> 'header' : (bytes) header buffer | |--> 'datagram' : (bytearray) data buffer, holes set to b'\\x00' | |--> 'timestamp' : (float) capture timestamp of the | | first-arriving fragment | |--> 'conflict' : (list) octet ranges on which an arriving | | fragment disagreed with bytes already in | | 'datagram' | | |--> (tuple) (first, last), absolute and | | inclusive | | |--> ... |--> (tuple) BUFID ...
Note
conflictis only ever appended to, and is checked againstdatagramandRCVBTbefore an arriving fragment’s own write and bookkeeping touch them – seeIP._detect_conflicts.RCVBTrecords receipt in 8-octet blocks, which is coarser than the octet a conflict needs;TDLis what recovers the exact extent for the one block marked received that can be partially real – the final fragment’s own tail – so a conflict here is never wider than the octets that genuinely disagreed, even inside that block. Any other partial last block – a non-final, truncated or clipped fragment’s – is left unmarked (#1567), and its octets are not compared: a later fragment that overwrites them records no conflict (#1585).Note
A buffer is abandoned once the reassembly timeout elapses on the capture’s clock – 60 seconds by default, per RFC 1122 Section 3.3.2 for IPv4 and RFC 8200 Section 4.5 for IPv6, counted from the first-arriving fragment. Its datagram is reported with
completedset toCompletion.TIMEOUTrather thanPARTIAL, which is what tells “these fragments are gone” apart from “these fragments had not arrived yet”. Seeexpire().