Base Class

pcapkit.foundation.reassembly.ip contains IP only, which reconstructs fragmented IP packets back to origin. The following algorithm implement is based on IP reassembly procedure introduced in RFC 791, using RCVBT (fragment receivedbit table). Though another algorithm is explained in RFC 815, replacing RCVBT, however, this implement still used the elder one.

class pcapkit.foundation.reassembly.ip.IP(*, strict=True, store=True, timeout=None)[source]

Bases: ReassemblyBase[Packet[_AT], Datagram[_AT], tuple[_AT, _AT, int, TransType], Buffer[_AT]], Generic[_AT]

Reassembly for IP payload.

Parameters:
  • strict (bool) – if return all datagrams (including those not implemented) when submit

  • store (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; None selects the protocol’s __timeout__ default

Important

This class is not intended to be instantiated directly, but rather used as a base class for the protocol-aware reassembly classes.

reassembly(info)[source]

Reassembly procedure.

Parameters:

info (Packet[TypeVar(_AT, IPv4Address, IPv6Address)]) – info dict of packets to be reassembled

submit(buf, *, bufid, checked=False, timeout=False)[source]

Submit reassembled payload.

Parameters:
  • buf (Buffer[TypeVar(_AT, IPv4Address, IPv6Address)]) – buffer dict of reassembled packets

  • bufid (tuple[TypeVar(_AT, IPv4Address, IPv6Address), TypeVar(_AT, IPv4Address, IPv6Address), int, TransType]) – buffer identifier

  • checked (bool) – buffer consistency checked flag

  • timeout (bool) – whether this buffer is being submitted because expire() abandoned it under the reassembly timeout, which is what separates Completion.TIMEOUT from Completion.PARTIAL

Return type:

list[Datagram[TypeVar(_AT, IPv4Address, IPv6Address)]]

Returns:

Reassembled packets.

Data Models

class pcapkit.foundation.reassembly.data.ip.Packet(*args: VT, **kwargs: VT)[source]

Bases: Info, Generic[_AT]

Data model for IPv4 and/or IPv6 packet representation..

bufid: tuple[_AT, _AT, int, TransType]

Buffer ID.

num: int

Original packet range number.

fo: int

Fragment offset.

ihl: int

Internet header length.

mf: bool

More fragments flag.

tl: int

Total length, header included.

header: bytes

Raw bytes type header.

payload: bytearray

Raw bytearray type payload.

timestamp: float

Capture timestamp of the fragment, in seconds since the Unix epoch. This is the capture’s clock, not the host’s: it is what drives the RFC 791 and RFC 8200 Section 4.5 reassembly timeout, since an offline parser replaying a file has no other notion of time passing.

pcapkit.foundation.reassembly.data.ip.BufferID: Tuple[_AT, _AT, int, pcapkit.const.reg.transtype.TransType]

Data module for buffer ID.

class pcapkit.foundation.reassembly.data.ip.Buffer(*args: VT, **kwargs: VT)[source]

Bases: Info, Generic[_AT]

Data model for IPv4 and/or IPv6 reassembly buffer entry.

TDL: int

Total data length.

RCVBT: bytearray

Fragment received bit table.

index: list[int]

List of reassembled packets.

header: bytes

Header buffer.

datagram: bytearray

Data buffer, holes set to b'\x00'.

timestamp: float

Capture timestamp of the first-arriving fragment of this datagram, in seconds since the Unix epoch. This is the origin of the reassembly timer: RFC 8200 Section 4.5 counts its 60 seconds “of the reception of the first-arriving fragment”, so a later fragment does not extend the deadline and this field is never revised once set.

conflict: list[tuple[int, int]]

Octet ranges, absolute into datagram and both inclusive, on which an arriving fragment disagreed with bytes already placed there by an earlier one. Accumulated across every fragment merged into this buffer, in the order the conflicts were found; carried onto Datagram.conflict verbatim when the buffer is submitted.

class pcapkit.foundation.reassembly.data.ip.DatagramID(*args: VT, **kwargs: VT)[source]

Bases: Info, Generic[_AT]

Data model for IPv4 and/or IPv6 original packet identifier.

src: _AT

Source address.

dst: _AT

Destination address.

id: int

IP protocol identifier.

proto: TransType

Payload protocol type.

class pcapkit.foundation.reassembly.data.ip.Datagram(*args: VT, **kwargs: VT)[source]

Bases: DeferredPacket, Info, Generic[_AT]

Data model for IPv4 and/or IPv6 reassembled datagram.

completed: Completion

How completely the datagram was reassembled, and why reassembly stopped. Only Completion.COMPLETE is truthy, so if datagram.completed: still reads as it did while this was a bool; equality against True or False no longer holds.

id: DatagramID[_AT]

Original packet identifier.

index: tuple[int, ...]

Packet numbers.

header: bytes

Initial IP header.

payload: bytes | tuple[bytes, ...]

Reassembled IP payload.

packet: ProtocolBase | None

Parsed IP payload. Analysed on first read, not at construction time; a Deferred may be passed in its place, and reading this attribute then runs it and keeps the result.

conflict: tuple[tuple[int, int], ...]

Octet ranges, absolute into the reassembled payload and both inclusive – the same convention as Packet.fo combined with its length – on which two fragments disagreed, i.e. an arriving fragment overlapped octets already buffered but did not repeat them. Empty when the datagram never saw a contested octet.

RFC 791 resolves the disagreement itself: “In the case that two or more fragments contain the same data either identically or through a partial overlap, this procedure will use the more recently arrived copy in the data buffer and datagram delivered.” So payload always holds whichever fragment arrived last over a contested range – the opposite resolution from TCP’s first-write-wins (RFC 9293 Section 3.10) – and this field is what lets a caller tell a clean datagram from a contested one, since a resolved conflict does not, on its own, leave a hole for completed to report.

Buffer.RCVBT only records receipt in 8-octet blocks, coarser than the octet granularity a conflict needs. Every fragment but the last is required to be block-aligned (and Packet.fo is always a multiple of 8, being wire-encoded in 8-octet units), so the only block that can be partially real is the one holding the final fragment’s own tail – and a range reported here never extends past Buffer.TDL for exactly that reason, even though a whole RCVBT block straddling it reads as “received”. See IP._detect_conflicts.

Type Variables

pcapkit.foundation.reassembly.data.ip._AT: ipaddress.IPv4Address | ipaddress.IPv6Address