← All posts

Zero Footprint

How Osprey sees a network without becoming part of it

Michel Wijnberg

Every monitoring product on earth claims to be “agentless”. Most of them mean they SSH in and screen-scrape show output, which is agentless in the same sense that a burglar is a houseguest.

Osprey takes a different route, and I want to be precise about it, including the part where the marketing line is not quite true.

There are three ways Osprey gets data. All three are visible to an operator who looks (this is not a stealth product), but only one of them puts Osprey inside a routing protocol’s own database, and that is the one worth scrutinising.


1. The GRE recorder: a real adjacency that can never carry traffic

The most interesting one. Osprey can form a genuine IGP adjacency over a GRE tunnel: a real OSPFv2, OSPFv3 or IS-IS neighbour relationship, with real Hellos, real DBD exchange, real flooding. It learns the LSDB the way a router learns it: by being told.

This is strictly better than walking a MIB, because you get the database as the protocol actually distributes it, with sequence numbers, ages and checksums intact, and you see changes the instant they flood rather than at the next poll interval.

It also means the routers do see a neighbour. Let me be blunt about that: Osprey appears in the LSDB. It has to. RFC 2328 §12.4.1 requires every router in an area to originate a Type 1 Router-LSA, and a router that forms an adjacency without originating one is a broken router.

So the question is not “can we be invisible”, because we can’t, but “can we be incapable of affecting forwarding”. That one has a real answer:

// originateRouterLSA creates and installs our Router-LSA in the LSDB.
// RFC 2328 Section 12.4.1 requires every router to originate a Type 1 LSA.
// We use cost 65535 (maximum) so no router will ever route traffic through
// Osprey. Only the P2P link is advertised. NO stub network for the tunnel
// inner subnet. Advertising the tunnel subnet (even at max cost) causes SPF
// on other routers to install a route for the tunnel prefix, which can
// disrupt the GRE outer path and kill the adjacency.

Two deliberate decisions in there, and the second one took a lab outage to learn properly.

Maximum metric. The recorder advertises its single point-to-point link at cost 65535, and that is the weaker of the two guarantees. A metric only makes a path unattractive, and an unattractive path still wins when it is the only one. The real guarantee is the shape: the recorder has exactly one adjacency, so it is a leaf in the graph, and there is no path through a leaf for any SPF to find. Add that it advertises no prefix of its own and there is nothing to route to either. The maximum metric is belt and braces on top of a topology that already cannot carry transit.

No stub network. The recorder advertises the P2P link and nothing else. In particular it does not advertise the tunnel’s inner subnet as a stub network. This is the subtle one. If it did, every router in the area would install a route toward the tunnel prefix. That route can then override the underlay path the GRE tunnel itself is riding on, which breaks the outer transport, which kills the adjacency, which drops the recorder, a beautifully self-inflicted flap. Advertising nothing reachable avoids the whole class of problem, and still satisfies RFC 2328.

The IS-IS version is stricter

IS-IS gives you a purpose-built mechanism for exactly this, so the IS-IS recorder uses it. Its self-originated LSP carries a set of hard invariants:

InvariantWhy
OL bit = 1The Overload bit. ISO 10589 mandates that every other router treat an overloaded router as non-transit. This is the primary guarantee, not a metric trick.
ATT bits = 0000Never attract L1 default routes. An L1-only router must not point its default at Osprey.
P bit = 0No partition repair.
TLV 22 metric = 0xFFFFFFMaximum wide metric, belt and suspenders alongside the OL bit.
No TLV 128 / 130 / 135 / 236These are the IP reachability TLVs. Emitting any of them would inject routes.
No TLV 134No TE Router ID. Would insert Osprey into the traffic-engineering database.
No TLV 222 / 242No multi-topology, no router capability / segment routing.

Read that table as a list of things the code cannot do rather than a list of settings. There is no configuration option that turns the Overload bit off and no code path that marshals an IP reachability TLV, because the safest way to guarantee you never advertise a prefix is to not implement advertising one.

So: visible, participating, and structurally unable to attract a single packet. That is a much stronger claim than “invisible”, and unlike “invisible” it is true.


2. SNMP: the LSDB the boring way, plus everything the LSDB does not carry

Not every device will give you an adjacency, and some things simply are not in a link-state database. So Osprey also polls.

SNMP targets: 192 devices, all active, 300-second poll interval, credentials masked
SNMP targets: 192 devices, all active, 300-second poll interval, credentials masked

192 targets in this lab, every one green, polling on a 300-second cycle, last poll measured in seconds.

The credentials column shows ***, and it is worth being precise about where that redaction happens: in the API handler, not in the browser. Credentials are stored AES-256-GCM encrypted at rest, and every read path runs the response through a mask before serialising it. The frontend never receives the community string or the v3 auth/priv passwords in the first place, so there is no “reveal” button, and there could not be one without changing the server.

