neuron-explorer
Steering-first neuron explorer for Qwen3.5-4B: push every MLP neuron, keep every rollout, browse the results
Open source, cross-platform log viewer built with C++23 and Qt6. Handles 10 GB+ files with SIMD-accelerated regex.
Developer toolsC++ · PythonGitHub @64x-lunicornOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
LogSquirl plugin that opens tcpdump and libpcap captures (.pcap, .cap, .dmp) as readable Wireshark-style packet lists, with protocol dissection and stream tracking. Qt6/C++17.
Taken from the repository README
Loading the full README.
Read the full README ↗Packet captures you can actually read.
A LogSquirl plugin that turns
.pcap and .pcapng files into a readable packet list.
Protocol dissection, application-layer detection and stream tracking — rendered as text, so LogSquirl's regex search and highlighters work on it.
Install / Usage / Build / Architecture / Changelog
You have a capture and a log from the same incident, and they live in two different tools. This one renders the capture as text in the viewer you already have the log open in.
| Reads the capture | Shows the story |
|---|---|
pcap and pcapng files. .pcap, .pcapng, .cap, .dmp, also gzip-compressed (.pcap.gz, .pcapng.gz, .cap.gz, as Wireshark opens them, decompressed on the fly), both endiannesses, microsecond and nanosecond timestamps, with a text preamble scan for adb exec-out tcpdump output. pcapng captures from Wireshark or macOS's tcpdump -P may mix interfaces of different link types and timestamp resolutions, and hold several sections. Read packet by packet in the background, so multi-GB captures work and can be cancelled. | Wireshark-style columns. No., Stream, UTC Time (2026-10-09 08:41:12.123456Z, the packet's wall-clock time in UTC), Time (since the first packet), Source, Destination, Protocol, Length (on the wire), Info — TCP flags in bracket notation, sequence and acknowledgement numbers relative to the start of each direction as in Wireshark (the SYN is Seq=0), the window scaled once both SYNs negotiated window scaling, a SYN's options as Wireshark shows them (MSS=1460 SACK_PERM TSval=… TSecr=0 WS=128), the handshake's initial round-trip time on the ACK that completes it ([iRTT=0.012345]), and Wireshark's TCP analysis markers at the start of Info in its words — [TCP Retransmission], [TCP Fast Retransmission], [TCP Dup ACK 7#1], [TCP Out-Of-Order], [TCP Window Full], [TCP ZeroWindow], [TCP Keep-Alive] and more — so highlighters and filters written for Wireshark carry over; a packet cut at the snaplen is marked [cut to N bytes]. |
| Protocol dissection. IPv4, IPv6 with its extension headers, TCP, UDP, ICMP, ICMPv6 and ARP; IP fragments after the first are shown as such. VXLAN, GRE and IP-in-IP tunnels are unwrapped: a tunnelled packet is shown by the packet inside, its stream keyed by the inner addresses and ports, the tunnel named first in Info (`VXLAN VNI 100 | 50000 → 8080 [SYN] Seq=0 Win=64240, GRE |
| Application layers. TLS records, every one in a segment, with the server name (SNI), version and ALPN of a hello, QUIC packets on UDP with their type, version and connection IDs (short headers known from their connection's long headers), HTTP requests with their host and responses with their content type and length, HTTP/2 (cleartext) by its connection preface and the frames that follow, with their types and streams, DNS queries and responses with their transaction id, query type and answers, over UDP and TCP, DHCP messages with their type, transaction id, client MAC, address and host name (), DHCPv6 messages with their type and client DUID, NTP packets with their version, mode and stratum (), NMEA 0183 sentences, SOCKS4/5 handshakes with their destinations and credentials, MQTT 3.1.1 and 5.0 control packets (TCP 1883, or any port behind a CONNECT), every one in a segment, with client id, topic, QoS, packet id, reason code and a payload preview (), SIP requests and responses on any port, over UDP and TCP, with CSeq, Call-ID and their SDP bodies (), RTP and RTCP on the addresses and ports SDP announced (, ), SOME/IP over UDP and TCP on port 30490, on configured ports and wherever its header fits, every message of a datagram or segment, with its IDs, message type and return code (, names from an optional name table), and SOME/IP-SD entries with their endpoint options (, ), DoIP (ISO 13400-2) on UDP and TCP port 13400, every message of a datagram or segment as Wireshark names it, vehicle announcements with their VIN, logical address, EID and GID, routing activation with its source address, type and response code, and diagnostic messages with their addresses and the UDS service, sub-function and negative response code they carry (, ), and SSH on any port by its banner, port 22 as a hint, as Wireshark shows it: the banners (), the key exchange with KEXINIT's algorithm lists shortened (, ), and each direction's packets after its NEWKEYS as , and WebSocket from the HTTP upgrade on, whatever the port, every frame as Wireshark names it, the client's text unmasked for the preview (, , compressed messages ), and SMB2/3 on TCP port 445 and over NetBIOS on 139, every command of a segment, compounded ones too, as Wireshark names it, with the dialects, share, file name, read and write lengths and offsets, FSCTL and find pattern, a failed response with its NT status (, , ), encrypted messages as and SMB1 by its command. A TLS record, an HTTP header section, a DNS-over-TCP message, a SIP message, an MQTT packet on port 1883, a SOME/IP message, a DoIP message, an SSH packet of the key exchange, a WebSocket frame or an SMB message that spans TCP segments is reassembled and described once, on the segment that completes it (), the segments before it as ; segments are put in sequence order, retransmissions and overlaps dropped, within bounded memory. With an , TLS 1.2 and 1.3 sessions are and the HTTP/1.1 and HTTP/2 inside described, HPACK decoded (). |
Packets that are not dissected further are named, not numbered: IP protocols
such as IGMP, GRE, ESP, AH, OSPF, PIM, VRRP, L2TP and SCTP, and EtherTypes such
as LLDP, MPLS, 802.1X (EAPOL), PTP and Wake-on-LAN;
an unknown one keeps its number, IP(200) or ETH(0x1234). Port-based hints
name about a hundred TCP and UDP services: SSH, FTP, SMTP, IMAP, SNMP, Syslog,
TFTP, STUN/TURN, WireGuard, LLMNR, NBNS, DHCPv6, RTSP, LDAP, SMB, RDP, VNC,
Kerberos, MySQL, PostgreSQL, Redis, MongoDB, MQTT, AMQP, Kafka, ADB and more,
each only on the transport it runs over.
Plugins → Browse Plugins… → tcpdump / pcap Viewer → Install. The archive is downloaded, verified against its SHA-256 checksum and loaded — no file copying.
Download the archive for your platform from the releases page and unpack it into LogSquirl's plugin directory:
| Platform | Plugin Directory |
|---|---|
| macOS | ~/Library/Application Support/logsquirl/plugins/io.github.logsquirl.tcpdump/ |
| Linux | ~/.local/share/logsquirl/plugins/io.github.logsquirl.tcpdump/ |
| Windows | %APPDATA%/logsquirl/plugins/io.github.logsquirl.tcpdump/ |
The archive also holds, next to the library, tcpdump_log.json, the
Log Format for the converted packet list, and
tcpdump_highlighter.conf and tcpdump_filter.conf, the highlighter set
and filter group. LogSquirl loads none of them
from there: copy the Log Format into its formats directory and import the
other two, as described below.
See Build, then:
DEST="$HOME/Library/Application Support/logsquirl/plugins/io.github.logsquirl.tcpdump"
mkdir -p "$DEST"
cp build/liblogsquirl_tcpdump.dylib "$DEST/"
cp plugin.json icon.png "$DEST/"
and copy the Log Format into LogSquirl's formats directory.
After installing, restart LogSquirl or re-scan via Plugins → Manage Plugins….
.pcap, .pcapng, .cap, or .dmp file,
or a gzip-compressed one (.pcap.gz, .pcapng.gz, .cap.gz; told by
its content, not its name). A compressed capture is decompressed while
it is read, never as a whole, and the progress bar counts its compressed
bytes; one whose gzip stream is cut off or corrupt shows its packets up
to there and says so in the summary. A gzip bomb is refused: the
conversion fails with a message once a capture decompresses to more
than 64 GiB, or, past its first 256 MiB, to more than 200 times the
compressed bytes read (real captures compress 2 to 20 times). zstd and
xz are not read:
decompress such a file first. The
same dialog opens from Plugins → tcpdump → Open pcap…, and so from
the Command Palette (Ctrl+Shift+P, Cmd+Shift+P on macOS); while a
capture is being read, it only says sosettings.ini and is back after a restart; until you drag it,
the summary is as tall as it needs (up to 30 lines, then it scrolls)
and the Packet panel takes the rest, always keeping room for its tabs and
a few Conversations rowstcp.stream eq N
filter that Wireshark's Follow TCP Stream applies does, select one of
its packet lines and click Filter stream in Regex Lab in the
sidebar, or choose .
LogSquirl's Regex Lab opens
with a pattern that matches that TCP or UDP stream's lines: its number in
the Stream column, its two addresses and its two ports, in either
direction (TCP and UDP streams are numbered each on their own). Apply it,
and the filtered view shows the conversation alone. The button is
disabled, its tooltip saying why, while the tab in front shows no
capture, no packet line is selected, or the selected one has no stream
( or ); the menu entry then shows a notification saying why.
Needs LogSquirl ≥ 26.11Start a live capture with Start live capture… in the Live capture section of the plugin's sidebar tab, or with Plugins → tcpdump → Start live capture… (also in the Command Palette). Either opens a dialog with these fields:
host 10.0.0.1 and tcp port 443, passed to the source as it is (empty captures everything). A line
break, a leading -, unbalanced parentheses or a Wireshark display
filter field such as ip.addr are pointed out below the field and keep
Start disabled; anything else is checked by the capture program, whose
error the sidebar's Live capture section shows[cut to N bytes]-a duration,
packets, filesize). The size is that of the raw capture, in MB of
1024 × 1024 bytes. The packet that reaches a count or a size is the last
one; the time also runs out while no packet comes. No limit by default-b). With N files, the raw capture is split into
files of the size or duration given, and only the newest N are kept;
see Ring buffer below. Off by default/dev/bpf0 cannot be read; dumpcap or tcpdump is denied the
permission), Start stays disabled until Refresh, or another source or
device, lists anew and finds it can. A listing that merely finds no
interfaces leaves Start to an interface typed by hand. While the capture runs, the sidebar's Live capture section shows
its counters and Stop (also Plugins → tcpdump → Stop live
capture). Start live capture… is disabled while a capture file is
read; one live capture runs at a time, and the menu entry offers to stop
the running one firstThe choices started last are remembered in the plugin's settings.ini and
shown again after a restart, and so are each source's own options, for a
source that has any. The plugin never stores a password: one a source's
options ask for (an extcap's password argument) is kept in memory for the
session only. While the capture runs, the section shows what the capture
program writes to stderr, its last 200 lines and how many earlier ones
were dropped (up to 100 of its lines are also in LogSquirl's log, the first
ones unless some were dropped before); if it fails, the
section shows why, with what the source says to do about it.
The Local source captures on this computer's interfaces with
Wireshark's dumpcap if it is installed (preferred: it is on every OS and
writes pcapng), else with tcpdump. Both are looked for on PATH and where
their installers put them: /Applications/Wireshark.app/Contents/MacOS/dumpcap
and /usr/sbin/tcpdump on macOS, /usr/bin/dumpcap and /usr/sbin/tcpdump
on Linux, Program Files\Wireshark\dumpcap.exe on Windows. Without either,
the source says what to install. Its interfaces are those dumpcap -D or
tcpdump -D lists; it captures with dumpcap -i <interface> -s <snaplen> -q -f <filter> -w - or tcpdump -i <interface> -s <snaplen> -U -w - <filter>, the filter passed as one argument.
The plugin never runs sudo and never asks for a password: the capture
program must be allowed to capture as you. When it is not (it says
"permission denied" or "You don't have permission", lists no interfaces,
or, on macOS, /dev/bpf0 cannot be read), the section says what to do on
your OS, and when listing the interfaces already finds it so, the dialog
says it and keeps Start disabled; run the command yourself, once, then
press Refresh:
/dev/bpf*. Install
ChmodBPF from the Wireshark
disk image ("Install ChmodBPF.pkg"): it lets the group access_bpf read
/dev/bpf* and adds you to it. If it is installed but you are not in the
group: sudo dseditgroup -o edit -a "$USER" -t user access_bpf, then log
out and in againCAP_NET_RAW and CAP_NET_ADMIN. With
dumpcap, join the wireshark group (on Debian and Ubuntu first sudo dpkg-reconfigure wireshark-common, answering Yes): sudo usermod -aG wireshark "$USER", then log out and in again; or give the program the
capabilities: sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap
(or /usr/sbin/tcpdump)The Android source captures on an Android device with the device's own
tcpdump, through adb. It finds adb on PATH, in the SDK's
platform-tools below ANDROID_HOME or ANDROID_SDK_ROOT, and where
Android Studio and package managers put it (~/Library/Android/sdk,
~/Android/Sdk, %LOCALAPPDATA%\Android\Sdk, /opt/homebrew/bin,
/usr/lib/android-sdk); without it, the source says where to get the
Platform-Tools.
adb devices -l, with their model. One that is
unauthorized (allow USB debugging in the prompt on the device),
offline (reconnect it, or adb kill-server) or without permissions
(Linux: a udev rule) is listed with what to do, and cannot be chosenany and those of ip -o link on the device. Listing
them also asks the device whether it can capture, and says what to do if
it cannotadb already runs
as root (an emulator image without Google Play, or a userdebug or eng
build, after you ran adb root yourself: LogSquirl never runs it), or
when su -c gives root without a prompt (a rooted device whose su
manager, e.g. Magisk, granted the Shell app). Otherwise it says that a
stock, unrooted device cannot capture this wayPATH, in /system/bin and
/system/xbin (emulators and userdebug builds have it), and in
/data/local/tmp: on another device, push a static tcpdump built for
its CPU there (adb push tcpdump /data/local/tmp/, adb shell chmod 755 /data/local/tmp/tcpdump)It captures with adb -s <serial> exec-out (binary-clean: no terminal, no
CR/LF translation) running tcpdump -i <interface> -s <snaplen> -U -w - <filter> on the device, through su -c where that is how root is had.
The interface and the filter are single-quoted for the device's shell, so
a filter is always one argument of tcpdump and never shell syntax.
tcpdump's stderr is kept in a file on the device and shown if the capture
fails. Its pid and stderr files are in a directory of its own in
/data/local/tmp, which root makes for the capture and only root can
write into: a capture whose directory is already there, put there by an
app, a symlink, or anything else, does not start, and Stop ends only a
positive process number from such a directory. Stop ends tcpdump on
the device too, not only the local adb, also when it comes before
tcpdump has fully started, and removes its files.
If LogSquirl crashes or is killed during a capture, nothing tells the
device: tcpdump runs on until it next writes to the closed stream (its
next captured packet), and its logsquirl-* directory stays in
/data/local/tmp, which adb shell rm -r /data/local/tmp/logsquirl-*
(through su -c for a capture through su) removes.
The SSH source captures on a server: it runs tcpdump there over
your system's OpenSSH client (ssh on PATH; on Windows
C:\Windows\System32\OpenSSH\ssh.exe, the optional feature OpenSSH
Client) and shows the traffic live. Type the Host as
[user@]host[:port] ([address]:port for an IPv6 address with a port), or
pick one of the Host entries of ~/.ssh/config that name one host (no
wildcards; with none, the dialog says so and the empty field shows the form
to type); press Enter to list its interfaces, which are what tcpdump -D
lists on the server (run with sudo -n while Run tcpdump with sudo -n is
on, and listed anew when it is turned on or off), or, if that lists
nothing, what ip -o link lists, with what tcpdump said. Every ssh runs as
ssh -T -o BatchMode=yes -o ConnectTimeout=10 [-p <port>] -- <user@host> <remote command>
so only your keys and the SSH agent are used: ssh never asks for a
password, a passphrase or whether to trust a host key, and the plugin never
asks for or stores one. The remote command runs /bin/sh on the server,
whatever your login shell is (it only has to read one single-quoted word,
as sh, bash, dash, zsh, ksh, csh and tcsh do; fish reads a \ inside
single quotes, so a filter with a backslash needs a POSIX login shell). The
script /bin/sh runs has the interface and the capture filter in single
quotes, so nothing in them is run:
exec /bin/sh -c '… [sudo -n] tcpdump -i <interface> -s <snaplen> -U -w - "(<filter>) and not (host <client> and tcp port <SSH port>)" & <watchdog> …'
Stop ends tcpdump on the server too, also when it runs as root: ssh's stdin is a pipe the plugin holds open while the capture runs, and a watchdog in the script ends tcpdump once it closes, as Stop ends ssh or the connection drops (without a terminal the server sends no hangup, and tcpdump alone would notice only at its next packet). If LogSquirl itself is killed, the connection closes and the watchdog ends tcpdump all the same.
Two options below the fields:
sudo -n, which never prompts; a sudo that wants a password fails, and
the section says how to allow tcpdump without one. On the server, sudo visudo -f /etc/sudoers.d/tcpdump and add <user> ALL=(root) NOPASSWD: /usr/bin/tcpdump (the path command -v tcpdump prints). Off, tcpdump
runs as the SSH user, who then needs the capture capabilities: sudo setcap cap_net_raw,cap_net_admin=eip $(command -v tcpdump)and not (host <client> and tcp port <SSH port>), the address and port
the server sees this connection come from and arrive at ($SSH_CLIENT),
so that the capture does not capture its own transportWhen ssh, sudo or tcpdump fail, the section shows what they wrote and what
to do: a host key ssh does not know (connect once in a terminal and accept
it, after checking its fingerprint) or one that changed (ssh-keygen -R),
keys the server refuses (ssh-add, ssh-copy-id, IdentityFile), a sudo
that wants a password, tcpdump missing on the server or lacking
permissions, a host that cannot be reached.
The Wireshark extcap source makes every
extcap
you have a live source: the ones Wireshark ships (sshdump, ciscodump,
androiddump, udpdump, randpktdump, wifidump, …) and any other,
e.g. a vendor's. It looks for them, in this order, in
WIRESHARK_EXTCAP_DIR (separated as PATH is),~/.local/lib/wireshark/extcap or
~/.config/wireshark/extcap; %APPDATA%\Wireshark\extcap on Windows,/Applications/Wireshark.app/Contents/MacOS/extcap on
macOS (also ~/Applications/…, and Homebrew's lib/wireshark/extcap),
/usr/lib/<arch>-linux-gnu/wireshark/extcap, /usr/lib/wireshark/extcap,
/usr/lib64/… or /usr/libexec/… on Linux,
Program Files\Wireshark\extcap on Windows,each with its wireshark subdirectory, where Wireshark 4.2 and later keep
theirs. Without any extcap, the source says where they come from.
--extcap-interfaces). One that fails is listed with its error and
cannot be chosen--extcap-interface <interface> --extcap-config)
are shown as a form below the snaplen: text, numbers (checked against
their range), check boxes, drop-down lists, radio buttons, lists of check
boxes and file paths, each with its default; a required one left empty
keeps Start disabled. The link type it captures (--extcap-dlts) is
shown above them. The values are remembered per interface in
settings.ini, except a password (or an argument the extcap says not
to save), which is kept for this session only and never written thereIt captures with <extcap> --capture --extcap-interface <interface> --fifo <pipe> [--extcap-capture-filter <filter>] --<argument>=<value> …, every
value one argument, never through a shell. The extcap writes into a FIFO
the plugin makes in its private temporary directory (readable by you
alone), or a named pipe \\.\pipe\logsquirl-tcpdump-… on Windows; its
stderr is shown as a capture program's, and Stop ends it with what it
started. An extcap's toolbar controls (--extcap-control-in/-out) are
not used: extcaps capture without them. Note that, as with Wireshark, a
password argument is on the extcap's command line, which other users of
the computer can see in its process list while it runs; the form says so
below a password field. Where the extcap offers one, log in with a key
instead: sshdump takes an SSH key file (its private key option) or uses
your SSH agent. The snaplen is not passed: an extcap has its own option for
that, if any.
On Windows an extcap that is a batch file (.bat, .cmd) is run by
cmd.exe, which reads its arguments again, quoted or not. For such an
extcap the capture filter, the interface and every argument's value must
not hold %, !, ^, &, |, <, >, (, ), " or a line break:
Start says which one is in the way, and the plugin never runs it with one.
The Custom command source runs a command you write and converts what it
writes to stdout, which must be a pcap or pcapng capture (text before
the capture's header, up to 4 KB, is skipped; messages belong on stderr,
which the section shows). Use it for what the other sources do not cover:
a vendor tool, nc -l 9999, a capture on a router. Examples to start from
(choosing one fills the line; nothing runs until you press Start):
tcpdump -i {interface} -U -w - {filter}
adb exec-out tcpdump -i {interface:sh} -s {snaplen} -U -w - {filter:sh}
ssh -o BatchMode=yes user@host tcpdump -i {interface:sh} -s {snaplen} -U -w - {filter:sh}
The line is a program and its arguments, split like a shell splits it,
but no shell runs it: blanks separate arguments, '…' and "…" quote
(in "…", \" and \\ escape), \ escapes the next character outside
quotes; nothing is expanded ($HOME, ~, *, $(…) stay as they are)
and |, ;, && or > make no pipe, list or redirection (the form
refuses them unquoted). The placeholders {interface}, {filter} and
{snaplen} are replaced by the Interface, Capture filter and Snaplen fields
below the command inside the argument they are
in, so a filter with spaces or quotes is still one argument; {filter}
alone is left out when the filter is empty. {interface} needs an
interface (this computer's are suggested; type any).
adb shell, adb exec-out and ssh do not pass their arguments on as
they are: they join them with spaces into one line that the device's or
the server's shell reads again, so a {filter} there would be shell
syntax on the device or server (;, $(…) would run). After adb or
ssh write {interface:sh} and {filter:sh}: their values are
single-quoted for that POSIX shell ('…', a ' as '\''), so it reads
each as one argument, as the examples do. {filter:sh} alone is left out
for an empty filter, too.
Run through the shell (off by default) hands the line to /bin/sh -c
(Windows: cmd.exe /c) as it is, for pipes and redirections, e.g. ssh router 'tcpdump -U -w - {filter}' | tee router.pcap. Everything in the
line then runs, as you. The placeholders are put in quoted as one word
each (single quotes; on Windows double quotes, and an interface or filter
with ", % or ! is refused): write them outside of quotes. A
{…:sh} placeholder is quoted twice, for the remote shell and then for the
local one.
Commands can be saved under a name (Save; saving under an existing
name replaces it), chosen again from the list, and deleted; they are
kept in settings.ini ([live], options/command/saved) as soon as they
are saved. A command whose output is not a capture fails with Not a
capture, its last stderr lines and what it must write instead.
A capture read from a running source (a capture program's output, a pipe) is converted while it runs:
<name>.pcap or <name>.pcapng. Both live in the private temporary
directory, removed when LogSquirl quits: Save capture… in the sidebar
copies the raw capture elsewhere, to convert again or open in WiresharkStops after 10:00 (35%), or at 1,000 packets (12%)); one that
stops it says so in a notificationThe stop conditions and the ring buffer are the same for every source, and are remembered with the rest of the choice.
With a ring buffer of N files, the raw capture is written to
<name>_00001_<time>.pcap, <name>_00002_<time>.pcap, … (.pcapng for
a pcapng stream; the time is when the file was started). A file ends after
the packet that fills it, or the last packet before its duration is over,
when the next packet comes: so no file is empty, a file may be one packet
bigger than the size given, and one may cover more than its duration when
no packet came for a while. Every file is a capture of its own: a pcap's
header, or a pcapng's section header and interfaces, are repeated at its
start, so Wireshark opens any of them. A capture whose headers come to
more than 16 MiB, far more than any real one's, ends at the next file with
a message instead of copying them. When the N+1st file starts, the
oldest is deleted.
Each file has a text of its own, <name>_00001_<time>.log, …: when a new
file starts, its text opens in a new tab, following it, once its first
packet line is there, so no tab grows without bound. A text is never
rewritten: the tabs of files the ring buffer has deleted stay open, as
they are, until you close them, and their Packet Panel says Rotated away
for their packets (their lines stay readable; only the packets' bytes are
gone). The packet numbers in the No. column go on across the files, up
to 4,294,967,295: a capture that gets to that packet stops with it, saying
so in a notification (some 12 hours at 100,000 packets/s); a capture file
with more packets is converted up to it, the summary saying so.
Follow stream content starts at the first packet kept. The .log files
stay in the private temporary directory until LogSquirl quits; close the
tabs of old files to let LogSquirl forget them. Save capture… writes the files kept as one capture
(packets of the deleted files are gone); Export packets… copies the
selected packets from the files they are in.
Plugins → Plugin Management…, Configure… on the plugin's card, opens its options, on three tabs: Columns and Info, Protocols and Advanced. Restore Defaults resets the options of all three tabs.
| Tab | Option | Default | Choices |
|---|---|---|---|
| Columns and Info | Time | UTC time and time since the first packet | UTC time only; time since the first packet only |
| Columns and Info | Show MAC addresses as columns | off | Source MAC and Destination MAC before Info, - for a packet that is not on Ethernet or Wi-Fi |
| Columns and Info | Show names from the capture's DNS answers with the addresses | off | on: Source and Destination show an address a DNS answer earlier in the capture named as 93.184.216.34(www.example.com), see Names from DNS answers |
| Columns and Info | Preview payloads no protocol is recognised in | on, 200 characters | off; 1 to 200 characters |
| Columns and Info | Show TCP timestamps (TSval, TSecr) on every segment | off: on SYNs only, among their options | on: every segment with the option, as Wireshark shows it |
| Protocols | SOME/IP also on ports | none | ports, 30501, 30502, on which SOME/IP is read whatever its header says (besides 30490, and any port where its header fits) |
| Protocols | SOME/IP name table | none | a text file naming services, methods and eventgroups, one per line: service 0x1234 Navigation, method 0x1234 0x0001 GetRoute, event 0x1234 0x8001 RouteChanged, eventgroup 0x1234 0x0010 Route; # begins a comment |
| Protocols | TLS decryption: key log file | none | an SSLKEYLOGFILE to decrypt TLS sessions with, see TLS decryption |
| Advanced | Streams numbered at most | 1,000,000 | 1 to 10,000,000; later streams show ? |
| Advanced | Endpoints counted at most | 100,000 | 1 to 1,000,000; the rest count as other endpoints |
They are kept in settings.ini in the plugin's configuration directory
(plugin_config/io.github.logsquirl.tcpdump beside LogSquirl's plugin
directory) and read when a capture is opened: a capture already open keeps
the options it was converted with; open it again to apply new ones.
The defaults write the packet list shown under Example Output, which the Log Format and every highlighter and filter written for it expect. The plugin's own patterns, those of Filter stream in Regex Lab, the summary's filters, the display filters and the highlighter set and filter group, read every layout. Three options change the column layout:
time is empty; with the time since the first packet only, timestamp
is empty, so the line has no timestamp, and Δt, Go to timestamp, time
ranges and the Chart Panel's rates are lost. A highlighter or filter
that counts columns, or expects a time where the other one was, no
longer matches.body; the other
columns stay where they are. A highlighter or filter anchored at the
start of Info no longer matches.The payload preview, the TCP timestamps and the caps change only what Info says or which streams are numbered, not the columns.
With Show names from the capture's DNS answers with the addresses on, an address that a DNS answer in the capture resolved shows its name, as Wireshark's name resolution does when it is fed from the capture:
1 0 … 192.0.2.10 93.184.216.34 HTTPS 54 50000 → 443 [SYN] Seq=0 Win=64240
4 0 … 192.0.2.53 192.0.2.10 DNS 105 53 → 53001 Len=63 | Standard query response 0x1a2b A www.example.com CNAME example.com A 93.184.216.34
5 0 … 192.0.2.10 93.184.216.34(www.example.com) HTTPS 54 50000 → 443 [ACK] Seq=1 Ack=1 Win=64240 [iRTT=0.006000]
www.example.com, not
example.com); PTR answers for in-addr.arpa and ip6.arpa names name
the address they spell. Only the answer section is read (in mDNS the
additional records too, where a service's addresses come; an mDNS
goodbye, TTL 0, names nothing), and only responses without an error. DNS over TLS and over HTTPS are not read-, _ and ., or longer than 120 characters, is not
shownip.addr == 93.184.216.34) match a line
with or without the name. The sidebar summary lists an endpoint with the
name it had last: 93.184.216.34 (www.example.com): 7 pktsWith a key log, the plugin decrypts the TLS sessions it has the secrets of, and describes what they carry, as Wireshark does with (Pre)-Master- Secret log filename:
8 0 … HTTP 171 50101 → 443 [ACK, PSH] Seq=194 Ack=628 Win=64240 Len=117 | TLS (decrypted) | GET www.example.com/index.html HTTP/1.1
21 1 … HTTP2 165 443 → 50102 [ACK, PSH] Seq=677 Ack=293 Win=64240 Len=111 | TLS (decrypted) | SETTINGS[0], SETTINGS[0], HEADERS[1]: 200, Content-Type: application/javascript, Content-Length: 12, DATA[1]
SSLKEYLOGFILE names, e.g. SSLKEYLOGFILE=~/sslkeys.log firefoxA segment with a record decrypted says TLS (decrypted) before what the
records hold: HTTP/1.1 requests and responses, as for plain HTTP, labelled
HTTP; HTTP/2 frames with the requests (:method, :authority, :path)
and responses (:status, Content-Type, Content-Length) of their header
blocks decoded (HPACK), labelled HTTP2; the encrypted handshake messages
(Encrypted Extensions, Certificate, …, Finished) and alerts
(Alert: close_notify). Sessions the key log has no secrets for, or wrong
ones, keep their lines as without a key log. The sidebar summary counts the
sessions decrypted, or says why the key log could not be read.
CLIENT_RANDOM); TLS 1.3 with
AES-128-GCM, AES-256-GCM and ChaCha20-Poly1305 (the handshake and
application traffic secrets, *_TRAFFIC_SECRET*), key updates followed.
Not decrypted: TLS 1.0 and 1.1, TLS 1.3 early data (0-RTT), a TLS 1.2
renegotiation's new keys, QUICSecurity. The key log decrypts every session whose secrets it holds: keep it as private as the traffic itself, and delete it when done. The plugin only reads it, while converting, and keeps the secrets in memory only, wiped when the conversion ends; it never writes, copies, logs or shows them. The text it writes holds what was decrypted, in the private temporary directory of the converted captures.
The cryptography is Mbed TLS 3.6 (Apache-2.0 or GPL-2.0-or-later, see NOTICE), built into the plugin: nothing more to install.
LogSquirl recognises the packet list as a log with fields once it has the
plugin's Log Format definition, formats/tcpdump_log.json
(lnav-compatible, like LogSquirl's built-in formats). It is a one-time copy:
Take tcpdump_log.json from the release archive (it lies next to the
library), or from the formats folder of this repository
In LogSquirl, open Options → Log Formats, click Open Formats Folder… and copy the file there. The folder is
| Platform | Formats Directory |
|---|---|
| macOS | ~/Library/Application Support/logsquirl/formats/ |
| Linux | ~/.local/share/logsquirl/formats/ |
| Windows | %APPDATA%/logsquirl/formats/ |
(a portable LogSquirl uses the formats folder beside its executable)
Turn on Auto-detect log format (table view) on the same tab and click OK; formats are read again when the Options dialog is applied. A capture opened before keeps its format: open it again
A converted capture then gets:
number, stream, timestamp
(the UTC Time), time (since the first packet), source,
destination, protocol, length and body (the Info column)Ctrl+Shift+L) and Set search limits to time
range…: the UTC Time is read with %Y-%m-%d %H:%M:%S.%f%z, at 6 or 9
decimals (LogSquirl keeps milliseconds); type times in UTC, as the
column shows themlength, the bytes on
the wire, over timeThe sidebar summary links here once per session, since the plugin cannot tell whether the format is installed.
Two files in the presets folder colour and filter the packet
list much as Wireshark's default colouring rules and display filters do. Get
them from the release archive, where they lie next to the library, or from
this repository, and import each once:
tcpdump_highlighter.conf:
Highlighters → Configure highlighters…, Import, OK, then switch
the set tcpdump on in the Highlighters menutcpdump_filter.conf:
Tools → Predefined filters…, Import, OK; the filters of the group
tcpdump are then listed in the Filters tab of the Filters Panel, where
checking one filters the viewImporting a newer version of a file offers to replace the one imported before. The highlighters, the first that matches a line colouring it:
| Highlighter | Colours the lines of | Colour |
|---|---|---|
| TCP problems | TCP analysis markers but Window Update and Keep-Alive ([TCP Retransmission], [TCP Dup ACK 7#1], [TCP Window Full], [TCP ZeroWindow], …) and a bogus TCP header length | orange |
| TCP RST | TCP segments with RST | strong red |
| ICMP errors | ICMP destination unreachable, source quench, redirect, time exceeded; ICMPv6 destination unreachable, packet too big, time exceeded, parameter problem | orange |
| DNS NXDOMAIN | DNS and mDNS responses for a name that does not exist ([NXDOMAIN]) | red |
| HTTP 4xx/5xx | HTTP responses with a client or server error status | red |
| TCP SYN/FIN | TCP segments with SYN or FIN: connections opened and closed | green |
| TLS | Protocol TLS | blue |
| ARP | Protocol ARP | grey |
The filters:
| Filter | Shows the lines of |
|---|---|
| TCP handshakes | TCP segments with SYN or FIN |
| TCP errors | what the TCP problems and TCP RST highlighters colour |
| DNS | Protocol DNS or mDNS |
| HTTP | HTTP requests and responses (Protocol HTTP…, Info naming a request line or status line) |
| TLS | Protocol TLS |
| ICMP | Protocol ICMP or ICMPv6 |
| ARP | Protocol ARP |
Each pattern reads the columns, so it never matches a word that only
appears in Info's payload text: a Protocol is read from the Protocol
column, TCP flags from the bracket right after the ports, an HTTP status
or a DNS response code from the description after the first | (after
TLS (decrypted) | for decrypted HTTP). A
tunnelled packet is matched by the packet inside: the tunnels Info names
first (VXLAN VNI 100 | , GRE | , …) are skipped. They match in every
choice of columns.
Plugins → tcpdump → Display filter…, and the sidebar's Display filter field, translate a Wireshark-style display filter into a Regex Lab pattern over the packet line's columns. The supported subset:
| Filter | Selects the packet lines |
|---|---|
ip.addr, ip.src, ip.dst | whose Source or Destination, Source, Destination is the IPv4 address, or lies in the network: ip.addr == 10.0.0.0/8 |
ipv6.addr, ipv6.src, ipv6.dst | the same for an IPv6 address, in any of its forms (2001:DB8:0::1) |
tcp.port, tcp.srcport, tcp.dstport | of TCP packets with either port, the source port, the destination port |
udp.port, udp.srcport, udp.dstport | the same of UDP packets |
tcp.stream, udp.stream | of the TCP or UDP stream with that number in the Stream column |
frame.len | whose Length, the length on the wire, compares |
dns, http, tls, quic, arp, icmp, http-alt, … | whose Protocol column is the name, case aside |
tcp, udp, ip, ipv6 | of TCP, UDP, IPv4, IPv6 packets, whatever their Protocol column |
Fields compare with ==, !=, <, >, <= and >= (or eq, ne,
lt, gt, le, ge), addresses with == and != only; numbers are
decimal or 0x hexadecimal. A field alone, tcp.port, selects the packets
that have it. Conditions combine with !/not, &&/and, ||/or and
parentheses (nested at most 64 deep), ! binding tighter than &&, and
&& than ||. As in
Wireshark, != selects the packets that have the field and no value of it
equal: ip.addr != 10.0.0.1 is the IPv4 packets with neither address
10.0.0.1, while !(ip.addr == 10.0.0.1) also selects every packet without
IPv4.
The fields are read from the line, not from the capture: the addresses
from Source and Destination (an ARP packet's IPv4 addresses do not count
as ip), the ports from the start of a TCP or UDP packet's Info, so a
tunnelled packet is matched by the packet inside, as its line shows it, and
the ports an ICMP error quotes do not count; a stream past the stream cap
(?) has no number. Anything else is rejected with its position and the
reason, never approximated: other fields (tcp.flags, http.host, …),
strings, contains, matches, sets (in {…}), slices, IPv6 prefixes and
comparing two fields.
The pattern is a lookahead from the start of the line per condition, so
that conditions on different columns combine exactly, and a number range
(frame.len > 1000, tcp.port < 1024) is spelled out digit by digit. It
is for the Regex Lab and LogSquirl's search, which run it with Qt's regular
expressions (with Vectorscan as the engine too: LogSquirl searches a
pattern Vectorscan cannot read with Qt's engine); Vectorscan alone has no
lookaheads, which is why the shipped highlighter set and filter group use
none.
No. Stream UTC Time Time Source Destination Protocol Length Info
1 0 2026-10-09 08:41:12.123456Z 0.000000 192.168.1.100 10.0.0.1 TCP 54 443 → 54321 [SYN] Seq=0 Win=65535
2 0 2026-10-09 08:41:12.123956Z 0.000500 10.0.0.1 192.168.1.100 TCP 54 54321 → 443 [SYN, ACK] Seq=0 Ack=1 Win=65535
3 0 2026-10-09 08:41:12.124456Z 0.001000 192.168.1.100 10.0.0.1 TCP 54 443 → 54321 [ACK] Seq=1 Ack=1 Win=65535
4 0 2026-10-09 08:41:12.173456Z 0.050000 192.168.1.100 10.0.0.1 DNS 72 53 → 12345 Len=34
5 - 2026-10-09 08:41:12.223456Z 0.100000 192.168.1.100 10.0.0.1 ICMP 74 Echo (ping) request id=0x0001, seq=1
6 0 2026-10-09 08:41:12.243456Z 0.120000 10.0.0.1 192.168.1.100 TCP 1514 54321 → 443 [ACK] Seq=1 Ack=1 Win=65535 Len=1460 [cut to 96 bytes]
7 0 2026-10-09 08:41:12.243956Z 0.120500 192.168.1.100 10.0.0.1 TCP 54 443 → 54321 [ACK] Seq=1 Ack=1461 Win=65535
8 0 2026-10-09 08:41:12.244456Z 0.121000 192.168.1.100 10.0.0.1 TCP 54 [TCP Dup ACK 7#1] 443 → 54321 [ACK] Seq=1 Ack=1461 Win=65535
# Clone
git clone https://github.com/64x-lunicorn/LogSquirl-tcpdump.git
cd LogSquirl-tcpdump
# Configure
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
# If Qt6 is not in PATH (e.g. Homebrew on macOS):
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="$(brew --prefix qt6)"
# Build
cmake --build build
# The shared library is in build/:
# macOS: build/liblogsquirl_tcpdump.dylib
# Linux: build/liblogsquirl_tcpdump.so
# Windows: build/logsquirl_tcpdump.dll
cmake --preset dev # on macOS with Homebrew's Qt: dev-homebrew
cmake --build build
ctest --preset dev
Testing in the developer guide has the rest.
graph TD
A[User chooses Open pcap…, sidebar or menu] --> B[QFileDialog]
B --> C[Worker thread: pcap_converter]
C -->|.gz: GzipSource decompresses on the fly| D[PcapReader or PcapngReader, by the first block]
D --> E[Next packet record]
E --> F[Link layer, VLAN tags]
F --> G[IPv4 / IPv6 + extension headers / ARP]
G --> H[TCP / UDP / ICMP, application layer]
G -->|GRE, IP-in-IP| T[Tunnel: inner packet, at most 4 deep]
H -->|VXLAN| T
T --> G
H --> I[PacketFormatter: one line]
H -->|DNS answers, with host names shown| N[HostNames: address → name]
N --> I
I --> J[Append to private .log file]
J --> E
J --> K[host API: open_file]
K --> L[LogSquirl main viewer]
E -->|every 10,000 packets| X[CaptureIndex: checkpoint]
L -->|selected line's No.| P[Packet Panel]
X --> P
P -->|re-read from the nearest checkpoint| D
P -->|Follow stream content: the stream's packets, in order| D
L -->|selected lines' No.| Q[Export packets]
X --> Q
Q -->|records copied as they are| R[new .pcap / .pcapng]
GPL-3.0-or-later — see LICENSE for the full license text.
The vendored include/logsquirl_plugin_api.h header is MIT-licensed, so
plugins of any license can build against the LogSquirl Plugin SDK without
taking on GPL obligations. The plugin links in zlib, under the zlib
license. See NOTICE for details.
The plugin links in parts of Mbed TLS (Apache-2.0 or GPL-2.0-or-later, taken under the GPL) for the TLS decryption; see NOTICE.
Every release package carries LICENSE and NOTICE. NOTICE names the corresponding source: this repository at the release's tag, and the exact zlib and Mbed TLS archives the build fetches, by URL and SHA-256.
DHCP Offer - Transaction ID 0x3903f326, 192.168.1.50 for 00:11:22:33:44:55NTP Version 4, server, stratum 2Publish Message (QoS 1, id=2) [alerts/door] "open"Status: 200 OK (INVITE), CSeq 1, Call-ID a84b4c76e667…, SDP (audio 3456 RTP/AVP 0)PT=PCMU, SSRC=0x1234ABCD, Seq=1000, Time=8000Sender Report, Source descriptionService 0x1234 Method 0x0001 Client 0x0010 Session 0x0001 ERROR (E_NOT_OK), 0 bytesOffer Service 0x1234 Instance 0x0001 v1.0 TTL=3 (192.0.2.10:30501 UDP)Subscribe Eventgroup Ack …Diagnostic message 0x0E00 → 0x1000, UDS ReadDataByIdentifier 0xF190UDS Negative Response ReadDataByIdentifier NRC=0x31 (requestOutOfRange)Client: Protocol (SSH-2.0-OpenSSH_9.6)Client: Key Exchange Init kex=curve25519-sha256,… hostkey=ssh-ed25519,… [email protected],…Elliptic Curve Diffie-Hellman Key Exchange Reply, New KeysEncrypted packet (len=64)WebSocket Text [FIN] [MASKED] len=5 "Hello"WebSocket Connection Close [FIN] len=2 Normal Closure (1000)[COMPRESSED]Tree Connect Request Tree: \\server\shareCreate Request File: dir\file.txtSession Setup Response, Error: STATUS_MORE_PROCESSING_REQUIREDEncrypted SMB3Client Hello, SNI=example.com, TLS 1.3 [reassembled from 3 segments][TCP segment of a reassembled PDU]SSLKEYLOGFILETLS (decrypted) | GET example.com/ HTTP/1.1| Payload you can skim. Printable text shown, other bytes as dots, cut at 200 characters; mostly-binary payloads suppressed. |
Link layers and tags. Ethernet, Raw IP, Linux cooked capture v1 and v2, BSD loopback (DLT_NULL, DLT_LOOP); Wi-Fi (802.11, with or without a Radiotap header), PPP, Cisco HDLC and PPPoE; stacked 802.1Q and QinQ tags stripped transparently. 802.11 management and control frames are named as Wireshark names them, between MAC addresses (Beacon frame, SN=1000, FN=0, BI=100, SSID="HomeNet", Probe Request, Authentication, Request-to-send); data frames reach IP through LLC/SNAP, encrypted ones show as QoS Data, SN=6, FN=0, Protected. PPP and PPPoE sessions reach IP; LCP, IPCP, IPv6CP, PAP and CHAP are named with their message (Configuration Request, Echo Reply, Authenticate-Request, no credentials), PPPoE discovery with its stage (Active Discovery Offer (PADO) AC-Name='isp'). | A capture at a glance. Sidebar panel with protocol breakdown, top endpoints, duration, packets per second, file size and the TCP analysis markers per kind. |
-?iRTT in its TCP layer. The panel follows the selection while the tcpdump tab is in view
(it asks LogSquirl at most every 250 ms, and not at all while hidden),
and Plugins → tcpdump → Packet details shows the selected line's
packet at once. The packet is read again from the capture file, found
by the line's No., off LogSquirl's UI thread ("Reading packet N…"): the plugin keeps a file position every 10,000 packets
while converting, so no packet is kept in memory. A capture file that
was changed, moved or removed after it was opened is reported, not
misread; open it again. Needs LogSquirl ≥ 26.11-). The panel's Stream tab shows the payload of
the whole TCP or UDP stream, the client's bytes (the side that sent the
first packet) in red and the server's in blue, as Text (UTF-8 kept,
other control bytes, bidi controls and other invisible characters as
\xNN, a line longer than 4,096 characters broken) or Hex (Wireshark's dump, the
offset counted per direction, the server's lines indented), both
directions or one. TCP bytes are put in sequence order as the
reassembly orders them: a segment captured early waits for those before
it, retransmitted and overlapping bytes are shown once, and bytes the
capture lacks show as [n bytes missing]; UDP shows its datagrams, each
on its line. The stream is read again from the capture file in the
background, with a progress bar and Cancel; the first 1 MB is
shown, Show more reads 1 MB more each time, up to 16 MB, and a note
says when there is more. Export… writes the whole stream to a file,
however long: the raw bytes of the directions shown (gaps left out), or
the text as shown. Needs LogSquirl ≥ 26.11TCP 3, UDP 0), its
protocol (the label its stream was recognised by, else TCP or UDP),
ends A (which sent its first packet) and B with their ports, packets and
bytes in all and each way (bytes on the wire, as the Length column),
its start in seconds after the capture's first packet and its duration.
The packets and bytes each way are hidden at first, so that the table
fits the sidebar: right-click a column header for a menu that shows or
hides each column, kept across restarts. Click a column header to sort
by it. A click on a conversation selects it; double-click it, press
Enter, or choose Filter conversation in Regex Lab from its context
menu, and the Regex Lab opens with the pattern of that stream's lines,
as Filter stream in Regex Lab builds it; apply it to filter the view
(needs LogSquirl ≥ 26.11). The
table is counted while converting, for the streams that get a number:
past the stream cap (see Options), the packets of all other
streams are one row, ? Other streams1-5, 9): change them, or paste packet lines
copied in LogSquirl, then choose the file. LogSquirl tells the plugin at
most the first 1,000 selected lines (and at most 1 MiB of them); the
dialog says when there were more, and pasting the copied lines exports
them all. The packets are copied from the capture file record by record,
unchanged: a pcap gives a .pcap with the capture's header, a pcapng a
.pcapng with the section headers and interfaces of the exported
packets (other pcapng blocks, such as name resolution, are left out);
the packets of a gzip-compressed capture are exported uncompressed.
The capture is read once from front to back in the background; a
progress dialog shows how far, and Cancel leaves no file. Needs
LogSquirl ≥ 26.11ip.addr == 10.0.0.1 && tcp.port == 443: the Regex Lab opens
with the pattern of the packet lines it selects; apply it to filter the
view. A filter outside the supported subset is
rejected below the field with its column and the reason. Needs
LogSquirl ≥ 26.11| Advanced | TCP reassembly memory at most | 64 MB | 1 to 1,024 MB, all streams together, at most 64 KB a stream direction; a message that does not fit keeps its per-segment description, followed by [reassembly limit], and the segments after it up to its announced end are labelled [continuation of a message past the reassembly limit] |