Documentation

Reference for NetTopology Core 4.2.x · doc revision 87 · last updated 2026-08-24
You are reading documentation for the 4.2 series. Documentation for 3.8 (end-of-life) is kept at /docs/3.8/ for archival purposes and is no longer updated.

1. Overview

NetTopology Core runs as a single daemon, nettopo, that passively collects routing state and makes it queryable. It does not forward packets, does not install routes into the kernel FIB, and never modifies the configuration of the routers it observes.

BMP peers ─▶ ingest ─┐ ├─▶ normalizer ─▶ snapshot store ─▶ CLI / HTTP API MRT dumps ─▶ import ─┘

Data enters through one of two paths:

  • BMP (RFC 7854) — live sessions from routers. Preferred: near real-time, includes withdrawals and policy changes.
  • MRT / RIB dumps — batch import of historical tables in bgpdump-compatible format. Used for backfilling and lab work.

The normalizer merges both sources into a single RIB view with full history (see rib_retention). Snapshots are written to snapshot_dir every snapshot_interval and can be queried with nettopo-cli or the HTTP API.

2. Requirements

ComponentRequirement
Operating systemLinux ≥ 4.19 (x86-64, aarch64), FreeBSD ≥ 13. macOS ≥ 12 compiles but is best-effort.
MemoryRule of thumb: ~2 GB per 1M RIB entries, including history.
Disk~250 MB per 100k prefixes with 72h retention enabled.
PrivilegesNone in the default configuration. Local BGP peering on port 179 requires CAP_NET_RAW.

3. Configuration

The daemon reads a single file, /etc/nettopo/nettopo.conf. The format is INI-like with brace blocks. Reload with SIGHUP or systemctl reload nettopo.

Example

# /etc/nettopo/nettopo.conf — example for a lab collector listen_bmp = 0.0.0.0:1179 listen_api = 127.0.0.1:8421 log_level = info log_file = /var/log/nettopo/bgp.log rib_retention = 72h snapshot_interval = 15m snapshot_dir = /var/lib/nettopo peer "edge01" { remote_as = 64515 neighbor = 192.168.1.1 password = "hf7,2nNQp9" # MD5, optional }

Directives

DirectiveTypeDefaultDescription
listen_bmpaddr:port0.0.0.0:1179BMP listener socket.
listen_apiaddr:port127.0.0.1:8421HTTP API bind address.
log_levelenuminfoOne of: debug, info, warn, err.
log_filepathsyslogLog destination; reopened on SIGHUP.
rib_retentionduration72hHow long withdrawn prefixes stay queryable.
snapshot_intervalduration15mBackground snapshot cadence.
snapshot_dirpath/var/lib/nettopoSnapshot storage location.
maxmind_dbpathunsetMaxMind ASNum database for enrichment.
api_tokenstringunsetRequired for POST endpoints (X-Auth-Token header).
Changed in 4.2.0: unknown directives are now a fatal error at startup. Earlier versions ignored them silently. Validate a file before deploying with nettopo --check-config.

4. CLI reference

Commands

CommandDescription
nettopo-cli peers listSession table with state, uptime and prefix counts.
nettopo-cli peers dump <asn>Full RIB as received from a given peer.
nettopo-cli route lookup <prefix>Longest-prefix match against the local RIB.
nettopo-cli table export --format=csv|jsonExport the normalized RIB.
nettopo-cli rpki validate <prefix>Check a prefix/origin pair against the ROA set.
nettopo-cli topology dotEmit the AS-level graph as Graphviz DOT.
nettopo-cli snapshot statusShow snapshot age, size and last write result.

Signals

SignalEffect
SIGHUPReload configuration, re-open the log file.
SIGUSR1Force a snapshot flush.
SIGTERM, SIGINTGraceful shutdown: flush, close BMP sessions, exit.

5. HTTP API

EndpointDescription
GET /v1/healthLiveness, version, uptime.
GET /v1/peersBGP session table.
GET /v1/routes?prefix=&peer=RIB query, paginated (200 per page).
GET /v1/rpki/statusValidator cache state and ROA counts.
POST /v1/snapshotForce a snapshot. Requires X-Auth-Token.

Example

$ curl -s http://127.0.0.1:8421/v1/peers [ { "peer": "192.168.1.1", "asn": 64515, "state": "established", "uptime_s": 1218401, "prefixes_v4": 4123, "prefixes_v6": 1842 } ]
The API binds to 127.0.0.1 by default. Do not expose it unauthenticated: set api_token and terminate TLS on a reverse proxy if remote access is required.

6. Logging

Every line follows one format: <timestamp> [<level>] <module> <message>. The module is the internal subsystem that emitted the line (bgp, ospf, rib, rpki, store and others).

2026-08-24 08:42:01 [INFO] bgp peer 192.168.1.1 (AS64515) established, holdtime 180s
2026-08-24 08:48:31 [WARN] hijack MOAS alert: 192.168.50.0/24 seen via AS64521
2026-08-24 08:51:02 [ERR] store snapshot write failed: No space left on device

Rotation

The daemon does not rotate logs itself. A standard logrotate snippet ships in contrib/logrotate/:

/var/log/nettopo/*.log { weekly rotate 12 compress missingok postrotate systemctl kill -s HUP nettopo.service endscript }

7. FAQ

How is nettopo different from a full routing daemon?

Routing daemons such as FRR or Bird build a RIB in order to forward traffic. nettopo builds a RIB in order to answer questions about it — who announced what, when, and through which AS path. It never programs the kernel and performs no best-path selection of its own beyond mirroring what its sources report.

Does the daemon need root privileges?

No. In the default configuration it only opens TCP ports above 1024 and writes to its own directories. Local BGP peering on port 179 requires CAP_NET_RAW: setcap cap_net_raw+ep /usr/local/bin/nettopo.

Which routers can feed it?

Anything that speaks BMP (RFC 7854): FRR ≥ 7.5, Bird 2.x, Cisco IOS-XR, Juniper Junos, Nokia SR OS. For everything else there is MRT dump import.

Is Windows supported?

No, and it is not on the roadmap. It compiles under WSL2, but nobody on the team tests it.

Where is the web interface?

There is none on purpose. The workspace view on the landing page is rendered from the HTTP API by a separate contrib project, nettopo-web. Use nettopo-cli or the API directly.

How much memory does a full table need?

Rule of thumb: ~2 GB per 1M IPv4 prefixes including history. A full Internet table (≈950k prefixes) with 72h retention fits comfortably in 2.5 GB.

See also: Changelog · Downloads · Quick Start
Found an error in this page? Open an issue or edit the sources under docs/ in the repository.