Cell: Qualcomm DIAG
Added: 2026-10
The cell DIAG source (celldiag) reads the Qualcomm diagnostic (DIAG) port of a cellular modem. It sets the modem’s diagnostic log mask, reads the stream of log records the modem produces, and decodes them inside the capture tool into cell observations for the Cellular phy, the same devices the cell AT source creates.
From the DIAG stream, Kismet gets:
- Every LTE and NR serving and neighbor cell measurement the modem makes (LTE ML1 serving, idle and connected neighbor, and per-cell measurements; NR ML1).
- The LTE RRC messages the modem receives, including each cell’s SIB1. The identity in a SIB1 (MCC, MNC, TAC, and cell ID) is added to later measurements of that cell, so cells the AT commands only report by PCI and channel get their full identity.
- NR RRC, and the serving cell bandwidth and number of antennas.
- The modem’s GNSS position reports.
- The security signalling, which raises the cell security alerts.
For how this compares to the AT source, and running both on one modem, see AT and DIAG.
Supported hardware
The DIAG source needs a Qualcomm-based modem which exposes its DIAG port:
- Quectel RM520N-GL series (SDX62), over USB or PCIe. This is the reference modem for the DIAG source.
- Quectel RM500Q series (SDX55).
- Quectel EG25-G / EC2x / EG2x (MDM9607). These need an SPC unlock before the modem accepts a log mask.
- Telit LM960 (SDX24).
The supported modem models for the AT source are listed on the cell AT page. A modem without a DIAG port, or one from a vendor which doesn’t use Qualcomm chipsets, can only use the AT source.
Adding the source
Like the AT source, the DIAG source is named by the modem IMEI:
$ kismet -c celldiag-<imei>
The source type is celldiag. To find the modem, the source sends AT+CGSN to the modem’s AT ports and matches the IMEI, then opens the DIAG port of the same USB device. To list the modems:
$ kismet_cap_cell_diag --list
If the DIAG port can’t be found, name it with diagport=; if the AT port can’t be found, name it with atport=, which skips the scan. A PCIe modem uses /dev/mhi_DIAG and /dev/mhi_DUN on Linux; with more than one PCIe modem, name both ports:
source=celldiag-<imei>:atport=/dev/mhi_DUN,diagport=/dev/mhi_DIAG
The CELLDIAG_SCAN_PORTS environment variable replaces the set of ports scanned for the AT port, as a space-separated list of paths or globs.
On macOS, supported Quectel modems are reached over libusb. The same method works on Linux, for a modem with no serial driver bound or, with usb=force, in place of the option driver; see cell modems over libusb.
Linux permissions
The capture tool needs read and write access to the modem’s DIAG and AT ports; see the cell AT permissions.
One DIAG client at a time
The DIAG source claims the DIAG port exclusively while it runs. Other DIAG tools can’t use the port at the same time, and the DIAG source can’t open a port another program holds.
What the source changes on the modem
To get the log records, the DIAG source sends the modem a log mask when it opens. The mask is held in the modem’s RAM; when the source closes, it clears the mask it set, so the modem doesn’t keep streaming to the next DIAG client. nomask=true reads whatever an earlier tool enabled, and sets and clears nothing.
SPC unlock
The EG25-G, EC2x, and EG2x (MDM9607) refuse a log mask until they are unlocked with the service programming code (SPC). Without it, the open fails with DIAG LOG_CONFIG handshake failed on <port> (wrong port, or SPC-gated?). Pass the code, which is 000000 by factory default:
source=celldiag-<imei>:spc=000000
The RM500Q, RM520N-GL, and LM960 don’t need it. The code is never written to the logs.
Masks
The mask=wardrive default subscribes to the cell log records Kismet decodes, at low volume: LTE ML1 serving cell, neighbor, and per-cell measurement records (0xB193, 0xB192, 0xB195, 0xB17F), LTE serving cell configuration (0xB197), LTE RRC (0xB0C0), NR RRC (0xB821), NR ML1 (0xB97F), and GNSS (0x1476, 0x14D8). Only the records the modem advertises are requested.
mask=full subscribes to every log record the modem advertises. This is a very high volume and is intended for diagnosing a modem, not for normal capture.
nas=true adds the LTE NAS (EMM) messages needed for the NAS security alerts. These messages carry the SIM’s own IMSI and GUTI into the raw stream, so they are off by default; mask=full includes them.
F3 and QSH debug messages
f3=all, f3=high, or f3=error turns on the modem’s F3 debug messages, and qsh=on turns on the QSH trace. Both are high volume and only for debugging the modem. Kismet doesn’t decode them; they are kept in the raw DIAG stream in the kismetdb. The QSH trace setting survives a reboot of the modem, so the source turns it off again when it closes.
Alerts
The decoder watches the RRC and NAS signalling for the patterns a cell-site simulator (“IMSI catcher”) shows, following the heuristics of the EFF Rayhunter project. Each match is logged as a CellSecurity record in the kismetdb, and raises a Kismet alert naming the cell:
| Alert | Severity | Raised when | Needs |
|---|---|---|---|
CELLNULLCIPHER | high | an RRC or NAS security mode command selects the null cipher (EEA0) | RRC: the default mask; NAS: nas=true |
CELLNULLINTEGRITY | high | an RRC or NAS security mode command selects null integrity (EIA0) | as above |
CELL2GREDIRECT | high | an RRC connection release redirects the modem to a 2G (GERAN) carrier | the default mask |
CELLINCOMPLETESIB | low | a SIB1 schedules fewer than two system information messages (once per cell per capture) | the default mask |
CELLIDREQUEST | high | the network requests the modem’s identity after authentication, or after a disconnect with no new connection | nas=true |
CELLIDREQNOAUTH | low | an identity request followed by a disconnect or attach reject without authentication | nas=true |
Some of these happen on normal networks:
- A network without VoLTE redirects to 2G for a voice call (circuit switched fallback), which also raises
CELL2GREDIRECT. CELLIDREQNOAUTHis often a roaming or inactive SIM.- A redirect to a carrier other than 2G is logged as a
CellSecurityrecord but raises no alert.
CELLINCOMPLETESIB only needs the SIB1 each cell broadcasts; the other alerts come from the signalling when the modem connects to the network.
The alerts are configured like any other Kismet alert; see alerts.
Live SIBs in Wireshark
With gsmtap=on, each LTE RRC and LTE NAS message is also sent as a GSMTAP UDP packet to 127.0.0.1 port 4729, for Wireshark to decode as it is received:
$ wireshark -k -i lo -f "udp port 4729"
$ kismet -c celldiag-<imei>:gsmtap=on
gsmtap=<ipv4> or gsmtap=<ipv4>:<port> sends it elsewhere; only IPv4 addresses and localhost are accepted. NR RRC isn’t sent, because GSMTAP has no type for it. Sending never blocks the capture; failures are counted and reported when the source closes.
Logging
The source logs the decoded observations, a session record for each capture, the security records, and the raw DIAG stream into the kismetdb; nothing is written to separate files.
The raw DIAG stream is logged by default as packets with link type 147 (LINKTYPE_USER0), so a capture can be decoded again later; kismetdb_to_hdlc extracts it. rawpackets=off leaves it out, and kis_log_packets=false in kismet_logging.conf leaves out all packets.
The raw stream holds everything the modem logs under the selected mask, which can include identifiers from the modem’s own signalling; treat the kismetdb from a DIAG capture as sensitive.
See kismetdb cell logs for the record types and the tools to extract them.
Status
The source reports its state in the datasource details in the web UI and on the message bus every stats_interval seconds: whether data is arriving from the port, the number of observations, and how many records were decoded.
A busy stream can lose bytes between the modem and Kismet; the only sign of it is DIAG frames which fail their checksum. When 10 or more frames fail in one status interval, the source raises an error message and sets a warning on the source.
DIAG source parameters
Naming and description options
All data sources accept the common naming and description options.
Port options
diagport={ /path/to/tty } / { usb:bus-addr:if }
atport={ /path/to/tty } / { usb:bus-addr:if }
usb={ auto } / { force } / { off }
Modem options
spc={ 0 }
mask={ wardrive } / { full }
wardrive (the default) requests the cell records Kismet decodes; full requests every record the modem advertises, at a very high volume.nas={ true } / { false }
nomask={ true } / { false }
f3={ off } / { all } / { high } / { error }
all), or without the lowest severities (high, error). They are kept in the raw stream, not decoded. Off by default.qsh={ on } / { off }
profiles={ /path/to/profiles }
capture_cell_at/profiles/ in the Kismet source), which supply per-modem defaults for mask=, f3=, and spc=, matched on the modem firmware. An option set on the source always wins. Profiles are not used by default; CELLDIAG_PROFILES in the environment sets the directory for every source.Output options
rawpackets={ on } / { off }
gsmtap={ off } / { on } / { ipv4:port }
127.0.0.1:4729 or the given address. Off by default.Other options
enabled={ true } / { false }
enabled=false, the source is defined but idle: the DIAG port isn’t opened.replay={ /path/to/capture.hdlc }
kismetdb_to_hdlc, instead of a live modem; use the full path to the file. No modem is opened and no mask is sent.stats_interval={ seconds }
0 turns them off.clock_anchor_sec={ seconds }
0 reads it only at the start and end of the capture.debug={ true } / { false }
debug=false, or KISMET_CELLDIAG_DEBUG=0 in the environment, turns it off.None of the DIAG source options can be changed while the source runs; close the source and open it again with a new definition.