How to Capture 802.11 Frames on a Connected Interface on Linux

Jacob Pradels
Jacob Pradels··37 min read

Founding engineer at yeet, working on kernel-side observability and the tooling around it. I write about eBPF, Linux internals, and why your telemetry bill looks the way it does.

To capture 802.11 frames on a connected interface on Linux, read them inside the kernel's Wi-Fi stack instead of switching the radio. airtop attaches two eBPF fentry programs, one on ieee80211_rx_list for every received frame and one on cfg80211_inform_bss_frame_data for every access point a scan turns up, and streams both to userspace over ring buffers. The interface never leaves managed mode, so the association you are trying to diagnose survives the diagnosis. The cost is fixed and worth knowing up front: a radio tuned to one channel hears one channel, so live frame counts are mostly your own traffic and the neighbour list fills in only as the operating system scans.

I write eBPF programs for a living and I am not an RF engineer. I have never run a proper site survey, I do not own a spectrum analyzer, and the people who plan 5 GHz channel reuse for a conference hall know things about antenna patterns that I do not. What I have spent time on is kernel receive paths, and mac80211's is a strange one to read. By the time a frame reaches your network stack it is wearing a synthesized Ethernet header, and the parts that would have told you why your call dropped, the 802.11 frame subtype, the transmitter address, the RSSI the driver measured, existed for a few microseconds and were then discarded as uninteresting. They are not gone because capturing them is hard. They are gone because nothing asked.

The alternatives: airodump-ng, Kismet, Wireshark, wavemon, and airtop

Every Wi-Fi tool on Linux answers one of two different questions, and the split is what makes the advice confusing. One group asks "what is happening in the band", which requires hearing channels your radio is not tuned to, which requires monitor mode and channel hopping, which requires giving up your association. The other group asks "what is happening on my link", which does not. Picking the wrong group is how people end up disconnecting the laptop they were trying to debug.

ToolKeeps your connection upSees channels other than yoursLists nearby APsWhat it needs
airtopYes, the interface stays in managed modeNo, your channel plus whatever brief scans touchYes, from cfg80211 scan resultsLinux with BTF, a mac80211/cfg80211 driver, the yeet daemon
wavemonYesNoYes, in the F3 scan windowCAP_NET_ADMIN for scans and encryption info
iw and nmcliYesNoYes, one snapshot per scanRoot for iw dev <iface> scan
airodump-ngNo, monitor mode and connection managers killedYes, hops channels by defaultYes, with PWR and Beacons per BSSIDA monitor-capable adapter, airmon-ng check kill
KismetNo, creates wlan0mon and downs the parent interfaceYes, hops all detected bandsYesA monitor-capable adapter
Wireshark and tsharkNo, 802.11 headers require monitor modeOne channel at a timeOnly from beacons you capturedMonitor mode, otherwise you get fake Ethernet headers
sparrow-wifiYes for ordinary scanningNeeds a HackRF One or Ubertooth One for spectrumYesPython 3.8+, PyQt5, root for iw scan
LinSSIDYesNoYes, with signal-by-channel graphsA Qt5 and Qwt desktop

Read the first column and the second together, because that pairing is the whole decision. Four of these tools cannot run on the machine whose Wi-Fi is broken while it is broken, and three of the four are the ones everybody recommends. The routes below are in the same order as the table, and each one is genuinely the right answer to something.

airtop: eBPF fentry hooks on mac80211 and cfg80211

airtop is an eBPF 802.11 RF dashboard for Linux: it draws live frame activity, per-station signal traces and a frequency spectrum of nearby access points in a terminal, on the interface you are already connected with. A single BPF object attaches two fentry programs and streams events over ring buffers. fentry/ieee80211_rx_list catches every received 802.11 frame and reads its type and subtype, its addresses, and the RSSI out of ieee80211_rx_status. fentry/cfg80211_inform_bss_frame_data catches every access point the kernel's own scans discover, with SSID, channel and signal. There is no mode switch anywhere in that path, because it is a read of data the kernel is already handling.

curl -fsSL https://yeet.cx | sh
yeet run https://github.com/yeet-src/airtop

That is the whole setup. It runs until Ctrl-C, reflows when the terminal resizes, and wants at least 80 by 24. What you get is a header carrying uptime, total frames seen, live stations, discovered APs, beacon count, deauth count and your current channel, over five panels: a frequency spectrum with each AP drawn as a hump at its real center frequency in MHz, a braille line graph of RSSI over time per live station, a heatmap of frame types over time, a histogram of received frames by RSSI, and a list of discovered SSIDs sorted strongest first. The dashboard runs in yeet's V8 runtime, subscribing to those ring buffers, which is why the rendering is JavaScript and the capture is not.

