API Reference
Every public name, generated from the package's own docstrings.
The same surface in prose, with the bounds and the gotchas, is the API header
that ships inside the installed package, pktcap/AGENTS.md, readable with
importlib.resources.files("pktcap") and printed in the Guide of this site.
pktcap -- capture files, read, dissected, written and replayed.
Reads any pcap or pcapng capture, dissects each frame layer by layer with dissectors anyone can register (Ethernet, VLAN, IPv4, IPv6, UDP and TCP are built in), writes frames and datagrams back as pcap, pcapng or structured records, and replays a capture. What is not dissected is handed back as octets; what a protocol means stays with the library that registers its dissector.
Every public name is imported from :mod:pktcap; the modules beside this one
are private.
CapturedDatagram
Bases: NamedTuple
One UDP datagram from a capture.
:ivar time: seconds since the epoch; for a reassembled datagram, the time
of the fragment that completed it.
:ivar source: (host, port) of the sender, the host as address text.
:ivar destination: (host, port) it was sent to.
:ivar payload: the octets after the UDP header.
:ivar fragmented: payload is only what the first IP fragment carried.
Set only by a decoder that was told not to reassemble.
:ivar truncated: payload is shorter than the datagram's own length
field says, because the capture's snap length cut the frame.
CapturedFrame
Bases: NamedTuple
One packet as a capture recorded it, before any dissection.
:ivar time: seconds since the epoch, as the capture states them. The file
controls this value: it may be zero (a pcapng simple packet block has
no timestamp) or far outside any calendar.
:ivar linktype: the LINKTYPE_ number saying what data starts with.
:ivar data: the captured octets, which a snap length may have cut short.
:ivar interface: in a pcapng capture, the number of the interface the
packet came from, counted from 0 within its section; None in a
pcap capture, which has one interface and does not number it.
CopyResult
Bases: NamedTuple
What :func:copy_frames did.
:ivar read: items taken from the source.
:ivar written: items the writer wrote.
:ivar skipped: items select refused, or that had no datagram when
datagrams was asked for.
:ivar refused: items the writer turned away for its file budget during
this call.
DissectedFrame
Bases: NamedTuple
A captured frame and the layers that were read from it.
:ivar frame: the frame as captured.
:ivar layers: the layer records, outermost first.
:ivar payloads: for each layer, the octets that follow it. The last one is
what was not dissected.
:ivar error: why dissection stopped early, naming the selector whose
dissector could not read its layer; None when it simply ran out of
dissectors or of octets.
:ivar reassembled: the layers after an IP fragment were read from a
datagram put together from several frames, of which this is the last.
time
property
The frame's time, in seconds since the epoch.
payload
property
The octets no dissector read: after the last layer, or the whole frame when there is none.
layer(kind)
The outermost layer that is a kind, or None.
Source code in src/pktcap/_dissect.py
118 119 120 121 122 123 | |
payload_of(kind)
The octets that follow the outermost kind layer, or None.
Source code in src/pktcap/_dissect.py
125 126 127 128 129 130 | |
datagram()
This frame as a UDP datagram: its time, both socket addresses and
the octets after the UDP header, whatever dissected them further.
None when the frame carries no UDP over IP.
Source code in src/pktcap/_dissect.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
DissectStats
Bases: NamedTuple
What a :class:FrameDissector has met in the frames it was given.
:ivar frames: frames given to dissect.
:ivar malformed: frames in which a dissector could not read its layer
(a ValueError), or that had more layers than the ceiling.
:ivar failed: frames in which a dissector raised anything else, or
returned something that is not a :class:Dissected: a defect in it.
:ivar unsupported: frames whose link type has no dissector;
:attr:FrameDissector.unsupported_linktypes says which.
:ivar fragments: frames that were a piece of a fragmented IP datagram.
:ivar dropped: reassemblies discarded: an overlap, a ceiling, old age.
:ivar pending: reassemblies still waiting for a fragment.
FrameDissector(registry=None, *, reassemble=True, max_reassemblies=256, reassembly_timeout=30.0)
Dissects captured frames into :class:DissectedFrame objects.
Feed it every frame of a capture, in order: with reassemble on it
keeps IP fragment state between frames. It raises nothing for a frame:
every frame comes back, with the layers that were read and the rest as
payload, and :attr:stats counts what went wrong. Not safe to share
between threads.
:param registry: where dissectors are looked up; :func:default_registry
when None. It is read at each frame, so a dissector registered
later is used from then on.
:param reassemble: put IP fragments back together. Off, no state is kept:
the layers of a first fragment are read from the octets it carries,
and a later fragment's octets are left as payload.
:param max_reassemblies: the most datagrams being reassembled at once; one
more discards the oldest.
:param reassembly_timeout: seconds of capture time after which an
unfinished datagram is discarded.
:raises ValueError: a limit that is not positive.
Source code in src/pktcap/_dissect.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | |
registry
property
The registry dissectors are looked up in.
stats
property
The counters as they stand.
unsupported_linktypes
property
How many frames each link type with no dissector had, by its
LINKTYPE_ number: a read-only snapshot. The first 64 distinct
link types are told apart; stats.unsupported counts every frame.
dissect(frame)
frame with every layer a registered dissector could read.
Source code in src/pktcap/_dissect.py
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
DissectorRegistry(*, builtins=True)
The dissectors one :class:FrameDissector chooses from.
A selector is a kind and a number. The built-in kinds are linktype
(a capture's LINKTYPE_ number), ethertype, ip (an IP protocol
number), udp and tcp (a port, the destination tried before the
source). A dissector may name selectors of any other kind for what
follows it, and whoever registers one of that kind is found.
Two registries share nothing, so two users in one process do not collide:
build one, register into it, and pass it to :class:FrameDissector.
:param builtins: start with the built-in dissectors. False starts
empty, for a caller that wants nothing decoded it did not choose.
Source code in src/pktcap/_dissectors/__init__.py
62 63 64 65 66 67 68 | |
register(kind, value, dissector, *, replace=False)
Make dissector the one that reads what (kind, value) selects.
:param replace: take the place of the dissector already there. Without
it a selector that is taken is an error: two libraries claiming
one port should find out when they register, not when a capture
decodes as the wrong protocol.
:raises ValueError: the selector is taken and replace is false.
:raises TypeError: dissector cannot be called.
Source code in src/pktcap/_dissectors/__init__.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
unregister(kind, value)
Forget the dissector for (kind, value); what it selected comes
back undecoded. ValueError when there is none.
Source code in src/pktcap/_dissectors/__init__.py
92 93 94 95 96 97 98 | |
get(kind, value)
The dissector for (kind, value), or None: a selector with
no dissector is the ordinary end of a dissection, not an error.
Source code in src/pktcap/_dissectors/__init__.py
100 101 102 103 | |
selectors()
Every selector with a dissector, sorted.
Source code in src/pktcap/_dissectors/__init__.py
105 106 107 | |
register_layer(layer, *, name=None, keys=None, replace=False)
Declare the class of a layer a dissector returns, so a filter can name it, and optionally the filter keys that read it.
:param layer: the layer's class. Every field of a named tuple is a
filter key NAME.FIELD with no further code.
:param name: how a filter calls the layer: lower case, [a-z][a-z0-9_]*.
Omitted: the class name in lower case without a trailing
Layer, which is also how proto= and frame_record name it.
:param keys: key name ([a-z][a-z0-9_-]*) to build(clause),
called once per clause when a filter is compiled; it returns the
test of one layer record and raises ValueError for a value
that can never match.
:param replace: take the place of a layer registered under that name.
:raises ValueError: a name that is not valid, is a built-in layer's
name, or is taken and replace is false; a key name that is
not valid.
:raises TypeError: layer is not a class, keys is not a
mapping of callables.
Source code in src/pktcap/_dissectors/__init__.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
unregister_layer(name)
Forget a registered layer and its keys. ValueError when there
is none, a built-in layer included.
Source code in src/pktcap/_dissectors/__init__.py
173 174 175 176 177 178 179 | |
layers()
The registered layers, by name: a copy, built-in layers not in it.
Source code in src/pktcap/_dissectors/__init__.py
181 182 183 | |
copy()
A registry with the same dissectors, layers and keys that then changes on its own.
Source code in src/pktcap/_dissectors/__init__.py
207 208 209 210 211 212 | |
Dissected
Bases: NamedTuple
What a dissector returns for the octets it was given.
:ivar layer: the record of this layer: any immutable value, the built-in
ones being named tuples. None for a step that only chooses what
follows and has nothing to report.
:ivar payload: the octets left for the next layer: a part of what the
dissector was given, never more.
:ivar next: the selectors to try for the next dissector, in order; the
first one registered is used. Empty when nothing follows.
:ivar fragment: set when payload is a piece of a larger unit.
Fragment
Bases: NamedTuple
Says that a dissector's payload is one piece of a larger unit.
The frame dissector puts the pieces together, within its bounds, and carries on with the whole once it is complete.
:ivar key: what identifies the unit: equal for every piece of it and for nothing else (addresses and an identification number, for IP). :ivar offset: where this piece's octets start in the unit. :ivar last: this piece ends the unit.
CaptureFilterError
Bases: PktcapError, ValueError
A capture-filter expression that cannot be compiled.
Raised when the filter is parsed or compiled, never while it is applied:
a clause that is not key=value, an or, or a key or value that the
protocol library's own builder refused (its ValueError is chained).
CaptureFormatError(message, *, offset=None)
Bases: PktcapError, ValueError
The input is not a pcap or pcapng capture, or it is a damaged one.
The one type every malformed container raises: a wrong magic number, a record or block cut short, a length over its ceiling, a block that contradicts itself. The message never quotes the file.
:ivar offset: how many octets of the input had been read when the problem
was found, or None when that is not known.
Source code in src/pktcap/_exceptions.py
47 48 49 50 51 | |
CaptureHookError(message, *, status=None, timed_out=False)
Bases: PktcapError, OSError
A hook command failed and was asked to end the capture (fail_fast).
An :class:OSError, as the failure of any other program or socket a
capture relies on is. The message names the program and ends with a
bounded, escaped tail of the program's error output.
:ivar status: the program's exit status, or None when it was killed
for running past its time limit.
:ivar timed_out: the program ran past its time limit and was killed with
everything it had started.
Source code in src/pktcap/_exceptions.py
201 202 203 204 205 206 207 208 209 210 | |
CapturePluginError(message, *, plugin, source)
Bases: PktcapError, ValueError
A plugin the user named cannot be loaded.
Raised for a name that is no module, a module whose import failed, a
module with no pktcap_plugin hook, a hook that takes no registry or
raised, and an item of a list from a variable or a settings file that is
no dotted Python name. The registry is left as it was before the call. A
malformed item passed as an argument is a plain ValueError instead.
:ivar plugin: the item as written.
:ivar source: where the list came from: "argument", or what the
command line names ("PKTCAP_LOAD", a settings file's path).
Source code in src/pktcap/_exceptions.py
173 174 175 176 | |
DissectError
Bases: PktcapError, ValueError
Octets that are not the layer a dissector was asked to read: a header cut short, or one whose length fields contradict what is there.
What the built-in dissectors raise. It never reaches a caller of
:meth:pktcap.FrameDissector.dissect, which keeps the frame, leaves that
layer undecoded and counts it; a caller that runs a dissector by hand
catches it. A dissector of the caller's own may raise any ValueError.
LiveCaptureError
Bases: PktcapError, OSError
This platform cannot capture live: it has no AF_PACKET.
An :class:OSError, as the failure to open any other socket is. A process
that lacks the capability gets the kernel's own PermissionError, which
is not this class.
MissingExtraError(message, *, format, extra)
Bases: PktcapError, ImportError
An output format whose optional dependency is not installed.
An :class:ImportError, so except ImportError still catches it. The
message names the extra to install; a caller that offers the format under
its own extra words its own message from the attributes.
:ivar format: the format's name, as in OUTPUT_FORMATS ("toml").
:ivar extra: pktcap's extra that installs what the format needs.
Source code in src/pktcap/_exceptions.py
89 90 91 92 | |
PktcapError
Bases: Exception
The base of every exception pktcap raises on its own account.
RecordFormatError(message, *, format, lineno=None)
Bases: PktcapError, ValueError
Text that is not one record in the format it is read as.
Raised by :func:pktcap.loads_record for text that does not parse, that
is not one document with a mapping at its top, that repeats a key, or that
holds what a record cannot (a NaN in JSON, a tag that is not plain data,
a nesting past the recursion limit). The message says what is wrong and
where, and never quotes the text; the reader's own exception is not
chained.
:ivar format: the format's name, as in RECORD_FORMATS ("yaml").
:ivar lineno: the 1-based line of the problem, or None when the
reader keeps none.
Source code in src/pktcap/_exceptions.py
119 120 121 122 123 124 125 126 127 | |
UnsupportedFormatError
Bases: PktcapError, ValueError
No output format of that name, or none can be told from a file name.
The message lists the formats there are. A format that exists and whose
extra is not installed is not this error: it raises
:class:MissingExtraError.
FilterClause
Bases: NamedTuple
One key=value or key!=value clause of a capture filter.
:ivar key: the text before the operator, as written: case is kept.
:ivar value: the text after it, surrounding space removed.
:ivar negated: the operator was !=.
values
property
value split on commas, each item stripped and empty ones left
out, for a key that reads a comma as "any of".
EthernetLayer
Bases: NamedTuple
An Ethernet II header.
:ivar destination: the destination MAC, as aa:bb:cc:dd:ee:ff.
:ivar source: the source MAC, in the same form.
:ivar ethertype: what follows: 0x0800 IPv4, 0x86DD IPv6,
0x8100 a VLAN tag. Below 1,536 it is an 802.3 length, and nothing
is selected to follow.
summary()
The addresses and what follows: aa:.. > bb:.. ethertype 0x0800.
Source code in src/pktcap/_layers.py
76 77 78 79 80 81 82 | |
IPv4Layer
Bases: NamedTuple
An IPv4 header. Options are skipped.
:ivar source: the source address, dotted. :ivar destination: the destination address. :ivar protocol: the protocol number of what follows (6 TCP, 17 UDP). :ivar ttl: the time to live. :ivar identification: the identification field, shared by the fragments of one datagram. :ivar dont_fragment: the DF flag. :ivar more_fragments: the MF flag: more fragments follow this one. :ivar fragment_offset: where this fragment's octets start in the datagram; 0 for a whole datagram and for a first fragment. :ivar length: the total length the header states, 0 when a network card that segments the send left it unset.
is_fragment
property
Whether this packet is one piece of a fragmented datagram.
summary()
Addresses, protocol, time to live and identification, and where a
fragment starts: 10.0.0.5 > 10.0.0.1 protocol 17 ttl 64 id 1.
Source code in src/pktcap/_layers.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | |
IPv6ExtensionLayer
Bases: NamedTuple
A hop-by-hop (0), routing (43) or destination-options (60) header.
:ivar next_header: the protocol number of what follows. :ivar data: the header's octets after its first two.
summary()
What follows and how many octets the header holds past its first two.
Source code in src/pktcap/_layers.py
248 249 250 | |
IPv6FragmentLayer
Bases: NamedTuple
An IPv6 fragment header.
:ivar next_header: the protocol number of the reassembled payload. :ivar fragment_offset: where this fragment's octets start, in octets. :ivar more_fragments: more fragments follow this one. :ivar identification: shared by the fragments of one datagram.
is_fragment
property
False for an "atomic" fragment: a whole datagram in one piece.
summary()
What follows, the identification and where this piece starts.
Source code in src/pktcap/_layers.py
272 273 274 275 276 277 278 279 | |
IPv6Layer
Bases: NamedTuple
An IPv6 fixed header. Extension headers are layers of their own.
:ivar source: the source address. A v4-mapped address is written
::ffff:10.0.0.5 on every Python.
:ivar destination: the destination address.
:ivar next_header: the protocol number of what follows.
:ivar hop_limit: the hop limit.
:ivar payload_length: the payload length the header states.
:ivar traffic_class: the traffic class octet.
:ivar flow_label: the 20-bit flow label.
summary()
Addresses, what follows and the hop limit.
Source code in src/pktcap/_layers.py
228 229 230 231 232 233 234 235 | |
LinuxCookedLayer
Bases: NamedTuple
A Linux cooked-capture header, version 1 or 2: what the any device
records in place of a link-layer header.
:ivar packet_type: 0 to this host, 1 broadcast, 2 multicast, 3 to another
host, 4 sent by this host.
:ivar hardware_type: the ARPHRD_ number of the device (1 Ethernet,
772 loopback).
:ivar address: the sender's link-layer address as hex text, as many
octets as the header says are valid (at most eight).
:ivar ethertype: what follows.
:ivar interface: the interface index; None in version 1, which has
none.
summary()
Direction, device type, sender address when there is one, and what
follows: host, hardware type 772, ethertype 0x0800.
Source code in src/pktcap/_layers.py
130 131 132 133 134 135 136 137 138 139 140 | |
LoopbackLayer
Bases: NamedTuple
The four-octet header of a BSD loopback capture.
:ivar family: the address family as the capturing host numbered it: 2 is IPv4 everywhere; IPv6 is 10, 24, 28 or 30 by operating system.
summary()
The address family: family 2.
Source code in src/pktcap/_layers.py
152 153 154 | |
TCPLayer
Bases: NamedTuple
A TCP header. The segment's payload follows it; :class:TCPReassembler
puts the streams back together.
:ivar source_port: the source port.
:ivar destination_port: the destination port.
:ivar sequence: the sequence number, as on the wire.
:ivar acknowledgment: the acknowledgment number, as on the wire.
:ivar flags: the flag bits: 0x01 FIN, 0x02 SYN, 0x04 RST,
0x08 PSH, 0x10 ACK, 0x20 URG, 0x40 ECE, 0x80 CWR,
0x100 the accurate-ECN bit.
:ivar window: the window field, unscaled.
:ivar checksum: the checksum field, not verified.
:ivar urgent: the urgent pointer.
:ivar options: the option octets, undecoded.
syn
property
The SYN flag: the segment opens a connection.
ack
property
The ACK flag: the acknowledgment number means something.
fin
property
The FIN flag: the sender has no more to send.
rst
property
The RST flag: the connection is reset.
summary()
The ports, the flags that are set, the sequence and acknowledgment
numbers and the window: 40000 > 80 [SYN,ACK] seq 100 ack 7 window 4096.
Source code in src/pktcap/_layers.py
354 355 356 357 358 359 360 361 362 363 364 365 | |
UDPLayer
Bases: NamedTuple
A UDP header.
:ivar source_port: the source port. :ivar destination_port: the destination port. :ivar length: the length the header states, header included; 0 for a jumbogram or a send the network card segmented. More than the octets that follow means the capture cut the datagram short. :ivar checksum: the checksum field, not verified.
summary()
The ports and the stated length: 50000 > 69 length 13.
Source code in src/pktcap/_layers.py
298 299 300 301 302 303 304 | |
VLANLayer
Bases: NamedTuple
One 802.1Q or 802.1ad tag. A QinQ frame has two of these in a row.
:ivar id: the VLAN identifier, 0 to 4095. :ivar priority: the priority code point, 0 to 7. :ivar drop_eligible: the drop-eligible indicator. :ivar ethertype: what follows the tag.
summary()
The tag: vlan 5 priority 0 ethertype 0x0800.
Source code in src/pktcap/_layers.py
99 100 101 102 103 104 105 106 | |
LiveCapture(interface=None, *, timeout=1.0)
A live capture of every packet on one interface, or on all of them.
Linux only. A context manager: constructing one opens nothing; open()
or with opens the socket, which needs CAP_NET_RAW. Each packet
comes back as a :class:CapturedFrame of link type 276 (Linux cooked
capture v2, what tcpdump writes for its any device), stamped with the
time it was read: ready for a :class:FrameDissector, and for a writer,
whose output tcpdump and Wireshark read. On a loopback device, where every
packet is seen leaving and arriving, only the arriving copy is returned.
:param interface: the interface to capture on: a name, a
:class:netimps.Interface, or anything netimps.get_interface
finds one by (an address, a MAC). None is every interface.
:param timeout: how many seconds :meth:read waits for a packet.
:raises ValueError: a timeout that is not positive.
Source code in src/pktcap/_live.py
94 95 96 97 98 99 100 101 102 103 104 105 | |
open()
Open the socket. Does nothing when it is already open.
:raises LiveCaptureError: this platform has no AF_PACKET.
:raises PermissionError: the process lacks CAP_NET_RAW.
:raises ValueError: no interface matches interface, or the capture
was closed.
:raises OSError: the kernel refused the interface.
Source code in src/pktcap/_live.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
read()
The next packet, or None when timeout passes without one.
:raises ValueError: the capture is not open.
Source code in src/pktcap/_live.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
fileno()
The socket's descriptor, for a caller's own event loop or selector.
:raises ValueError: the capture is not open.
Source code in src/pktcap/_live.py
198 199 200 201 202 203 204 205 | |
close()
Close the socket. Final, and harmless when repeated.
Source code in src/pktcap/_live.py
207 208 209 210 211 212 | |
CaptureWriter(target, format=None, *, per_record=False, append=False, fields=(), max_files=1000)
Writes what was captured, or the records made of it, in one format.
What is written is a :class:CapturedDatagram (the UDP view) or a
:class:DissectedFrame (anything). pcap and pcapng write the
datagram under synthesised headers, or the frame as captured, and ignore
the record. A record format writes the record given, or
:func:datagram_record or :func:frame_record when none is given:
json one line per record, yaml one document per record. toml
and ini cannot hold two records in one file and need
per_record=True. text writes :func:frame_summary of a frame, a
line for a datagram, and ignores the record: many lines to a file, or one
file a line.
Constructing a writer opens nothing: the file is opened by the first
write. Not safe to share between threads.
:param target: a path or a binary stream; with per_record, a file-name
pattern such as "cap/{timestamp}_{xid}.json".
:param format: one of :data:OUTPUT_FORMATS. None takes it from the
ending of target's name.
:param per_record: write one file per record, named by the pattern.
:param append: add to an existing file. Not for pcap or pcapng.
:param fields: the pattern fields the caller will supply in names, on
top of timestamp, index and format.
:param max_files: the most files a per_record writer creates. A field
value may come from the network, so a peer would otherwise choose how
many files land on the disk.
:raises UnsupportedFormatError: no such format, or none can be told.
:raises MissingExtraError: the format's extra is not installed.
:raises ValueError: a combination that cannot work, or a bad pattern.
Source code in src/pktcap/_output.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
format
property
The name of the format being written.
write(item, record=None, *, names=None)
Write one datagram or frame, or the record made of it.
:param item: what was captured: a :class:CapturedDatagram or a
:class:DissectedFrame. A capture format writes it; a record
format reads its time for {timestamp}.
:param record: plain data for a record format. None writes
:func:datagram_record or :func:frame_record of item.
:param names: with per_record, a value for each of fields.
:raises ValueError: a closed writer, a field with no value, or an item
or record the format must refuse.
:raises TypeError: an item of another type, or a record the format
cannot represent.
:raises OSError: the file cannot be opened or written.
Source code in src/pktcap/_output.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
close()
Close the file if this writer opened it. Harmless when repeated.
Source code in src/pktcap/_output.py
293 294 295 296 297 298 299 300 301 | |
LoadedPlugin
Bases: NamedTuple
One plugin :func:load_plugins loaded.
:ivar name: the item as written.
:ivar source: where the list came from: "argument" for the one
:func:load_plugins was given, or whatever the command line names
(a variable, a settings file's path); "always" for a plugin the
caller's code loads whatever the user listed.
:ivar selectors: the selectors its hook registered a dissector under.
:ivar layers: the names of the layers its hook declared.
ReplayResult
Bases: NamedTuple
What :func:replay_to did.
:ivar sent: datagrams sent.
:ivar partial: datagrams not sent because they were marked fragmented
or truncated: their payload is not the whole message.
TCPReassembler(*, max_streams=1024, max_buffered=16777216, idle_timeout=300.0)
Bases: Engine
Puts TCP streams back together from dissected frames.
Feed it every frame of a capture, in capture order. It raises nothing for a frame and keeps its state within the bounds below. Not safe to share between threads.
:param max_streams: the most connections tracked; one more forgets the least recently active, whose held octets are dropped. :param max_buffered: the most octets held out of order over all connections, each held piece charged its length plus 64. Past it, the direction that has waited longest gives up its holes and delivers. :param idle_timeout: seconds of capture time after which a silent connection is forgotten when its addresses are next seen; they then start a new stream. :raises TypeError: an option of the wrong type. :raises ValueError: a limit that is not positive.
Source code in src/pktcap/_streams/_tcp.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
add(frame)
One frame; what it made deliverable, in the order it became so.
A frame can release the other direction's held octets, on an
acknowledgment, before it delivers its own. A frame with no TCP over
IP gives ().
Source code in src/pktcap/_streams/_tcp.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
flush()
The end of the capture: everything still held, each run after a
hole with its missing, connections in order of stream. The
table is empty afterwards. A FIN that was captured still ends its
direction; flushing marks no other end.
Source code in src/pktcap/_streams/_tcp.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |
TCPStreamData
Bases: NamedTuple
Octets of one direction of a TCP connection, in order.
:ivar time: the capture time of the frame that made the octets deliverable.
:ivar source: the sender's (host, port).
:ivar destination: the receiver's (host, port).
:ivar data: the octets; empty only when end is true.
:ivar offset: where data[0] lies in this direction, counted from 0 and
never wrapping; octets given up on count.
:ivar missing: the octets given up on immediately before data; they
are already counted in offset.
:ivar stream: the connection's number, shared by both directions, from 0
in the order connections first appeared.
:ivar end: this direction is over after data. When octets were given
up between the last ones handed out and the end, the end is an item
of its own, with no data and those octets in missing.
TCPStreamStats
Bases: NamedTuple
What a :class:TCPReassembler has met in the segments it was given.
:ivar segments: TCP segments taken, whatever became of them.
:ivar streams: connections numbered so far.
:ivar delivered: octets handed out.
:ivar retransmitted: octets dropped as copies of delivered or held ones,
and octets captured once after their place was given up.
:ivar out_of_order: segments that arrived ahead of a hole.
:ivar missing: octets given up on.
:ivar conflicts: held octets that a later copy disagreed with.
:ivar ignored: segments and acknowledgments set aside: octets before the
start or beyond a FIN, an out-of-sequence RST, a segment, FIN or RST
too far ahead, a SYN not yet confirmed, a SYN-ACK that contradicts the
connection, an acknowledgment beyond everything believed, a segment in
an IP fragment that was not reassembled.
:ivar evicted: connections forgotten at max_streams or for age; their
held octets are dropped. A connection every direction of which had
ended gives up its place first and is never counted.
:ivar pending: connections in the table.
:ivar held: octets held out of order at the moment.
:ivar dropped: octets held when their connection was forgotten.
UDPCapture(endpoints, *, timeout=1.0, max_size=_MAX_SIZE)
Datagrams arriving at sockets the caller bound, read as raw-IP frames.
The twin of :class:LiveCapture on every platform and with no privilege.
The frames have link type 101 and a made-up IP header: the addresses are
the sender's and the receiver's as the socket reports them (the
destination is the address the datagram was sent to when the host says so,
else the address the socket is bound to, which may be a wildcard), the
time is when it was read and interface the arrival interface when the
host reports one. The capture owns the endpoints from construction: it
closes them, once, in :meth:close. Not safe to share between threads.
:param endpoints: bound :class:netimps.UDPEndpoint objects, at most 256.
An endpoint limited to interfaces (UDPEndpoint(interfaces=)) is
asked about every datagram: one it does not admit is counted in
:attr:not_admitted and dropped before a frame is built.
:param timeout: how many seconds :meth:read waits for a datagram.
:param max_size: the largest payload returned; a bigger datagram is
counted in :attr:truncated and dropped, since what a socket returns
of it is only its start.
:raises TypeError: an endpoint that is not a UDPEndpoint, or an option
of the wrong type. The endpoints are then still the caller's.
:raises ValueError: no endpoint, more than 256, an endpoint given twice, a
timeout that is not positive or a max_size outside 1 to 65535.
Source code in src/pktcap/_sources/_udp.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
read()
The next frame, or None when timeout seconds pass without
one. A datagram over max_size is counted and skipped.
:raises ValueError: the capture is closed. :raises OSError: a socket failed.
Source code in src/pktcap/_sources/_udp.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
aread()
async
:meth:read, awaited: one task for each endpoint feeds a bounded
queue, started by the first call, and the next frame is taken from it.
Use one event loop for a capture's life, and :meth:aclose from a
coroutine. Do not mix it with :meth:read.
:raises ValueError: the capture is closed. :raises OSError: a socket failed.
Source code in src/pktcap/_sources/_udp.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
close()
Close every endpoint. Final, and harmless when repeated.
From a coroutine use :meth:aclose: this waits for netimps' reader
thread, if the asynchronous read started one.
Source code in src/pktcap/_sources/_udp.py
288 289 290 291 292 293 294 295 296 297 298 299 300 | |
aclose()
async
:meth:close, awaited: the readers are cancelled and have left, and
the endpoints are closed, when it returns. Harmless when repeated.
Source code in src/pktcap/_sources/_udp.py
302 303 304 305 306 307 308 309 310 311 312 313 314 315 | |
PcapngWriter(target)
Bases: _CaptureFileWriter
Writes a pcapng file that tcpdump and Wireshark read.
One section, little-endian, microsecond timestamps, one interface description for each link type in the order they are first written, and an enhanced packet block for each frame or datagram. Unlike pcap, one file holds frames of any mix of link types, so any capture can be written back. No option is written: nothing about the host, the tool or the interfaces beyond their link types.
The lifecycle is :class:PcapWriter's: nothing is opened until the first
write, each block is flushed, a stream stays the caller's.
:param target: a path, or a binary stream.
Source code in src/pktcap/_writer.py
329 330 331 | |
PcapWriter(target)
Bases: _CaptureFileWriter
Writes a pcap file that tcpdump and Wireshark read.
Little-endian, microsecond pcap. A pcap file has one link type, fixed
by the first thing written: raw IP (101) for a datagram, the frame's own
for a frame. A later write of another link type is refused; a capture of
several link types is written with :class:PcapngWriter.
For a datagram, a v4-mapped IPv6 address is written as IPv4, which is what was on the wire when a dual-stack socket reported it; a datagram with one IPv4 and one IPv6 end is written as IPv6.
Constructing a writer touches nothing: a path is opened (and an existing file replaced), and the file header written, by the first write. A writer that never writes creates no file. Each record is flushed, so a capture can be followed while it grows. Not safe to share between threads.
:param target: a path, or a binary stream. A stream stays the caller's to
close; a path this writer opened is closed by :meth:close.
Source code in src/pktcap/_writer.py
285 286 287 | |
read_frames(source, *, max_frame_size=_DEFAULT_MAX_FRAME_SIZE)
Every packet of a pcap or pcapng capture, in file order.
Nothing is read until the first frame is asked for, and frames are yielded
as they are read, so a live pipe (sys.stdin.buffer) works and a damaged
capture gives every frame before the damage and then raises. An empty
input is an empty capture.
:param source: a path, or a binary stream, which need not be seekable. A
stream stays the caller's to close.
:param max_frame_size: the most octets one captured frame may have. A
record or block that claims more is refused before it is read.
:raises CaptureFormatError: the input is not a capture, is cut short, or
states a length over its limit.
:raises OSError: the path cannot be opened or the stream cannot be read.
:raises ValueError: max_frame_size is not positive.
Source code in src/pktcap/_container.py
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | |
command_hook(command, *, format='json', timeout=10.0, fail_fast=False, names=None, datagrams=False)
A hook that runs a program for each frame it is given.
Pass it to :func:copy_frames as each. The program is found once,
here, as an absolute path: a name with a directory part is that file from
the working directory then, a bare name is looked up on PATH. It is
started with no argument and no shell, in its own process group, with the
record of the frame (:func:frame_record, or :func:datagram_record of
its datagram with datagrams) on standard input in format, a copy of
the environment, PKTCAP_HOOK_FORMAT and one PKTCAP_HOOK_<FIELD>
for each item names(frame) gives. A value passes the same rule as a
field of a file name: no separator, no control character, at most 64
characters. A field name is the caller's code: letters, digits and
underscores.
Past timeout seconds the program and everything it started are killed.
A run that exits with a non-zero status or is killed is a failure: it is
counted in the failures attribute of the returned callable and logged
on pktcap._copy._hook at ERROR with a bounded, escaped tail of the error
output, the first one and then at most one a minute. With fail_fast
the first failure raises :class:CaptureHookError and so ends a copy.
:param command: a program name or path.
:param format: the record format of standard input, one of
:data:RECORD_FORMATS.
:param timeout: seconds one run may take, positive and finite.
:param names: fields for the environment, as for :func:copy_frames.
:param datagrams: hand over the record of the frame's datagram.
:raises ValueError: at the call, a command that does not exist, is not a
file, is not executable, is a batch file on Windows or is empty; a
timeout that is not positive and finite. On a call of the hook, a
field name that is not a plain identifier or is set twice, or a frame
with no datagram when datagrams is set.
:raises TypeError: a command that is not text or a timeout that is
not a number.
:raises UnsupportedFormatError: format is no record format.
:raises MissingExtraError: the format's extra is not installed.
Source code in src/pktcap/_copy/_hook.py
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
copy_frames(frames, writer, *, select=None, datagrams=False, limit=None, names=None, each=None)
Write the frames select accepts to writer, in order.
frames is any iterable of :class:DissectedFrame: a file through
:func:read_dissected, a live source through :func:sniff_frames. The
writer is the caller's: it is neither opened nor closed here.
:param select: a predicate over a frame, such as
compile_capture_filter(text, frame_filter); None accepts all.
:param datagrams: write each frame's UDP datagram (reassembled) and skip
a frame that has none, instead of the frame.
:param limit: stop once this many items are written; the source is not
read past that. A refused item does not count.
:param names: called with each frame select kept, before it is
written; the mapping it returns is the writer's names, the values
of its fields. It is given the frame even when datagrams is
set, and may be called again for the same frame by a hook.
:param each: called with the frame after its item is written, in order,
and not for a frame select dropped, one with no datagram, or one
the writer turned away. An exception from it ends the copy; the item
stays written.
:raises TypeError: writer is not a :class:CaptureWriter, select,
names or each is not callable, limit is not an int,
names gives something that is not a mapping, or frames yields
something that is not a :class:DissectedFrame.
:raises ValueError: limit is below zero.
:raises OSError: the writer could not write. What was written before it
stays written; the counts of the call are lost with the exception, so
read writer.written for them.
Source code in src/pktcap/_copy/_frames.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
read_datagrams(source, *, dissector=None, max_frame_size=262144)
Every UDP datagram in a pcap or pcapng capture, in capture order.
:func:read_dissected through :meth:DissectedFrame.datagram: the view
a UDP protocol library works on. IP fragments are reassembled. Frames
that carry no UDP are not yielded; pass a dissector to read its
:attr:~FrameDissector.stats and tell an empty capture from one of
another link type.
:raises CaptureFormatError: the container is not a capture or is damaged.
Source code in src/pktcap/_dissect.py
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | |
read_dissected(source, *, dissector=None, max_frame_size=262144)
Every frame of a pcap or pcapng capture, dissected, in capture order.
:func:read_frames and :meth:FrameDissector.dissect in one call. Every
frame is yielded, whatever its link type and whatever it carries.
:param source: a path, or a binary stream, which need not be seekable.
:param dissector: the frame dissector to use, for its registry, its
options and its :attr:~FrameDissector.stats; a new
FrameDissector() by default.
:param max_frame_size: as for :func:read_frames.
:raises CaptureFormatError: the container is not a capture or is damaged.
Source code in src/pktcap/_dissect.py
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | |
check_dissector(dissector, samples, *, rounds=2000, seed=0)
Assert that dissector keeps the contract whatever it is given.
Each sample is tried as it is and then rounds times damaged (octets
changed, the tail cut off). For every input the dissector must return a
:class:Dissected whose payload is bytes and not longer than the
input, or raise ValueError. The suite the built-in dissectors pass, for a
protocol library to run on its own.
:raises AssertionError: the first input that broke the contract, as hex, and what came out of it. :raises ValueError: no sample was given.
Source code in src/pktcap/_dissectors/__init__.py
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
default_registry()
The registry a :class:FrameDissector uses when given none.
One object for the process: what is registered into it is seen by every caller that did not pass a registry of its own.
Source code in src/pktcap/_dissectors/__init__.py
218 219 220 221 222 223 224 | |
register_dissector(kind, value, dissector, *, replace=False)
Register dissector in :func:default_registry. See
:meth:DissectorRegistry.register.
Source code in src/pktcap/_dissectors/__init__.py
227 228 229 230 231 232 | |
compile_capture_filter(text, build)
One predicate for a capture-filter expression.
build is called once per clause, here and never per item, and returns
the test for that clause's key and value; a != clause is inverted for
it, and the predicate is true when every clause holds. So a key this
library has never heard of, or a value that does not convert, is one
error when the filter is compiled and not one per packet.
:param text: the expression; None or blank matches everything.
:param build: turns one :class:FilterClause into a test. It should
raise ValueError for an unknown key or a bad value.
:raises CaptureFilterError: the expression is malformed, or build
raised ValueError (which is chained, the clause named).
:raises TypeError: build returned something that cannot be called.
Source code in src/pktcap/_filter.py
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
parse_capture_filter(text)
The clauses of a capture-filter expression, in order.
:param text: the expression. None, empty or blank is no clause at all.
:raises CaptureFilterError: a clause that is not key=value or
key!=value, an or, an expression over 4,096 characters.
:raises TypeError: text is neither str nor None.
Source code in src/pktcap/_filter.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
dumps_record(record, format='json')
One record as text in a named format, ending in a newline.
:param record: a mapping of text keys to plain data: dict, list,
str, int, float, bool, None.
:param format: one of :data:RECORD_FORMATS.
:raises UnsupportedFormatError: no record format of that name.
:raises MissingExtraError: the format's extra is not installed; it is an
ImportError and its message names the extra.
:raises TypeError: record is not a mapping, or holds a value the
format cannot represent.
:raises ValueError: a value the format must refuse, such as a NaN.
Source code in src/pktcap/_formats/__init__.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
has_output_format(name)
Whether the format of that name can be written on this installation.
False for a format whose extra is not installed; the name stays in
:data:OUTPUT_FORMATS either way. An unknown name raises
UnsupportedFormatError.
Source code in src/pktcap/_formats/__init__.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
loads_record(text, format='json')
The one record text holds, as written by :func:dumps_record.
:param text: the content (never a file name) of one record.
:param format: one of :data:RECORD_FORMATS.
:raises UnsupportedFormatError: no record format of that name.
:raises MissingExtraError: the format cannot be read on this installation
(yaml without PyYAML; toml on Python before 3.11 without
tomli); it is an ImportError and its message names the extra.
:raises TypeError: text is not a str.
:raises RecordFormatError: text is not one record in that format. The
message says what is wrong and where and never quotes the text.
Source code in src/pktcap/_formats/__init__.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
frame_filter(clause)
The test of one filter clause against a :class:DissectedFrame.
A comma in the value means "any of". The keys:
src, dst, host
the source, the destination, or either address of the outermost IP
layer: an address or a network (10.0.0.5, 10.0.0.0/8,
2001:db8::/32).
sport, dport, port
the source, the destination, or either port of the UDP or TCP layer.
proto
a layer the frame has, by name (udp, tcp, ipv4, ipv6,
vlan, ethernet, or the name of a registered dissector's layer:
its class name in lower case, without a trailing Layer), or an IP
protocol number (17).
vlan
a VLAN identifier on any tag of the frame.
linktype
the capture's link-type number.
LAYER.FIELD
a field of a built-in layer (ipv4.ttl, udp.destination_port),
compared by the type of its value.
:raises ValueError: a key that is none of these, or a value that is not
what the key takes. :func:compile_capture_filter turns it into a
CaptureFilterError naming the clause.
Source code in src/pktcap/_frame_filter.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
frame_filter_for(registry)
The build of :func:compile_capture_filter for a registry: the
keys of :func:frame_filter, plus LAYER.KEY and LAYER.FIELD of
every layer the registry declares, and a registered key by its bare name
while exactly one layer has it.
:raises TypeError: registry is not a :class:DissectorRegistry.
Source code in src/pktcap/_frame_filter.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
frame_filter_keys(registry=None)
Every filter key that compiles, sorted: the built-in ones, each
LAYER.KEY and LAYER.FIELD of the built-in layers and of the
registry's, and the bare form of a registered key exactly one layer has.
:raises TypeError: registry is neither None nor a registry.
Source code in src/pktcap/_frame_filter.py
160 161 162 163 164 165 166 167 168 169 | |
has_live_capture()
Whether this platform can capture live: whether it has AF_PACKET.
It says nothing about permission: opening the socket still needs
CAP_NET_RAW.
Source code in src/pktcap/_live.py
42 43 44 45 46 47 48 | |
sniff(interface=None, *, stop=None, dissector=None)
UDP datagrams seen on interface, as they arrive. Linux only.
:func:sniff_frames through the datagram view, for a UDP protocol
library; for every packet, use that or iterate a :class:LiveCapture. The
socket is opened when the first datagram is asked for and closed when the
iterator ends or is closed.
:param interface: as for :class:LiveCapture; None is every one.
:param stop: called between packets, and at least once a second on a
quiet interface; returning true ends the iteration.
:param dissector: the frame dissector to use; a new FrameDissector()
by default.
:raises LiveCaptureError: when the first datagram is asked for, where the
platform cannot capture; ask :func:has_live_capture beforehand.
:raises PermissionError: at the same point, without CAP_NET_RAW.
Source code in src/pktcap/_live.py
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | |
sniff_frames(interface=None, *, stop=None, dissector=None)
Every frame seen on interface, dissected, as they arrive. Linux only.
:class:LiveCapture and a :class:FrameDissector in one call: what
:func:read_dissected is for a file. The socket is opened when the first
frame is asked for and closed when the iterator ends or is closed.
:param interface: as for :class:LiveCapture; None is every one.
:param stop: called between packets, and at least once a second on a
quiet interface; returning true ends the iteration.
:param dissector: the frame dissector to use; a new FrameDissector()
by default.
:raises LiveCaptureError: when the first frame is asked for, where the
platform cannot capture; ask :func:has_live_capture beforehand.
:raises PermissionError: at the same point, without CAP_NET_RAW.
:raises TypeError: stop is not callable or dissector is not a
:class:FrameDissector, at the call.
Source code in src/pktcap/_live.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
load_plugins(registry, plugins=None, *, always=())
Import the plugins a list names and call each one's hook with registry.
The list is plugins and nothing else: this reads no variable and no
file. An item is a dotted Python name: a module with a
pktcap_plugin(registry) function, or MODULE.CALLABLE for any
callable taking the registry. Items are separated by , ; : or
white space; none alone is the empty list. Importing a module runs its
code, so a program passes a list only from a place it trusts.
:param registry: where the hooks register. There is no default one.
:param plugins: None (no plugin), one text, or an iterable of texts.
:param always: items a caller's code loads whatever the user listed, as
dotted names written as for plugins. They load first, with the
source "always". An item of plugins that resolves to the same
hook callable is skipped, not loaded twice and not an error; a
different callable that claims a selector already taken is the plugin
error.
:returns: one :class:LoadedPlugin per item loaded, in order.
:raises CapturePluginError: an item cannot be loaded. The registry is left
as it was before the call; modules already imported stay imported.
:raises ValueError: a malformed item of plugins or always.
:raises TypeError: registry is not a :class:DissectorRegistry, or a
list is not text.
Source code in src/pktcap/_plugins/_load.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
datagram_record(datagram)
The record of a datagram for a caller with no protocol to decode it:
time, source, destination, length and payload as hex,
plus fragmented or truncated when the datagram is partial.
source and destination are host:port text, an IPv6 host in
brackets. time is the number of seconds the capture states.
Source code in src/pktcap/_records.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
frame_record(frame)
The record of a dissected frame: time, linktype, length
(of the captured frame), layers and payload.
layers is a list, outermost first, of one mapping per layer: layer
is its name (ethernet, ipv4, udp: the class name without
Layer, in lower case) and the rest are its fields, octets as hex.
payload is the hex of what no dissector read. interface,
error and reassembled appear only when they say something.
Source code in src/pktcap/_records.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
replay(source, deliver, *, speed=1.0, max_delay=5.0, limit=None)
replay(
source: CaptureSource,
deliver: Callable[[CapturedDatagram], object],
*,
speed: Optional[float] = ...,
max_delay: float = ...,
limit: Optional[int] = ...
) -> int
replay(
source: Iterable[_T],
deliver: Callable[[_T], object],
*,
speed: Optional[float] = ...,
max_delay: float = ...,
limit: Optional[int] = ...
) -> int
Hand each item of a capture to deliver, in order and in time.
Blocks, sleeping between items as :func:replay_schedule says. What
deliver does with an item is the caller's: a protocol library that
knows a reply goes to another port builds a faithful replay on this, and
frames that are not UDP are replayed by passing read_frames(path) or
read_dissected(path) as the source. An exception from deliver ends
the replay and propagates.
:param deliver: called with each item, partial datagrams included; what it returns is ignored. :returns: how many items were delivered.
Source code in src/pktcap/_replay.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | |
replay_schedule(source, *, speed=1.0, max_delay=5.0, limit=None)
replay_schedule(
source: CaptureSource,
*,
speed: Optional[float] = ...,
max_delay: float = ...,
limit: Optional[int] = ...
) -> Iterator[Tuple[float, CapturedDatagram]]
replay_schedule(
source: Iterable[_T],
*,
speed: Optional[float] = ...,
max_delay: float = ...,
limit: Optional[int] = ...
) -> Iterator[Tuple[float, _T]]
(delay, item) for each item of a capture, in order.
delay is how many seconds to wait before that item: the time since the
one before it in the capture, divided by speed and never more than
max_delay. This reads no clock and waits for nothing, so an event loop
can drive it with its own sleep.
:param source: a path or binary stream of a pcap or pcapng capture, which
is read as UDP datagrams; or an iterable of anything with a time
in seconds, such as read_frames(path) for every frame or a
generator that selects the datagrams to replay.
:param speed: 1.0 keeps the recorded timing, 2.0 halves every
wait; None makes every delay zero.
:param max_delay: the longest single wait, in seconds. The capture
controls its timestamps: a jump of a year must not hang a replay. A
step backwards in time is a wait of zero.
:param limit: stop after this many items.
:raises ValueError: a speed that is not positive, a negative
max_delay or limit.
:raises CaptureFormatError: while iterating, for a damaged capture.
Source code in src/pktcap/_replay.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
replay_to(source, dst, port, *, endpoint=None, speed=1.0, max_delay=5.0, limit=None)
Send the payload of each UDP datagram of a capture to (dst, port).
Blocks. Every datagram goes to the one destination named here; the
addresses recorded in the capture are never sent to. A datagram marked
fragmented or truncated is counted and not sent.
:param source: a capture, or an iterable of :class:CapturedDatagram.
:param dst: where to send: an address or a host name, looked up once.
:param port: the UDP port there, 1 to 65535.
:param endpoint: a :class:netimps.UDPEndpoint to send through, left
open. By default a socket is bound for dst's address family with
netimps.bind's defaults (any free port, no broadcast) and closed
afterwards: pass an endpoint to choose the source port or interface,
or to send to a broadcast address.
:param speed: as for :func:replay_schedule; the recorded timing by
default, so a replay sends no faster than the capture did.
:raises OSError: dst does not resolve, or a send fails; a payload
longer than the destination's family allows is one of these.
:raises ValueError: a port outside 1-65535, or a bad option.
:raises TypeError: an item of source is not a datagram.
Source code in src/pktcap/_replay.py
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | |
read_tcp_streams(source, *, reassembler=None, dissector=None, max_frame_size=262144)
The TCP streams of a pcap or pcapng capture, in the order they become deliverable.
:func:read_dissected through :meth:TCPReassembler.add, then
:meth:TCPReassembler.flush when the capture ends. Nothing is read
before the first item is asked for, and a caller that stops early leaves
the rest unread. A damaged container gives every item the frames before
the damage made deliverable and what was still held, then raises.
:param source: a path, or a binary stream, which need not be seekable.
:param reassembler: the reassembler to use, for its options and its
:attr:~TCPReassembler.stats; a new TCPReassembler() by default.
:param dissector: the frame dissector to use, as for
:func:read_dissected.
:param max_frame_size: as for :func:read_frames.
:raises TypeError: a reassembler or dissector of the wrong type,
before anything is read.
:raises CaptureFormatError: the container is not a capture or is damaged.
Source code in src/pktcap/_streams/_read.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
asniff_udp(endpoints, *, dissector=None)
:func:sniff_udp for an event loop, on UDPEndpoint.arecv.
One task for each endpoint feeds one bounded queue. When the iterator
ends, fails, is cancelled or its aclose() is awaited, every task has
been cancelled and every socket closed; aclose() is what a loop that
stops early (break) awaits. Python imports asyncio only when the
first frame is asked for.
:raises TypeError: dissector is not a :class:FrameDissector, or an
item of endpoints is not a :class:netimps.UDPEndpoint.
:raises ValueError: no endpoint, or more than 256.
:raises OSError: a socket failed, while iterating.
Source code in src/pktcap/_sources/_sniff.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
sniff_udp(endpoints, *, stop=None, dissector=None)
Every datagram that reaches the endpoints, as a dissected raw-IP frame.
:class:UDPCapture and a :class:FrameDissector in one call, as
:func:sniff_frames is for an interface. The endpoints are the iterator's
from the call: it closes them when it ends, fails, is closed or is
dropped. stop() is called between datagrams, and at least once a
second on a quiet socket; returning true ends the iteration.
:raises TypeError: stop is not callable, dissector is not a
:class:FrameDissector, or an item of endpoints is not a
:class:netimps.UDPEndpoint; the endpoints are then still the caller's.
:raises ValueError: no endpoint, or more than 256.
:raises OSError: a socket failed, while iterating.
Source code in src/pktcap/_sources/_sniff.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
frame_summary(frame)
One line for a frame: its time (UTC), its two socket addresses when it
has them, the name of its innermost layer and that layer's summary().
A layer with no summary method, or whose summary() raises or gives
something that is not text, is described by its name; the failure is
logged once for the layer's class. Everything is escaped to printable
ASCII on one line and cut at 512 characters (... ends a cut line).
:raises TypeError: frame is not a :class:DissectedFrame.
Source code in src/pktcap/_summary.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | |
datagram_frame(datagram, *, interface=None, ident=0)
A datagram seen at a socket as the raw-IP frame (link type 101) a dissector reads: the octets the writers put in a capture for it.
The IP header is made up, as it is in a capture file: version, TTL 64, no
fragmentation, ident as the identification, valid checksums. A v4-mapped
pair of addresses is an IPv4 packet, a pair of one IPv4 and one IPv6 address
an IPv6 packet, and a zone is not on the wire. Dissected, the frame gives
datagram back with those hosts.
:param interface: the number of the interface it arrived on, or None.
:param ident: the IPv4 identification, kept to 16 bits.
:raises ValueError: a host that is not an address, a port outside 0-65535,
a payload over 65,507 octets (IPv4) or 65,527 (IPv6), a time out of
range, or an interface outside 0 to 2**32 - 1.
:raises TypeError: an argument of the wrong type.
Source code in src/pktcap/_writer.py
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |