Look routes up, and see and alert on the ones that were refused
Two things were hard to find out from the status page and the API: what the table has for a given prefix or address, and what a peer announced that was refused and why. Looking up. /api/routes/received takes a query, and the status page has a box for it: - prefix: every route for that prefix, the best first; - address: every prefix that covers the address, most specific first; - within: a prefix and everything inside it; - peer (by name or address) and best narrow any of these. A query that makes no sense is answered 400 with the reason, which the page shows. Results come in address order. Routes are shown with the peer's configured name. Refused routes. A refused route is not stored, so until now there was only a count. For each peer the server now keeps how many routes were refused for each reason, and the latest 200 with the AS path they came with. The record is kept by the peer's address and outlives the session, so that a peer that was cut off can still be looked into. It is at /api/routes/rejected and in a new section of the status page; the peers table links to it. Alerting. bgp_routes_rejected_total is now by peer as well as reason. New: bgp_peer_routes and bgp_peer_max_prefixes, to see a peer nearing its limit, and bgp_prefix_limit_exceeded_total. The README has rules to start from. The log has a line for a peer's first refused route and then at most one a minute, with the count of the others. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2b1979e38
commit
599cd0ad8b
16 files changed
+1110
-49
No files matched your search
+66
-1
@@ -21,7 +21,8 @@ When `http_endpoint` is set the binary also serves:
|
||||
| `GET /api/config` | The configuration file as read at startup |
|
||||
| `GET /api/peers` | Configured peers and established sessions |
|
||||
| `GET /api/routes/local` | Originated routes |
|
||||
| `GET /api/routes/received` | Adj-RIB-In contents |
|
||||
| `GET /api/routes/received` | Routes learned from peers; see "Finding a route" |
|
||||
| `GET /api/routes/rejected` | Routes peers announced that were refused, by peer |
|
||||
| `GET /api/events` | Server-sent events for session and route changes |
|
||||
| `GET /metricz` | Prometheus metrics |
|
||||
|
||||
@@ -63,6 +64,70 @@ A refused route is dropped, along with the route it would have replaced.
|
||||
`bgp_routes_rejected_total` counts them by reason, and the status page shows
|
||||
how many routes each peer has and how many were refused.
|
||||
|
||||
## Finding a route
|
||||
|
||||
The status page has a look-up box over the received routes, and
|
||||
`/api/routes/received` takes the same questions:
|
||||
|
||||
| Query | Answer |
|
||||
| --- | --- |
|
||||
| `?prefix=193.0.0.0/21` | Every route for that prefix, the best first |
|
||||
| `?address=193.0.0.1` | Every prefix that covers the address, most specific first |
|
||||
| `?within=193.0.0.0/16` | That prefix and everything inside it |
|
||||
| `&peer=NAME` | Only routes from that peer (its name or its address) |
|
||||
| `&best=true` | Only the route chosen for each prefix |
|
||||
| `&limit=N` | At most N routes (1000 unless given, 100000 at most) |
|
||||
|
||||
`within`, `peer` and `best` on their own look through the whole table, which
|
||||
on a full table takes a moment.
|
||||
|
||||
## Seeing and alerting on refused routes
|
||||
|
||||
A refused route is not in the table. What is kept instead, for each peer, is
|
||||
how many were refused for each reason and the latest 200 of them with their
|
||||
AS paths. The status page lists them under "Refused routes";
|
||||
`/api/routes/rejected[?peer=NAME]` has them as JSON. The record is kept when
|
||||
the session ends, so a peer that was cut off can still be looked into. The
|
||||
log has a line for a peer's first refused route and then at most one a
|
||||
minute.
|
||||
|
||||
For alerting, `/metricz` has:
|
||||
|
||||
| Metric | Meaning |
|
||||
| --- | --- |
|
||||
| `bgp_routes_rejected_total{peer,reason}` | Routes refused, by peer and why |
|
||||
| `bgp_peer_routes{peer}` | Routes an established peer has in the table |
|
||||
| `bgp_peer_max_prefixes{peer}` | The peer's limit, where it has one |
|
||||
| `bgp_prefix_limit_exceeded_total{peer}` | Sessions ended for going over the limit |
|
||||
| `bgp_connections_refused_total{reason}` | Connections closed without a session |
|
||||
|
||||
Rules to start from:
|
||||
|
||||
```yaml
|
||||
groups:
|
||||
- name: bgp
|
||||
rules:
|
||||
# A peer is announcing what its policy does not allow: a leak or a
|
||||
# hijack on its side, or a list on ours that is out of date.
|
||||
- alert: BgpRoutesRefused
|
||||
expr: sum by (peer, reason) (increase(bgp_routes_rejected_total[10m])) > 0
|
||||
labels: { severity: warning }
|
||||
# A route that has been through our own AS came back: a loop, or
|
||||
# someone announcing our AS number.
|
||||
- alert: BgpOwnAsInPath
|
||||
expr: increase(bgp_routes_rejected_total{reason="own AS in path"}[10m]) > 0
|
||||
labels: { severity: page }
|
||||
# A peer is close to its limit; the limit may need raising before the
|
||||
# session is cut.
|
||||
- alert: BgpPeerNearPrefixLimit
|
||||
expr: bgp_peer_routes / on (peer) bgp_peer_max_prefixes > 0.8
|
||||
for: 10m
|
||||
labels: { severity: warning }
|
||||
- alert: BgpPrefixLimitExceeded
|
||||
expr: increase(bgp_prefix_limit_exceeded_total[1h]) > 0
|
||||
labels: { severity: page }
|
||||
```
|
||||
|
||||
## Trying it without a router
|
||||
|
||||
`fake_peer` is a minimal BGP speaker that connects to the daemon, announces a
|
||||
|
||||
@@ -50,6 +50,7 @@ async fn main() -> Result<()> {
|
||||
registry: server.registry.clone(),
|
||||
local_rib: server.local_rib.clone(),
|
||||
rib: Arc::clone(&server.rib),
|
||||
refusals: server.refusals.clone(),
|
||||
};
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = bgp_web::serve_on(state, listener).await {
|
||||
|
||||
Reference in new issue
Block a user