wavemon: your own link, plus an F3 scan window

wavemon is the incumbent in the terminal slot and it is better than its reputation suggests. The man page lists an info screen on F1 with "a condensed overview of wireless-specific parameters and network statistics, as well as bar graphs", a level histogram on F2 that is a "full-screen histogram plot showing the evolution of levels with time", and a scan window on F3 showing "access points and other wireless clients". So the common claim that wavemon only shows your own connection is not quite right, and it is worth being precise about, because the actual gap is narrower and more interesting.

What wavemon gives you is your link's signal over time and a periodically refreshed list of what a scan found. What it does not give you is the two views that answer a congestion question: overlapping signal humps plotted against a real frequency axis, so you can see that three neighbours are stacked on channel 6 with yours, and per-frame activity broken out by 802.11 frame type, so a burst of management frames is visually distinct from a burst of data. Note also that some wavemon operations, "such as displaying encryption information or performing scans, require CAP_NET_ADMIN privileges", which is why distributions ship it setuid-root or ask you to grant the capability.

iw and nmcli: point-in-time dBm from the command line

Before reaching for any dashboard, two commands answer a surprising share of Wi-Fi questions, and both work on a connected interface without privileges beyond what a scan needs. iw dev <iface> link prints the current association including a signal: -29 dBm line, and nmcli dev wifi prints the visible access points with a SIGNAL percentage and a BARS column. The ostechnix walkthrough collects the whole family, iwconfig with its Link Quality=70/70 and Signal level=-40 dBm, and iwlist <iface> scanning filtered for signal.

iw dev                       # which interfaces exist, and their type
iw dev wlan0 link            # current association: BSSID, freq, signal in dBm
nmcli -f SSID,CHAN,SIGNAL dev wifi list

The limitation is not accuracy, it is time. Every one of these is a point-in-time reading, so a link that is fine now and terrible in eleven seconds looks identical to a link that is fine. There is no history, no distribution, and no way to correlate a signal drop against what kind of frame arrived at the same moment. That is the gap a dashboard fills, and it is the reason the same reader ends up looking for something past nmcli rather than being badly served by it.

airodump-ng: monitor mode, channel hopping, and no association

airodump-ng is the correct answer to a band survey and it does not pretend otherwise. Its own documentation tells you to "make sure you have started your card in monitor mode with airmon-ng", and then, unambiguously, to "be sure to stop all connection managers prior to using the aircrack-ng suite", recommending airmon-ng check kill because a connection manager "can remove the card from monitor mode and halt data capture". That is the airodump-ng disconnects wifi managed mode conflict in the vendor's own words, and it is a design decision rather than a bug.

In exchange you get things a connected interface cannot produce. By default airodump-ng cycles through channels rather than sitting on one, so a single adapter covers the band over time. The output columns are BSSID, PWR, Beacons, #Data, CH, MB, ENC and ESSID, where PWR is the "signal level reported by the Wi-Fi adapter" and the docs calibrate it for you: "a strong signal is around -40. An average one is around -55, and a weak one starts around -70", with -1 meaning the driver does not report signal at all. If your question is "what is in this building", run airodump-ng on a second adapter and accept that the first one is doing the connecting.

Kismet: a wlan0mon vif and full channel hopping

Kismet is the heavyweight, and its Linux Wi-Fi datasource documentation is specific about the cost. Monitor mode is "a special mode where the card is told to report all packets seen, and to report them at the 802.11 link layer instead of emulating an Ethernet device", which is exactly the property you want and exactly the property that makes it incompatible with staying online. Kismet creates a virtual interface for capture, so configuring wlan0 gets you wlan0mon, and it channel hops across all detected bands unless you set channel_hop=false.

The part people miss is the parent interface. Kismet "must disable (set to down state)" the non-monitor interfaces on the same card for reliable capture and channel control, which temporarily removes that card from normal networking. It restores afterwards, assuming NetworkManager has not been reconfigured underneath it. For long-running wireless intrusion detection with multiple radios, GPS and a logging backend, this is the tool and nothing here competes with it. For twenty minutes of "why does this call keep stuttering" on a single laptop, the setup cost exceeds the question.

Wireshark and tshark: per-frame decode from a pcap

Wireshark's own WLAN capture setup page states the constraint in one line: "in order to see 802.11 headers, you will have to capture in monitor mode." Without it, adapters "often transform 802.11 data packets into fake Ethernet packets before supplying them to the host", so what lands in your capture is a synthesized Ethernet header where the 802.11 header used to be. The management and control frames that carry the diagnostic signal, beacons, probes, deauths, never had an Ethernet equivalent to be transformed into, so they simply are not there.

