Skip to main content

KeelMap

Passive asset discovery for ship and port networks. Give KeelMap a packet capture and it builds an inventory of the devices on the network, where every field shows the exact frames that prove it.

KeelMap asset chart showing a Modbus client writing to a server, with the operations listed in a side panel

Why

IACS UR E26 asks new ships, contracted for construction on or after 1 July 2024, to identify the computer-based systems on board, with mandatory and non-mandatory scope depending on vessel type and size. Older ships and port operators face the same question without the rule. Commercial OT visibility platforms are priced for large enterprises, and the open-source ICS tools we know of are generic rather than maritime, and do not show the evidence behind each field.

KeelMap is built around one rule: report what the traffic proves, nothing more.

  • Passive only. It reads capture files. It never sends a packet, because active probing can disrupt fragile OT devices.
  • Evidence or unknown. Every value cites the capture's SHA-256 and the frame numbers behind it, so you can check it in Wireshark. Missing data reads as unknown, never as a guess.
  • Conflicts are shown, not resolved. If two frames disagree, both claims are kept.
  • Claims are kept apart from observations. What a device says about itself is marked as self-reported. Talker ids, MAC prefixes and free-text descriptions are never upgraded into device types or vendors.
  • Safe to share by default. Vessel identifiers and capture filenames are redacted unless you opt out.

Install

Python 3.11 or later, no runtime dependencies.

git clone https://github.com/KanenasCS/keelmap.git
cd keelmap
pip install .

Quick start

keelmap scan bridge.pcapng -o inventory.json --map chart.html

This writes a JSON inventory and an offline HTML chart. The summary lists everything seen but not turned into evidence, so coverage gaps are never hidden.

Add network card makers with IEEE registry files you download yourself:

keelmap scan bridge.pcapng -o inventory.json --map chart.html --oui oui.csv --oui mam.csv --oui oui36.csv

Draw a chart from an existing inventory:

keelmap map inventory.json -o chart.html

Captures from tcpdump -i any (Linux cooked format) work as well as plain Ethernet captures.

Redacted exports replace vessel identifiers with tokens made with a secret key, so they cannot be reversed by trying every MMSI or IMO number. By default the key is random and discarded, so tokens differ between exports. To compare exports over time, create a key once and keep it private:

keelmap keygen -o redaction.key
keelmap scan bridge.pcapng -o inventory.json --redaction-key redaction.key

--no-redact keeps vessel identifiers and filenames, and only writes to *.unredacted.json and *.unredacted.html, which git ignores.

What it understands

Protocol What KeelMap learns
ARP Which MAC owns which IPv4 address on the sensor's segment
DHCP Client and server roles, hostnames, addresses a client is seen using
LLDP and LLDP-MED Hostname, description as sent, enabled capabilities, vendor, model, firmware, serial number, MUD URL
Modbus/TCP Client, server and gateway roles, unit ids, every function code used, accepted and rejected writes, device identification
NMEA 0183 over UDP Talker ids, sentence types, proprietary maker codes, on any port
IEC 61162-450 Senders, multicast groups, system function ids
AIS Own-ship MMSI, IMO, call sign and name. Other vessels heard over radio are counted, not inventoried
IEEE OUI Network card maker, from files you supply

The full rules, including what each decoder deliberately does not store, are in docs/decoders.md.

How it is validated

Every decoder is checked against an independent reference on public captures, and every difference is explained rather than tuned away.

Area Reference Result
Modbus/TCP tshark, 8 Zeek traces including a 60,227-frame capture and fuzzed files 22,785 request and response pairs, all matching
AIS pyais, 86,168 real sentences 89,077 identity fields, 0 differences
NMEA 0183 pynmea2 Same sentences accepted and rejected
LLDP tshark, 19 frames from tcpdump and Zeek All accepted frames match, every discarded frame is one tshark marks malformed
IEEE OUI netaddr, all 35,084 MA-L prefixes 0 differences
DHCP tshark, 10 Zeek and Wireshark traces, 575 frames Roles, hostnames and addresses match on every trace
ARP tshark, Zeek traces including cooked format and crafted frames Every binding matches, crafted frames rejected

Cross-checking also found real bugs, both in KeelMap and in assumptions the references make. They are listed in the validation log.

Every test run also fuzzes 800 damaged and hostile captures and checks that nothing is stored without evidence, no cited frame falls outside its capture, counts reconcile across roles and communications, scans are deterministic and no redacted export leaks an identifier. tools/fuzz.py runs longer campaigns. Memory stays flat however long the capture: about 20 MB for nine hours of a 10 Hz source.

All validation uses public captures and real recorded sentences, so anyone can repeat it. The next step is a live bridge network. If you operate vessels or port systems and can share an authorised capture under suitable terms, please get in touch.

The asset chart

One HTML file that opens with no network access, drawn like a nautical chart. Magenta is reserved for Modbus writes. Every value opens to the frames behind it.

Inventories carry strings from network traffic, so the chart is built to be opened safely even when a capture was crafted by an attacker. It loads nothing external, runs only scripts whose SHA-256 is pinned at render time, and places captured text without ever treating it as markup. A browser test with injected script, image and SVG payloads ships in tools/. Details in docs/map.md.

Status

Version 0.1.1. Captures in, inventory and chart out. See CHANGELOG.md.

Planned: S7comm and DNP3, change detection between captures, Microsoft Sentinel ingestion through a data collection rule, a live sensor mode, and an IACS UR E26 inventory report.

Documentation

Contributing and security

See CONTRIBUTING.md. Report vulnerabilities privately, as described in SECURITY.md. Never attach real captures to issues.

Only capture traffic you are authorised to capture.

Licence

Apache License 2.0, see LICENSE. Third-party material is listed in NOTICE.

Metadata

Release files for keelmap 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keelmap 0.1.1
File Size Uploaded Provenance
keelmap-0.1.1.tar.gz 331.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for keelmap 0.1.1
File Interpreter ABI Platform
keelmap-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 514.1 kB

Release files / keelmap-0.1.1.tar.gz

Download URL keelmap-0.1.1.tar.gz
Size 331.0 kB
Tags Source
SHA-256 checksum
How to use checksums
4f996a1790c55cb0e99421a95058b2fa264ae9495f02ce63c17c188c91cbfde2
BLAKE2b-256 checksum
How to use checksums
dc832c3dbca730757b7862f5c1ff806d8be0f62d155e8a0f53db522f338f7e59
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / keelmap-0.1.1-py3-none-any.whl

Download URL keelmap-0.1.1-py3-none-any.whl
Size 183.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7794bf5f32c07e84931aa9daa277f2d4a3b5ed4814ccc21786c345024f5c1672
BLAKE2b-256 checksum
How to use checksums
08b8acaa5a93781029babf818c1e7643be409a6611685063189b015801f73787
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Security for keelmap 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page