MeshCore Wireshark Dissector

March 15, 2026 ยท View on GitHub

A Wireshark protocol dissector and pcapng converter for MeshCore, a LoRa mesh networking protocol.

MeshCore dissector showing packet list with protocol tree and decrypted group message

  • meshcore_dissector.lua -- Wireshark Lua dissector that decodes MeshCore packets with full protocol tree, display filters, and info column summaries (download)
  • meshcore_json2pcap.py -- Python script that converts JSON radio logs to pcapng files (download)

Features

  • Decodes all MeshCore packet types: ADVERT, ACK, GRP_TXT, GRP_DATA, TXT_MSG, REQ, RESPONSE, ANON_REQ, PATH, TRACE, MULTIPART, CONTROL, RAW_CUSTOM
  • Header bitmask dissection (route type, payload type, payload version)
  • ADVERT appdata parsing: node type, GPS coordinates, node name
  • CONTROL sub-type parsing: DISCOVER_REQ, DISCOVER_RESP
  • Two-layer dissection (like 802.11 Radiotap): radio metadata and wire protocol are separate protocol entries
  • Radio metadata (SNR) from the JSON-to-pcapng converter, filterable via meshcore_radio.snr_raw
  • Display filter support (e.g. meshcore.payload_type == 4 for ADVERTs)

Installation

The dissector is a single Lua file -- no compilation required. Copy meshcore_dissector.lua to your Wireshark personal plugins folder and it auto-loads on startup.

Linux

mkdir -p ~/.local/lib/wireshark/plugins
cp meshcore_dissector.lua ~/.local/lib/wireshark/plugins/

macOS

mkdir -p ~/.local/lib/wireshark/plugins
cp meshcore_dissector.lua ~/.local/lib/wireshark/plugins/

Alternatively, if you installed Wireshark as an app bundle:

mkdir -p "$HOME/.config/wireshark/plugins"
cp meshcore_dissector.lua "$HOME/.config/wireshark/plugins/"

Windows

Copy meshcore_dissector.lua to:

%APPDATA%\Wireshark\plugins\

Typically this is C:\Users\<YourName>\AppData\Roaming\Wireshark\plugins\. Create the plugins folder if it doesn't exist.

Verifying the plugin directory

In Wireshark, go to Help > About Wireshark > Folders to see the "Personal Plugins" path for your system. You can also find it via Edit > Preferences > Appearance > Folders.

Reloading

After copying the file, restart Wireshark or press Ctrl+Shift+L (Analyze > Reload Lua Plugins) to load the dissector without restarting.

Getting a capture

From the companion app

In the official MeshCore Companion app, go to Tools > Rx Log > Save Log to export a JSON radio log from your node. This JSON file can be converted to pcapng for analysis in Wireshark.

Sample capture

A sample pcapng capture is included at docs/samples/sample_1.pcapng so you can try the dissector without needing a radio.

Converting JSON logs to pcapng

The converter requires Python 3.6+ with no external dependencies.

python3 meshcore_json2pcap.py capture.json

This writes capture.pcapng in the same directory. You can also specify an output path:

python3 meshcore_json2pcap.py capture.json output.pcapng

Input format

The JSON input is an array of packet objects:

[
  {
    "timestamp": 1773364730762,
    "snr": 8.25,
    "packet": "150220ee81fbaf4d..."
  }
]
FieldTypeDescription
timestampintegerMilliseconds since Unix epoch
snrfloatSignal-to-noise ratio in dB
packetstringHex-encoded raw packet bytes

Radio metadata header

By default, a radio metadata header is prepended to each packet in the pcapng. The format follows the same extensibility pattern as 802.11 Radiotap: a fixed version/length prefix followed by fields. The length field tells the dissector how many bytes to skip, so older dissectors can still find the start of the wire protocol even if new fields are added in a future version.

BytesTypeDescription
0uint8Version (currently 0)
1uint8Pad (0)
2-3uint16 LEHeader length in bytes (currently 6)
4-5int16 LESNR * 100 (e.g. 8.25 dB = 825)

The dissector uses a two-layer architecture (like 802.11 Radiotap + 802.11) where the radio metadata and the MeshCore wire protocol appear as separate protocol entries in Wireshark's packet tree. This makes it clear which bytes are capture metadata vs. actual over-the-air data.

Pcapng files produced by meshcore_json2pcap.py use DLT_USER0 (147), a link-layer type from the "user-defined" range reserved for private use.

This value is temporary and will change. DLT_USER0-USER15 is a 16-slot global namespace shared by every custom protocol tool. If two unrelated dissector plugins claim the same slot, the last one loaded wins silently -- there is no conflict detection. We plan to register an official LINKTYPE with tcpdump.org to eliminate this risk. When that happens, the assigned value will replace DLT_USER0 in both the converter and the dissector. Existing pcapng files will need to be regenerated from the original JSON logs.

Usage in Wireshark

Open a converted pcapng file. Packets are automatically decoded as MeshCore. The dissector provides protocol tree dissection and display filters. Wireshark's built-in I/O graphs can visualize mesh traffic over time:

Wireshark I/O graph showing MeshCore throughput over time

Display filters

FilterDescription
meshcore.payload_type == 4ADVERT packets
meshcore.payload_type == 5GRP_TXT packets
meshcore.route_type == 1FLOOD routed packets
meshcore.advert.name contains "Repeater"ADVERTs with "Repeater" in node name
meshcore.channel_hash == 0x81Group messages on a specific channel
meshcore_radio.snr_raw > 0Packets with positive SNR

Command-line with tshark

tshark -X lua_script:meshcore_dissector.lua -r capture.pcapng
tshark -X lua_script:meshcore_dissector.lua -r capture.pcapng -Y "meshcore.payload_type == 4" -V

Running tests

Tests require Python 3.6+ and pytest. Dissector integration tests additionally require tshark (they are skipped if tshark is not available).

pip install pytest
python3 -m pytest tests/ -v

License

GPLv2 -- see LICENSE for the full text. This license is compatible with the main Wireshark project.