The same page is clear that ordinary operation delivers only unicast addressed to your adapter plus multicast and broadcast, and that seeing traffic between two other stations means turning off the hardware's MAC address filtering, which is what monitor mode does. None of this makes Wireshark the wrong tool. It makes it an offline analysis tool: when you have a pcap and you need to know the exact contents of the fourth frame in a failed association, nothing else comes close. It just cannot be the thing that was running when the failure happened, on the machine that failed, unless you planned for that in advance with separate hardware.

sparrow-wifi and LinSSID: GUI spectrum and channel graphs

sparrow-wifi describes itself as a 2.4 GHz and 5 GHz Wi-Fi and Bluetooth spectral awareness tool for Linux, built on PyQt5, needing Python 3.8 or later and root for iw scan. Ordinary scanning does not require monitor mode; the features that do are the DroneID RemoteID capture and the Falcon pen-testing plugin. It also does real spectrum analysis, but through a HackRF One or an Ubertooth One rather than through your Wi-Fi card, which is a hardware purchase rather than a mode switch. LinSSID is the simpler one, a Qt5 and Qwt application showing a sortable table of visible access points plus graphs of signal by channel and signal over time on both bands.

Both are good and both are desktop applications. That matters more than it sounds, because the reader who has hit wavemon's ceiling is usually on a machine reached over SSH, or on a laptop where launching a Qt app to diagnose the wireless link is a category error. sparrow-wifi's own description positions it as "a more comprehensive GUI-based replacement for tools like inSSIDer and linssid", which is an accurate read of where that whole family sits. The set a reader ends up comparing runs the same way throughout: Kismet, sparrow-wifi, Kismon, LinSSID and WiFiman, with the neighbour and spectrum views concentrated in the graphical ones and the terminal holding wavemon and the iw family.

My laptop drops off the network the second I run airmon-ng. Where did the 802.11 frames go?

They went into the kernel, got their 802.11 headers stripped, and came out as Ethernet-shaped packets. Monitor mode does not create those frames, it stops the discarding, and the price of stopping the discarding is the association. The monitor mode definition is precise about why: it "allows packets to be captured without having to associate with an access point or ad hoc network first", and "usually the wireless adapter is unable to transmit in monitor mode and is restricted to a single wireless channel", subject to the driver, firmware and chipset. A radio that cannot transmit cannot ACK, and a station that cannot ACK is not on the network.

The mechanics of the switch are the giveaway. A practitioner walkthrough of Linux wireless monitoring shows the canonical sequence, and the first command is the one that ends your connectivity:

sudo ip link set wlan1 down    # the monitor-mode path: step one ends your connectivity
sudo iw wlan1 set monitor control
sudo iw wlan1 set channel 149
sudo ip link set wlan1 up

That author's answer to the one-channel-at-a-time limit was to build a laptop with seven USB adapters, which is a completely reasonable engineering response and also a clear statement of what the technique costs. The three standard ways to enable monitor mode on Linux are that iw dev sequence, airmon-ng, which renames the interface so wlan1 becomes wlan1mon, and the older iwconfig <iface> mode monitor. All three change what the radio is doing. None of them are a way to capture 802.11 frames on a connected interface.

The third path is to stop treating the radio as the capture point. ieee80211_rx_list is a function inside mac80211, the kernel's software MAC layer, and by the time it runs the driver has already handed the frame up with its ieee80211_rx_status attached, RSSI included. An fentry program there sees the frame as the kernel sees it, which is to say with its type and subtype intact, before anything downstream flattens it. cfg80211, the configuration API for 802.11 devices in Linux that "bridges userspace and drivers", is where scan results land, and cfg80211_inform_bss_frame_data is the doorway every discovered BSS walks through. Two hooks, no mode change, and passive wifi monitoring without a second USB adapter.

Why can't Wireshark show me 802.11 headers on my connected interface?

Because your adapter is handing the host something else. Wireshark's capture setup page for WLAN says adapters "often transform 802.11 data packets into fake Ethernet packets before supplying them to the host", which means the 802.11 header is not truncated or corrupted in your capture, it was never delivered. Turning off that transformation is what monitor mode is, and turning it off is what takes the link down. The same document notes that in ordinary operation the hardware only passes you unicast addressed to your adapter, plus multicast and broadcast.

An in-kernel hook sidesteps the first problem and inherits the second, and both halves of that sentence matter. fentry/ieee80211_rx_list runs before the Ethernet-shaped conversion, so the frame's type, subtype and addresses are all readable there, which is how a frame-type heatmap is possible at all (extrapolated, grounding 2 — review). What it cannot do is conjure frames the hardware filtered out before mac80211 ever saw them, so the frames a connected interface hears are broadcast and multicast, which includes every beacon and every broadcast deauth on your channel, plus unicast addressed to you (extrapolated, grounding 3 — review). That is a real boundary and it is the reason airtop counts frames rather than claiming a full picture of the channel.

