+++ title = "HTTP API Reference" description = "Describes the endpoints of the Ferrix HTTP interface: the JSON API, the event stream, the status page, and the metrics endpoint." weight = 3 [extra] nav = "HTTP API" +++ Ferrix serves its HTTP interface on the address configured with `http_endpoint`. All endpoints are read-only and use the `GET` method. An error response has a `text/plain` body that describes the error. The server returns the following error statuses: | Status | Meaning | |---|---| | 400 (Bad Request) | A query parameter is invalid. | | 500 (Internal Server Error) | The server could not produce the response, for example because the metrics could not be encoded. | | 503 (Service Unavailable) | A table is unavailable because the daemon is shutting down. | > [!CAUTION] > The HTTP interface does not require authentication. Bind it to a loopback > address, or place it behind a reverse proxy that requires authentication. ## Endpoints | Endpoint | Description | |---|---| | `/` | Redirects to `/statusz`. | | `/statusz` | Status page. | | `/api/status` | Router ID, AS number, uptime, table sizes, memory use (`memory`, as jemalloc accounts for it: `allocated`, `active`, `metadata`, `resident`, `mapped`, `retained` octets), and whether control communities are enabled (`control_communities`). | | `/api/config` | The configuration that was read at startup. | | `/api/peers` | Configured neighbors and their sessions. | | `/api/routes/local` | Originated routes. | | `/api/routes/received` | Received routes. | | `/api/routes/rejected` | Rejected routes, by neighbor. | | `/api/rpki` | RPKI cache, data, and data age. | | `/api/kernel` | Kernel route installation state and interfaces. | | `/api/tcp` | The TCP connection of one session, as the kernel reports it. | | `/api/events` | Session and route changes, as server-sent events. | | `/metricz` | Prometheus metrics. See [Metrics Reference](@/docs/reference/metrics.md). | ## Peers `/api/peers` returns an array with one object for each configured neighbor: ```json { "name": "test-peer", "peer_addr": "127.0.0.1:64815", "peer_asn": 65001, "state": "Established", "direction": "inbound", "local_addr": "127.0.0.1:1790", "uptime_secs": 2, "four_octet_asn": true, "extended_next_hop": false, "address_families": ["IPv4 unicast", "IPv6 unicast"], "add_path_receive": [], "add_path_send": [], "routes": 1, "rejected_routes": 1, "advertised_routes": 2, "held_routes": 0, "expected_routes": null, "role": null, "role_confirmed": null, "last_error": null, "retry_at_unix": null, "graceful_restart": { "restart_time_secs": 120, "notification": true, "address_families": ["IPv4 unicast", "IPv6 unicast"], "forwarding_preserved": ["IPv4 unicast", "IPv6 unicast"] }, "stale_routes": null, "stale_until_unix": null } ``` | Field | Description | |---|---| | `state` | Session state: `Established`, `Connecting`, `Backoff`, `Idle`, or `Passive` (a passive neighbor that has no session). | | `direction` | `inbound` if the neighbor initiated the connection, `outbound` otherwise. | | `routes` | Number of the neighbor's routes in the routing table. | | `rejected_routes` | Number of the neighbor's routes rejected by its import policy. | | `advertised_routes` | Number of routes advertised to the neighbor and not withdrawn since. With ADD-PATH and `all_paths`, every route advertised counts. | | `held_routes` | Number of held routes: routes rejected for a reason that can change without a new UPDATE, such as RPKI validity or next hop resolution. | | `expected_routes` | The `expected_routes` value from the configuration, or `null` if it is not set. | | `role_confirmed` | Whether the neighbor advertised a BGP role that matches the configured role. | | `retry_at_unix` | For a neighbor without a session, the time of the next connection attempt. | | `graceful_restart` | Graceful restart, if both neighbors advertised it: the restart time of the neighbor, whether the N bit is in use (RFC 8538), the address families whose routes are retained, and those for which the neighbor preserved its forwarding state. | | `stale_routes` | Number of the neighbor's routes retained from its previous session and not yet advertised again, or `null` if there are none. | | `stale_until_unix` | For a restarting neighbor without a session, the time at which its retained routes are removed. | ## Received Routes `/api/routes/received` returns received routes, one page at a time. Table 1 lists the query parameters. | Parameter | Description | |---|---| | `prefix` | Returns all routes for the prefix, in order of path selection. Example: `prefix=193.0.0.0/21`. | | `address` | Returns all routes for the prefixes that contain the address, most specific first. Example: `address=193.0.0.1`. | | `within` | Returns the routes for the prefix and all more specific prefixes. Example: `within=193.0.0.0/16`. | | `peer` | Returns only routes from the neighbor, identified by name or address. | | `origin` | Returns only routes originated by the AS: the last AS of the AS path. The value is a number, with or without the prefix `AS`. Routes with an empty AS path are originated by the local AS. Example: `origin=AS64496`. | | `best` | If `true`, returns only the best path for each prefix. | | `limit` | Specifies the approximate number of routes in a page. Default: 1000. Maximum: 100000. | | `after` | Returns the page after the specified prefix. Use the `next_after` value of the previous page. | | `before` | Returns the page before the specified prefix. Use the `prev_before` value of the next page. | A page contains only complete prefixes. It ends before the prefix whose routes would exceed `limit`, but always contains at least one prefix. Prefixes are sorted by address, with IPv4 prefixes first. ```json { "routes": [{ "peer_name": "test-peer", "peer_addr": "127.0.0.1:64815", "peer_asn": 65001, "prefix": "203.0.113.0/24", "path_id": 0, "best": true, "rank": 1, "stale": false, "rpki": null, "aspa": null, "summary": { "origin": "IGP", "as_path": [65001], "next_hop": "10.0.0.2", "med": null, "local_pref": null, "communities": ["65001:1"], "large_communities": [], "other": [{ "name": "aggregator", "value": "AS65001 10.0.0.2" }] }, "attributes": [ ... ] }], "next_after": null, "prev_before": null } ``` | Field | Description | |---|---| | `path_id` | ADD-PATH path identifier, or 0. | | `best` | Whether the route is the best path for the prefix. | | `rank` | The position of the route in path selection for the prefix: 1 for the best path, 2 for the path that would be selected if the best path were withdrawn, and so on. | | `stale` | Whether the route is retained from a previous session of the neighbor and has not been advertised again (graceful restart). | | `rpki`, `aspa` | Route origin validation and ASPA verification state, if RPKI validation is configured. | | `summary` | The path attributes as text. `other` lists, in received order, the attributes that have no field of their own: atomic aggregate, aggregator, extended communities, AS4_PATH, only to customer, originator ID, cluster list, and unrecognized attributes. | | `attributes` | All path attributes, as received. | ## Rejected Routes `/api/routes/rejected` returns, for each neighbor with rejected routes, the number of rejected routes for each reason and the 200 most recent rejected routes. To return the records for one neighbor, add `?peer=NAME`. ```json [{ "peer": "test-peer", "peer_addr": "127.0.0.1", "total": 1, "by_reason": { "own AS in path": 1 }, "recent": [{ "prefix": "198.51.100.0/24", "path_id": 0, "reason": "own AS in path", "as_path": [65001, 64500], "at_unix": 1791020702, "summary": { "origin": "IGP", "as_path": [65001, 64500], "next_hop": "10.0.0.2", "med": null, "local_pref": null, "communities": [], "large_communities": [], "other": [] }, "attributes": [ { "Origin": 0 }, { "AsPath": { "segments": [{ "ordered": true, "path": [65001, 64500] }] } }, { "NextHop": "10.0.0.2" } ] }] }] ``` | Field | Description | |---|---| | `summary`, `attributes` | The path attributes of the rejected route, in the same format as for received routes. For a route rejected when RPKI data changed, the attributes as stored, including those added by the import policy. | ## Kernel `/api/kernel` returns the state of kernel route installation: ```json { "enabled": true, "table": 254, "metric": 20, "synced": true, "installed": 0, "kept": 0, "blackholes": 0, "unresolved": {}, "refused": 0, "errors": 0, "last_error": null, "interfaces": [ { "name": "eth0", "index": 2, "up": true, "addresses": ["192.168.5.15/24", "fe80::5055:55ff:fed4:2526/64"] } ] } ``` | Field | Description | |---|---| | `synced` | Whether the kernel routing table has been synchronized with the routing table since the daemon started. | | `installed` | Number of routes installed. | | `kept` | Number of routes from a previous run that are retained during startup. | | `blackholes` | Number of installed routes that are blackhole routes, which discard traffic for their prefix (RFC 7999). | | `unresolved` | Number of prefixes whose active route is not installed, by reason. | | `refused` | Number of prefixes whose active route the kernel rejected. | | `errors` | Number of requests that the kernel rejected. | ## TCP Connection `/api/tcp?peer=ADDRESS` returns what the kernel reports about the TCP connection of the session with the neighbor at `ADDRESS`, given as `peer_addr` in `/api/peers` shows it: the round-trip time, the congestion and receive windows, the queues, the retransmissions, and how long the connection has been held up by the neighbor's receive window or by the local send buffer. Linux only; on other systems, and for a session the kernel has no connection for, `available` is `false` and `reason` says why. ```json { "available": true, "reason": null, "connection": { "state": "established", "congestion": "cubic", "rtt_ms": 0.62, "rtt_variance_ms": 0.11, "min_rtt_ms": 0.4, "rto_ms": 201, "mss": 1428, "pmtu": 1500, "cwnd_segments": 10, "ssthresh_segments": null, "send_window": 1048576, "receive_window": 3145728, "receive_space": 65536, "send_window_scale": 7, "receive_window_scale": 10, "send_queue": 0, "not_sent": 0, "recv_queue": 0, "unacked_segments": 0, "retrans_segments": 0, "lost_segments": 0, "total_retrans": 0, "bytes_retrans": 0, "pacing_rate": 2309920000, "delivery_rate": 184860000, "bytes_sent": 312993, "bytes_acked": 312994, "bytes_received": 190, "segments_out": 230, "segments_in": 118, "busy_ms": 41, "limited_by_receive_window_ms": 0, "limited_by_send_buffer_ms": 0, "limited_by_receive_window_share": 0, "limited_by_send_buffer_share": 0, "last_data_sent_ms": 1234, "last_data_received_ms": 1300, "last_ack_received_ms": 1234 } } ``` Times are in milliseconds, sizes in octets, rates in octets per second. `send_window` is the neighbor's receive window, which bounds what Ferrix can send; `receive_window` is what Ferrix lets the neighbor send. The two `_share` values are the fractions of the busy time, 0 to 1, in which the connection was held up by the neighbor's window and by the local send buffer. A session that advertises a table to a neighbor that reads it slowly shows a high `limited_by_receive_window_share`. The status page shows the same information under the **tcp** link of each established session. ## Events `/api/events` is a stream of [server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html). Each event has the name `bgp` and contains a JSON object. The `type` field identifies the event, as listed in Table 2. | `type` | Description | |---|---| | `session_up` | A session was established. The `peer` field has the format of `/api/peers`. | | `session_down` | A session ended. Fields: `peer_addr`, `peer_asn`. | | `route_refresh_requested` | A neighbor sent a ROUTE-REFRESH message. | | `local_announced`, `local_withdrawn` | An originated route was advertised or withdrawn. | | `best_changed` | The best path for a prefix changed. The `route` field contains the new best path. | | `best_withdrawn` | A prefix no longer has a route. | | `lagged` | The client did not read events quickly enough and missed events on `stream`. Reload the corresponding table. The `missed` field gives the number of missed events for the `sessions` and `local_routes` streams; the `best_routes` stream omits it because the number is not known. | The following command displays the event stream: ```bash curl -sN 127.0.0.1:8080/api/events ```