3rd-Party Support

The adapters below are best effort (#1687): they need not match the Built-in Support adapters one to one, since protocol rules belong in the default engine only, so the same capture can reassemble or trace differently from one engine to the next. Engine Support tabulates the gaps.

Scapy Tools

pcapkit.toolkit.scapy contains the adapters for the Scapy engine. The reassembly and flow tracing adapters return the data their pcapkit.foundation counterpart consumes, or None if the frame cannot be used for it.

Warning

This module requires Scapy to be installed.

pcapkit.toolkit.scapy.ipv4_reassembly(packet, *, count=-1)[source]

Make data for IPv4 reassembly.

A packet can be reassembled if it contains an IPv4 layer (scapy.layers.inet.IP) whose header – as many octets as its IHL counts, and at least 20 – lies inside the frame and every IP datagram carrying it (see _within_datagrams()), and the DF (scapy.layers.inet.IP.flags.DF) flag is False. A zero-length frame has none, though Scapy hands it back as an IPv4 layer of defaults at the RAW and IPV4 link types (see _frame()).

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet[IPv4Address] | None

Returns:

Data for IPv4 reassembly (reasm.ipv4.packet) if the packet can be reassembled; otherwise None.

pcapkit.toolkit.scapy.ipv6_reassembly(packet, *, count=-1)[source]

Make data for IPv6 reassembly.

A packet can be reassembled if it contains an IPv6 layer (scapy.layers.inet6.IPv6), in no later IPv4 fragment, and an IPv6 Fragment header (RFC 2460 Section 4.5, i.e., scapy.layers.inet6.IPv6ExtHdrFragment) among the extension headers Scapy dissects in that layer’s own chain.

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet[IPv6Address] | None

Returns:

Data for IPv6 reassembly (reasm.ipv6.packet) if the packet can be reassembled; otherwise None.

Raises:

ModuleNotFound – If Scapy is not installed.

Note

A later IPv4 fragment’s data is a slice of the datagram’s payload, with no upper-layer header in it (RFC 791 Section 3.2), but Scapy dissects IPv6 there whatever the fragment offset. And the header and addresses handed on are those of the IPv6 layer the Fragment header follows, so one inside a packet that layer carries, such as the inner packet of 6in6, is not taken.

pcapkit.toolkit.scapy.tcp_reassembly(packet, *, count=-1)[source]

Make data for TCP reassembly.

A packet can be reassembled if it contains a TCP layer (scapy.layers.inet.TCP) whose fixed header lies inside its datagram, and whose Data Offset counts at least that header, in no later IPv4 fragment; or a TCP segment behind an AH or IPv6 extension header in IPv4, read off the octets. It is keyed by the IP layer that carries it – in an IP-in-IP tunnel, the innermost (see _tcp_carrier()).

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet | None

Returns:

Data for TCP reassembly (reasm.tcp.packet) if the packet can be reassembled; otherwise None.

pcapkit.toolkit.scapy.tcp_traceflow(packet, *, count=-1)[source]

Trace packet flow for TCP.

A packet can be traced if it contains a TCP layer (scapy.layers.inet.TCP), on the terms tcp_reassembly() sets.

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet | None

Returns:

Data for TCP flow tracing (trace.tcp.packet) if the packet can be traced; otherwise None.

Auxiliary Functions

pcapkit.toolkit.scapy.packet2chain(packet)[source]

Fetch Scapy packet protocol chain.

Parameters:

packet (scapy.packet.Packet) – Scapy packet.

Return type:

str

Returns:

Colon (:) separated list of protocol chain.

Raises:

ModuleNotFound – If Scapy is not installed.

pcapkit.toolkit.scapy.packet2dict(packet)[source]

Convert Scapy packet into dict.

Parameters:

packet (scapy.packet.Packet) – Scapy packet.

Return type:

dict[str, Any]

Returns:

A dict mapping of packet data. Its packet is the octets of the frame (see _frame()), none for a zero-length one. Each layer carries only the fields set on it (for a dissected packet, those read from the wire, with no defaults filled in). Field values that are themselves Scapy packets (e.g. IP options, DNS records), including those inside lists, are converted recursively. The packet itself is not modified.

Raises:

ModuleNotFound – If Scapy is not installed.

pcapkit.toolkit.scapy.packet2frame(packet)[source]

Report a Scapy packet to the flow tracer, with its PCAP frame record.

Parameters:

packet (scapy.packet.Packet) – Scapy packet.

Return type:

FrameRecord

Returns:

The packet2dict() mapping of the packet, which every trace format but PCAP writes unchanged, carrying the octets Scapy dissected the packet from, the PCAP record header the PCAP trace dumper writes, and the exact timestamp it reads the header’s resolution from.

Raises:

ModuleNotFound – If Scapy is not installed.

Note

The octets are packet.original, which Scapy’s readers keep as read, rather than bytes(packet), which rebuilds them; for a packet not read from a capture, such as one built by hand, they are the octets it builds into (see _frame()). orig_len is packet.wirelen, the record’s original length.

The fraction in frame_info.ts_usec is in the capture’s own resolution. That resolution is the one Scapy attaches with attach_resolution(), never one guessed from the value: a nanosecond count that is a whole number of microseconds has the same digits as the microsecond count. A packet that carries none, e.g. one built by hand, is taken to count microseconds.

pcapkit.toolkit.scapy.attach_resolution(packet, resolution)[source]

Stash the timestamp resolution of a packet’s capture on the packet.

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • resolution (int) – Timestamp resolution, in units per second: the PCAP global header’s, or the PCAP-NG interface’s the packet was captured on.

pcapkit.toolkit.scapy.attach_linktype(packet, linktype)[source]

Stash the data link type of a packet’s capture on the packet.

Parameters:
  • packet (scapy.packet.Packet) – Scapy packet.

  • linktype (int) – LINKTYPE_* number: the PCAP global header’s, or the PCAP-NG interface’s the packet was captured on.

pcapkit.toolkit.scapy._frame(packet)[source]

Return the octets of the frame a Scapy packet stands for.

Parameters:

packet (scapy.packet.Packet) – Scapy packet.

Return type:

bytes

Returns:

For a packet read from a capture, which every Scapy reader gives a wirelen, the record’s original length, packet.original: the octets of the record, which the readers keep as read. For any other, such as one built by hand, bytes(packet): the octets it builds into.

Note

Scapy dissects a packet only from a non-empty string, so a zero-length frame comes back as the first layer its link type names, every field left to its default. bytes() of it builds a header out of nothing – 20 octets at RAW and IPV4, 14 at ETHERNET – where the record has none (#1676).

pcapkit.toolkit.scapy._within_datagrams(layer)[source]

Return the octets from layer on that lie inside every IP datagram carrying it.

Parameters:

layer (scapy.packet.Packet) – A layer of a dissected Scapy packet.

Return type:

bytes

Returns:

The octets of layer and everything after it, up to the nearest end that the Total Length or Payload Length of an IP layer above it declares.

Note

When Scapy dissects an IP layer, it files whatever follows the datagram’s declared length as a Padding layer – an Ethernet frame’s padding up to its 60-octet minimum, or a trailer such as an FCS – and bytes() of any layer above that one ends with it. Those octets are no part of the datagram (#1584), so they are cut off here. An IPv4 Total Length of zero, as TCP segmentation offload leaves, declares nothing, so the datagram runs to the end of the frame (#1547); an IPv6 Payload Length of zero gives way to a Jumbo Payload option (#1434), whose length is read here rather than left to Scapy, which reads it in host byte order and adds the Hop-by-Hop Options header to it a second time.

Every IP layer above layer bounds it, not only the nearest: a datagram tunnelled in IP may declare more than the one carrying it holds, or nothing at all. An IPv4 layer built by hand has no ihl until it is built, so one given a Total Length alone has its IHL read off the octets it builds into (#1676).

pcapkit.toolkit.scapy._tcp_carrier(packet)[source]

Fetch the TCP segment of a Scapy packet, and the IP layer that carries it.

Parameters:

packet (scapy.packet.Packet) – Scapy packet.

Return type:

tuple[scapy.layers.inet.IP | scapy.layers.inet6.IPv6, scapy.layers.inet.TCP, bytes, bytes] | None

Returns:

The nearest IP or IPv6 layer above the packet’s TCP layer, that layer, and the segment’s header and payload (see _tcp_segment()); or None if there is no such IP layer, or an IPv4 layer above it is a later fragment, whose data is a slice of the datagram’s payload with no upper-layer header in it (RFC 791 Section 3.2, #1617).

An IPv4 layer whose protocol names an AH or IPv6 extension header (IPV4_EXTENSION_HEADERS) has those headers stepped over in its octets, up to EXTENSION_HEADER_LIMIT of them in a row (see _walk_extension_headers()), and what follows them is dissected from the octets: a tunnel, walked on as above, or a TCP segment (see _tcp_behind_extension_headers(), #1606). In the datagram an ICMP error quotes, they are left as Scapy dissects them.

Note

The nearest is the innermost IP layer of an IP-in-IP tunnel (RFC 2003, RFC 2473, RFC 4213), whose addresses are the TCP endpoints’ (#1581). packet['IP'] finds the outermost IPv4 layer instead, which is the tunnel’s, or with IPv4 carried in IPv6 the inner one. The extension header layers Scapy dissects between an IPv6 layer and its payload are not IP layers, so are stepped over.

Inside IPv4, Scapy dissects an AH alone of those headers, and behind one only IPv6 and, for a Next Header of 0, IPv4, so the octets are read instead. It also dissects IPv6 and AH inside a later fragment, binding neither to a zero fragment offset as it binds TCP.

pcapkit.toolkit.scapy._tcp_segment(tcp)[source]

Split the TCP segment tcp heads into its header and its payload.

Parameters:

tcp (scapy.layers.inet.TCP) – TCP layer of a dissected Scapy packet.

Return type:

tuple[bytes, bytes] | None

Returns:

The header, as many octets as the Data Offset counts, and the payload after it, up to the end of the datagrams that carry the segment, cf. _within_datagrams(); or None if the datagrams hold fewer than the 20 octets of the fixed header, or the Data Offset counts fewer (RFC 9293 Section 3.1), as there is then no header to split off.

Note

The Data Offset is read off the octets, which for a dissected layer carry the dataofs Scapy read, and for a layer built by hand the one it builds, as it leaves dataofs None until then (#1676).

pcapkit.toolkit.scapy._tcp_behind_extension_headers(segment)[source]

Split a TCP segment carried behind an AH or IPv6 extension header in IPv4.

Parameters:

segment (bytes) – Raw octets from the TCP header on, as far as the IPv4 layer carrying it goes (see _tcp_carrier()).

Return type:

tuple[bytes, bytes] | None

Returns:

The header, as many octets as the Data Offset counts, and the payload after it; or None if there are fewer than TCP_MIN_HEADER_LEN octets, the Data Offset counts fewer or more than were captured, or pcapkit’s own TCP parser rejects the options (see _default_accepts()).

Note

Scapy dissects no TCP layer there (see _tcp_carrier()), so the segment is read off the octets, and its options are left to pcapkit’s parser to check.

DPKT Tools

pcapkit.toolkit.dpkt contains the adapters for the DPKT engine. The reassembly and flow tracing adapters return the data their pcapkit.foundation counterpart consumes, or None if the frame cannot be used for it.

pcapkit.toolkit.dpkt.ipv4_reassembly(packet, timestamp, *, count=-1)[source]

Make data for IPv4 reassembly.

A packet can be reassembled if its network layer is IPv4 (dpkt.ip.IP) – one carried in IPv6 is not read – whose header was captured whole, and the DF (dpkt.ip.IP.df) flag is False.

Parameters:
  • packet (dpkt.dpkt.Packet) – DPKT packet.

  • timestamp (float) – Capture timestamp of the packet, which drives the reassembly timeout. DPKT’s reader yields it beside the record’s octets rather than on the packet, so it is passed in – as tcp_traceflow() does. A caller holding only a frame can read it back with packet2timestamp(), which is where DPKT leaves it. Handed on as a float, though DPKT reads a nanosecond PCAP’s as a Decimal (#1501), and as the one nearest the exact value it carries, if any, where DPKT’s own may be an ulp off (#1668; see _capture_time()).

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet[IPv4Address] | None

Returns:

Data for IPv4 reassembly (reasm.ipv4.packet) if the packet can be reassembled; otherwise None.

Note

The header and payload are sliced out of the record octets DPKT attached to the frame, so they are the captured ones. A packet built by hand carries no such buffer, and its octets are then dpkt’s re-serialisation of it, in which a zeroed checksum or length comes back recomputed – attach_buffer() is what makes them the wire’s again. They are that re-serialisation too where dpkt’s own lengths misplace the layer in the record, as behind some stacks of VLAN tags (#1646). The packet itself is left untouched either way.

The header is the IHL’s octets, and the payload ends at the Total Length, or at the end of the frame for a zero one. A Total Length shorter than the IHL leaves the header whole and the payload empty (#1616).

Changed in version 1.5.0: timestamp is a new second positional parameter, so a 1.4.x call with packet alone fails.

pcapkit.toolkit.dpkt.ipv6_reassembly(packet, timestamp, *, count=-1)[source]

Make data for IPv6 reassembly.

A packet can be reassembled if its network layer is IPv6 (dpkt.ip6.IP6) – one carried in IPv4 is not read – and has an IPv6 Fragment header (RFC 2460 Section 4.5, i.e., dpkt.ip6.IP6FragmentHeader).

Parameters:
Return type:

Packet[IPv6Address] | None

Returns:

Data for IPv6 reassembly (reasm.ipv6.packet) if the packet can be reassembled; otherwise None.

Note

The header and payload are sliced out of the record octets DPKT attached to the frame, so they are the captured ones. A packet built by hand carries no such buffer, and its octets are then dpkt’s re-serialisation of it, in which a zeroed checksum or length comes back recomputed – attach_buffer() is what makes them the wire’s again. They are that re-serialisation too where dpkt’s own lengths misplace the layer in the record, as behind some stacks of VLAN tags (#1646). The packet itself is left untouched either way.

Changed in version 1.5.0: timestamp is a new second positional parameter, so a 1.4.x call with packet alone fails.

pcapkit.toolkit.dpkt.tcp_reassembly(packet, timestamp, *, count=-1)[source]

Make data for TCP reassembly.

A packet can be reassembled if its network layer carries a TCP layer (dpkt.tcp.TCP), behind any IPv6 extension headers dpkt decodes. TCP in an IP-in-IP tunnel, or behind an AH or IPv6 extension header carried in IPv4, is not read.

Parameters:
Return type:

Packet | None

Returns:

Data for TCP reassembly (reasm.tcp.packet) if the packet can be reassembled; otherwise None.

Note

The header and payload are sliced out of the record octets DPKT attached to the frame, so they are the captured ones. A packet built by hand carries no such buffer, and its octets are then dpkt’s re-serialisation of it, in which a zeroed checksum or length comes back recomputed – attach_buffer() is what makes them the wire’s again. They are that re-serialisation too where dpkt’s own lengths misplace the layer in the record, as behind some stacks of VLAN tags (#1646). The packet itself is left untouched either way.

Changed in version 1.5.0: timestamp is a new second positional parameter, so a 1.4.x call with packet alone fails.

pcapkit.toolkit.dpkt.tcp_traceflow(packet, timestamp, *, data_link, count=-1)[source]

Trace packet flow for TCP.

A packet can be traced if its network layer carries a TCP layer (dpkt.tcp.TCP), behind any IPv6 extension headers dpkt decodes. TCP in an IP-in-IP tunnel, or behind an AH or IPv6 extension header carried in IPv4, is not read.

Parameters:
Return type:

Packet | None

Returns:

Data for TCP flow tracing (trace.tcp.packet) if the packet can be traced; otherwise None.

Note

The header and payload are sliced out of the record octets DPKT attached to the frame, so they are the captured ones. A packet built by hand carries no such buffer, and its octets are then dpkt’s re-serialisation of it, in which a zeroed checksum or length comes back recomputed – attach_buffer() is what makes them the wire’s again. They are that re-serialisation too where dpkt’s own lengths misplace the layer in the record, as behind some stacks of VLAN tags (#1646). The packet itself is left untouched either way.

Auxiliary Functions

pcapkit.toolkit.dpkt.attach_timestamp(packet, timestamp)[source]

Stash a frame’s capture timestamp on the frame.

Parameters:
  • packet (dpkt.dpkt.Packet) – DPKT packet.

  • timestamp (float) – Capture timestamp of the packet, as DPKT’s reader yielded it beside the record’s octets.

pcapkit.toolkit.dpkt.packet2timestamp(packet)[source]

Read back the capture timestamp of a DPKT packet.

Parameters:

packet (dpkt.dpkt.Packet) – DPKT packet, as stored by DPKT.

Return type:

float

Returns:

Capture timestamp of the packet, in seconds since the epoch. As DPKT attaches it, that is a Timestamp, or for a nanosecond PCAP file a DecimalTimestamp, which is a Decimal subclass rather than a float.

Raises:

UnsupportedCall – If the packet carries no timestamp, i.e. it did not come through DPKT. Raised rather than defaulted, because a plausible-looking zero would silently misdate whatever was going to use it.

pcapkit.toolkit.dpkt.attach_buffer(packet, buffer)[source]

Stash a frame’s record octets on the frame.

Parameters:
  • packet (dpkt.dpkt.Packet) – DPKT packet.

  • buffer (bytes) – Octets of the record the packet was parsed from, as DPKT’s reader yielded them.

pcapkit.toolkit.dpkt.packet2bytes(packet)[source]

Fetch the octets of a DPKT packet without modifying it.

Parameters:

packet (dpkt.dpkt.Packet) – DPKT packet.

Return type:

bytes

Returns:

The record octets attached by attach_buffer(), i.e. the frame as it was captured. A packet that carries none – one built by hand rather than read by DPKT – is serialised from a copy instead, so that DPKT’s checksum and length recomputation never reaches the packet itself.

pcapkit.toolkit.dpkt.packet2frame(packet, timestamp, *, data_link)[source]

Report a DPKT packet to the flow tracer, with its PCAP frame record.

Parameters:
Return type:

FrameRecord

Returns:

The packet2dict() mapping of the packet, which every trace format but PCAP writes unchanged, carrying the captured octets, from packet2bytes(), the PCAP record header the PCAP trace dumper writes, and the exact timestamp it reads the header’s resolution from.

Note

The fraction in frame_info.ts_usec is in the capture’s own resolution, and both it and orig_len come from the reader: DPKT reads PCAP and PCAP-NG alike into a RecordTimestamp. A timestamp that is not one – a packet built by hand, or read by a dpkt whose reader the engine could not extend – is read as DPKT yields it: a Decimal counts nanoseconds and a float microseconds, and the octets are taken to be whole.

pcapkit.toolkit.dpkt.ipv6_hdr_len(ipv6)[source]

Calculate length of headers before IPv6 Fragment header.

Parameters:

ipv6 (IP6) – DPKT IPv6 packet.

Return type:

int

Returns:

Length of headers before IPv6 Fragment header dpkt.ip6.IP6FragmentHeader (RFC 2460 Section 4.5).

As specified in RFC 2460 Section 4.1, such a header may be a Hop-by-Hop Options header dpkt.ip6.IP6HopOptsHeader (RFC 2460 Section 4.3), a Destination Options header dpkt.ip6.IP6DstOptsHeader (RFC 2460 Section 4.6) or a Routing header dpkt.ip6.IP6RoutingHeader (RFC 2460 Section 4.4). An Authentication header (RFC 4302) reaches here as well, though, so the headers are walked in the order they were parsed rather than looked up by a fixed list of types. Walking them is also what counts a repeated header type once per occurrence, and what leaves out one that sits after the Fragment header rather than before it.

pcapkit.toolkit.dpkt.packet2chain(packet)[source]

Fetch DPKT packet protocol chain.

Parameters:

packet (dpkt.dpkt.Packet) – DPKT packet.

Return type:

str

Returns:

Colon (:) separated list of protocol chain. It ends at the first payload that is no layer, as in packet2dict(), e.g. VRRP’s ''.

pcapkit.toolkit.dpkt.packet2dict(packet, timestamp, *, data_link)[source]

Convert DPKT packet into dict.

Parameters:
Return type:

dict[str, Any]

Returns:

A dict mapping of packet data. A layer’s payload is mapped as a layer of its own only where it is one, i.e. has the header fields a dpkt.dpkt.Packet has; any other is leaf data. DPKT leaves some undecoded payloads as a str rather than as bytes, e.g. '' for VRRP’s. A frame the engine keeps as raw data, as it keeps one DPKT cannot decode, has no header fields, so maps to an empty dict.

pcapkit.toolkit.dpkt._capture_time(timestamp)[source]

The capture timestamp reassembly and flow tracing are handed.

Parameters:

timestamp (float | Decimal) – Capture timestamp of the packet.

Return type:

float

Returns:

The float nearest the exact timestamp. DPKT reads a microsecond PCAP’s as tv_sec + tv_usec / 1e6, which rounds twice, so can be an ulp off at a small tv_sec: 1.0036909999999999 for 1.003691 (#1668). A RecordTimestamp is therefore read by its exact value. Any other timestamp – one passed by hand, or yielded by a dpkt whose reader the engine could not extend – carries none, and is handed on as it is.

Auxiliary Data

class pcapkit.toolkit.dpkt.RecordTimestamp[source]

Bases: object

A record’s capture timestamp, with what a PCAP frame record needs of it.

The readers DPKT uses yield (timestamp, octets) and nothing else, so a record’s exact timestamp, its resolution and its original length would be lost between the reader and packet2frame(). The engine’s readers yield a Timestamp or a DecimalTimestamp instead, which is the value DPKT yields – so a stored frame’s timestamp is exactly what it was before – and also carries what would otherwise be lost. Reassembly and flow tracing are handed a plain float of it (#1501): of its exact value, since DPKT’s own for a microsecond PCAP rounds twice (#1668; see _capture_time()).

A copy, a deep copy and a pickle at any protocol all rebuild a timestamp from what its __reduce__() returns, so each keeps the exact timestamp, the resolution and the original length. This class answers each of those protocols itself, ahead of the numeric base class: the pure-Python Decimal copies a subclass as type(self)(str(self)), with the constructor’s defaults for the rest (#1647).

exact: Decimal

Exact timestamp, in seconds since the UNIX epoch.

resolution: int

Timestamp resolution of the record, in units per second.

orig_len: int | None

Original length of the packet, or None if unknown.

class pcapkit.toolkit.dpkt.Timestamp(value=0.0, exact=None, resolution=RESOLUTION[False], orig_len=None)[source]

Bases: RecordTimestamp, float

A float capture timestamp; see RecordTimestamp.

Parameters:
  • value (Any) – The timestamp, as DPKT yields it.

  • exact (Decimal | None) – Exact timestamp; value itself if not given.

  • resolution (int) – Timestamp resolution of the record, in units per second.

  • orig_len (int | None) – Original length of the packet, or None if unknown.

class pcapkit.toolkit.dpkt.DecimalTimestamp(value='0', resolution=RESOLUTION[True], orig_len=None)[source]

Bases: RecordTimestamp, Decimal

A Decimal capture timestamp; see RecordTimestamp.

DPKT yields a Decimal for a nanosecond PCAP.

Parameters:
  • value (Any) – The timestamp, as DPKT yields it, which is exact.

  • resolution (int) – Timestamp resolution of the record, in units per second.

  • orig_len (int | None) – Original length of the packet, or None if unknown.

PyShark Tools

pcapkit.toolkit.pyshark contains the adapters for the PyShark engine. The reassembly and flow tracing adapters return the data their pcapkit.foundation counterpart consumes, or None if the frame cannot be used for it.

Note

Some pcapkit functions may not be available with the PyShark engine, for lack of the corresponding PyShark functionality.

pcapkit.toolkit.pyshark.tcp_traceflow(packet, *, count=-1)[source]

Trace packet flow for TCP.

A packet can be traced if it contains a TCP layer with a whole fixed header (see _has_fixed_header()), keyed by the IP layer that carries it – in an IP-in-IP tunnel, the innermost (see _carrier()) – if tshark reports both its addresses (see _has_addresses()).

Parameters:
  • packet (pyshark.packet.packet.Packet) – PyShark packet.

  • count (int) – Packet index. If not provided, defaults to -1. The engine passes its own frame count, not tshark’s frame.number, which also numbers records that are not packets (#1515).

Return type:

Packet | None

Returns:

Data for TCP flow tracing (trace.tcp.packet) if the packet can be traced; otherwise None.

Raises:
  • MissingKeyError – If the frame’s link type cannot be resolved.

  • ProtocolError – If the packet has no frame layer, a TCP flag field is not a boolean tshark spelling, or frame.time_epoch is not decimal text.

Changed in version 1.5.0: The link type is resolved from frame.encap_type through ENCAP_TYPE_TO_LINKTYPE, or, for a frame without that field, from the root layer’s name through FILTER_NAME_TO_LINKTYPE. One that cannot be resolved either way raises MissingKeyError; 1.4.x looked up the root layer’s name only, which gave the wrong DLT for ppp, null, lapd and raw (#843).

pcapkit.toolkit.pyshark.ENCAP_TYPE_TO_LINKTYPE: dict[int, LinkType]

frame.encap_type – Wireshark’s internal WTAP_ENCAP_* number, which pyshark exposes as packet.frame_info.encap_type – to the matching LinkType member. This is tcp_traceflow()’s primary link-type source; FILTER_NAME_TO_LINKTYPE below is only its fallback.

A WTAP_ENCAP_* number is not a DLT: it is 25 where the DLT is 113, 15 where it is 0, and 2 where it is 6, so it needs this table rather than a cast. What it does do is distinguish encapsulations that the PDML root layer’s filter name cannot, which is the whole reason this table exists: sll is both LINUX_SLL (113) and LINUX_SLL2 (276), null is both NULL (0) and LOOP (108), raw is RAW (101), IPV4 (228) and IPV6 (229), ppp is PPP (9) and PPP_WITH_DIR (204), lapd is LAPD (203) and LINUX_LAPD (177). frame.encap_type separates every one of those pairs.

Every entry was measured, not transcribed, with editcap/tshark 4.6.9:

editcap -F pcap -T <encap> examples/captures/in.pcap out.pcap
# value := the network field at offset 20 of out.pcap's pcap file header
# key   := the frame.encap_type that `tshark -r out.pcap -c 1 -T pdml` then reports

so each pair is one round trip through Wireshark’s own wiretap/pcap-common.c, in the read direction pyshark actually exercises. In the source, the trailing comment on each line names the editcap -T encapsulation that produced it.

What the sweep does not cover, since that bounds this table: of the 226 encapsulations editcap -T accepts, 157 could be written as pcap from the Ethernet source above and the other 69 refused the rewrite, so they are untested. Those 157 yielded 153 distinct frame.encap_type keys, with no key mapping to two DLTs and no DLT reading back as two keys. Each is below except frame.encap_type 32 (editcap -T hhdlc, DLT 121), left out because LinkType has no member for 121. An encapsulation absent from this table raises rather than resolving to a near-miss DLT.

pcapkit.toolkit.pyshark.FILTER_NAME_TO_LINKTYPE: dict[str, LinkType]

Wireshark display-filter name -> LinkType member, used by tcp_traceflow() only when the frame layer carries no frame.encap_type field for ENCAP_TYPE_TO_LINKTYPE above to key on – every capture measured for that table did carry one, so this is a defensive path rather than the usual one. PyShark takes the name verbatim from the PDML <proto name=...> attribute, which is Wireshark’s own dissector filter name – the third argument to proto_register_protocol() in the relevant epan/dissectors/packet-*.c – and that vocabulary is not LinkType’s: Ethernet’s filter name is eth, never ETHERNET.

Every entry comes from the same sweep as ENCAP_TYPE_TO_LINKTYPE above: the root <proto name=...> of tshark -r out.pcap -c 1 -T pdml against the DLT in out.pcap’s own file header. In the source, the trailing comment on each line names the editcap -T encapsulation that produced it, so an entry can be re-checked, or corrected when upstream moves, by rerunning that one rewrite. Names are listed even where they already spell their LinkType member (docsis, fddi, pflog, …): there is deliberately no fallback onto a like-named member, because upper-casing the name is exactly what would answer 101 for a rawip6 capture and 0 for a DLT_LOOP one – valid DLTs, wrong ones, and silent (#843).

These classes of name are absent, each measured rather than assumed:

  • Ambiguous – one name, several DLTs, and the PDML node does not say which arrived. These cannot be mapped at any size: user_dlt (16 DLTs), bluetooth (6), usb (5), usbll (4), raw (3), and arcnet, gfp, i2c, lapd, mtp2, netanalyzer, null, ppp, sll, wlan, wpan (2 each).

  • Pseudo-protocols – fake-field-wrapper, which pyshark reports as data and which was the root for 29 of the swept encapsulations (no dissector), and _ws.malformed, the root for the one capture tshark could not parse. Neither names a link type.

  • Unswept – the 69 encapsulations editcap -T would not write as pcap from an Ethernet source. No evidence either way was gathered for them.

Caveats on what “unambiguous” means here. It means unambiguous within the 157 swept encapsulations: one of the 69 unswept could still root at the same name (ether-nettl and tr-nettl are the obvious candidates, for eth and tr). And some entries – llc, sctp, pppoes, fpp and irlap – name a payload dissector that happened to be the outermost PDML node for exactly one encapsulation, rather than a link-layer dissector registered against a WTAP_ENCAP_*. These caveats are why this table is the fallback and frame.encap_type is the primary key; eth and tr additionally check out against Wireshark’s own registrations (packet-eth.c on WTAP_ENCAP_ETHERNET, packet-tr.c on WTAP_ENCAP_TOKEN_RING).

Auxiliary Functions

pcapkit.toolkit.pyshark.packet2dict(packet)[source]

Convert PyShark packet into dict.

Parameters:

packet (pyshark.packet.packet.Packet) – PyShark packet.

Return type:

dict[str, Any]

Returns:

A dict mapping of packet data, with the frame layer’s fields at the top level and each later layer nested in the one before it. A packet comment, which PyShark reports as frame_info in place of the frame layer, is kept under its own PKT_COMMENT key. A record with no frame layer keeps PyShark’s frame_info at the top level.

pcapkit.toolkit.pyshark._carrier(packet)[source]

Fetch the TCP layer of a PyShark packet and the IP layer that carries it.

Parameters:

packet (pyshark.packet.packet.Packet) – PyShark packet.

Return type:

tuple[Any, Any]

Returns:

The ip or ipv6 layer nearest above the packet’s first tcp layer, and that TCP layer; None for either the packet lacks.

Note

The nearest is the innermost IP layer of an IP-in-IP tunnel (RFC 2003, RFC 2473, RFC 4213), whose addresses are the TCP endpoints’. packet.ip is the first ip layer instead, which is the tunnel’s (#1581).

pcapkit.toolkit.pyshark._has_fixed_header(tcp)[source]

Whether tshark dissected the whole fixed TCP header of a PyShark TCP layer.

Parameters:

tcp (Any) – PyShark TCP layer.

Return type:

bool

Returns:

Whether the layer carries tcp.urgent_pointer.

Note

A segment is traced only if its 20-octet fixed header (RFC 9293 Section 3.1) was captured and its Data Offset is at least 5. tshark omits fields in both other cases, measured with tshark 4.6.9 (#1611):

  • a Data Offset below 5 stops the dissection at tcp.hdr_len, so tcp.ack and the tcp.flags fields are absent;

  • a header cut at octet k keeps the fields that fit: no TCP layer below 4, the ports only below 14, and no tcp.urgent_pointer below 20.

tcp.urgent_pointer covers octets 18 and 19, the last of the fixed header. dissect_tcp in epan/dissectors/packet-tcp.c adds it unconditionally once the Data Offset check passes, in 2.6.0, 3.0.0 and 4.6.0, after every field tcp_traceflow() reads.

pcapkit.toolkit.pyshark._has_addresses(ip)[source]

Whether tshark reported both addresses of a PyShark IP layer.

Parameters:

ip (Any) – PyShark ip or ipv6 layer.

Return type:

bool

Returns:

Whether the layer carries its src and dst fields.

Note

tshark reports no ip.dst behind an LSRR or SSRR option whose route data is no whole number of addresses (“Suboption would go past end of option”), measured with tshark 4.6.9. Reading it raised a bare AttributeError out of tcp_traceflow(), which ended the extraction (#1671).

PyPCAP Tools

pcapkit.toolkit.pypcap contains the adapters for the PyPCAP engine.

Note

PyPCAP performs no protocol dissection, so the reassembly and flow tracing adapters below cannot be implemented. They are defined anyway, so that calling one fails with an explanatory UnsupportedCall rather than an ImportError.

pcapkit.toolkit.pypcap.ipv4_reassembly(packet, *, count=-1)[source]

Make data for IPv4 reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as PyPCAP provides no IPv4 layer to read.

Return type:

Packet[IPv4Address] | None

pcapkit.toolkit.pypcap.ipv6_reassembly(packet, *, count=-1)[source]

Make data for IPv6 reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as PyPCAP provides no IPv6 layer to read.

Return type:

Packet[IPv6Address] | None

pcapkit.toolkit.pypcap.tcp_reassembly(packet, *, count=-1)[source]

Make data for TCP reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as PyPCAP provides no TCP layer to read.

Return type:

Packet | None

pcapkit.toolkit.pypcap.tcp_traceflow(packet, timestamp, *, data_link, count=-1)[source]

Trace packet flow for TCP.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • timestamp (float) – Timestamp of the packet.

  • data_link (LinkType) – Data link layer protocol (from the capture handle).

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as PyPCAP provides no TCP layer to read.

Return type:

Packet | None

Auxiliary Functions

pcapkit.toolkit.pypcap.packet2chain(packet, *, data_link)[source]

Fetch PyPCAP packet protocol chain.

Parameters:
  • packet (bytes) – Raw packet bytes, as returned by pcap.pcap iteration.

  • data_link (LinkType) – Data link type, from the capture handle.

Return type:

str

Returns:

Colon (:) separated list of protocol chain.

Note

As PyPCAP does not dissect the packet, the chain is only ever the link layer type followed by Raw, e.g. ETHERNET:Raw.

pcapkit.toolkit.pypcap.packet2dict(packet, timestamp, *, data_link)[source]

Convert PyPCAP packet into dict.

Parameters:
  • packet (bytes) – Raw packet bytes, as returned by pcap.pcap iteration.

  • timestamp (float) – Timestamp of packet, as returned by pcap.pcap iteration.

  • data_link (LinkType) – Data link type, from the capture handle.

Return type:

dict[str, Any]

Returns:

A dict mapping of packet data.

Note

The mapping carries the captured bytes verbatim rather than a decoded protocol tree, since PyPCAP does not decode anything.

pcap-ct Tools

pcapkit.toolkit.pcap_ct contains the adapters for the pcap-ct engine.

Note

pcap-ct is an independent reimplementation of the PyPCAP interface, so this module is deliberately a sibling of pcapkit.toolkit.pypcap rather than an alias of it: each engine names its own adapter, so a change made for one cannot quietly alter the other.

Like PyPCAP it performs no protocol dissection, so the reassembly and flow tracing adapters below cannot be implemented. They are defined anyway, so that calling one fails with an explanatory UnsupportedCall rather than an ImportError.

pcapkit.toolkit.pcap_ct.ipv4_reassembly(packet, *, count=-1)[source]

Make data for IPv4 reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as pcap-ct provides no IPv4 layer to read.

Return type:

Packet[IPv4Address] | None

pcapkit.toolkit.pcap_ct.ipv6_reassembly(packet, *, count=-1)[source]

Make data for IPv6 reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as pcap-ct provides no IPv6 layer to read.

Return type:

Packet[IPv6Address] | None

pcapkit.toolkit.pcap_ct.tcp_reassembly(packet, *, count=-1)[source]

Make data for TCP reassembly.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as pcap-ct provides no TCP layer to read.

Return type:

Packet | None

pcapkit.toolkit.pcap_ct.tcp_traceflow(packet, timestamp, *, data_link, count=-1)[source]

Trace packet flow for TCP.

Parameters:
  • packet (bytes) – Raw packet bytes.

  • timestamp (float) – Timestamp of the packet.

  • data_link (LinkType) – Data link layer protocol (from the capture handle).

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as pcap-ct provides no TCP layer to read.

Return type:

Packet | None

Auxiliary Functions

pcapkit.toolkit.pcap_ct.packet2chain(packet, *, data_link)[source]

Fetch pcap-ct packet protocol chain.

Parameters:
  • packet (bytes) – Raw packet bytes, as returned by pcap.pcap iteration.

  • data_link (LinkType) – Data link type, from the capture handle.

Return type:

str

Returns:

Colon (:) separated list of protocol chain.

Note

As pcap-ct does not dissect the packet, the chain is only ever the link layer type followed by Raw, e.g. ETHERNET:Raw.

pcapkit.toolkit.pcap_ct.packet2dict(packet, timestamp, *, data_link)[source]

Convert pcap-ct packet into dict.

Parameters:
  • packet (bytes) – Raw packet bytes, as returned by pcap.pcap iteration.

  • timestamp (float) – Timestamp of packet, as returned by pcap.pcap iteration.

  • data_link (LinkType) – Data link type, from the capture handle.

Return type:

dict[str, Any]

Returns:

A dict mapping of packet data.

Note

The mapping carries the captured bytes verbatim rather than a decoded protocol tree, since pcap-ct does not decode anything.

PyPCAPFile Tools

pcapkit.toolkit.pypcapfile contains the adapters for the PyPCAPFile engine. The reassembly and flow tracing adapters return the data their pcapkit.foundation counterpart consumes, or None if the frame cannot be used for it.

Note

PyPCAPFile has no IPv6 decoder, so ipv6_reassembly() raises UnsupportedCall rather than returning None – which would be indistinguishable from “this frame carries no IPv6 fragment”.

TCP over IPv6 is therefore left out of tcp_reassembly() and tcp_traceflow(), with an AttributeWarning once per capture when the IPv6 packet is straight over Ethernet. PyPCAPFile decodes no link layer but Ethernet, and no VLAN tag or tunnel, so a frame of any other link type is left undecoded, with the engine’s own warning, and TCP behind a VLAN tag, in a tunnel or behind an extension header (an IPv6 one, or AH in IPv4) is left out without one.

pcapkit.toolkit.pypcapfile.ipv4_reassembly(packet, *, count=-1)[source]

Make data for IPv4 reassembly.

A packet can be reassembled if it contains an IPv4 layer (pcapfile.protocols.network.ip.IP) whose header was captured whole (see _network()), and the DF flag is False.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet[IPv4Address] | None

Returns:

Data for IPv4 reassembly (reasm.ipv4.packet) if the packet can be reassembled; otherwise None.

Raises:

ProtocolError – If ipv4.src or ipv4.dst cannot be parsed as an IPv4 address.

pcapkit.toolkit.pypcapfile.ipv6_reassembly(packet, *, count=-1)[source]

Make data for IPv6 reassembly.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Raises:

UnsupportedCall – Always, as PyPCAPFile has no IPv6 decoder.

Return type:

Packet[IPv6Address] | None

pcapkit.toolkit.pypcapfile.tcp_reassembly(packet, *, count=-1)[source]

Make data for TCP reassembly.

A packet can be reassembled if it contains an IPv4 layer carrying a TCP segment (see _transport()); TCP over IPv6 cannot be, as PyPCAPFile has no IPv6 decoder.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet | None

Returns:

Data for TCP reassembly (reasm.tcp.packet) if the packet can be reassembled; otherwise None.

Raises:

ProtocolError – If ipv4.src or ipv4.dst cannot be parsed as an IPv4 address.

Warns:

AttributeWarning – If packet is an IPv6 packet straight over Ethernet whose Next Header is TCP (see _ipv6_carries_tcp()), which is left out, and is the first such frame of its capture to reach either tcp_reassembly() or tcp_traceflow(). The rest of that capture’s such frames are left out without another warning.

pcapkit.toolkit.pypcapfile.tcp_traceflow(packet, *, data_link, count=-1)[source]

Trace packet flow for TCP.

A packet can be traced if it contains an IPv4 layer carrying a TCP segment (see _transport()); TCP over IPv6 cannot be, as PyPCAPFile has no IPv6 decoder.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • data_link (LinkType) – Data link layer protocol (from the savefile header).

  • count (int) – Packet index. If not provided, defaults to -1.

Return type:

Packet | None

Returns:

Data for TCP flow tracing (trace.tcp.packet) if the packet can be traced; otherwise None.

Raises:

ProtocolError – If ipv4.src or ipv4.dst cannot be parsed as an IPv4 address.

Warns:

AttributeWarning – If packet is an IPv6 packet straight over Ethernet whose Next Header is TCP (see _ipv6_carries_tcp()), which is left out, and is the first such frame of its capture to reach either tcp_reassembly() or tcp_traceflow(). The rest of that capture’s such frames are left out without another warning.

Auxiliary Functions

pcapkit.toolkit.pypcapfile.packet2timestamp(packet)[source]

Calculate the timestamp of a PyPCAPFile packet.

Parameters:

packet (pcap_packet) – PyPCAPFile packet.

Return type:

float

Returns:

Timestamp of the packet, in seconds since the epoch, as the float nearest the exact value.

Note

PyPCAPFile keeps the two halves of the per-packet timestamp apart, and the sub-second half is nanoseconds rather than microseconds when the savefile carries the nanosecond magic number – which is recorded on the savefile header as ns_resolution.

The two are joined in one integer division, which rounds once. timestamp + timestamp_us / divisor rounds twice, so can be an ulp off at a small timestamp: 1.0036909999999999 for 1.003691 (#1668).

pcapkit.toolkit.pypcapfile.ipv4_header(ipv4)[source]

Rebuild the raw header bytes of a PyPCAPFile IPv4 packet.

Parameters:

ipv4 (IP) – PyPCAPFile IPv4 packet.

Return type:

bytes

Returns:

Raw IPv4 header bytes, options included.

Note

PyPCAPFile discards the header bytes once decoded, but it retains every IPv4 header field plus the options blob, so this reconstruction is byte-exact rather than approximate.

Raises:

ProtocolError – If ipv4.src or ipv4.dst cannot be parsed as an IPv4 address.

pcapkit.toolkit.pypcapfile.packet2chain(packet, *, data_link)[source]

Fetch PyPCAPFile packet protocol chain.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • data_link (LinkType) – Data link type, from the savefile header.

Return type:

str

Returns:

Colon (:) separated list of protocol chain.

Note

The chain reports what PyPCAPFile actually decoded and no more, so it ends in Raw – the transport segment is left undecoded on purpose, see the module notes.

pcapkit.toolkit.pypcapfile.packet2dict(packet, *, data_link)[source]

Convert PyPCAPFile packet into dict.

Parameters:
  • packet (pcap_packet) – PyPCAPFile packet.

  • data_link (LinkType) – Data link type, from the savefile header.

Return type:

dict[str, Any]

Returns:

A dict mapping of packet data.

Raises:

ProtocolError – If a decoded IPv4 layer’s src/dst cannot be parsed as an IPv4 address, or a decoded Ethernet layer’s src/dst cannot be parsed as a MAC address.

Note

A frame PyPCAPFile left undecoded is hex-encoded, as the engine reads it (see PyPCAPFile._read_records), so it is un-hexed (see _maybe_unhex()) and reported as its octets, not as twice as many characters of hex text (#1667).

Internal Definitions

pcapkit.toolkit.pypcapfile.IPV4_FLAG_DF = 2

IPv4 DF (don’t fragment) bit, within the three-bit flags field.

pcapkit.toolkit.pypcapfile.IPV4_FLAG_MF = 1

IPv4 MF (more fragments) bit, within the three-bit flags field.

pcapkit.toolkit.pypcapfile.IPV4_EXTENSION_HEADERS

IP protocol numbers of the headers _walk_extension_headers() steps over inside IPv4: AH and the IPv6 Hop-by-Hop Options, Routing, Fragment, Destination Options, Mobility and HIP headers.

pcapkit.toolkit.pypcapfile._walk_extension_headers(protocol, data)[source]

Step over the headers in IPV4_EXTENSION_HEADERS at the head of an IPv4 payload.

Parameters:
  • protocol (int) – The IPv4 packet’s protocol number.

  • data (bytes) – Raw octets of its payload.

Return type:

tuple[int, bytes, int] | None

Returns:

The protocol number after the last of them, the octets from its header on, and how many headers were stepped over – protocol, data and 0 if protocol is none of them – or None if one of them is cut short or rejected, or more than EXTENSION_HEADER_LIMIT follow in a row. An AH is (Payload Length + 2) * 4 octets long (RFC 4302 Section 2.2), and no shorter than AH_HEADER_LEN; any other is read by _default_extension_header().

pcapkit.toolkit.pypcapfile._default_extension_header(protocol, data)[source]

Read an extension header inside IPv4 with pcapkit’s own parser.

Parameters:
  • protocol (int) – The header’s protocol number, one of IPV4_EXTENSION_HEADERS other than AH.

  • data (bytes) – Raw octets from the header on, as far as the IPv4 packet goes.

Return type:

tuple[int, int] | None

Returns:

The header’s Next Header field and its length, or None if its parser raises anything at all, or fewer octets were captured than the header needs.

pcapkit.toolkit.pypcapfile._default_accepts(name, header)[source]

Test if pcapkit’s own IPv4 or TCP parser accepts a header.

Parameters:
  • name (str) – 'IPv4' or 'TCP', the parser to ask.

  • header (bytes) – The whole header, options included, and nothing after it.

Return type:

bool

Returns:

False if the parser raises anything at all; else True.

Note

The parser stops at its own layer, so nothing past the header is dissected. The answer depends on the octets alone, so the most recent answers are remembered, as many as the cache’s maxsize.

pcapkit.toolkit.pypcapfile._network(packet)[source]

Fetch the decoded IPv4 layer of a PyPCAPFile packet, if any.

Parameters:

packet (pcap_packet) – PyPCAPFile packet.

Return type:

IP | None

Returns:

The decoded pcapfile.protocols.network.ip.IP layer, or None when the frame was not decoded that far – which is the case for every non-IPv4 frame, PyPCAPFile having no other network layer decoder – or its header length (IHL) runs past the captured octets (#1591).

Note

PyPCAPFile slices the header at its IHL however few octets there are, so a header cut short has fewer options than its IHL declares.

pcapkit.toolkit.pypcapfile._transport(ipv4)[source]

Fetch the verbatim TCP segment carried by a PyPCAPFile IPv4 packet.

Parameters:

ipv4 (IP) – PyPCAPFile IPv4 packet.

Return type:

bytes | None

Returns:

The raw TCP segment, up to the packet’s Total Length, or None if the packet does not carry a TCP header. A later fragment carries none: from its offset on, its data is a slice of the datagram’s payload (RFC 791 Section 3.2, #1576). Nor does a segment too short for a header, or whose Data Offset is below 5 words (#1590).

Note

PyPCAPFile keeps every octet after the header as the payload, so a frame padded to the Ethernet minimum carries its padding there. The segment is cut at the Total Length to leave it out (#1577). A Total Length of 0, which TCP segmentation offload leaves in a capture taken on the sending host, keeps the rest of the frame (#1547).

pcapkit.toolkit.pypcapfile._ipv6_carries_tcp(packet)[source]

Test if a PyPCAPFile frame is an IPv6 packet carrying TCP.

Parameters:

packet (pcap_packet) – PyPCAPFile packet.

Return type:

bool

Returns:

True if the frame is IPv6 straight over Ethernet and its Next Header is TCP; otherwise False.

Note

PyPCAPFile leaves an IPv6 frame’s payload undecoded and hex-encoded (see _maybe_unhex()), so this reads the Next Header field of the raw IPv6 header and decodes nothing.

pcapkit.toolkit.pypcapfile._is_raw(layer)[source]

Test if a decoded layer is in fact undecoded raw bytes.

Parameters:

layer (Any) – Decoded layer, or raw bytes.

Return type:

bool

pcapkit.toolkit.pypcapfile._maybe_unhex(value)[source]

Undo binascii.hexlify(), if value looks hex-encoded.

Parameters:

value (bytes) – Raw bytes, possibly hex-encoded.

Return type:

bytes

Returns:

value hex-decoded, if it is a valid even-length hex string; otherwise value itself, unchanged.

Note

The released PyPCAPFile (0.12.0) stores an IPv4 packet’s options (IP.opt) and payload (IP.payload) as binascii.hexlify()’d ASCII text once decoding stops short of the transport layer – which is exactly how PyPCAPFile calls it – rather than as the raw bytes this module otherwise assumes. Both forms are bytes, hex-encoded or not, which is why _is_raw() cannot tell them apart, and falling back to the original value when it does not decode as hex is what keeps this a no-op for the raw, non-hex stand-ins used in unit tests.

This is not a guarantee against every false positive, only a cheap and, in practice, reliable heuristic: a genuinely raw payload whose every byte happens to fall in the ASCII hex-digit range (e.g. a 24-byte TCP header built entirely from digits and A-F) is indistinguishable from real hex text and gets silently halved. There is no way to tell the two forms apart from the bytes alone; this is acceptable here because that byte range is a small fraction of the possible values a real header or payload can take.