The practical difference is which questions each shape answers. If the question is "what exactly was in the association response", you need per-frame decode, and that means monitor mode and Wireshark and no association. If the question is "is anything deauthing, how congested am I, and how is my signal moving", the frames that answer it are broadcast management frames that arrive at a connected interface anyway. The second question is the one people actually have at 11pm, and it has been getting answered with tooling built for the first.

My wifi keeps disconnecting. Is someone sending deauth frames or is the router just flaky?

Count the deauth frames. A healthy network sits at zero, so a non-zero deauthentication count is the entire diagnostic, and airtop puts that counter in the header and turns it red the moment it is non-zero. In the frame feed panel, which is a heatmap of frame types over time with cell colour set by how many of that type arrived in that slice, a deauth flood lights up one row instantly while everything else stays flat. A flaky access point produces a different picture: reassociations, beacon gaps, a signal trace that sags before each drop, and no deauth row at all.

Detecting deauthentication attacks on your own network from Linux is normally where this gets circular. The GitHub deauth topic filtered to Python is dominated by tools for performing the attack, with roughly one detector for every nineteen attack tools, several of the latter built on Scapy. The detectors that exist follow the same pattern: a representative Scapy deauth detector needs Python 2.7 or later plus the Scapy module, and "works by analyzing the number of deauthentication packets captured" against a threshold. Scapy captures what the interface delivers, so getting management frames delivered means monitor mode, which means the adapter counting the deauths is not the adapter being deauthed.

That is the trap worth naming, because it is a genuine logical problem rather than a tooling inconvenience. The device you want protected is the device you must take offline to protect it, unless you buy a second radio and leave it running. A deauth frame counter without a monitor-mode adapter changes the economics: the counter runs on the laptop that keeps dropping, during the window when it is dropping, and the answer arrives as a number rather than a hypothesis. Its author is honest that a threshold detector "may require further investigation to confirm actual malicious activity", and the same caution applies here, because a neighbour's access point doing something legitimate but noisy also produces management frames.

One boundary, stated where it belongs rather than in a footnote: airtop detects and never transmits. There is no deauth injection, no probe flooding, no active scan of its own beyond what your operating system was already doing. That is not a policy choice bolted on afterwards, it follows from the mechanism, since two fentry hooks on a receive path and a scan callback have no transmit path available to them. If you need to test your own network's resilience by sending frames, aireplay-ng is the tool and it needs monitor mode like everything else in that family.

My signal reads -45 dBm and throughput is still terrible. Who else is on my channel?

Strong signal and bad throughput is co-channel congestion until proven otherwise, and it is invisible to every tool that reports only your own link. Wi-Fi channels are center frequencies in MHz, channel 6 sitting at roughly 2437 MHz and channel 161 at roughly 5805 MHz, and 2.4 GHz channels are wide enough to overlap their neighbours. airtop's frequency spectrum panel draws every discovered access point as a signal hump positioned at its real center frequency along a MHz axis, with height and colour carrying RSSI and the label carrying SSID and dBm. Overlapping humps are the picture of a shared channel, and they are the classic Wi-Fi-analyzer view that the terminal has mostly lacked.

The signal histogram next to it answers a different half of the same question by plotting the distribution of received frames by RSSI. A tight peak around -50 dBm means you are close to one strong access point and the environment is otherwise quiet. A smear from -60 to -80 dBm means a crowded one, with many transmitters at many distances, which is the shape of an apartment building or a conference floor. Neither panel needs monitor mode, because beacons are broadcast and scan results come from cfg80211, but both are bounded by what your radio and your scans have actually heard.

For a command line wifi spectrum view of co-channel congestion in dBm, that combination is the honest version of what a terminal can do. It is not a spectrum analyzer. A real spectrum analyzer sees energy that is not 802.11 at all, the microwave oven, the video sender, the cordless phone, and none of that appears here, because none of it produces frames for mac80211 to receive. sparrow-wifi handles that case by talking to a HackRF One or an Ubertooth One, and if your interference is non-Wi-Fi then that hardware is the answer and no software on the connected interface will substitute for it.

I only see one or two networks in the AP list. What makes the rest of them show up?

Your radio is listening to one channel, which is its own, most of the time. Neighbours appear when the operating system scans, because a scan is the only moment the radio visits other channels, and that is what fentry/cfg80211_inform_bss_frame_data is watching for. NetworkManager scans periodically on its own, and nmcli "ensures that the access point list is no older than 30 seconds and triggers a network scan if necessary", per the nmcli manual. So the AP list is not broken when it is short, it is waiting.

nmcli dev wifi rescan        # or: iw dev wlan0 scan

Run that in a second shell and watch the spectrum panel and the access-point list fill in over the next few seconds. The nmcli manual notes that NetworkManager already scans in the background but that manual rescanning "can be useful to start scanning manually (e.g. after resuming the computer)", which is also the right moment to do it here: right after you have started the dashboard and want the neighbour picture populated now rather than in thirty seconds. The same technique is how the project's own demo GIF gets a populated spectrum, by kicking off scans in another shell while recording.

This is the caveat with the sharpest teeth, so it is worth stating in the strongest form. A connected interface only hears its own channel plus whatever brief scans touch, so the spectrum and access-point list fill in as scans run, and live per-frame traffic is mostly your channel. If what you need is a full-band survey with every station on every channel captured continuously, that requires monitor mode and channel hopping, and airodump-ng or Kismet on a second adapter is the correct tool. Nothing about reading frames inside the kernel changes what a single radio can physically hear.

How do I watch RSSI in dBm change over time instead of reading one number?

Signal strength is a time series and every command-line tool reports it as a scalar. iw dev wlan0 link gives you the dBm right now; ask twice and you have two numbers and no idea what happened between them. airtop's RSSI by time panel plots a braille line graph per live station over the last several seconds, so a link fading as a device walks away from the access point looks like a slope, and a laptop being moved looks like a step. Per-station is the part that matters: it is one trace per station the interface has heard from, not a single number for your own association.

Reading dBm correctly is half the work, and the scale is not intuitive because the values are always negative and closer to zero is stronger. Between -30 and -50 dBm you are effectively next to the access point. Between -50 and -67 dBm is good, which is where most of a well-planned office sits. Between -67 and -80 dBm is usable, and between -80 and -90 dBm is marginal, where retries climb and video calls start to break up. airodump-ng's documentation calibrates the same scale slightly differently, calling -40 strong, -55 average and -70 the start of weak, which is a useful cross-check that these are conventions rather than thresholds in a standard.

What the trace cannot tell you is why the signal moved, and this is where the frame-type heatmap earns its place beside it. A signal dropping while the frame feed stays busy is a distance or obstruction problem. A signal holding steady while data frames stop is not an RF problem at all and you should be looking at the access point, the DHCP lease or the upstream link. Two panels side by side answer "is this the radio" faster than any single number can, which is the whole reason a dashboard beats a series of one-shot commands.

Is passive 802.11 capture safe to run on a work laptop?

Nothing is transmitted and no mode is changed, which removes the two things that usually make wireless tooling risky on a machine you do not own. The interface stays associated to whatever it was associated to, because there is no mode switch in the path, and the BPF programs read frames the kernel is already processing. Compare that against the alternative, where airmon-ng check kill terminates your connection manager and ip link set wlan1 down drops the interface: those are changes to a corporate machine's network state, and they are visible ones.

The legal position is worth separating from the policy position, because they are different questions with different answers. Passively observing 802.11 frames in the air around you is legal in most jurisdictions, on the straightforward grounds that your radio is already receiving them. Active interference is a different matter entirely, and the deauth panel here detects attacks rather than performing them. If you are on a corporate network, check the acceptable use policy before running any wireless tool, including this one, because "legal" and "permitted by your employer" are not the same test and the second one is the one that gets people in trouble.

There is one more practical consideration for a work machine, which is what ends up on your screen when a colleague walks past or when you paste a screenshot into a ticket. SSIDs and MAC addresses are identifying, both yours and your neighbours', so there is a flag for that:

yeet run https://github.com/yeet-src/airtop -- --anonymize

Every network becomes network-01 and every station becomes station-02, keeping the shape of the RF environment while removing the identifiers. Use it by default for anything you intend to share, since the spectrum, the histogram and the deauth count are all just as readable with the labels replaced.

What does a connected interface never show you, and when is monitor mode still the right call?

Four limits, and each one has a name and a reason rather than being a gap waiting to be closed. The honest caveats section of the project's README states all of them, and they are the boundary of the approach rather than a list of unimplemented features:

  • One channel, plus scans. A connected interface only hears its own channel plus whatever brief scans touch, so the spectrum and access-point list fill in as scans run and live per-frame traffic is mostly your channel. A full-band survey needs monitor mode and channel hopping, which means airodump-ng or Kismet on a separate adapter.
  • Frames, not bytes. Activity is measured as frame count rather than airtime, so a thousand small ACKs and a thousand full-size data frames register as the same amount of activity. That is fine for spotting a deauth flood and wrong for estimating how much of the channel someone is consuming.
  • No TX rate and no retries. Those come from a tx_status hook, which is a different attachment point and a different program, so the classic "my rate collapsed to 6 Mbps" diagnosis is not available here. iw dev <iface> link reports the current TX bitrate if that is what you need right now.
  • fentry targets are not a kernel ABI. ieee80211_rx_list and cfg80211_inform_bss_frame_data are stable in practice, not guaranteed, and exactly what data is available depends on your Wi-Fi driver. A kernel refactor could rename or restructure either one, and the failure mode would be an attach error rather than silently wrong numbers.

Monitor mode remains the right call whenever the question is about the band rather than the link. Site surveys, channel planning across a whole floor, wireless intrusion detection running continuously, anything requiring PCAP export, decryption with the key, or the exact byte contents of a specific frame. That is Kismet, airodump-ng and Wireshark territory and this does not displace them. The division is clean enough to state as a rule: for pen-testing and surveys, use the monitor-mode tools; to find out why the call you are on right now keeps dropping, use the interface you are on right now.

How do I check my kernel and driver can do this before I actually need it?

Three checks, none of which take longer than the outage will. The kernel needs BTF, meaning CONFIG_DEBUG_INFO_BTF=y and CONFIG_DEBUG_INFO_BTF_MODULES=y, which is the default on current Arch, Fedora, Ubuntu and Debian 12 or newer. The Wi-Fi interface needs to be using the standard cfg80211 and mac80211 stack, which any normal Wi-Fi card on Linux is. And the yeet daemon needs to be installed, since it handles the privileged BPF load.

ls /sys/kernel/btf/vmlinux                                  # BTF exposed by the running kernel
grep -E 'CONFIG_DEBUG_INFO_BTF(_MODULES)?=' /boot/config-$(uname -r)
iw dev                                                      # interface name, type should be managed

The first line is the fastest signal, because a kernel built with BTF exposes its own type information at /sys/kernel/btf/vmlinux, and if that file is missing then nothing CO-RE-based will load. BTF is the metadata format encoding debug info for BPF programs and maps, and CO-RE, which is Compile Once Run Everywhere, uses it to adjust field offsets at load time to match the running kernel. The practical consequence is that there is no per-kernel recompile, and the practical failure is that a kernel without BTF cannot participate at all.

Do this on a quiet afternoon rather than nine minutes into a problem, because "my kernel does not ship BTF" is a distribution decision you cannot fix during an incident. Note also that yeet run is unprivileged and never takes sudo; the daemon does the privileged load. If you want to build the BPF object yourself instead, make generates include/vmlinux.h and builds bin/airtop.bpf.o, needing clang with the BPF target and bpftool, and the BPF program is GPL because of the kernel helpers it uses.

The bottom line: monitor mode buys you the band, not the truth about your own channel

Monitor mode is a capture technique with a specific purchase and a specific price. It buys channels you are not tuned to and frames not addressed to you, and it costs the association, the ability to transmit, and on most setups a second adapter. If the question spans the band, pay it: airodump-ng for a quick survey with PWR and Beacons per BSSID, Kismet for continuous multi-radio detection with its own wlan0mon interface, Wireshark for per-frame decode of a capture someone already took.

If the question is about the link you are standing on, that price buys nothing, because the frames that answer it are already arriving. Beacons are broadcast. Broadcast deauths are broadcast. Your own RSSI is measured by your own driver on every frame it receives. airtop reads them where they exist, with fentry hooks on ieee80211_rx_list and cfg80211_inform_bss_frame_data, and you keep the connection. Past that, wavemon is still the fastest way to watch one link's dBm, iw dev <iface> link is still the right one-liner, and sparrow-wifi with a HackRF One is still the answer when the interference is not Wi-Fi at all.

Frequently asked questions

Does airtop need sudo or root?

No. yeet run is unprivileged and never takes sudo. The yeet daemon performs the privileged BPF load on its behalf, which is also why the install step and the run step are separate. If you find yourself typing sudo yeet run, that is a bug rather than a requirement.

Does airtop count bytes or airtime?

Frames. Every activity number in the dashboard, the header total, the frame-type heatmap cells and the per-type counts, is a count of frames rather than bytes or channel airtime. A burst of tiny control frames therefore looks as busy as a burst of full-size data frames. For spotting a flood by frame type that is the right unit, and for estimating how much of the channel a neighbour is consuming it is the wrong one.

Will airtop show me TX rate or retry counts?

No. Those are captured from a tx_status hook, which is a separate attachment point that this program does not use, so there is no transmit-side data in the dashboard at all. If you need the current negotiated bitrate, iw dev with the link subcommand reports it for your own association. Retry statistics are exposed by wavemon's info screen for your link.

What happens if my Wi-Fi driver does not use mac80211?

The hooks have nothing to attach to and the program will not load. Almost every ordinary Wi-Fi card on Linux runs on the standard cfg80211 and mac80211 stack, but full-MAC devices and some vendor out-of-tree drivers do not, and there is no fallback path. The failure is loud at attach time rather than a dashboard that quietly shows zeros.

Do the fentry hooks break when I upgrade my kernel?

They can. The two target functions are stable in practice but they are not a kernel ABI, so a refactor upstream could rename or restructure either one. CO-RE handles struct layout changes across kernels without a recompile, which covers the common case, but it cannot invent a function that no longer exists. Exactly which fields carry useful data also depends on your Wi-Fi driver.

Can I run this on macOS or Windows?

No. The mechanism is eBPF attached to Linux kernel functions, so there is no equivalent on other operating systems and no portability layer that would make one. macOS has its own supported capture path through Wireless Diagnostics and airport; Windows generally requires specific adapters and drivers for anything past the standard interface.

Is the deauth counter red because I am under attack?

Not necessarily. A non-zero count means deauthentication frames were seen, and that is worth investigating, but it is not a verdict. Access points send legitimate deauths, and a neighbouring network doing something noisy is close enough to be heard. Treat a sustained flood filling that row in the heatmap differently from a handful of frames over an hour.

Why does the layout look broken in a small terminal?

The minimum is 80 by 24. Below that there is not enough room for the panels and the header, so the rendering degrades. The layout reflows on resize, so making the window bigger fixes it live without a restart.

Can I share a screenshot without exposing my SSID and my neighbours' MAC addresses?

Yes. Passing --anonymize relabels every network as network-01, network-02 and every station as station-01, station-02, which keeps the spectrum shape, the RSSI distribution and the frame counts intact while removing the identifiers. Use it for anything going into a ticket, a chat channel or a talk.

Is this a replacement for Kismet or Wireshark?

No, and it is not trying to be. There is no PCAP export, no decryption, no per-frame byte-level decode, no logging backend, no GPS integration and no multi-radio support. Close the terminal and the numbers are gone, because nothing is stored. Those are the reasons to run Kismet or Wireshark, and they remain good reasons.

