aaronb/wireshark-meshcore
GitHub: aaronb/wireshark-meshcore
Stars: 15 | Forks: 0
# MeshCore Wireshark Dissector
A Wireshark protocol dissector and pcapng converter for [MeshCore](https://github.com/meshcore-dev/MeshCore), a LoRa mesh networking protocol.

- **`meshcore_dissector.lua`** -- Wireshark Lua dissector that decodes MeshCore packets with full protocol tree, display filters, and info column summaries ([download](https://raw.githubusercontent.com/aaronb/wireshark-meshcore/main/meshcore_dissector.lua))
- **`meshcore_json2pcap.py`** -- Python script that converts JSON radio logs to pcapng files ([download](https://raw.githubusercontent.com/aaronb/wireshark-meshcore/main/meshcore_json2pcap.py))
## 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
### 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\\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`](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..."
}
]
| Field | Type | Description |
|-------|------|-------------|
| `timestamp` | integer | Milliseconds since Unix epoch |
| `snr` | float | Signal-to-noise ratio in dB |
| `packet` | string | Hex-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.
| Bytes | Type | Description |
|-------|------|-------------|
| 0 | uint8 | Version (currently 0) |
| 1 | uint8 | Pad (0) |
| 2-3 | uint16 LE | Header length in bytes (currently 6) |
| 4-5 | int16 LE | SNR * 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.
### Link-layer type (LINKTYPE)
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](https://www.tcpdump.org/linktypes.html) 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:

### Display filters
| Filter | Description |
|--------|-------------|
| `meshcore.payload_type == 4` | ADVERT packets |
| `meshcore.payload_type == 5` | GRP_TXT packets |
| `meshcore.route_type == 1` | FLOOD routed packets |
| `meshcore.advert.name contains "Repeater"` | ADVERTs with "Repeater" in node name |
| `meshcore.channel_hash == 0x81` | Group messages on a specific channel |
| `meshcore_radio.snr_raw > 0` | Packets 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](LICENSE) for the full text. This license is compatible
with the main [Wireshark project](https://www.wireshark.org/).