Masking in the client is theatre. Masking in the handler is a control.

SNMP does four jobs here:

  • LSDB reconstruction where no recorder is attached (this is what produced the 13,382 LSAs in the previous post, with the missing header fields honestly declared)
  • Interface counters for traffic, errors and utilisation
  • EIGRP, which has no database to join at all and is read entirely from CISCO-EIGRP-MIB
  • L2 neighbours via LLDP and CDP

That last one matters more than it sounds:

CDP/LLDP neighbours: 303 adjacencies, 66 unique remote devices
CDP/LLDP neighbours: 303 adjacencies, 66 unique remote devices

303 L2 adjacencies, 66 unique remote devices. This is the layer that lets Osprey say which physical port an inter-AS BGP session actually crosses instead of hand-waving at “some link between these two routers”, and you can see one of those inter-AS links right there in the table, the CDP entry from ams1-gw1 Et1/1 to e-ams1-gw1, which is the AS 200 ↔ AS 100 border.


3. BMP: let the routers do the talking

For BGP, the preferred path is not to ask. It is to listen.

BMP (RFC 7854) is a protocol where the router pushes its Adj-RIB-In, the paths its peers advertised to it, to a monitoring station over a plain TCP session. There is no polling loop, no show ip bgp parsing, and no query load on the control plane beyond the session itself. The router decides what to send and when.

BGP peers: 378 sessions across 57 peers, 374 up, 4 down
BGP peers: 378 sessions across 57 peers, 374 up, 4 down

378 sessions across 57 peers here, but not all of them arrived the same way, and that distinction is recorded rather than smoothed over.

Six routers in this lab are BMP exporters (ams1-gw1, jfk1-gw1, e-ams1-gw1, e-sin1-gw1, i-jfk1-gw1, i-sin1-gw1), and they account for 24 sessions: including every eBGP session in the lab. The remaining 354 come from SNMP walks of the BGP4-MIB on routers that do not export BMP at all.

Both routes to the data are legitimate and both are worth having; they are just not equally good. A BMP feed carries every path the peer advertised, plus Peer Up and Peer Down as the router sees them. An SNMP walk gives you the session table at poll time and nothing between polls. Which RIB matters here: RFC 7854 monitors the Adj-RIB-In, what arrived before this router chose anything. The router’s own selected table, the Loc-RIB, is a separate later extension (RFC 9069) that these exporters do not send, so best-path selection over those candidates is Osprey’s computation and is labelled as such.

So every row carries a source column recording which one it came from, and that value is load-bearing further up the stack. When Osprey later stitches a cross-AS path and needs to justify an inter-domain hop, an eBGP session counts as evidence with its provenance attached (bmp-peer versus snmp-peer), never as an anonymous fact. Same table, graded.

Both address families ride a single BMP session per exporter, which is why you see IPv4 and IPv6 rows sharing a reporting router.

The eBGP rows in that table are the interesting ones, because they are the seams between the three tenants:

FromASToAS
e-ams1-gw1100ams1-gw1200
i-sin1-gw1300e-sin1-gw1100
i-jfk1-gw1300jfk1-gw1200

A triangle: AS 200 ↔ AS 100 at Amsterdam, AS 100 ↔ AS 300 at Singapore, AS 200 ↔ AS 300 at New York. Six sessions, twelve rows across both families. That triangle is what makes cross-domain path stitching possible at all, and it is the subject of post three.


What Osprey structurally cannot do

Some constraints are worth stating as constraints rather than features, because they are the reason the rest is trustworthy:

  • No route injection. The recorder originates exactly one Router-LSA describing exactly one max-cost P2P link, and nothing else. It has no code path that advertises a prefix.
  • No packet forwarding. Osprey is not in the data plane. There is no forwarding table, no FIB, nothing to misprogram.
  • No configuration writes. The SSH terminal in the UI is a terminal. The keystrokes are yours, the session is recorded for audit, and Osprey itself never issues a config command.
  • One-way data flow. Collectors and BMP ingest, publish to NATS; the engine persists to PostgreSQL; the API serves the frontend. No service calls back upstream. There is no path from the web UI to a router that is not a human typing into a terminal.

The honest summary

Osprey is passive in the way that matters: it cannot attract traffic, cannot inject a route, and cannot forward a packet. But it is not invisible, and I would rather tell you that than let you find out from a show ip ospf neighbor that has one more line than you expected.

If a vendor tells you their tool joins your IGP and nobody can tell, ask them what their Router-LSA looks like.


Next: why your path tool is probably lying to you, and the 4,032-pair measurement that proves it: Hop-by-Hop Truth.