Sources

  • Monitor mode (Wikipedia, 2026) — establishes that monitor mode "allows packets to be captured without having to associate with an access point or ad hoc network first", unlike promiscuous mode; states that "usually the wireless adapter is unable to transmit in monitor mode and is restricted to a single wireless channel", qualified as dependent on the adapter's driver, firmware and chipset; notes that support varies widely by vendor.
  • airodump-ng documentation (aircrack-ng, 2026) — instructs users to "make sure you have started your card in monitor mode with airmon-ng" and to "be sure to stop all connection managers prior to using the aircrack-ng suite", recommending airmon-ng check kill because a connection manager can remove the card from monitor mode; documents channel hopping by default unless -c is given, the BSSID, PWR, Beacons, #Data, CH, MB, ENC and ESSID columns, and the PWR calibration of -40 strong, -55 average, -70 weak, with -1 meaning the driver reports no signal.
  • Wireshark WLAN capture setup (Wireshark wiki, 2026) — states that "in order to see 802.11 headers, you will have to capture in monitor mode", and that adapters "often transform 802.11 data packets into fake Ethernet packets before supplying them to the host"; explains that ordinary operation delivers only unicast addressed to the adapter plus multicast and broadcast, so seeing traffic between other stations requires disabling hardware MAC address filtering.
  • Kismet Wi-Fi Linux datasource (Kismet docs, 2026) — defines monitor mode as reporting all packets seen "at the 802.11 link layer instead of emulating an Ethernet device"; documents that Kismet creates a capture vif, so wlan0 becomes wlan0mon unless overridden with vif=, channel hops all detected bands unless channel_hop=false, and "must disable (set to down state)" non-monitor interfaces on the same card during capture, restoring them afterwards.
  • wavemon(1) man page (Debian bookworm, 2023) — documents the info screen (F1) with "a condensed overview of wireless-specific parameters and network statistics, as well as bar graphs", the level histogram (F2) as a "full-screen histogram plot showing the evolution of levels with time", and the scan window (F3) as a "periodically updated network scan, showing access points and other wireless clients"; notes that scans and encryption information require CAP_NET_ADMIN.
  • Three ways to enable monitor mode on Linux (Greybeard WiFi, 2026) — enumerates the three standard routes: iw dev <iface> set type monitor, airmon-ng, which renames the interface so wlan1 becomes wlan1mon, and the older iwconfig <iface> mode monitor; each changes the radio's operating mode rather than adding a capture path alongside the existing one.
  • Monitoring wireless on Linux (Jason Murray, 2021) — the conventional workflow, ip link set wlan1 down, iw wlan1 set monitor control, iw wlan1 set channel 149, ip link set wlan1 up, which begins by taking the interface down; states that "wireless cards can only monitor one channel at a time", and records the author building a laptop with seven USB adapters to cover multiple channels at once.
  • Deauthentication Attack Detector (SolveniteOP, GitHub) — a representative incumbent detector requiring "Python 2.7 or higher" and the Scapy module, which "works by analyzing the number of deauthentication packets captured" against a threshold; its author describes it as "a preliminary detection mechanism" that can generate false alerts from legitimate network operations or neighbouring networks, and frames it as a learning exercise rather than production tooling.
  • GitHub deauth topic, Python (GitHub, 2026) — the tooling landscape for the term, dominated by attack tools: of the twenty repositories listed, nineteen perform deauthentication and one detects it, with Scapy named in several of the attack tools; establishes that continuous detection on a machine you are using is the under-served side of this problem.
  • nmcli(1) man page (Debian bookworm, 2023) — documents device wifi list for listing available access points, optionally filtered by ifname or bssid; states that by default nmcli "ensures that the access point list is no older than 30 seconds and triggers a network scan if necessary", overridable with --rescan; documents device wifi rescan as requesting an immediate scan, noting NetworkManager scans periodically but manual scanning helps after resuming a machine.
  • BPF Portability and CO-RE (Andrii Nakryiko, 2020) — defines CO-RE as writing a BPF program that compiles, verifies and runs correctly across kernel versions "without the need to recompile it for each particular kernel"; explains that a kernel built with BTF exposes its type information at /sys/kernel/btf/vmlinux, and that clang records relocations which libbpf resolves against the running kernel's BTF at load time.
  • BTF documentation (docs.kernel.org) — defines BTF as "the metadata format which encodes the debug info related to BPF program/map", covering both the kernel API and the ELF file format; notes that the .BTF.ext section "encodes func_info, line_info and CO-RE relocations which needs loader manipulation before loading into the kernel".
  • cfg80211 subsystem (kernel driver-api docs) — describes cfg80211 as "the configuration API for 802.11 devices in Linux", which "bridges userspace and drivers, and offers some utility functionality associated with 802.11", and separates scanning and BSS list handling into their own chapter, which is the subsystem area cfg80211_inform_bss_frame_data belongs to.
  • sparrow-wifi (ghostop14, GitHub) — a 2.4 GHz and 5 GHz Wi-Fi and Bluetooth spectral awareness tool for Linux built on PyQt5, requiring Python 3.8 or later and root for iw scan; monitor mode is needed only for DroneID RemoteID capture and the Falcon plugin, while real spectrum analysis requires a HackRF One or Ubertooth One; includes GPS integration, mapping and remote agents.
  • LinSSID (SourceForge) — a C++ Qt5 and Qwt 6.1 desktop application, described as graphically and functionally similar to inSSIDer, showing a sortable table of locally visible access points plus graphs of signal by channel and signal over time across 2.4 GHz and 5 GHz, distributed for amd64 and i386.
  • Check Wi-Fi signal strength from the command line (OSTechNix) — the baseline CLI approaches and their exact output fields: nmcli dev wifi with a SIGNAL percentage and BARS column, iw dev <iface> link reporting signal: -29 dBm, iwconfig reporting Link Quality=70/70 and Signal level=-40 dBm, and iwlist <iface> scanning filtered for signal; each is a point-in-time reading with no history.

Related resources

  • airtop on GitHub — the source, the 60-second 802.11 primer, and the panel-by-panel description of what each part of the dashboard is showing you.
  • airtop honest caveats — the four limits in the authors' own words: single channel plus scans, frames rather than bytes, no TX rate or retries, and fentry targets that are stable in practice but not an ABI.
  • airtop community questions — the short answers to monitor mode, connection drops, sparse access-point lists, legality, and how this differs from Kismet, airodump-ng and Wireshark.
  • airtop requirements — the BTF configuration flags, the cfg80211 and mac80211 dependency, and what the yeet daemon is doing during the privileged load.
  • yeet documentation — the runtime underneath, for writing your own eBPF programs and terminal dashboards in JavaScript rather than using a ready-made one.
  • VHS — the terminal recorder used to produce the project's demo GIF from a .tape file, if you want to record your own RF dashboard for a ticket or a talk.

See your machine the way the kernel sees it.

Built with yeet, a JS runtime for writing eBPF programs on Linux machines. Join us on discord.