Backend
Backend Documentation
Table of Contents
- Overview
- Features
- Requirements
- Usage
- Output Structure
- HTTP Service Mode
- Crypt tab / 802.11 (Wi-Fi) decryption
- Searchable Attributes
- Notes
- License
- Author
Overview
PacketSnitch is a Python tool for extracting payloads and rich metadata from network packet capture (.pcap) files. It generates testcases for fuzzing, protocol analysis, and research by saving raw packet data and detailed information about each packet, including protocol, entropy, geoip, banners, and more. The tool optionally performs active reconnaissance to enrich output with server banners, SSL certificate info, and web page titles.
In the desktop app, this parser is wrapped by an Electron bridge that can either spawn the backend per capture run or launch snitch.py in HTTP service mode and stream incremental results to the renderer.
Features
- Extracts TCP, UDP, ICMP, ICMPv6, and SCTP payloads from
.pcapfiles (over IPv4 and IPv6) and saves them as binary testcase files. - Generates JSON info files for each testcase, containing:
- Packet metadata (timestamps, MAC/IP addresses, ports, flags, checksums)
- MIME type and magic description
- Shannon entropy and character statistics
- GeoIP lookup for source/destination IPs
- Port descriptions (ICANN database)
- MAC vendor lookup
- Protocol-specific fields for link/WAN (ARP/RARP/PPP families, IEEE 802.11 / Wi-Fi), network (IPv4 and IPv6, ICMP/ICMPv6/IGMP), transport (TCP/UDP/SCTP), and application protocols including DNS, HTTP/HTTP2, SNMP, DHCP, DHCPv6, NTP, SIP, FTP, SMTP, POP3, IMAP, Telnet, IRC, SMB, MQTT, RTSP, TFTP, BGP, XMPP, LDAP, MySQL, PostgreSQL, NNTP, MTP/MMS, RADIUS, WebSocket, NFS, Kerberos, SSH, SMPP, Soulseek, and BitTorrent
- Active recon: server banners, SSL certificate info, web page titles (optional)
- IEEE 802.11 (Wi-Fi) link-layer metadata and decryption — the
wireless_80211.pydecoder surfaces SSID, BSSID, frame type/subtype, channel, cipher, RSN/IE info, and RadioTap signal/noise/rate on every 802.11 frame, and decrypts WEP / WPA-PSK (AES-CCMP) / pre-computed PMK payloads when the renderer supplies matching keys via the--wifi-keys-fileflag. See the Crypt tab / 802.11 (Wi-Fi) decryption section below for the full contract.
- Consolidates all testcase info into
hosts.json. - Supports incremental chunk snapshots (
hosts-<N>.json) for progressive frontend loading. - Supports an HTTP service mode used by the Electron bridge for status/stats,
ping,version,process, and control requests. - Supports filtering by source/destination port.
- Handles compressed payloads (gzip/zlib).
- Verbose/debug output modes.
Requirements
- Python 3.7+
- Dependencies:
- scapy
- numpy
- requests
- pyyaml
- python-magic
- chardet
- geoip2
- beautifulsoup4
- scipy
- Databases:
- GeoIP database (MaxMind
.mmdb) - MAC vendor CSV
- ICANN port description CSV
- GeoIP database (MaxMind
Usage
python3 snitch.py traffic.pcap -o output_dir [-s SRC_PORT] [-d DST_PORT] [-T TIMEOUT] [-a] [-c conf.yaml] [--host-chunk-size N] [--worker-threads N] [--server --server-host HOST --server-port PORT] [-v]
python3 snitch.py --version
Arguments
| Argument | Description |
|---|---|
traffic.pcap |
Path to the .pcap file to parse. |
-o, --output |
Output directory for testcases (default: testcases) |
-s, --source-port |
Only generate testcases from this source port. |
-d, --dest-port |
Only generate testcases for this destination port. |
-T, --timeout |
Timeout for network requests (default: 3 seconds) |
-a, --active-recon |
Perform active recon (banners, SSL, titles) |
-c, --conf |
Path to YAML config file (default: conf.yaml) |
--version |
Print backend version and exit |
--use-tor-check |
Enable Tor exit-node enrichment (default: on) |
--no-tor-check |
Disable Tor exit-node enrichment |
--host-chunk-size |
Packet count per incremental host snapshot |
--worker-threads |
Backend parser worker thread count |
--server |
Run in HTTP service mode |
--server-host |
Bind host for HTTP service mode (default: 127.0.0.1) |
--server-port |
Bind port for HTTP service mode (default: 9020) |
--wifi-keys-file |
Path to a JSON file containing 802.11 decryption keys passed in by the renderer (Crypt → Wireless sub-tab). Format: [{"ssid": "...", "bssid": "...", "psk": "passphrase", "pmkHex": "32-byte-hex", "wepKeyHex": "5/13/16-byte-hex"}, ...]. See Crypt tab / 802.11 (Wi-Fi) decryption below for the contract. |
-v, --verbose |
Increase verbosity (repeat for more detail) |
Example
python3 snitch.py traffic.pcap -o output_dir -T 5 -a -v
Output Structure
output_dir/<dest_port>/pcap.data_packet.<index>.dat: Raw payloadsoutput_dir/<dest_port>/pcap.info_packet.<index>.json: Metadata for each testcasehosts.json: Consolidated info for all testcaseshosts-<N>.json: Progressive chunk snapshots emitted during long capture processing for the desktop bridge/UI
HTTP Service Mode
When the Electron bridge has access to the Python backend, it can request long-lived HTTP service mode instead of spawning a fresh process for each capture.
Current bridge-facing endpoints:
GET /andGET /status: backend runtime status/statistics payload (uptime, runtime config, active job metadata)GET /ping: readiness probe used before capture work is submittedGET /version: reports backend service/app version metadataPOST /process: parse a PCAP and emit either accumulated JSON or NDJSON progress eventsPOST /control: control actions such as stop-processing, shutdown, and runtime config updates (set-runtime-config/set-config/configure)
Lookup/enrichment endpoints used by frontend tooling (including Conv Analyze Subnet):
GET /geoip?ip=<addr>&side=src|dst: GeoIP lookupGET /whois?ip=<addr>: WHOIS lookupGET /ipsum?ip=<addr>: IP reputation lookupGET /tor?ip=<addr>: Tor exit-node presence lookupGET /shodan?ip=<addr>: Shodan InternetDB lookup
Important behavior:
- Progress events may include filesystem snapshot paths or full in-memory capture payloads.
- NDJSON mode is used for incremental progress streaming.
- If service mode is unavailable, the Electron bridge falls back to legacy spawn mode.
Crypt tab / 802.11 (Wi-Fi) decryption
The backend ships an end-to-end 802.11 frame decoder and decryptor in src/backend/decoders/wireless_80211.py (with the WPA2 4-way PTK derivation in src/backend/decoders/wpa2_ptk.py). The frontend surfaces it as the Crypt → Wireless sub-tab; the backend contract is documented here so both halves of the round-trip stay in sync.
Decoder responsibilities
- Frame recognition: auto-detects Dot11 / RadioTap frames, walks management (Beacon, Probe Req/Resp, Auth, Assoc Req/Resp, Reassoc, Disassoc, Deauth, Action), control (RTS, CTS, ACK, Block Ack, PS-Poll, CF-End), and data sub-types. A4 / QoS / ToDS / FromDS layout is resolved to recover BSSID, source MAC (SA), destination MAC (DA), and station MAC.
- Metadata: every 802.11 frame populates the
link.proto = "IEEE 802.11"field plus aWirelesssub-section (see the 802.11 fields table below). - Decryption candidates: the decoder recognises
Dot11CCMP,Dot11TKIP,Dot11WEP, and the genericDot11Encryptedlayers and tries each path in turn with the active Wi-Fi key set.
Key delivery (--wifi-keys-file)
Keys are pushed in by the renderer’s setBackendWifiKeys IPC, which stages them on disk in testcaseOutputDir/wifi-keys-<jobId>.json (a sibling of the per-job jobOutputDir so the spawn-path fs.rmSync(jobOutputDir) cleanup cannot drop the file mid-run). The file is removed in the bridge’s backendProc.on('close', ...) handler. The CLI / spawn argv is:
python3 snitch.py traffic.pcap -o output_dir --wifi-keys-file <path>
The file is a JSON array; each element is one candidate key. At least one of psk, pmkHex, or wepKeyHex must be set; ssid and bssid are optional filters and can be omitted for “try this key against every frame”:
[
{
"ssid": "Coherer",
"bssid": "00:0c:41:82:b2:55",
"psk": "Induction"
},
{
"bssid": "c0:4a:00:80:76:e4",
"wepKeyHex": "A48153B4CF"
},
{
"ssid": "Lab-5G",
"bssid": "aa:bb:cc:dd:ee:ff",
"pmkHex": "a288fcf0caaacda9a9f58633ff35e8992a01d9c10ba5e02efdf8cb5d730ce7bc"
}
]
| Field | Type | Description |
|---|---|---|
ssid |
string | Optional. Restricts the key to frames whose SSID matches (case-insensitive). |
bssid |
string | Optional. Restricts the key to frames whose BSSID matches (colon-formatted or unspaced 12 hex). |
psk |
string | WPA / WPA2 / WPA3 passphrase. Combined with ssid via PBKDF2-HMAC-SHA1 (4096 iterations) to derive the PMK. |
pmkHex |
string | Pre-computed Pairwise Master Key as 32-byte (64 hex char) hex. Skips the PBKDF2 step. |
wepKeyHex |
string | Hex WEP key, 10 / 26 / 32 chars (5 / 13 / 16 bytes = WEP-40 / WEP-104 / WEP-128). |
WPA2 / AES-CCMP decryption
- The backend scans every EAPOL-Key frame once via
populateWifiHandshakeCacheand buckets the captured (ANonce, SNonce) pairs by(BSSID, client MAC). ToDS / FromDS / WDS addressing is resolved per IEEE 802.11 §9.3.2.1. - The cache is then queried with the data frame’s
(BSSID, client MAC)tuple. When both nonces are present and apsk/pmkHexentry exists for the BSSID,populatePtkForBssidderives the 64-byte PTK per IEEE 802.11i §8.5.1.1 (PRF-384 over the six-field canonical inputMin(AA,SA) || Max(AA,SA) || Min(ANonce,SNonce) || Max(ANonce,SNonce) || BSSID || ANonce) and stores the TK portion. - The CCMP frame is then decrypted using the same per-block AES-CCM primitive that airdecap-ng uses (manual RFC 3610 implementation so the output is byte-compatible with Wireshark). AAD layout, PN endianness, and CTR counter construction all match the airdecap-ng / libnl80211 reference.
- When a frame decrypts successfully, the inner LLC/SNAP+IP packet is spliced back into the regular packet loop and re-decoded as if it had been a normal Ethernet/IP frame. The host loop’s
link.protofor that packet becomes"IEEE 802.11", the source/destination MAC addresses are added tolink.src.mac.addr/link.dst.mac.addr, the wireless metadata is included underWireless, andwifi.decrypt.ok = truepluswifi.decrypt.algorithm = "CCMP"are set. Link-layer protocol names (WIFI,IEEE 802.11, …) are never prepended topacket.decoded_protocolsso the renderer’s App Protocol column always reports the real application-layer protocol of the decrypted payload.
WEP decryption
_wepDecrypt(weKey, wepBody)takes the raw WEP body (3-byte IV + 1-byte KeyID + ciphertext + 4-byte ICV) and RC4-decrypts it. The decryptor pullsARC4fromcryptography.hazmat.decrepit.ciphers.algorithms(with a fallback to the legacycryptography.hazmat.primitives.ciphers.algorithmspath so the code keeps working oncryptography < 43).- The WEP ICV (CRC-32 of the plaintext, little-endian) is verified leniently — real WEP pcaps in the wild often have a corrupt or zeroed ICV, so the ICV is reported (
icv_ok) but not used to gate the verdict. The “ok” verdict instead comes from_wepPlaintextLooksValid, which requires the plaintext to look like an 802.2 LLC / SNAP header (DSAP=0xAA SSAP=0xAA Control=0x03, I/G and C/R bits stripped) plus an IANA-assigned EtherType, or a raw Ethernet-II header with a known EtherType. A wrong key therefore never produces a false positive. - When scapy’s
Dot11WEPlayer is not present but the FC protected bit is set, the WEP body is sliced out of the raw frame using the 802.11 MAC header length (24 bytes for non-QoS, 26 for QoS, 30 for 4-address ToDS+FromDS frames).
Decryption status attributes
Every 802.11 frame that was a decryption candidate (CCMP / TKIP / WEP / generic Dot11Encrypted) carries the following attributes in packet.info so the renderer can render a per-frame decrypt status pill:
| Attribute | Type | Description |
|---|---|---|
wifi.decrypt.ok |
boolean | true when the decryptor produced a valid plaintext for this frame. |
wifi.decrypt.algorithm |
string | CCMP, TKIP, or WEP for the path that succeeded; "None" for a frame that could not be decrypted with the supplied keys. |
wifi.decrypt.error |
string | Optional. Error message for the failed decrypt path (e.g. "MIC mismatch", "key length invalid"). |
End-to-end decrypt result shape
decryptWifiPayload returns the same dict shape for every algorithm so the renderer’s crypt-wifi-decrypt-preview / Send to Conv path can be algorithm-agnostic:
{
"ok": true,
"plaintextHex": "aaaa03000000080045000054...",
"algorithm": "CCMP",
"ssid": "Coherer",
"bssid": "00:0c:41:82:b2:55"
}
Sample captures
The repo ships two ready-to-use Wi-Fi captures for end-to-end smoke testing:
| File | BSSID | Algorithm | Key |
|---|---|---|---|
samples/pcaps/wifi-Coherer-Induction.pcap |
00:0c:41:82:b2:55 |
CCMP | SSID Coherer, PSK Induction (PMK a288fcf0caaacda9a9f58633ff35e8992a01d9c10ba5e02efdf8cb5d730ce7bc) |
samples/pcaps/wep-A4-81-53-B4-CF.pcap |
c0:4a:00:80:76:e4 |
WEP | WEP-40 key A4:81:53:B4:CF |
Searchable Attributes
Each testcase JSON contains the following dot-notation keys as leaf nodes, which can be used to search, filter, or query testcase data in the frontend or via hosts.json. The filter syntax uses key:value notation with optional comparison operators (==, !=, >, >=, <, <=) and boolean combinators (&&, ||) with parentheses for grouping.
Core Packet Fields
| Attribute | Type | Description |
|---|---|---|
packet.timestamp |
string | Timestamp of the captured packet (YYYY-MM-DD HH:MM:SS.ffffff) |
packet.hex |
string | Full raw packet bytes as a hex string |
packet.proto |
string | Transport protocol key (e.g. tcp, udp, icmp) |
Ethernet Fields
| Attribute | Type | Description |
|---|---|---|
ether.src.mac.addr |
string | Source MAC address |
ether.dst.mac.addr |
string | Destination MAC address |
ether.src.mac.vendor |
string | Vendor name for the source MAC address |
ether.dst.mac.vendor |
string | Vendor name for the destination MAC address |
Note: Ethernet frame attributes (
ether.*) are only populated when both source and destination IPs resolve to the local network.
IP Fields
| Attribute | Type | Description |
|---|---|---|
ip.src.addr |
string | Source IP address |
ip.dst.addr |
string | Destination IP address |
ip.chksum |
string | IP header checksum (hex) |
ip.len |
integer | IP layer length in bytes |
ip.src.class |
string | Network class of the source IP (e.g. Localnet, A, B, C) |
ip.dst.class |
string | Network class of the destination IP |
IPv6 Address Fields
IPv6 packets share the same
IPJSON section as IPv4 packets. The parser detects IPv6 viapacket.haslayer("IPv6")and writes the sameip.src.addr/ip.dst.addr/ip.chksum/ip.lenkeys, distinguishing the family withnetwork.proto. There is no separateip6.*prefix at the JSON level — theip.*keys are reused so a filter expression such asip.src.addr:2001:db8::1works for both families.
| Attribute | Type | Description |
|---|---|---|
ip.src.addr |
string | Source IP address (IPv4 or IPv6). For IPv6, the RFC 5952 compressed form is used. |
ip.dst.addr |
string | Destination IP address (IPv4 or IPv6) |
ip.chksum |
string | Network header checksum (hex). IPv6 has no on-wire checksum for the header itself; the field is 0x0 for plain IPv6 unless the pseudo-header is computed. |
ip.len |
integer | Network layer length in bytes (IPv4 total length or IPv6 payload-length + 40-byte header) |
ip.src.class |
string | Network class of the source IP. IPv4: Localnet, A, B, C, D, E. IPv6: IPv6 (the dedicated classify helper returns that label). |
ip.dst.class |
string | Network class of the destination IP — same convention as ip.src.class. |
network.proto |
string | Network family label: IPv4 or IPv6 — set by getPacketNetworkProtocolLabel(networkLayer). |
network.ip.src.addr |
string | Network-namespaced alias of ip.src.addr |
network.ip.dst.addr |
string | Network-namespaced alias of ip.dst.addr |
network.ip.chksum |
string | Network-namespaced alias of ip.chksum |
network.ip.len |
integer | Network-namespaced alias of ip.len |
TCP Fields
| Attribute | Type | Description |
|---|---|---|
tcp.src.port |
integer | TCP source port number |
tcp.dst.port |
integer | TCP destination port number |
tcp.chksum |
string | TCP checksum (hex) |
tcp.urgptr |
boolean | Whether the TCP urgent pointer is set |
tcp.flags |
string | Active TCP flags (e.g. SYN\|ACK) |
tcp.options |
list | TCP options list |
tcp.len |
integer | TCP header length in bytes |
tcp.proto |
string | Service/protocol name for the destination port |
tcp.desc |
string | ICANN port description for the destination port |
UDP Fields
| Attribute | Type | Description |
|---|---|---|
udp.src.port |
integer | UDP source port number |
udp.dst.port |
integer | UDP destination port number |
udp.chksum |
string | UDP checksum (hex) |
udp.len |
integer | UDP datagram length in bytes |
ICMP Fields
| Attribute | Type | Description |
|---|---|---|
icmp.type |
string | ICMP message type string (e.g. Echo Request, Destination Unreachable) |
icmp.code |
integer | ICMP code value |
icmp.id |
integer | ICMP identifier field |
icmp.seq |
integer | ICMP sequence number |
icmp.chksum |
string | ICMP checksum (hex) |
Wire / Payload Fields
| Attribute | Type | Description |
|---|---|---|
wire.len |
integer | Total wire length of the segment in bytes |
payload.hex |
string | Raw payload as a hex string |
payload.ascii |
string | Raw payload decoded as ASCII (lossy) |
payload.len |
integer | Length of the payload in bytes |
payload.mime |
string | MIME type of the payload (e.g. text/html, application/octet-stream) |
payload.entropy |
float | Shannon entropy of the payload (bits per byte) |
payload.charset |
string | ascii if all bytes are printable ASCII, otherwise binary |
payload.encoding |
string | Detected character encoding (e.g. utf-8, iso-8859-1) |
payload.chars.used |
integer | Number of distinct byte values present in the payload |
payload.decompressed.hex |
string | Decompressed payload as a hex string (only present if payload was compressed) |
payload.decompressed.ascii |
string | Decompressed payload decoded as ASCII (only present if payload was compressed) |
GeoIP / Location Fields
| Attribute | Type | Description |
|---|---|---|
loc.src.country |
string | Country of the source IP (GeoIP lookup) |
loc.src.city |
string | City of the source IP (GeoIP lookup) |
loc.src.postal |
string | Postal code of the source IP (GeoIP lookup) |
loc.src.tz |
string | Time zone of the source IP — alias for loc.src.timezone |
loc.src.timezone |
string | Time zone of the source IP (GeoIP lookup) |
loc.dst.country |
string | Country of the destination IP (GeoIP lookup) |
loc.dst.city |
string | City of the destination IP (GeoIP lookup) |
loc.dst.postal |
string | Postal code of the destination IP (GeoIP lookup) |
loc.dst.tz |
string | Time zone of the destination IP — alias for loc.dst.timezone |
loc.dst.timezone |
string | Time zone of the destination IP (GeoIP lookup) |
Note: GeoIP attributes (
loc.*) are only populated for non-private/routable IP addresses.
Active Recon Fields
| Attribute | Type | Description |
|---|---|---|
host.banner |
string | Server banner retrieved via active recon (requires -a) |
Note:
host.banneris only populated when the-a(active recon) flag is used.
DNS Fields (UDP/TCP port 53)
| Attribute | Type | Description |
|---|---|---|
dns.id |
integer | DNS transaction ID |
dns.qr |
boolean | true if this is a response, false if a query |
dns.qname |
string | First queried domain name |
dns.qnames |
list | All queried domain names |
dns.aname |
string | First answer name from DNS response |
dns.anames |
list | All answer names from DNS response |
dns.aip |
string | First resolved IP address from DNS response |
dns.aips |
list | All resolved IP addresses from DNS response |
dns.qdcount |
integer | Number of questions in the DNS message |
dns.ancount |
integer | Number of answer records in the DNS message |
dns.hostnames |
object | Resolved hostnames from reverse DNS lookup |
HTTP Fields (TCP port 80/443/8080/8443)
| Attribute | Type | Description |
|---|---|---|
http.type |
string | Message type: Request or Response |
http.method |
string | HTTP request method (e.g. GET, POST) — requests only |
http.url |
string | Request URL path — requests only |
http.version |
string | HTTP version (e.g. HTTP/1.1) |
http.host |
string | Host header value — requests only |
http.user_agent |
string | User-Agent header value — requests only |
http.content_type |
string | Content-Type header value |
http.content_length |
string | Content-Length header value |
http.referer |
string | Referer header value — requests only |
http.accept |
string | Accept header value — requests only |
http.accept_encoding |
string | Accept-Encoding header value — requests only |
http.connection |
string | Connection header value |
http.status_code |
string | HTTP status code (e.g. 200) — responses only |
http.status_msg |
string | HTTP status message (e.g. OK) — responses only |
http.server |
string | Server header value — responses only |
http.content_encoding |
string | Content-Encoding header value — responses only |
http.transfer_encoding |
string | Transfer-Encoding header value — responses only |
http.location |
string | Location redirect header — responses only |
SNMP Fields (UDP/TCP port 161/162)
| Attribute | Type | Description |
|---|---|---|
snmp.version |
string | SNMP version string (e.g. v1, v2c, v3) |
snmp.community |
string | SNMP community string |
snmp.pdu_type |
string | SNMP PDU type (e.g. GetRequest, GetResponse, Trap) |
DHCP Fields (UDP port 67/68)
| Attribute | Type | Description |
|---|---|---|
dhcp.msg_type |
string | DHCP message type (e.g. DISCOVER, OFFER, REQUEST, ACK) |
dhcp.xid |
string | Transaction ID (hex) |
dhcp.ciaddr |
string | Client IP address |
dhcp.yiaddr |
string | Your (offered) IP address |
dhcp.siaddr |
string | Server IP address |
DHCPv6 Fields (UDP port 546/547)
DHCPv6 is parsed by the same Conv decoder family as DHCPv4 and is exposed in the frontend’s Conv → Decodes sub-tab. The dot-notation keys below are populated by the backend’s
dhcpdecoder when the conversation runs on UDP/546 or UDP/547.
| Attribute | Type | Description |
|---|---|---|
dhcpv6.msg_type |
string | DHCPv6 message type (e.g. SOLICIT, ADVERTISE, REQUEST, REPLY, RENEW, REBIND, INFORMATION-REQUEST, RELEASE, DECLINE, RELAY-FORW, RELAY-REPL) |
dhcpv6.xid |
string | Transaction ID (24-bit, hex) |
dhcpv6.hop_count |
integer | Hop count for relay-agent forwarding |
dhcpv6.linkaddr |
string | Link-address field (relay messages) |
dhcpv6.peeraddr |
string | Peer-address field (relay messages) |
dhcpv6.server_id |
string | Server Identifier option (DUID) when present |
dhcpv6.client_id |
string | Client Identifier option (DUID) when present |
dhcpv6.options |
list | Decoded option list (e.g. OPTION_IA_NA, OPTION_IA_PD, OPTION_ORO, OPTION_SOL_MAX_RT, OPTION_RAPID_COMMIT) |
NTP Fields (UDP port 123)
| Attribute | Type | Description |
|---|---|---|
ntp.leap |
string | Leap indicator status (e.g. no warning, last minute has 61s) |
ntp.version |
integer | NTP version number |
ntp.mode |
string | NTP mode string (e.g. client, server, broadcast) |
ntp.stratum |
integer | Stratum level (0 = unspecified, 1 = primary, 2+ = secondary) |
ntp.ref_id |
string | Reference ID (IP address or 4-character ASCII string) |
SIP Fields (UDP/TCP port 5060/5061)
| Attribute | Type | Description |
|---|---|---|
sip.type |
string | Message type: Request or Response |
sip.method |
string | SIP request method (e.g. INVITE, REGISTER) — requests only |
sip.uri |
string | Request URI — requests only |
sip.from |
string | From header value |
sip.to |
string | To header value |
sip.call_id |
string | Call-ID header value |
sip.status_code |
string | SIP status code (e.g. 200) — responses only |
sip.status_msg |
string | SIP status message (e.g. OK) — responses only |
IEEE 802.11 (Wi-Fi) Fields
The following dot-notation keys are populated for every IEEE 802.11 frame detected in the capture. Decryption-related fields are only present on frames that were decryption candidates (CCMP / TKIP / WEP / generic Dot11Encrypted); see the Crypt tab / 802.11 (Wi-Fi) decryption section above for the algorithm-specific contracts.
| Attribute | Type | Description |
|---|---|---|
link.proto |
string | Set to "IEEE 802.11" for every 802.11 frame (carries link-layer identity on the packet). |
link.src.mac.addr |
string | 802.11 source MAC (SA / TA) |
link.dst.mac.addr |
string | 802.11 destination MAC (DA / RA) |
link.src.mac.vendor |
string | OUI vendor lookup for the source MAC |
link.dst.mac.vendor |
string | OUI vendor lookup for the destination MAC |
wireless.wifi.ssid |
string | SSID recovered from Beacon / Probe Req / Probe Resp (Hidden/N/A when redacted) |
wireless.wifi.bssid |
string | BSSID (AP MAC) for the frame |
wireless.wifi.type |
string | Dot11 type label: Management / Control / Data / Extension |
wireless.wifi.subtype |
string | Dot11 subtype label (e.g. Beacon, QoS Data, Action) |
wireless.wifi.subtype_num |
integer | Numeric subtype nibble (0-15) of the frame |
wireless.wifi.channel |
string | Operating channel as recovered from RadioTap / Dot11 |
wireless.wifi.frequency |
string | Operating frequency (<N> MHz) |
wireless.wifi.cipher |
string | Detected cipher: Open, WEP, TKIP (RC4), CCMP-128 (AES), etc. |
wireless.wifi.crypto |
string | Crypto suite label: Open, WPA, WPA2, WPA3 |
wireless.wifi.signal_dbm |
string | RadioTap signal strength (e.g. -58 dBm) |
wireless.wifi.noise_dbm |
string | RadioTap noise floor (e.g. -95 dBm) |
wireless.wifi.rate_mbps |
string | RadioTap data rate (e.g. 54.0 Mbps) |
wireless.wifi.rsn |
object | Parsed RSN IE (version, group cipher, pairwise ciphers, AKM suites) when present |
wireless.wifi.vendor_ies |
list | Vendor IEs (WPA / Microsoft) when present in the frame |
wifi.decrypt.ok |
boolean | true when the decryptor produced a valid plaintext for this frame |
wifi.decrypt.algorithm |
string | CCMP, TKIP, WEP, or None |
wifi.decrypt.error |
string | Optional error message for the failed decrypt path |
IPv6 Support
The backend parses IPv6 end-to-end. IPv6 packets populate the standard ip.*, tcp.*, udp.*, and icmp.* fields where the transport protocol is the same, and the network section is disambiguated with network.proto. The contract is:
- Network-layer detection —
getPacketNetworkLayer(p)returns the IPv6 layer when present (and falls back to IPv4 when not).network.protois set to"IPv6"for IPv6 packets and stays"IPv4"for IPv4 packets, so the renderer’s App Protocol column produces accurate groupings. - Class label —
getNetclass(ip)returns"IPv6"for IPv6 addresses (no IPv4 class mapping) and the standard IPv4 class labels (Localnet,A,B,C,D,E) for IPv4. The result is written toip.src.class/ip.dst.class. - IPv6 protocol number —
getPacketNetworkProtocolNumber(networkLayer)reads the IPv6nh(next header) field. The numeric value is propagated into theip.proto.numattribute and the wire protocol number is used to drive transport dispatch (TCP=6, UDP=17, ICMPv6=58). - ICMPv6 — the existing ICMP decoder is reused for ICMPv6 because the wire and field names match (
icmp.type,icmp.code,icmp.chksum,icmp.id,icmp.seq). ICMPv6 packets are reachable onip.proto.num == 58and the regularauto-detectheuristic correctly maps them to the ICMP decoder. - Extract path — TCP/UDP/ICMP extraction works over IPv6 because
buildFallbackPacketEntryusesgetPacketNetworkLayer(p)to discover the source/destination. Theip.src.addr/ip.dst.addrkeys are populated with the IPv6 address verbatim (compressed form). - Front-end handoff — the renderer receives the same
ip.*keys for IPv4 and IPv6 packets, so the filter bar, the right-sidebar Datagram Frame, and the List panel all show IPv6 values without any per-protocol switch.network.protois used to detect the family at display time. - Lookup endpoints —
/geoip,/whois,/tor,/shodan, and VirusTotal all accept IPv6 addresses. The response envelope always includes aversionfield (4or6) so the renderer can format the icon and CLI correctly. WHOIS responses for IPv6 reuse thev6prefixCIDR mechanism; Tor exit-list matching strips the optional[…]brackets (_extractTorIpFromAddress). - IPSum — explicitly IPv4-only. The
/ipsumendpoint returns{ "supported": false }for any IPv6 address, and the renderer’s Conv → Analyze Subnet disables the Use analyzed IP IPSum button when the address is IPv6.
IPv6-aware test cases
The IPv6 path is exercised end-to-end by the following backend tests, which build IPv6 packets at runtime via scapy.wrpcap and run them through the parser:
tests/test_backend_json.py::test_packet_loop_decodes_ipv6_tcp_packet— verifies that an IPv6/TCP packet round-trips throughpacketLoopand producesIP.network.proto == "IPv6"withip.src.addr/ip.dst.addrset to the IPv6 addresses.tests/test_backend_json.py::test_backend_fallback_preserves_ipv6_packet_protocol— checks thebuildFallbackPacketEntrypath preserves the IPv6 protocol label when the SUT falls back to the lighter decoder.
Notes
- Active recon (
-a) may take longer and requires network access. - Ensure database files are present and paths are correct in
conf.yaml. - The tool will prompt before overwriting output directories.
- LLM summaries are now handled by the Electron frontend/main-process bridge, not by the Python parser itself.
License
GPL v3
Author
Marshall Whittaker
