3rd-Party Engines¶
Scapy Support¶
This module contains the implementation for Scapy engine
support, as is used by pcapkit.foundation.extraction.Extractor.
Note
Constructing this engine imports scapy.all, which is what populates
Scapy’s layer registries – conf.l2types and the bind_layers
payload table, both of which exist only as import side effects of the layer
modules. Importing a narrower submodule leaves them empty, and
PcapReader then returns every frame as one opaque
Raw layer without raising, so the engine dissected
nothing at all and said so only on sys.stderr. See
Scapy.__init__() for why naming the layer modules individually is not a
cheaper route to the same place.
One side effect is worth knowing about in advance: scapy.all loads
scapy.layers.dcerpc, which reaches Scapy’s TLS layer and there
triggers a CryptographyDeprecationWarning from cryptography about
finite-field Diffie-Hellman. It concerns a key-exchange code path
pcapkit never executes, but it subclasses UserWarning rather
than DeprecationWarning, so Python’s default filters show it.
pcapkit deliberately does not filter it away – it is Scapy’s to
emit and the consumer’s to silence, on the same footing as every other
category (see pcapkit.utilities.warnings):
import warnings
from cryptography.utils import CryptographyDeprecationWarning
warnings.filterwarnings('ignore', category=CryptographyDeprecationWarning)
- class pcapkit.foundation.engines.scapy.Scapy(extractor)[source]¶
Bases:
EngineBase[ScapyPacket]Scapy engine support.
- __engine_name__ = 'Scapy'¶
Engine name.
- __engine_module__ = 'scapy'¶
Engine module name.
- run()[source]¶
Call
scapy.sendrecv.sniff()to extract PCAP files.This method assigns
self._extmpas an iterator fromscapy.sendrecv.sniff(), reached throughself._expkg– which__init__()binds toscapy.all, since that is the import that populates the layer registriessniffneeds to dissect anything.- Warns:
AttributeWarning – If
self.extractor._exlyrand/orself.extractor._exptlis provided as the Scapy engine currently does not support such operations; or ifself.extractor._exctxis provided, as the Scapy engine does not parse withpcapkit’s own protocol implementations.
- read_frame()[source]¶
Read frames with Scapy engine.
- Return type:
- Returns:
Parsed frame instance.
See also
Please refer to
PCAP.read_framefor more operational information.
- _expkg: scapy.all¶
Engine extraction package.
- _extmp: Iterator[ScapyPacket]¶
Engine extraction temporary storage.
DPKT Support¶
This module contains the implementation for DPKT engine
support, as is used by pcapkit.foundation.extraction.Extractor.
- class pcapkit.foundation.engines.dpkt.DPKT(extractor)[source]¶
Bases:
EngineBase[DPKTPacket]DPKT engine support.
- __engine_name__ = 'DPKT'¶
Engine name.
- __engine_module__ = 'dpkt'¶
Engine module name.
- run()[source]¶
Call
dpkt.pcap.Readerto extract PCAP files.This method assigns
self._expkgasdpktandself._extmpas an iterator fromdpkt.pcap.Reader.- Warns:
AttributeWarning – If
self.extractor._exlyrand/orself.extractor._exptlis provided as the DPKT engine currently does not support such operations; or ifself.extractor._exctxis provided, as the DPKT engine does not parse withpcapkit’s own protocol implementations.- Raises:
FormatError – If the file format is not supported, i.e., not a PCAP and/or PCAP-NG file.
- read_frame()[source]¶
Read frames with DPKT engine.
- Return type:
- Returns:
Parsed frame instance.
See also
Please refer to
PCAP.read_framefor more operational information.
- _expkg: dpkt¶
Engine extraction package.
- _extmp: Reader¶
Engine extraction temporary storage.
PyShark Support¶
This module contains the implementation for PyShark engine
support, as is used by pcapkit.foundation.extraction.Extractor.
Important
PyShark has two requirements beyond installing it, and neither is
visible to an import – the package imports cleanly and then fails when used,
which is why
unsupported_reason() checks
both up front.
Python 3.13 or older. pyshark 0.6 builds its event loop with
asyncio.get_event_loop_policy().get_event_loop(). Measured in a fresh
interpreter with no running loop: 3.10 and 3.11 return a loop silently, 3.12
returns one with a DeprecationWarning, and 3.14 raises
RuntimeError: There is no current event loop in thread 'MainThread'. Python
3.13 was not available to measure and is expected to work, being on the
deprecated-but-functional side of that change.
Wireshark’s tshark binary. pyshark shells out to it and
parses nothing itself. It need not be on PATH: pyshark consults
tshark_path in its config.ini first, then PATH on POSIX,
both Program Files directories on Windows, and
/Applications/Wireshark.app on macOS. The check delegates to
pyshark’s own resolver for exactly that reason, so a correctly configured
install off PATH is not refused.
- class pcapkit.foundation.engines.pyshark.PyShark(extractor)[source]¶
Bases:
EngineBase[PySharkPacket]PyShark engine support.
- __engine_name__ = 'PyShark'¶
Engine name.
- __engine_module__ = 'pyshark'¶
Engine module name.
- PYTHON_CEILING = (3, 14)¶
First Python version PyShark does not work on, as a
(major, minor)pair. Released 0.6 builds its event loop withasyncio.get_event_loop_policy().get_event_loop(), and Python 3.14 madeasyncio.get_event_loop()raiseRuntimeErrorwhen no current event loop exists instead of quietly creating one.
- classmethod unsupported_reason()[source]¶
Why this engine cannot run here, or
Nonewhen it can.Consulted by
pcapkit.foundation.extraction.Extractor.run()before the import test, because neither of the two things that stop this engine is visible to an import. PyShark imports perfectly well and then fails when it is used, which without this hook escapes fromrun()as a hard error rather than degrading to the default engine with a warning.The interpreter.
pyshark0.6 doesasyncio.get_event_loop_policy().get_event_loop()atpyshark/capture/capture.py:183, in a fresh interpreter with no running loop. Measured on four interpreters: 3.10 and 3.11 return a loop silently, 3.12 returns one with aDeprecationWarning, and 3.14 raisesRuntimeError: There is no current event loop in thread 'MainThread'. HencePYTHON_CEILINGis(3, 14). Python 3.13 was not available on the machine this was measured on; it is expected to work, since it is on the deprecated-but-functional side of that progression, and that expectation is the one thing here that is inferred rather than observed.The tshark binary.
pysharkis a wrapper around Wireshark’s command-line tool and does no parsing itself, so it is useless without it. The check delegates topyshark’s ownget_process_path()rather than callingshutil.which(), because the two are not equivalent andwhichwould refuse setups that work:pysharklooks attshark_pathin itsconfig.inifirst, and then atPATHon POSIX, at both Program Files directories on Windows, and at/Applications/Wireshark.appon macOS. Askingpysharkgets all of that for free and cannot disagree with whatpysharkwill do a moment later.Note
Deliberately not cached, and the cost was measured rather than assumed: the failing path – which is the expensive one, since it exhausts every candidate – takes about 290 microseconds with 39
PATHentries, against about 120 for a bareshutil.which(). This runs once perExtractor, not once per frame, so it is far below the cost of opening the capture. Caching would trade that for an answer about the environment that cannot change within the process – so installing Wireshark, or fixingPATH, would not take effect until restart. The Windows path does more work than the POSIX one (two Program Files directories, andshutil.which()there would multiply byPATHEXT), but it is still a bounded handful ofos.stat()calls.
- run()[source]¶
Call
pyshark.FileCaptureto extract PCAP files.This method assigns
self._expkgaspysharkandself._extmpas an iterator frompyshark.FileCapture.- Warns:
AttributeWarning – Warns under following circumstances:
if
self.extractor._exlyrand/orself.extractor._exptlis provided as the PyShark engine currently does not support such operations.if reassembly is enabled, as the PyShark engine currently does not support such operation.
if
self.extractor._exctxis provided, as the PyShark engine does not parse withpcapkit’s own protocol implementations.
- read_frame()[source]¶
Read frames with PyShark engine.
- Return type:
Packet- Returns:
Parsed frame instance.
See also
Please refer to
PCAP.read_framefor more operational information.
- close()[source]¶
Close engine.
This method is to be used for closing the engine instance. It is to close the engine instance after the extraction process is finished.
- _expkg: pyshark¶
Engine extraction package.
- _extmp: FileCapture¶
Engine extraction temporary storage.
PyPCAP Support¶
This module contains the implementation for PyPCAP engine
support, as is used by pcapkit.foundation.extraction.Extractor.
Important
PyPCAP publishes no wheels, so installing it compiles a C extension and
needs both the libpcap(3) headers and its shared library on the
system – a header alone is not enough. It is therefore not part of the
all extra, and is installed on its own once libpcap is available:
pip install pypcapkit[PyPCAP]
On Python 3.12 and newer it cannot be installed at all, which is why the
extra carries a python_version < '3.12' marker. PyPCAP 1.3.0 ships a
pcap.c pre-generated by Cython 0.29.32 and never runs Cython at build
time, and that generated C does not compile against the 3.12+ C API. Measured
on four interpreters with libpcap present and found:
Python |
|
|---|---|
3.10 |
builds, imports |
3.11 |
builds, imports |
3.12 |
fails – |
3.14 |
fails – those, plus |
Upstream is unmaintained – one doc-only commit since 1.3.0, and its Python
3.12 issue (pynetwork/pypcap#116) has been open and
uncommented since May 2024 – so the cap is not expected to lift. Use
PCAP_CT on 3.12 and newer.
Important
pcap-ct installs the same top-level pcap module as PyPCAP. This
engine therefore checks which of the two it got and raises
UnsupportedCall when it is pcap-ct,
naming engine='pcap_ct' in the message. Running pcap-ct under the
PyPCAP name would report an engine that is not the one in use, and the two
have different install requirements and different version coverage to report.
Important
PyPCAP is a libpcap(3) binding aimed primarily at live capture.
Offline it performs no protocol dissection: each frame is the
(timestamp, bytes) pair that pcap_next_ex() produced. Reassembly
and flow tracing are therefore unavailable and are disabled – with an
AttributeWarning – when requested.
The engine also reads PCAP savefiles only, and only from a file on disk.
PCAP-NG is rejected with a
FormatError, because
libpcap(3) opens such a file without complaint and then yields no
frames at all; and a non-file input is rejected with an
UnsupportedCall, because
pcap_open_offline() opens a savefile by name.
- class pcapkit.foundation.engines.pypcap.PyPCAP(extractor)[source]¶
Bases:
EngineBase[RawFrame]PyPCAP engine support.
PyPCAP is a binding over libpcap(3), primarily aimed at live capture. Offline it is a savefile reader and nothing more: iteration yields the
(timestamp, bytes)pair frompcap_next_ex()and no protocol dissection is performed. Consequently this enginereturns each frame as a
(timestamp, bytes)tuplerather than as a parsed packet object, anddisables both reassembly and flow tracing, warning as it does so, since neither can be derived without an IP or TCP layer to read.
It also requires the input to be a real file on disk, because
pcap_open_offline()opens by name – there is no way to hand libpcap(3) an already-open Python stream.Important
This engine is upstream PyPCAP specifically, which cannot be installed on Python 3.12 or newer: it ships no wheels and its
pcap.cwas pre-generated by Cython 0.29.32, which does not compile against the 3.12+ C API. pcap-ct is an independent reimplementation of the same interface that does install there, andPCAP_CTis the engine for it.Because pcap-ct installs the same top-level
pcapmodule, this engine checks which distribution it got and refuses the other one rather than running under the wrong name – see__init__().- Parameters:
- Raises:
UnsupportedCall – If the installed
pcapis pcap-ct rather than upstream PyPCAP.
- __engine_name__ = 'PyPCAP'¶
Engine name.
- __engine_module__ = 'pcap'¶
Engine module name. Note that this cannot separate the two distributions that own it – see
pcapkit.foundation.engines._pcap_backend.probe()andunsupported_reason(), which is where that is done.
- __engine_distribution__ = 'pypcap'¶
Distribution this engine drives.
- classmethod unsupported_reason()[source]¶
Why this engine cannot run here, or
Nonewhen it can.Consulted by
pcapkit.foundation.extraction.Extractor.run()before the import test, because the import test cannot answer this question:import pcapsucceeds when pcap-ct is installed and upstream PyPCAP is not, so the guard is satisfied and this engine would run on a distribution it does not drive – reportingPyPCAPfor work thatpcap-ctdid.The condition is deliberately not a Python version ceiling, even though upstream cannot be installed on 3.12 or newer. What matters is which distribution is actually present: a version check would refuse a hypothetical upstream build that someone got working on a newer interpreter, and would say nothing useful on 3.10 and 3.11, where both distributions install happily and either could be the one in place.
- property backend: str¶
The distribution, version and location this engine is driving.
Two distributions provide
pcap, so “the PyPCAP engine failed” is not a complete statement of what ran. This says which one it was, e.g.pypcap 1.3.0 (/.../pcap.cpython-310-x86_64-linux-gnu.so).
- run()[source]¶
Call
pcap.pcapto extract PCAP files.This method assigns
self._expkgaspcapandself._extmpas an iterator frompcap.pcap.- Warns:
AttributeWarning – Warns under following circumstances:
if
self.extractor._exlyrand/orself.extractor._exptlis provided as the PyPCAP engine currently does not support such operations.if reassembly and/or flow tracing is enabled, as the PyPCAP engine performs no protocol dissection and so cannot support either operation.
- Raises:
FormatError – If the file format is not supported, i.e., not a PCAP file. PCAP-NG is rejected explicitly rather than left to libpcap(3), which opens such a file without complaint and then yields no frames at all.
UnsupportedCall – If the input is not a file on disk, as
pcap_open_offline()can only open a savefile by name.
- read_frame()[source]¶
Read frames with PyPCAP engine.
See also
Please refer to
PCAP.read_framefor more operational information.
- close()[source]¶
Close engine.
This method closes the underlying
pcap.pcaphandle. It is idempotent, asExtractor._cleanupandExtractor.__exit__may both reach it, andpcap.pcap.close()is not safe to call twice.
- _expkg: pcap¶
Engine extraction package.
- _extmp: Iterator[RawFrame]¶
Engine extraction temporary storage.
- _dlink: Enum_LinkType¶
Data link layer protocol, from the capture handle.
pcap-ct Support¶
This module contains the implementation for pcap-ct engine
support, as is used by pcapkit.foundation.extraction.Extractor.
Important
pcap-ct is an independent ctypes reimplementation of the PyPCAP
interface, by a different author, on top of the libpcap distribution. It is
a separate engine rather than a second backend for
PyPCAP: select it with
engine='pcap_ct'.
Its reason for existing is coverage. Upstream PyPCAP stops at Python 3.11
(see the note under PyPCAP Support above); pcap-ct and libpcap both
publish py3-none-any wheels, so installing this engine needs no
compiler and no pcap.h:
pip install pypcapkit[PCAP_CT]
Verified end to end on Python 3.10.20 and 3.14.7: both read
examples/captures/in.pcap through engine='pcap_ct' and return the
same six frames with identical timestamps. So the two engines together cover
every interpreter the project supports, and PCAP_CT covers all of it on
its own:
Warning
A system libpcap(3) is still required at run time, and this
is the easiest thing to get wrong about pcap-ct. libpcap ships a vendored
libpcap.so under _platform/{linux,macos,windows}/ but does not
use it by default: its libpcap.cfg reads LIBPCAP = None as
published, which sends the loader to ctypes.util.find_library(), so what
gets mapped is the host’s libpcap.so.1.
Measured, not assumed: the same libpcap 1.11.0b29 wheel loaded
/usr/lib64/libpcap.so.1.5.3 under one interpreter on this host and
linuxbrew’s 1.11.0 under another, purely because their loader search paths
differ. Setting LIBPCAP = tcpdump in libpcap.cfg selects the
vendored copy instead, which reports libpcap 1.10.6.
With no system libpcap(3) at all, import pcap raises
OSError rather than ImportError, which would escape
Extractor.import_test and abort the
extraction. PCAP_CT.unsupported_reason detects it and
reports it as an ordinary “engine unavailable”, so the extraction falls back to
the default engine with a warning naming the missing library.
Warning
Both distributions are pre-releases. pcap-ct 1.3.0b3 and libpcap
1.11.0b29 are the newest published versions, and neither project has ever
published a stable release – which is also why pip install resolves them
without --pre. pcap-ct further documents itself as tracking the
PyPCAP 1.2.3 interface rather than 1.3.0.
Every attribute this engine touches – the pcap.pcap(name=..., promisc=...)
constructor, datalink(), snaplen, iteration yielding
(timestamp, bytes), and close() – is present on both and
was measured to behave identically, byte for byte and timestamp for timestamp,
on pcap-ct 1.3.0b3 against pypcap 1.3.0. That is a statement about the
versions tested, not a guarantee from upstream. PCAP_CT is therefore not
included in the all extra: a beta should be asked for by name.
Important
Being the same interface, pcap-ct has the same limits. It performs no
protocol dissection: each frame is the (timestamp, bytes) pair that
pcap_next_ex() produced. Reassembly and flow tracing are therefore
unavailable regardless of which backend is installed, and are disabled – with
an AttributeWarning – when requested;
the adapters in pcapkit.toolkit.pcap_ct raise
UnsupportedCall rather than fail
obscurely. It also reads from a file on disk only, because
pcap_open_offline() opens a savefile by name; a non-file input is
rejected with an UnsupportedCall.
The timestamp is seconds since the epoch as a float. pcap-ct opens
savefiles asking for nanosecond precision via
pcap_open_offline_with_tstamp_precision(), so a microsecond-resolution
savefile is scaled up by libpcap(3) rather than truncated; for
examples/captures/in.pcap the result matches each record header’s
ts_sec + ts_usec * 1e-6 exactly.
Note
PCAP-NG is rejected with a
FormatError, for both engines, and the
reason is that accepting it would be unpredictable rather than merely
limited. libpcap(3) can read a PCAP-NG savefile, but how well
depends on the version the host provides – which, per the warning above, the
libpcap wheel does not pin. Both measured on
examples/captures/dhcp.pcapng:
System libpcap |
Result |
|---|---|
1.11.0 |
correct – the same four frames and the same sub-second
timestamps as
|
1.5.3 |
four frames, but nonsense sub-second timestamps
( |
Even on a good version, pcap_datalink() reports a single link type for
the whole file, so a capture whose interfaces differ would have one interface’s
link type applied to every frame.
PCAPNG reads the per-interface
blocks properly and does not depend on the host’s library at all, so PCAP-NG is
routed there rather than read approximately – or wrongly – here.
- class pcapkit.foundation.engines.pcap_ct.PCAP_CT(extractor)[source]¶
Bases:
EngineBase[RawFrame]pcap-ct engine support.
pcap-ct is a
ctypesreimplementation of the PyPCAP API on top of the libpcap package, which supplies the libpcap(3) bindings (and ships a vendored copy of the library that it does not, by default, use – see the warning below). Both distributions install a top-levelpcapmodule and expose the same interface, so this engine is a sibling ofPyPCAPrather than a replacement for it – see__engine_module__for how the two are told apart, and the engine documentation for which one to install.It exists because upstream PyPCAP cannot be installed on a current interpreter: it ships no wheels and its
pcap.cwas pre-generated by Cython 0.29.32, which does not compile against the Python 3.12+ C API. pcap-ct and libpcap both publishpy3-none-anywheels, so installing this engine needs no compiler and nopcap.h.Warning
It does still need a system libpcap(3) at run time, and this is easy to get wrong: libpcap ships a vendored
libpcap.sounder_platform/linux,macos,windows/but does not use it by default. Itslibpcap.cfgreadsLIBPCAP = Noneas published, which sends its loader toctypes.util.find_library(), so the library actually mapped is the host’slibpcap.so.1. Measured on this host: the samelibpcap1.11.0b29 wheel loaded/usr/lib64/libpcap.so.1.5.3under one interpreter and linuxbrew’s 1.11.0 under another, purely because their loader search paths differ.Two consequences. With no system libpcap(3) at all,
import pcapraisesOSError– notImportError– whichunsupported_reason()exists partly to catch. And the libpcap(3) version in play is a property of the host rather than of the wheel, which is why the PCAP-NG note onrun()does not rely on it. SettingLIBPCAP = tcpdumpinlibpcap.cfgselects the vendored copy instead – verified loading, and reporting libpcap 1.10.6.Being the same interface, it has the same limits. Offline,
pcapis a savefile reader and nothing more: iteration yields the(timestamp, bytes)pair frompcap_next_ex()and no protocol dissection is performed. Consequently this enginereturns each frame as a
(timestamp, bytes)tuplerather than as a parsed packet object, anddisables both reassembly and flow tracing, warning as it does so, since neither can be derived without an IP or TCP layer to read.
It also requires the input to be a real file on disk, because
pcap_open_offline()opens by name – there is no way to hand libpcap(3) an already-open Python stream.Important
Both distributions are published as pre-releases only –
pcap-ct1.3.0b3 andlibpcap1.11.0b29 at the time of writing – andpcap-ctdocuments itself as tracking the PyPCAP 1.2.3 API rather than 1.3.0. Every attribute this engine touches is present and behaves identically on both (measured onpcap-ct1.3.0b3 againstpypcap1.3.0), but that is a statement about the versions tested, not a guarantee from upstream.- __engine_name__ = 'PCAP_CT'¶
Engine name.
- __engine_module__ = 'pcap._pcap'¶
Engine module name. Note that this is
pcap._pcap, notpcap: pcap-ct and upstream PyPCAP both install a top-levelpcap, so importing that name cannot tell which of the two is present, andExtractor.import_testdecides engine availability by import alone.pcap._pcapis the pcap-ct implementation module; upstream shipspcapas a single extension module rather than a package, so the submodule import fails there and the two engines stay distinguishable.
- __engine_distribution__ = 'pcap-ct'¶
Distribution this engine drives.
- classmethod unsupported_reason()[source]¶
Why this engine cannot run here, or
Nonewhen it can.Consulted by
pcapkit.foundation.extraction.Extractor.run()before the import test, and it answers two questions the import test cannot.Which distribution owns :mod:`pcap`. Upstream PyPCAP owns that name just as legitimately, and
import pcap._pcap– what__engine_module__names – separates them only as long as upstream keeps shipping a single extension module. Askingprobe()states the check rather than inferring it from an import that happens to fail.Whether a system :manpage:`libpcap(3)` exists at all. This is the important one, and it is measured rather than theoretical. pcap-ct needs no compiler because it is
ctypes, but at runtime it imports the libpcap distribution, whose Linux loader callsctypes.util.find_library()and raisesOSError–Cannot find libpcap.so library– when there is none.OSErroris not anImportError, soExtractor.import_testlets it through and the extraction dies rather than falling back. Reporting it here turns that into the ordinary “engine unavailable” path.There is deliberately no Python version bound in either direction. Measured on 3.10.20 and 3.14.7:
pcap-ct1.3.0b3 withlibpcap1.11.0b29 installs and readsexamples/captures/in.pcapidentically on both, so this engine covers the whole range the project supports. The floor in thePCAP_CTextra’s markers exists only becausepyproject.tomlstill advertisesrequires-python >= 3.6, and on an interpreter below 3.10 the distributions simply will not be installed – which the import test then reports correctly on its own.
- property backend: str¶
The distribution, version and location this engine is driving.
Two distributions provide
pcap, so “the pcap-ct engine failed” is not a complete statement of what ran. This says which one it was, e.g.pcap-ct 1.3.0b3 (/.../pcap/__init__.py).
- __init__(extractor)[source]¶
Initialise the engine.
- Warns:
EngineWarning – If both distributions that provide
pcapare installed. Only one of them can win the import – measured on Python 3.10 with both present, thepcap-ctpackage wins and upstream’s extension module is shadowed – so the other is unreachable, and nothing else would say whyengine='pypcap'had stopped working.- Raises:
UnsupportedCall – If the environment cannot support this engine, for either of the reasons
unsupported_reason()describes. That hook is only consulted byExtractor.run, which degrades to the default engine with a warning; constructing the engine directly bypasses it, so this is the backstop for that path.
- run()[source]¶
Call
pcap.pcapto extract PCAP files.This method assigns
self._expkgaspcap,self._handleas thepcap.pcapcapture handle andself._extmpas an iterator over it.- Warns:
AttributeWarning – Warns under following circumstances:
if
self.extractor._exlyrand/orself.extractor._exptlis provided as the pcap-ct engine currently does not support such operations.if reassembly and/or flow tracing is enabled, as the pcap-ct engine performs no protocol dissection and so cannot support either operation.
- Raises:
FormatError – If the file format is not supported, i.e., not a PCAP file. PCAP-NG is rejected explicitly even though the vendored libpcap(3) can read one – see the note below.
UnsupportedCall – If the input is not a file on disk, as
pcap_open_offline()can only open a savefile by name.
Note
The PCAP-NG rejection is a deliberate narrowing, and the reason is that the alternative is unpredictable rather than merely limited. libpcap(3) can read a PCAP-NG savefile, but how well depends on the version the host happens to provide – which, per the warning in this class’s docstring, is not something the libpcap wheel pins. Both measured on
examples/captures/dhcp.pcapng:against libpcap 1.11.0 it reads correctly, yielding the same four frames and the same sub-second timestamps as
PCAPNG;against libpcap 1.5.3 it yields the four frames but with nonsense sub-second timestamps –
1102274184.0000002and the like – silently, with no error.
Even on a good version it cannot represent more than one interface:
pcap_datalink()returns a single link type for the whole file, so a capture whose interfaces differ would have one interface’s link type applied to every frame.PCAPNGreads the per-interface blocks properly and does not depend on the host’s library at all, so PCAP-NG is routed there rather than read approximately – or wrongly – here.
- read_frame()[source]¶
Read frames with pcap-ct engine.
Note
The timestamp is seconds since the epoch as a
float. pcap-ct opens savefiles withpcap_open_offline_with_tstamp_precision()asking for nanosecond precision, so a microsecond-resolution savefile is scaled up by libpcap(3) rather than truncated; the resulting value matches the record header’sts_sec + ts_usec * 1e-6exactly forexamples/captures/in.pcap.See also
Please refer to
PCAP.read_framefor more operational information.
- close()[source]¶
Close engine.
This method closes the underlying
pcap.pcaphandle. It is idempotent, asExtractor._cleanupandExtractor.__exit__may both reach it.Note
pcap-ct1.3.0b3’s ownpcap.pcap.close()happens to tolerate a second call, where upstreampypcap1.3.0 segfaults on one. The_closedflag is kept regardless: it is not this engine’s business to depend on a pre-release’s undocumented forgiveness, and the flag also covers the case of an engine that was never opened.
- _expkg: pcap¶
Engine extraction package.
- _handle: Handle¶
Capture handle, kept separately from the iterator it is read through so that
close()does not depend oniter(handle) is handle.
- _extmp: Iterator[RawFrame]¶
Engine extraction temporary storage.
- _dlink: Enum_LinkType¶
Data link layer protocol, from the capture handle.
PyPCAPFile Support¶
This module contains the implementation for PyPCAPFile engine
support, as is used by pcapkit.foundation.extraction.Extractor.
Important
PyPCAPFile is a pure Python savefile reader that decodes Ethernet, IPv4,
TCP and UDP and nothing else. IPv6 reassembly is therefore unavailable and is
disabled – with an AttributeWarning –
when requested; IPv4 and TCP reassembly and TCP flow tracing remain
available. PCAP-NG is rejected with a
FormatError.
- class pcapkit.foundation.engines.pypcapfile.PyPCAPFile(extractor)[source]¶
Bases:
EngineBase[PCAPFilePacket]PyPCAPFile engine support.
PyPCAPFile is a pure Python savefile reader. It decodes Ethernet, IPv4, TCP and UDP, and nothing else – in particular there is no IPv6 decoder and no PCAP-NG support. Consequently this engine
reads PCAP savefiles only, raising
FormatErroron PCAP-NG, anddisables IPv6 reassembly, warning as it does so, while leaving IPv4 and TCP reassembly and TCP flow tracing in place.
The engine stops decoding at the network layer rather than descending into the transport layer. PyPCAPFile decoders replace the payload bytes of the layer they decode, so descending further would discard the verbatim TCP segment that
tcp_reassembly()needs in order to report an exact header/payload split.- __engine_name__ = 'PyPCAPFile'¶
Engine name.
- __engine_module__ = 'pcapfile'¶
Engine module name.
- LAYERS = 2¶
Number of layers to descend while decoding, i.e. link plus network. See the class docstring for why this stops short of the transport layer.
- PYTHON_CEILING = (3, 12)¶
First Python version PyPCAPFile does not work on, as a
(major, minor)pair. Released 0.12.0 importsimpfrompcapfile.linklayer, andimpwas removed in Python 3.12.
- classmethod unsupported_reason()[source]¶
Why this engine cannot run here, or
Nonewhen it can.Consulted by
pcapkit.foundation.extraction.Extractor.run()before the import test, because the import test cannot answer this question.pcapfile’s top-level package imports perfectly well on Python 3.12 and newer – it ispcapfile.linklayerthat fails, andpcapfile.savefileimports it – so a guard that only triesimport pcapfileis satisfied and theModuleNotFoundErrorthen escapes from__init__()as a hard error instead of degrading to the default engine with a warning, which is what happens when the package is simply absent.The version is checked rather than the import attempted so that the answer does not depend on which of the package’s submodules happens to be imported first, and so it is the same answer on a machine that has never installed
pcapfileat all.
- run()[source]¶
Call
pcapfile.savefile.load_savefile()to extract PCAP files.This method assigns
self._expkgaspcapfileandself._extmpas an iterator over the lazily generated savefile packets.The savefile is loaded with
layers=0, so that each frame arrives with its bytes verbatim, andread_frame()then decodes it toLAYERSdepth usingpcapfile.linklayer.clookup()– the very callpcapfilemakes internally. Doing it this way round is what lets the link layer type be inspected (and reported on) before the first frame is decoded.- Warns:
AttributeWarning – Warns under following circumstances:
if
self.extractor._exlyrand/orself.extractor._exptlis provided as the PyPCAPFile engine currently does not support such operations.if IPv6 reassembly is enabled, as
pcapfilehas no IPv6 decoder.if
pcapfilehas no decoder for the savefile’s link layer type, in which case frames are left undecoded.
- Raises:
FormatError – If the file format is not supported, i.e., not a PCAP file.
pcapfilereads libpcap savefiles only.
- read_frame()[source]¶
Read frames with PyPCAPFile engine.
- Return type:
pcap_packet- Returns:
Parsed frame instance.
See also
Please refer to
PCAP.read_framefor more operational information.
- _expkg: pcapfile¶
Engine extraction package.
- _extmp: Iterator[PCAPFilePacket]¶
Engine extraction temporary storage.
- _dlink: Enum_LinkType¶
Data link layer protocol, from the savefile header.
Internal Definitions¶
- class pcapkit.foundation.engines.pypcapfile._NamedStream(stream, name)[source]¶
Bases:
objectRead-only proxy that gives a stream the
nameattribute.pcapfile.savefile.load_savefile()dereferencesinput_file.nameunconditionally, on the way into its trace helper. That is fine for theBufferedReaderExtractornormally holds, but not for theSeekableReaderit substitutes when the caller supplied a non-seekable stream – that class exposes noname, so the load would fail withAttributeErrorbefore a single byte was read.- name¶
Name of the underlying stream.
- PyPCAPFile._get_decoder(linktype)[source]¶
Return the
pcapfilelink layer decoder for a link layer type.
- PyPCAPFile._decode(packet, frnum)[source]¶
Decode a raw savefile packet down to
LAYERSdepth.A new
pcapfile.structs.pcap_packetis built rather than the given one mutated, so that a decoding failure leaves the original intact.- Parameters:
packet (
pcap_packet) – Undecoded savefile packet, i.e. as loaded withlayers=0.frnum (
int) – Frame number, for the warning message below.
- Return type:
pcap_packet- Returns:
The decoded packet, or
packetunchanged when it could not be decoded.- Warns:
AttributeWarning – If
pcapfilecould not decode the frame, e.g. because the capture is truncated. One bad frame should not abort the extraction, but it should not pass silently either.
Backend Detection¶
Two unrelated PyPI distributions install a top-level module named pcap –
PyPCAP, a Cython binding shipped as a single extension module, and pcap-ct,
a ctypes reimplementation shipped as a package. They therefore collide,
and import pcap resolves to whichever the import system finds first.
PyPCAP and
PCAP_CT each have to know which one
they actually got rather than assume, and this module is the one place that
answers it – deliberately shared, since the two engines must agree and two
copies of the detection would be two chances to disagree. It is only detection,
and it imports nothing from pcapkit, so it cannot introduce an import
cycle.
- pcapkit.foundation.engines._pcap_backend.DISTRIBUTIONS = ('pypcap', 'pcap-ct')¶
Every distribution known to provide
pcap, in a fixed order so that messages naming several of them read the same way every time.
- pcapkit.foundation.engines._pcap_backend.ENGINE_NAMES = {'pcap-ct': 'pcap_ct', 'pypcap': 'pypcap'}¶
The
engine=string that drives each distribution. Kept here rather than in either engine so that a message pointing the user at the other engine cannot name one that does not exist.
- class pcapkit.foundation.engines._pcap_backend.Probe(*args: VT, **kwargs: VT)[source]¶
Bases:
InfoWhat one attempt to import
pcapfound.Note
This is an
Infosubclass, so it is aMappingrather than atuple: its fields are reached by name, not by position, and it cannot be unpacked as a sequence.- name: str | None¶
Distribution that provided the imported module –
PYPCAP,PCAP_CT, orNonewhen the import did not succeed.
- origin: str | None¶
pcap.__file__, which is what distinguishes an extension module from a package directory to a human reading a bug report.
- missing: bool¶
Whether the failure was simply “not installed”, i.e. an
ImportError. This matters becauseExtractor.import_testalready reports that case perfectly well, whereas the other kind of failure escapes it – seeprobe().
- pcapkit.foundation.engines._pcap_backend.probe()[source]¶
Import
pcapand report what was found.Not cached. The cost after a successful first call is a
sys.moduleslookup, and a cache would make the answer depend on when it was first asked – which the tests, and anything that manipulatessys.path, would have to work around. It is idempotent instead, via_purge().Note
The bare
except Exceptionis deliberate and is much of the point of this function.pcap-ctimports thelibpcapdistribution, whose Linux loader callsctypes.util.find_library()and raisesOSError–Cannot find libpcap.so library– when no system libpcap(3) is present.OSErroris not anImportError, soExtractor.import_testdoes not catch it and it escapes as a hard error instead of degrading to the default engine. Catching it here is what lets an engine report it as a reason instead.
- pcapkit.foundation.engines._pcap_backend.identify(module)[source]¶
Which distribution does an imported
pcapmodule come from?pcap-ct ships
pcapas a package whose__init__doesfrom ._pcap import *, which binds the submodule as an attribute; upstream PyPCAP ships a single extension module, which has no such attribute. That is a structural difference rather than a cosmetic one, which is why it is preferred here over the alternatives:pcap.__version__is1.3.0b3against1.3.0today, but that is a coincidence of release timing and would stop separating them the momentpcap-ctcuts a 1.3.0 final.pcap.ex_namelooked like apcap-ctmarker and is not – measured present on upstreampypcap1.3.0 as well.
- Parameters:
module (
ModuleType) – An already-importedpcapmodule.- Return type:
- Returns:
- pcapkit.foundation.engines._pcap_backend.installed_distributions()[source]¶
Which of
DISTRIBUTIONSare installed, per package metadata.Asked of the metadata rather than of
pcapitself, because that is the only way to see the distribution the import did not resolve to – which is exactly the collision worth reporting.- Return type:
- Returns:
The installed subset of
DISTRIBUTIONS, in that order.
- pcapkit.foundation.engines._pcap_backend.wrong_backend_reason(wanted, found)[source]¶
Why
wantedcannot run, when some other distribution ownspcap.- Parameters:
- Return type:
- Returns:
A reason naming what was found and which
engine=string wants it, orNonewhenwantedis what is installed, or when nothing is – an absent module is not this function’s business, sinceExtractor.import_testreports that.
- pcapkit.foundation.engines._pcap_backend.collision_reason(found)[source]¶
A description of both distributions being installed at once, if they are.
Internal Definitions¶
- pcapkit.foundation.engines._pcap_backend._purge()[source]¶
Drop the
pcapandlibpcapmodule trees fromsys.modules.Called after a failed import so that the next probe reproduces the same failure. Without it a second attempt reports something else entirely, which was measured rather than imagined.
Both
pcap-ct’s andlibpcap’s package initialisers open withfrom .__about__ import * ; del __about__, which is not safe to re-run: thedelneeds a name that only gets bound as a side effect of importing the submodule fresh. When the initialiser fails part-way – as it does with no system libpcap(3), where the real error isOSError: Cannot find libpcap.so library– Python removes the package it was executing but leaves the__about__submodule cached, so the retry reaches thedelwith nothing bound and dies withNameError: name '__about__' is not defined.That message names neither the missing library nor the package it came from, and it is what the user would otherwise be shown.
libpcapis purged as well aspcapbecause the residue is in whichever of the two got part-way: purging onlypcapmoved theNameErrorfrom one to the other rather than removing it.