Building Kismet with ESP32 C5 Support - oshri-almog/esp32c5-kismet-wifi-interface GitHub Wiki
This page explains the Kismet build that the install pages run: why Kismet is built from source, what add-to-kismet.sh changes in Kismet's source tree, which packages and configure options it needs, how many parallel jobs a machine can take, and how installing, rebuilding and uninstalling work. It is for readers who want to understand or adapt those steps. For a working install, follow Install on Raspberry Pi or Install on Linux, which link back here where it matters.
The examples use the same layout as the install pages: this project in ~/esp32c5-kismet-wifi-interface, Kismet's source in ~/src/kismet, and the installation in ~/kismet-install.
A Kismet capture source has two halves, and the Kismet you run needs both:
| Part | File in this project | Where it ends up |
|---|---|---|
The server side, which registers the source type esp32c5
|
kismet/datasource_esp32c5.h |
Compiled into the kismet program |
The C helper, kismet_cap_esp32c5, which talks to the board |
kismet/capture_esp32c5/capture_esp32c5.c |
A program of its own, installed next to kismet
|
The server side is compiled into Kismet itself, so it cannot be added to a Kismet that is already installed. No Kismet release contains it yet, and neither do Kismet's distribution packages. A Kismet without it cannot open these boards, and it also turns away remote sources of this type: when a remote helper connects, it logs Kismet could not find a datasource driver for incoming remote source 'esp32c5' ....
The kismet/ folder is licensed GPL-2.0-or-later, like Kismet, so that it can become part of Kismet. Until a Kismet release has it, you build Kismet from source with this project's files added. The Docker image is the same build, done for you.
The source was developed and tested against one Kismet commit:
| Commit |
cfe427074 (cfe427074b7ffcfcbc055a123c1df3b3cde60d59) |
| Title | "Merge branch 'shrout1-ble-uuid-manufacturer-data'" |
| Date | 16 September 2026 |
git clone https://github.com/kismetwireless/kismet.git ~/src/kismet
git -C ~/src/kismet checkout cfe427074Git reports a "detached HEAD": the tree is on a fixed commit, not on a branch. That is what you want.
A Kismet built from git names its version after the build date and the commit. The test Pi's build printed Kismet 2026.09.0-cfe427074: year, month, 0, then the start of the commit hash. A build made in another month shows another date, and git may abbreviate the hash to a different length (the Docker build printed cfe42707). The commit part is what tells you which source you built.
Kismet's own documentation is at kismetwireless.net. The source tree has no docs/ folder, and its README.OLD describes an older Kismet; do not follow it.
Newer commits have not been tested. To try one, first return the tree to Kismet's own files. add-to-kismet.sh has edited files that git tracks (kismet_server.cc, configure and others), and git refuses to check out a commit that changes a file with local edits ("Your local changes to the following files would be overwritten by checkout").
-
Undo the script and delete everything the build produced.
clean -xfdalso removes the compiled files, which a new commit has to rebuild anyway:git -C ~/src/kismet checkout -- . git -C ~/src/kismet clean -xfd
-
Fetch and check out the commit you want. Replace
<commit>with it:git -C ~/src/kismet fetch git -C ~/src/kismet checkout <commit>
-
Run
add-to-kismet.sh,configure,makeandmake installas forcfe427074, in the sections below.
Cloning Kismet again into another folder works too, and keeps the tested tree as it is.
What can go wrong:
-
add-to-kismet.shfinds where to add its lines by looking for the lines of Kismet's CatSniffer Zigbee source. If Kismet has moved or renamed them, the script stops withanchor not found, Kismet has changed: <the line it looked for>, before it regeneratesconfigure. Edits made before that point stay in the tree, so run the two commands of step 1 again before you try another commit. - Each of the capture framework fixes (below) is skipped without a word when Kismet already has it, and skipped with a note when the code it replaces has changed, such as
capture_framework.c: cf_commit_packet has changed, its metadata leak not fixed. The login and Host header fixes are recognised only as this script applies them, so a Kismet that fixed the login its own way gets the notecapture_framework.c: the websocket login has changed, it still goes in the URI. The script carries on either way. - The C helper is built on Kismet's capture framework. If a newer Kismet changes that framework, the helper may no longer compile.
For the Docker image, the same choice is the build argument KISMET_REF, for example --build-arg KISMET_REF=<commit>. Changing it rebuilds Kismet from the start. See Docker Reference.
sh ~/esp32c5-kismet-wifi-interface/kismet/add-to-kismet.sh ~/src/kismetThe script (kismet/add-to-kismet.sh) needs python3, which makes its edits, and autoconf, automake and pkg-config, because it regenerates Kismet's configure after editing configure.ac. It checks the path first: if kismet_server.cc or capture_framework.c is missing there, it prints <path> does not look like a Kismet source tree and exits with status 1.
Every change it makes, in the Kismet tree:
| File | Change |
|---|---|
datasource_esp32c5.h |
Copied in; on later runs, only when this project's copy differs |
capture_esp32c5/capture_esp32c5.c, capture_esp32c5/Makefile.in
|
Copied into a new folder; on later runs, likewise only when they differ |
kismet_server.cc |
Includes datasource_esp32c5.h and registers the esp32c5 source type, right after Kismet's CatSniffer Zigbee source |
configure.ac |
Always builds the helper, which needs only a serial port, so there is no platform test and no new dependency. Adds capture_esp32c5/Makefile to the generated files and a ESP32-C5: yes line to the summary |
Makefile.in |
A build rule for capture_esp32c5/kismet_cap_esp32c5, a line in make clean, and install lines copied from both of the CatSniffer helper's install blocks, the plain one and the setuid one (see "Installing" below) |
.gitignore |
Ignores the built helper |
capture_framework.c |
Seven upstream bug fixes (next section) |
capture_framework.h |
Three fields that the login fix needs, and one that the Host header fix needs |
configure |
Regenerated with aclocal -I m4 && autoconf when configure.ac is newer than it (autoconf alone fails with "possibly undefined macro: AC_DEFINE") |
It prints a line for each file it copies and each edit it makes, so a file changed several times is listed several times. A first run on a clean cfe427074 tree:
copied datasource_esp32c5.h
copied capture_esp32c5/capture_esp32c5.c
copied capture_esp32c5/Makefile.in
edited kismet_server.cc
edited kismet_server.cc
edited Makefile.in
edited Makefile.in
edited Makefile.in
edited Makefile.in
edited configure.ac
edited configure.ac
edited configure.ac
edited configure.ac
edited configure.ac
edited .gitignore
edited capture_framework.c
edited capture_framework.c
edited capture_framework.c
edited capture_framework.c
edited capture_framework.h
edited capture_framework.c
edited capture_framework.c
edited capture_framework.c
edited capture_framework.c
edited capture_framework.h
regenerating configure (needs autoconf and automake)
Done. Now: cd /home/pi/src/kismet && ./configure && make
Use the configure options in "Configure" below rather than the bare ./configure it suggests. Afterwards, git shows what changed:
git -C ~/src/kismet status --short M .gitignore
M Makefile.in
M capture_framework.c
M capture_framework.h
M configure
M configure.ac
M kismet_server.cc
?? capture_esp32c5/
?? datasource_esp32c5.h
Running the script again is safe: every edit is skipped when it is already there, a file is copied only when its content differs from the tree's copy, and configure is regenerated only when configure.ac is newer than it. A second run changes no file and prints only its Done. line. That is how an updated helper gets into the tree without rebuilding more than it has to ("Rebuilding after a helper change" below). On the test Pi (2026-10-02), a run after updating this project printed only the two helper files it copied and the framework edits that were new, datasource_esp32c5.h kept its time, and a second run changed nothing.
Run the script before configure. A tree that was configured before the script ran has to be configured again. Until it is, every make prints 'Makefile.in' or 'configure' are more current than this Makefile. You should re-run 'configure'. That is only a notice, and make carries on, but the Makefile that configure wrote before has no rule for the helper, so Kismet is built without it.
Kismet's capture helpers share one piece of code, capture_framework.c, built into libkismetdatasource.a. At this commit it has bugs that every capture helper has, not only this one. The script fixes seven of them. They are upstream bug fixes, not part of the ESP32-C5 source, and each goes to Kismet as a change of its own:
| Fix | What was wrong |
|---|---|
| A memory leak |
cf_commit_packet() never frees the small holder (cf_frame_metadata) that cf_prepare_packet() allocates for each packet, so a helper loses about 32 bytes per packet for as long as it runs |
| Remote capture in bursts | Over a websocket, a helper's packets went out in bursts every 5 seconds, when Kismet's next PING woke the helper's loop. With the fix, the first packet over --connect reached Kismet in about 1.2 s on the test Pi, instead of 5 to 6 s |
| A missed wake-up | A websocket that closed just as the helper's loop went to sleep left the loop asleep: the helper kept its port and never reconnected |
| The remote login | The login went into the websocket's URL, where a proxy's log keeps it, and a user, password or API key with a space, % or & in it could not log in. Now a user and password go in an Authorization: Basic header and an API key in Kismet's session cookie. Only a user name containing :, which that header cannot carry, still goes in the URL, and such a login cannot also hold an &. A websocket answered with a redirect is refused, so that the login does not go along to another server. With libwebsockets 4.0 and later (Debian 13 and Ubuntu 24.04 have 4.3) the helper does not even connect to where the redirect points, and its message says where that was, without the query, which may hold the login. Older libwebsockets connects there first, then stops before it sends anything |
| libwebsockets queue warnings | Every websocket connection printed rejecting message on queue depth 40 on a machine with more than about 40 network routes |
| An empty log line | A remote helper printed an empty INFO: line on its terminal for every channel set it accepted |
| The Host header | The websocket request's Host and Origin headers named the server without its port (Host: 127.0.0.1 for --connect 127.0.0.1:2501), which a reverse proxy that goes by host and port can take for another site. Now they carry the port, unless it is the scheme's own (80, or 443 with --ssl); TLS still checks the certificate against the bare name. With libwebsockets before 4.2, a host and port longer than 110 bytes keep the bare host, because those versions cannot write a longer Origin header |
The leak, measured:
| Measured over 200,000 packets | 6,400,000 bytes lost before the fix (32.0 bytes per packet), 0 after |
| Estimated at 1,000 frames a second | about 115 MB an hour (an estimate from a review, not a measurement) |
The script frees only that holder. Freeing the whole record instead would commit the frame's ring-buffer space a second time.
Each fix is skipped when the tree has it already, from an earlier run or from Kismet itself. The login and Host header fixes are the exceptions: the script recognises only its own versions of them, so a Kismet that fixed either its own way gets that fix's note below. When the code a fix replaces has changed, the script prints one of these lines and carries on without that fix:
capture_framework.c: cf_commit_packet not found, its metadata leak not fixed
capture_framework.c: cf_commit_packet has changed, its metadata leak not fixed
capture_framework.c: the websocket send path has changed, its 5 second bursts not fixed
capture_framework.c: LWS_CALLBACK_CLIENT_CLOSED has changed, its missed wake-up not fixed
capture_framework.c: the websocket login has changed, it still goes in the URI
capture_framework.c: the websocket's connection has changed, where a redirect points still connected to
capture_framework.c: lws_create_context has changed, its queue warnings not fixed
capture_framework.c: cf_send_configresp has changed, its empty INFO line not fixed
capture_framework.c: the websocket's Host header has changed, its port not added
The redirect line is the one partial case: telling libwebsockets not to follow a redirect is an edit of its own, so when only that edit cannot be made, the rest of the login fix still goes in. libwebsockets then still connects to where a redirect points, as the note says.
A tree that an earlier version of this script patched is brought up to date in place: the script copies the helper files that changed and adds the fixes, or the parts of a fix, that are new. The result is the same as a fresh tree patched by the current script, and a second run changes nothing. This was checked with every earlier version of the script in this repository.
One more note is for a tree that holds an earlier, unreleased version of the login fix, which followed redirects with the login. The script does not upgrade that version in place, and says how to replace it:
capture_framework.c: an earlier version of the websocket login fix is there, which follows redirects; git checkout capture_framework.c capture_framework.h and run this again
A run that applies a fix changes capture_framework.c, and the login and Host header fixes also capture_framework.h, so the next make rebuilds libkismetdatasource.a and every capture helper, once. It does not rebuild kismet, which uses neither.
What Kismet's configure at cfe427074 does when a package is missing, with the Debian and Ubuntu package names:
| Package | What it is for | If it is missing |
|---|---|---|
build-essential |
C and C++ compilers, make
|
Nothing builds |
git |
Cloning Kismet; the commit in Kismet's version string | Required |
pkg-config |
How configure finds the libraries, and add-to-kismet.sh, whose aclocal needs pkg-config's macros |
Required; the script fails without it |
autoconf, automake
|
add-to-kismet.sh regenerates configure
|
The script fails |
python3 |
add-to-kismet.sh makes its edits in Python |
The script fails |
zlib1g-dev |
Compression |
configure stops |
libsqlite3-dev |
Kismet's log database (.kismet files) |
configure stops |
libssl-dev |
OpenSSL |
configure stops |
libpcap-dev |
libpcap |
configure stops |
libwebsockets-dev |
The capture helpers' websocket client, for remote capture. Version 3.1.0 or later, with client support |
configure stops, unless --disable-libwebsockets
|
libusb-1.0-0-dev |
Kismet's USB capture helpers (not this one) |
configure stops, unless --disable-libusb
|
libsensors-dev |
Temperature readings through lm-sensors |
configure stops, unless --disable-lmsensors
|
libmosquitto-dev |
MQTT |
configure stops, unless --disable-mosquitto
|
libnm-dev |
NetworkManager, for Kismet's own Wi-Fi capture | A warning |
libnl-3-dev, libnl-genl-3-dev
|
Netlink, for Kismet's own Linux Wi-Fi capture | A warning |
libcap-dev |
Lets capture helpers drop capabilities; the C helper drops all of them at start, whoever runs it | Built without it; the C helper then drops nothing |
libpcre2-dev |
Regular expressions in filters | A warning |
libdw-dev |
Full stack traces if Kismet crashes | Partial stack traces |
The ESP32-C5 source itself adds no library: the C helper needs only a serial port. The only packages this project adds to Kismet's own list are autoconf, automake and python3, for the script. The script also needs pkg-config, which Kismet's build needs anyway: Kismet's configure.ac uses pkg-config's macros, and its own m4 folder has no copy of them.
Built without libwebsockets (--disable-libwebsockets), the capture helpers can do remote capture only over Kismet's legacy TCP port, with --tcp; the Kismet server keeps its own websockets either way. When libwebsockets is missing, the error configure prints is pkg-config's Package requirements (libwebsockets >= 3.1.0) were not met; the option that turns it off is --disable-libwebsockets.
Two package sets that work:
The full set, as on the test Raspberry Pi (Raspberry Pi OS (64-bit), based on Debian 13):
sudo apt-get install -y build-essential git pkg-config autoconf automake python3 \
libwebsockets-dev zlib1g-dev libnl-3-dev libnl-genl-3-dev libcap-dev libpcap-dev \
libnm-dev libdw-dev libsqlite3-dev libsensors-dev libusb-1.0-0-dev libmosquitto-dev \
libpcre2-dev libssl-devThis set has not been tried on a freshly installed system, where more may be missing; configure names any library it cannot find. The smaller set below is what the Docker image installs on a fresh Debian 13, and that does build.
The smaller set, as in the Docker image. It needs --disable-lmsensors --disable-mosquitto on the configure line (below). The Docker image also passes --disable-libnm, which is optional: without libnm, configure only warns, and it prints the NetworkManager warning at the end with or without that option.
sudo apt-get install -y --no-install-recommends ca-certificates git build-essential pkg-config \
autoconf automake python3 libwebsockets-dev zlib1g-dev libnl-3-dev libnl-genl-3-dev \
libcap-dev libpcap-dev libsqlite3-dev libpcre2-dev libssl-dev libusb-1.0-0-dev libdw-devFedora and Arch have not been tested; likely package names are on Install on Linux. macOS and the BSDs are on Install on macOS and BSD.
The two configure lines this project uses:
| Where | Command |
|---|---|
| Raspberry Pi, installed into the home directory | ./configure --prefix=$HOME/kismet-install --disable-python-tools --disable-librtlsdr --disable-ubertooth --disable-bladerf --disable-btgeiger |
| Docker image | ./configure --prefix=/usr --sysconfdir=/etc/kismet --localstatedir=/var --disable-python-tools --disable-librtlsdr --disable-ubertooth --disable-bladerf --disable-btgeiger --disable-libnm --disable-lmsensors --disable-mosquitto |
For the Pi layout:
cd ~/src/kismet
./configure --prefix=$HOME/kismet-install --disable-python-tools --disable-librtlsdr \
--disable-ubertooth --disable-bladerf --disable-btgeigerLeave out --prefix to install into /usr/local, Kismet's default. Add --disable-lmsensors --disable-mosquitto if you installed the smaller package set (the Docker image adds --disable-libnm too).
What each option means. None of them affects the ESP32-C5 boards:
| Option | What it leaves out | Without the option |
|---|---|---|
--prefix=<dir> |
Nothing; it sets where make install puts everything |
/usr/local |
--disable-python-tools |
Kismet's legacy Python modules and Python-only sources | Already off by default at this commit |
--disable-librtlsdr |
Sources for RTL-SDR software-defined radio dongles |
configure stops unless librtlsdr's development files are installed |
--disable-ubertooth |
Ubertooth One support |
configure warns and builds without it when the Ubertooth libraries are missing |
--disable-bladerf |
bladeRF 2 Wi-Fi support | Already off by default |
--disable-btgeiger |
A Bluetooth LE Geiger counter source, which also needs the Python tools | Already off by default |
--disable-libnm |
NetworkManager support | A warning |
--disable-lmsensors |
Temperature readings |
configure stops unless libsensors-dev is installed |
--disable-mosquitto |
MQTT |
configure stops unless libmosquitto-dev is installed |
The Docker image leaves these out because they are hardware the image is not for (SDRs, Ubertooth, bladeRF) or host services a container does not have (NetworkManager, lm-sensors, MQTT).
One more option you may want: --with-suidgroup=<group> changes the group of setuid helpers from kismet (see "Installing" below).
At the end, configure prints a summary. Check these lines:
Installing into: /home/pi/kismet-install
Websocket datasources: yes
ESP32-C5: yes
-
ESP32-C5: yesmeansadd-to-kismet.shran on this tree. If the line is missing, run the script and thenconfigureagain. -
Websocket datasources: yesmeans the helpers can do remote capture over Kismet's web port. -
Setuid group: kismetandPrelude SIEM : norun together on one line. That is an upstream cosmetic bug (a missing newline), not a problem.
Kismet is a large C++ program. Each parallel compiler can use well over a gigabyte, so memory, not the number of cores, decides how many jobs to run. The Dockerfile's rule of thumb is about 1.5 GB per compiler.
What has been measured:
| Machine | Jobs | Result |
|---|---|---|
| Raspberry Pi 4, 8 GB, Raspberry Pi OS (64-bit) based on Debian 13, arm64 | make -j4 |
About 78 minutes, no out-of-memory kill. At the peak, phy_80211.cc (2.4 GB), phy_80211_dissectors.cc (1.8 GB) and phy_80211_components.cc (1.7 GB) compiled at once, leaving about 1.1 GB available and about 40 MB of swap in use |
| Raspberry Pi 4, 8 GB, Docker image |
-j4, chosen automatically |
About 80 minutes for the whole image, almost all of it compiling Kismet |
| Windows PC, 20 cores, 16 GB, Docker Desktop | -j4 |
18.5 minutes for the whole image; the configure, compile and install step took 1,051.6 s |
| WSL2, 15 GB | make -j20 |
Exhausted memory; the Windows host was left with 1.2 GB free and the WSL distribution had to be terminated |
| WSL2, 15 GB |
make -j4 under nice
|
Completed (time not recorded) |
From these, for machines that were not measured (worked out from the peaks above and the 1.5 GB rule; none of these has been tried):
| Memory | Jobs |
|---|---|
| 8 GB or more | -j4 |
| 4 GB | -j2 |
| 2 GB |
-j1, with at least 2 GB of swap: one file alone needed 2.4 GB on the test Pi |
The Docker image build on a 2 GB machine picks -j1 and needs the same swap.
Warning: Do not use
make -jwith the number of cores on a machine with many cores. At-j20, WSL2 used up the memory of a 15 GB machine.
The Docker build works the number out from free memory: MemAvailable divided by 1.5 GB, capped at the number of cores and at 4, and at least 1. The same calculation for a native build:
jobs=$(awk -v cores="$(nproc)" '/^MemAvailable:/ { j = int($2 / 1500000); if (j > cores) j = cores; if (j > 4) j = 4; if (j < 1) j = 1; print j }' /proc/meminfo)
echo "building with -j$jobs"On a Pi, or over SSH, run the build in the background so that it survives the session ending. $jobs is the number worked out above; in a new shell, run the jobs= line again first, or write the number in its place:
cd ~/src/kismet
nohup nice make -j"$jobs" > ~/kismet-build.log 2>&1 &
tail -f ~/kismet-build.logCtrl+C stops tail, not the build. nice keeps the machine responsive while it compiles.
Warnings are expected: the Pi build logged 76 warning lines, all from Kismet's own files. The C helper compiles without warnings of its own under -Wall, and under -Wextra apart from unused-parameter and sign-compare; the three it shows come from Kismet's capture_framework.h and appear for every helper.
When the build has finished, both programs exist:
ls -l ~/src/kismet/kismet ~/src/kismet/capture_esp32c5/kismet_cap_esp32c5Once the whole tree has been built, make -C capture_esp32c5 in it builds only the C helper. To install a new version of the helper, run make in the whole tree instead ("Rebuilding after a helper change" below explains why).
make install puts everything under the prefix given to configure:
| Folder | What goes there |
|---|---|
bin/ |
kismet, kismet_server (an old name that now only starts kismet), every capture helper kismet_cap_* (19 on the test Pi, kismet_cap_esp32c5 among them), the kismetdb_* log tools and kismet_discovery
|
etc/ |
Kismet's config files: kismet.conf, kismet_httpd.conf, kismet_alerts.conf, kismet_memory.conf, kismet_logging.conf, kismet_filter.conf, kismet_uav.conf, kismet_80211.conf, kismet_wardrive.conf
|
share/kismet/ |
The web UI (httpd/) and the manufacturer and aircraft lists |
lib/pkgconfig/ |
kismet.pc |
-
Config files are never replaced. An existing file stays, with the message
<file> already exists; it will not be automatically replaced.make forceconfigsoverwrites them all with Kismet's stock versions. Keep your own settings inkismet_site.conf, whichmake installnever creates or touches (Kismet Configuration). -
The helper must sit next to
kismet. Kismet starts capture helpers only from its ownbinfolder (helper_binary_path=%B). A missing helper shows asUnable to find driver for '<definition>'for a source withouttype=. Withtype=esp32c5, Kismet at this commit does not say so: it stops at start withUncaught exception "kis_external tried to write with no io handler"and a stack trace. -
DESTDIRis honoured, for staging an install elsewhere. The Docker build usesmake install DESTDIR=/out INSTUSR=root INSTGRP=root SUIDGROUP=root.
Kismet's install rules take their owners and groups from these make variables. A value given on the make command line overrides the default, including in the rules make runs from other rules:
| Variable | Default | Used for |
|---|---|---|
INSTUSR |
root |
Owner of every installed file |
INSTGRP |
root (wheel on a system without a root group) |
Group of the programs and config files |
SUIDGROUP |
kismet (staff on macOS), or what --with-suidgroup= set |
Group of the setuid helpers, and of kismet_cap_rz_killerbee even in a plain make install (and of the CoreWLAN helper on macOS) |
MANGRP |
Not set by configure
|
No install rule uses it at this commit |
Two of these defaults stop a plain make install:
-
The owner. Every file is installed with owner
INSTUSR, which isroot. A normal user cannot give a file to root, so without the variablesmake installstops at its first file,kismet:/usr/bin/install: cannot change ownership of '/home/pi/kismet-install/bin/kismet': Operation not permitted -
The
kismetgroup. Even a plainmake installinstalls Kismet'skismet_cap_rz_killerbeehelper with groupSUIDGROUPand mode 4550, and that helper is built whenever libusb is found. Run as root on a system with nokismetgroup,make installgets pastkismetand the log tools and stops at that helper:/usr/bin/install: invalid group 'kismet'
Three ways out; all three have been run:
| Situation | Command |
|---|---|
| Installing into a prefix you own, without root (the Pi) | make install INSTUSR=$(id -un) INSTGRP=$(id -gn) SUIDGROUP=$(id -gn) |
Installing as root, without a kismet group (WSL2) |
make install INSTGRP=root SUIDGROUP=root |
| Creating the group Kismet expects |
sudo groupadd kismet, then sudo make install
|
With the kismet group in place, a plain make install as root installs kismet_cap_rz_killerbee with group kismet and the rest as root's. The WSL2 run also passed MANGRP=root. It does no harm, but at this commit it has no effect.
Kismet offers two install targets:
| Target | What it does | Needs root |
|---|---|---|
make install |
Installs everything as ordinary programs, then the config files that do not exist yet. No groupadd, and no setuid bits except on kismet_cap_rz_killerbee (mode 4550) |
Yes, unless you pass INSTUSR, INSTGRP and SUIDGROUP for a prefix you own |
make suidinstall |
Runs groupadd -r -f kismet, installs as make install does, then reinstalls the helpers that need privileges as setuid root, group kismet, mode 4550, so that members of kismet can run them |
Yes |
Kismet recommends make suidinstall for its Wi-Fi and Bluetooth helpers, which have to reconfigure network interfaces. After a plain make install it prints Kismet has NOT been installed suid-root. This means you will need to start it as root.... That advice is about those helpers.
The C helper needs neither root nor any capability, only read and write access to the board's serial port. With make install it is an ordinary program (on the Pi, -rwxr-xr-x, owned by the user) that runs as whoever runs Kismet. That user needs the serial port's group, dialout on Debian, Ubuntu and Raspberry Pi OS:
sudo usermod -aG dialout $USERLog out and in again afterwards.
The Raspberry Pi tests ran this way: make install with the three variables above, Kismet run as a normal user who was already in dialout, and all three radios captured. The WSL2 tests ran Kismet as root, and the Docker image runs it as root too.
make suidinstall would also install kismet_cap_esp32c5 setuid root, mode 4550, group kismet, because add-to-kismet.sh copies the CatSniffer helper's install lines. The C helper does not need that. Installed that way, it still drops every capability first thing, as it does whoever starts it, and keeps only root's user ID: it can open device nodes that root owns, the boards' among them, and, with the permissions of the user who runs it, a port that user may open, such as their own pseudo-terminal; nothing else. Built without libcap-dev, though, it drops nothing, and a setuid install would run it with full root rights. Setuid installs have been checked only by the C test harness, which simulates them, never with a real board. If you want make suidinstall for Kismet's other helpers, users have to be in kismet (sudo usermod -aG kismet $USER, then log in again), or opening a source fails with IPC cannot run binary '<path>', Kismet was installed setgid and you are not in that group....
Running all of Kismet as root also works, but Kismet raises its ROOTUSER alert, and its web login and API keys then live in /root/.kismet.
Kismet compiles with debug information and make install copies the programs as they are. On the test Pi the installed kismet is 489,329,152 bytes, and kismet_cap_esp32c5 424,288 bytes (built from commit f8e6792). The Docker build runs strip --strip-debug on every program, which keeps the symbol table for stack traces and took kismet from 469 MB to 14.7 MB on amd64. Stripping a native install has not been tried, and the disk space the source tree and build need has not been measured.
~/kismet-install/bin/kismet --version
~/kismet-install/bin/kismet_cap_esp32c5 --version
~/kismet-install/bin/kismet_cap_esp32c5 --list 2>&1| Command | Prints | Exit status |
|---|---|---|
kismet --version |
Kismet 2026.09.0-cfe427074 (test Pi) |
1 (Kismet always exits 1 here) |
kismet_cap_esp32c5 --version |
The same version without the word Kismet, for example 2026.09.0-cfe427074
|
0 |
kismet_cap_esp32c5 --list |
The boards it can see, on stderr, hence 2>&1
|
2 |
kismet_cap_esp32c5 --help |
The usage of Kismet's capture framework, with the remote capture options | 255 |
The exit statuses are upstream behaviour, not failures; a script should check the text. --list reads only Linux sysfs and /proc/locks and never opens a port, since opening one can reset the board. With one idle board on ttyACM0 it prints each board three times, once per radio:
esp32c5 supported data sources:
esp32c5-ttyACM0:mode=wifi (Espressif USB-Serial-JTAG (F0:F5:BD:01:02:03))
esp32c5zigbee-ttyACM0:mode=zigbee (Espressif USB-Serial-JTAG (F0:F5:BD:01:02:03))
esp32c5btle-ttyACM0:mode=btle (Espressif USB-Serial-JTAG (F0:F5:BD:01:02:03))
- The three lines are alternatives: a board captures with one radio at a time.
- A board that a running source is using is left out, all three lines of it.
- Every Espressif chip on its native USB port has the same USB ID,
303a:1001, so other ESP32 boards are listed too. The hardware label names the USB device, not the chip, for that reason. - With no free board:
esp32c5 - No supported data sources found...
Finally, Kismet itself has to know the source type. Start it with a board as on Install on Linux and look for Found type 'esp32c5' in its output. A Kismet built without add-to-kismet.sh answers a definition with type=esp32c5 with Unable to find datasource for 'esp32c5'.
The project's C test harness also runs against a built tree: sh tests/c/run.sh ~/src/kismet, run in this project's folder, compiles the repo's helper against the tree's libkismetdatasource.a and prints ALL OK. See Development and Testing.
A new version of the C helper does not need a new Kismet build:
-
Update this project:
git -C ~/esp32c5-kismet-wifi-interface pull -
Copy the new files into the Kismet tree. The script copies only the files that changed and skips edits already made:
sh ~/esp32c5-kismet-wifi-interface/kismet/add-to-kismet.sh ~/src/kismet
-
Rebuild, as your normal user, with
makein the tree:cd ~/src/kismet nice make -j4
This rebuilds only what the new files change, not all of Kismet. A file the script did not copy keeps its time, so
makeleaves alone what depends on it:- A new
capture_esp32c5.c: only the C helper is compiled again, in seconds. - A new
datasource_esp32c5.h:kismet_server.cc, which includes it, is compiled again, and thekismetprogram (about 490 MB) is linked again. That took 87 seconds on a 4-core x86_64 machine, and about 3 minutes on the test Pi 4 in a rebuild that also relinked every capture helper. - A capture framework fix new to this tree: every capture helper is rebuilt once, but not
kismet. On the test Pi,libkismetdatasource.aand all 19 capture helpers took 8.5 s at-j4.
Run
makein the whole tree rather thanmake -C capture_esp32c5: the helper's own Makefile does not rebuildlibkismetdatasource.a, andmake installin step 4 builds whatever is still out of date anyway, as root when you install withsudo. Runmakefirst, so thatmake installonly copies files.If the script edited Kismet's
Makefile.inor regeneratedconfigure, which a helper update rarely does,makeprints'Makefile.in' or 'configure' are more current than this Makefile. You should re-run 'configure'.It is only a notice, andmakecarries on. When you see it, or when the update changedkismet/capture_esp32c5/Makefile.in, from whichconfigurewrites the helper's Makefile, run the same./configureline you used before, thenmakeagain. Runningconfigureagain with the same options does not make Kismet compile again:config.hkeeps its time, andmakethen finds nothing to do. - A new
-
Install with the same command as the first time. Config files you already have are left alone:
cd ~/src/kismet make install INSTUSR=$(id -un) INSTGRP=$(id -gn) SUIDGROUP=$(id -gn)
For a system-wide install, this is
sudo make install INSTGRP=root SUIDGROUP=root. Run it only aftermakein step 3 has finished:sudo make installon a tree that still needs compiling compiles as root and leaves root-owned files in your source tree, which can make the nextmakeas your user fail. -
Restart Kismet. A source that is already running keeps using the helper program it started with.
This was checked on the test Pi on 2026-10-02: the script, as of commit f8e6792, copied the new helper files, brought the login fix up to date and added two more framework fixes; make rebuilt the capture helpers in 8.5 s and left kismet as it was; and make install without sudo put the new helper next to it. Later that day the current script updated the same tree in place: it copied capture_esp32c5.c, made the redirect and Host header edits to capture_framework.c and capture_framework.h, and on a second run printed only its Done. line; the tree then matched a fresh cfe427074 tree patched by the same script, make took 9.2 s, and make install again needed no sudo. The Pi's earlier rebuilds ran with a first version of the script, which copied the files on every run, so that kismet was relinked every time.
For the Docker image, a change to capture_esp32c5.c alone rebuilds in about a minute on the Pi, because the Kismet layer stays cached. A change to datasource_esp32c5.h, add-to-kismet.sh or the helper's Makefile.in rebuilds Kismet from the start (about 80 minutes on a Pi 4). Guide: Updating covers updating the helper, the firmware and the image together.
Kismet has no make uninstall.
A home-directory install is one folder. Stop Kismet, then:
rm -rf ~/kismet-installA system-wide install into /usr/local has to be removed by hand. These are the files make install puts there:
sudo rm -f /usr/local/bin/kismet /usr/local/bin/kismet_server /usr/local/bin/kismet_discovery
sudo rm -f /usr/local/bin/kismet_cap_* /usr/local/bin/kismetdb_*
sudo rm -f /usr/local/lib/pkgconfig/kismet.pc
sudo rm -rf /usr/local/share/kismet
sudo rm -f /usr/local/etc/kismet*.confChecked against a fresh install into /usr/local: these commands remove every file it made and leave only the empty folders.
The last line also removes your own kismet_site.conf; copy it somewhere first if you want to keep it.
Your per-user state is in ~/.kismet in the home directory of the user Kismet ran as: the web login (kismet_httpd.conf), the API keys (session.db) and the server's ID. Delete it if you want those gone too. A systemd unit you set up (Guide: Running as a Service) has to be disabled and removed separately.
The Kismet source tree. Delete ~/src/kismet if you no longer need it. To keep the clone but undo add-to-kismet.sh, run these two commands (or delete the folder and clone Kismet again):
git -C ~/src/kismet checkout -- .
git -C ~/src/kismet clean -xfdclean -xfd also removes everything the build produced, so the next build starts from the beginning. Afterwards git -C ~/src/kismet status shows no change, and another commit can be checked out.
| What you see | Cause | What to do |
|---|---|---|
does not look like a Kismet source tree |
The script was given the wrong path | Give it the top folder of the Kismet clone |
The script fails because aclocal or autoconf is not found |
autoconf or automake is missing |
sudo apt-get install autoconf automake, then run the script again |
The script stops after regenerating configure with configure.ac:83: error: possibly undefined macro: AC_DEFINE (and the same for AC_MSG_WARN and AC_MSG_ERROR) |
pkg-config is missing, so aclocal cannot find its macros |
sudo apt-get install pkg-config, then run the script again |
anchor not found, Kismet has changed: ... |
The tree is on a Kismet commit the script does not know | Check out cfe427074, or see "Trying a newer Kismet" |
configure: error: Package requirements (librtlsdr) were not met |
No librtlsdr, and --disable-librtlsdr not given |
Add --disable-librtlsdr
|
Package requirements (libmosquitto) were not met |
No libmosquitto-dev
|
Install it, or add --disable-mosquitto
|
Package requirements (libwebsockets >= 3.1.0) were not met |
No libwebsockets-dev
|
Install it |
Package requirements (libusb-1.0) were not met |
No libusb-1.0-0-dev
|
Install it |
required libsensors lm-sensors missing, configure with --disable-lmsensors ... |
No libsensors-dev
|
Install it, or add --disable-lmsensors
|
No ESP32-C5: yes line in the configure summary |
The script did not run on this tree, or failed | Run the script, read its output, then run configure again |
'Makefile.in' or 'configure' are more current than this Makefile. You should re-run 'configure'. |
A notice, not an error: the script changed Kismet's Makefile.in or regenerated configure after the tree was configured. make carries on |
Run configure again with the options you used before |
| A compiler is killed, or the machine stops responding | Out of memory | Fewer jobs: -j2 or -j1
|
/usr/bin/install: cannot change ownership of '...': Operation not permitted |
make install as a normal user without the variables: it tries to make root the owner |
Pass INSTUSR, INSTGRP and SUIDGROUP; see "Owners, groups and the kismet group" |
/usr/bin/install: invalid group 'kismet' |
make install as root, with no kismet group |
See "Owners, groups and the kismet group" |
A source fails with Unable to find driver for '<definition>'
|
kismet_cap_esp32c5 is not in the same bin folder as kismet, or the definition is one the helper does not accept (Install on Linux) |
Install both from the same tree with the same prefix |
Kismet stops at start with Uncaught exception "kis_external tried to write with no io handler" and a stack trace |
A source with type=esp32c5, while kismet_cap_esp32c5 is not in the same bin folder as kismet
|
Install both from the same tree with the same prefix |
IPC cannot run binary ... |
Installed with make suidinstall, and the user is not in kismet
|
Add the user to kismet and log in again, or use make install
|
ERROR: Tried to re-register duplicate alert FLIPPERZERO at every start |
Upstream Kismet at this commit | Nothing; it is harmless |
More on Troubleshooting.