Wheelhouse docs

Static routes#

Network → Static routes, at /routes, titled Routes on the screen. It owns the protocols static subtree. The page puts two things side by side: the router's forwarding table exactly as FRR prints it, and the static routes the configuration declares — so a route that was configured and never installed is visible as such rather than being assumed to work.

Everything here is IPv4. The table comes from show ip route; there is no IPv6 forwarding view in the product.

Where the data comes from#

GET /api/routes, polled every 15 seconds, returns three things in one response:

json
{
  "routes": [ { "prefix": "0.0.0.0/0", "protocol": "static", "selected": true, "fib": true, "…": "" } ],
  "static": { "route": { "0.0.0.0/0": { "next-hop": { "203.0.113.1": {} } } } },
  "raw": "S>* 0.0.0.0/0 [1/0] via 203.0.113.1, eth1, weight 1, 2d16h22m"
}

routes is show ip route parsed line by line; static is the protocols static configuration subtree; raw is the untouched output, which the side panel shows. A separate GET /api/config/commands?path=protocols static feeds the CLI view — agent/opmode.go (handleRoutes), agent/parse.go (parseRoutes).

The four tiles#

TileValueSub-line
Routesrows in the forwarding tablehow many are installed in the FIB
Staticrows whose protocol is statichow many static routes the configuration declares
Connectedrows whose protocol is connected
Default routethe next hop of 0.0.0.0/0, or nonethe interface it leaves by

The Static tile is the one to read twice. When the two numbers differ — three declared, two in the table — a route is not doing what the configuration says it should.

The forwarding table#

ColumnMeaning
Prefixthe destination
Sourcewhich protocol put the route there, as a coloured badge
Next hopthe gateway, or directly connected
Interfacethe interface the route leaves by
Distance/metricFRR's [distance/metric] pair
In FIBinstalled when the kernel is really using it, no otherwise
Agehow long the route has been there

Source, and the letter it came from#

FRR prefixes each line with a one-letter protocol code. The agent maps seventeen of them; a line whose first letter is not in the table is skipped rather than guessed at.

CodeSourceCodeSource
KkernelBbgp
CconnectedEeigrp
LlocalNnhrp
SstaticTtable
Rripvvnc
OospfVvnc-direct
IisisAbabel
Fpbrfopenfabric
ttable-direct

Four of these are coloured on the page: connected green, static blue, bgp and ospf amber, local and kernel grey, everything else grey.

kernel is the one that surprises people. A route the failover daemon installs appears as a kernel route at distance 0, which is why it outranks every static and DHCP default route while it is there — see Uplinks.

Selected and In FIB#

FRR prints two flags after the code: > for the selected route to a prefix, and * for a route that is in the forwarding table. The parser reads them as separate columns rather than as one word, because a route that is installed but not selected — every multi-WAN backup — has a space where the > would be, and reading the flags as a single token dropped those lines entirely.

  • In FIB — installed: the kernel is using this route.
  • In FIB — no: FRR knows about the route and is not using it. Usually because a better one exists for the same prefix.
  • The side panel shows Selected and Installed in FIB separately, which is the distinction the table's single column compresses.

Interface, and what the parser will not call one#

The interface is taken from the comma-separated remainder of the line, skipping anything that is plainly not an interface name: a via … clause, a weight … clause, the distance in brackets, a parenthesised reason like unreachable (blackhole), and the uptime. A token followed by onlink or inactive is still an interface. This is why a blackhole route shows no interface rather than showing its uptime in that column — agent/parse.go (parseRoutes).

Declared static routes#

Below the table, one row per prefix under protocols static route, present only when the configuration declares at least one. Each row shows the prefix, its next hops, and a badge:

  • in FIB — a route for this prefix is in the forwarding table.
  • declared, not installed — it is not. The commonest causes are a next hop that is not on any connected subnet, an interface that is down, and a better route from another protocol for the same prefix.

The badge compares the prefix against the forwarding table, not the prefix and the next hop together. A prefix reached through a different gateway than the one declared still shows in FIB.

Delete on a row stages, without confirmation:

delete protocols static route 172.16.0.0/12

That removes the prefix and every next hop under it.

The route detail panel#

Clicking a row in the forwarding table opens a panel with the parsed fields — prefix, protocol, next hop, interface, distance/metric, selected, installed in FIB, age — and below them the line exactly as FRR printed it, under the heading As FRR printed it. When the parsed view and the raw line disagree, the raw line is the truth and the parse is the bug.

There is no editor in this panel. A route that came from BGP, OSPF or a DHCP client is not something this page can change; delete the static route that produced it, or change the protocol that did.

Adding a static route#

+ Add static route opens a panel with four fields. The prefix and the next hop are required; nothing is staged until both are filled.

FieldWritten asNotes
Destination prefixprotocols static route <prefix>CIDR. 0.0.0.0/0 is a default route
Next hop… next-hop <address>must be reachable on a connected subnet
Administrative distance… next-hop <address> distance <n>optional; lower wins, and 1 is the static default
Descriptionprotocols static route <prefix> description <text>optional
set protocols static route 172.20.0.0/16 next-hop 10.0.3.1
set protocols static route 172.20.0.0/16 next-hop 10.0.3.1 distance 20
set protocols static route 172.20.0.0/16 description 'lab network'

Note where distance hangs: under the next hop, not under the route. Two next hops for one prefix can carry different distances, which is how a backup path is expressed.

The page does not offer an interface-based route (next-hop-interface), a blackhole route, or a route in an alternate table. The first two are CLI; alternate tables are written by the policy-routing editor on Uplinks.

What this page will not do#

  • No IPv6. show ip route is IPv4. A dual-stack router's v6 table has to be read on the CLI.
  • No VRF or namespace view. Not built — PLAN.md §7.
  • No editing of a route that is not static. Connected, kernel, BGP and OSPF routes are read-only here by construction.
  • No reordering or renumbering. There is nothing to order; distance is the only ranking, and you set it per next hop.

Worked example: reach a lab network behind a jump router#

10.0.3.1 is a second router on the LAN, and 172.20.0.0/16 lives behind it.

  1. Network → Static routes → + Add static route.
  2. Destination prefix 172.20.0.0/16, next hop 10.0.3.1, description lab.
  3. The Commands block reads:

    set protocols static route 172.20.0.0/16 next-hop 10.0.3.1
    set protocols static route 172.20.0.0/16 description lab
  4. Stage route, then commit.

Now check the result on this same page, not on the router. The Declared static routes panel should show 172.20.0.0/16 with an in FIB badge, and a row should appear in the forwarding table with source static, next hop 10.0.3.1 and installed. If the badge says declared, not installed, the next hop is not on a connected subnet — check that some interface holds an address in 10.0.3.0/24 on Interfaces.

App integrations#

The page renders any integration offers installed apps make for it — the hint block sits between the header and the tiles, and is present only when an installed app declares a hint for the routes page. See Apps.

See also#

  • Interfaces — where the connected routes come from.
  • Addressing — the distances a DHCP or PPPoE client gives its own default route.
  • Uplinks (multi-WAN) — everything about the default route, ranked and health-checked.
  • BGP / OSPF — the dynamic sources of the routes in this table.
  • Diagnostics → Tools — traceroute from the router, for when the table looks right and the traffic still does not arrive.

Checked against ui/src/pages/Routes.tsx, agent/opmode.go (handleRoutes), agent/parse.go (parseRoutes, routeCodes), ui/src/lib/format.ts (isDangerousPath), ui/src/lib/api.ts.

Updated 2026-09-02 manual network routing static-routes fib