The attention strip's names lead to the peer's row, which lights up for a
moment. The IPv4 and IPv6 sessions of one neighbour (NAME-v4, NAME-v6) are
listed together, joined down the side, the name said once in full. A route
count that moved by more than 2% between refreshes carries ▲ or ▼ for two
minutes; a row whose state or count changed while the tab was in the
background carries ● until it is clicked. A compact mode drops the next
hop and session columns, and is remembered by the browser. Colours are
variables, with a second set for prefers-color-scheme: dark.
A new per-peer `expected_routes` says about how many routes a peer sends
when all is well. The daemon only passes it to the API; the page lists an
established peer with under half of it as needing attention, a minute
after the session came up, with the expected count beside the actual one.
An established session with next to no routes looked, until now, exactly
like one that was fine.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
<spanclass="dim">Glyphs: <spanclass="g">v4</span><spanclass="g">v6</span> address families · <spanclass="g">GR</span> graceful restart · <spanclass="g">ENH</span> IPv4 with IPv6 next hops · <spanclass="g">AP</span> ADD-PATH · <spanclass="g amber">2B</span> two-octet AS numbers only · <spanclass="g">↑</span> provider <spanclass="g">↓</span> customer <spanclass="g">↔</span> peer <spanclass="g">RS</span> route server <spanclass="g">RSC</span> its client <spanclass="g">iBGP</span> our AS. Point at one for its meaning; click a peer's name to look at its routes.</span>
<labeltitle="Fewer columns and tighter rows, for a wall screen or a phone"><inputtype="checkbox"id="compact"> compact</label>
<spanid="since-ctl"></span>
<spanclass="dim legend">Glyphs: <spanclass="g">v4</span><spanclass="g">v6</span> address families · <spanclass="g">GR</span> graceful restart · <spanclass="g">ENH</span> IPv4 with IPv6 next hops · <spanclass="g">AP</span> ADD-PATH · <spanclass="g amber">2B</span> two-octet AS numbers only · <spanclass="g">↑</span> provider <spanclass="g">↓</span> customer <spanclass="g">↔</span> peer <spanclass="g">RS</span> route server <spanclass="g">RSC</span> its client <spanclass="g">iBGP</span> our AS. Point at one for its meaning; click a peer's name to look at its routes.</span>
</div>
</div>
<tableclass="data">
<tableclass="data">
<thead><tr><th>name</th><th>peer</th><th>state</th><th>up for</th><th>our address (next hop)</th><th>session</th><thclass="num">routes</th><thclass="num"title="Announcements rejected by the import policy since the daemon started, and routes held out of the table as invalid or unreachable now">rejected · held</th><th>detail</th></tr></thead>
<thead><tr><th>name</th><th>peer</th><th>state</th><th>up for</th><thclass="c-opt">our address (next hop)</th><thclass="c-opt">session</th><thclass="num">routes</th><thclass="num"title="Announcements rejected by the import policy since the daemon started, and routes held out of the table as invalid or unreachable now">rejected · held</th><th>detail</th></tr></thead>
<tbodyid="peers-body"></tbody>
<tbodyid="peers-body"></tbody>
</table>
</table>
@@ -353,13 +405,71 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
tick();
tick();
}
}
// An established peer with under half the routes it is expected to send
// (expected_routes), once it has had a minute to send them.
provider:['↑','provider: we buy transit from it'],
provider:['↑','provider: we buy transit from it'],
customer:['↓','customer: it buys transit from us'],
customer:['↓','customer: it buys transit from us'],
@@ -413,41 +523,62 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
$('peers-count').textContent=`${up} of ${peers.length} established`+(down?`, ${down} down`:'')+(waiting?`, ${waiting} waiting`:'');
$('peers-count').textContent=`${up} of ${peers.length} established`+(down?`, ${down} down`:'')+(waiting?`, ${waiting} waiting`:'');
if(!peers.length){setHTML(b,'<tr><td colspan="9" class="dim">No peers configured and none connected.</td></tr>');return;}
if(!peers.length){setHTML(b,'<tr><td colspan="9" class="dim">No peers configured and none connected.</td></tr>');return;}
constproblemsOnly=$('peers-problems').checked;
constproblemsOnly=$('peers-problems').checked;
constrow=p=>{
// The IPv4 and IPv6 sessions of one neighbour, named <stem>-v4 and
// <stem>-v6, are one neighbour to the reader: listed together, the
// name once in full and once quietly, joined down the side.
constFAMILY_SUFFIX=/^(.*)-v([46])$/;
constrow=(p,pair)=>{
constup=p.state==='Established';
constup=p.state==='Established';
constname=p.name?`<a href="#received" class="peer-link" data-peer="${esc(p.name)}" title="Look at this peer's routes">${esc(p.name)}</a>`:'<span class="dim">(inbound)</span>';
constname=p.name?`<a href="#received" class="peer-link" data-peer="${esc(p.name)}" title="Look at this peer's routes">${shown}</a>`:'<span class="dim">(inbound)</span>';
constheld=up&&p.held_routes?` · <span class="amber" title="Routes rejected as RPKI or ASPA invalid, or for a next hop that cannot be reached, and kept aside, to be taken if that changes">${fmtCount(p.held_routes)} held</span>`:'';
constheld=up&&p.held_routes?` · <span class="amber" title="Routes rejected as RPKI or ASPA invalid, or for a next hop that cannot be reached, and kept aside, to be taken if that changes">${fmtCount(p.held_routes)} held</span>`:'';
conststale=up&&p.stale_routes?`${glyph(`${fmtCount(p.stale_routes)} stale`,'Kept from the peer’s last session, and not sent again yet','amber')}`:'';
conststale=up&&p.stale_routes?`${glyph(`${fmtCount(p.stale_routes)} stale`,'Kept from the peer’s last session, and not sent again yet','amber')}`:'';
return`<tr class="${peerRank(p)===3?'dim':''}">
constshort=belowExpected(p);
<td class="name">${name}</td>
constexpected=up&&p.expected_routes>0
?` <span class="${short?'amber':'dim'}" title="${short?'Well under':'About'} the ${fmtCount(p.expected_routes)} routes this peer is expected to send (expected_routes)">of ~${fmtCount(p.expected_routes)}</span>`:'';
setHTML($('since-ctl'),marked?`${glyph('●','Rows that changed while this tab was in the background','since')} <span class="dim">${marked} changed since you last looked ·</span> <a href="#" id="since-clear">clear</a>`:'');
renderPeerChoices();
renderPeerChoices();
}
}
@@ -455,10 +586,14 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
consttoPeer=p=>`<a href="#peers" class="to-peer" data-peer="${esc(p.name||p.peer_addr)}" title="Go to this peer's row">${esc(p.name||p.peer_addr)}</a>`;
if(restarting.length)items.push(`<span class="amber">${restarting.length}restarting or stale:</span> ${restarting.map(p=>`<b>${esc(p.name||p.peer_addr)}</b>`).join(', ')}`);
if(short.length)items.push(`<span class="amber">${short.length}sending far fewer routes than expected:</span> ${short.map(p=>`${toPeer(p)} <span class="dim">(${fmtCount(p.routes)} of ~${fmtCount(p.expected_routes)})</span>`).join(', ')}`);
$('compact').addEventListener('change',()=>{applyCompact();try{localStorage.setItem(COMPACT_KEY,$('compact').checked?'1':'0');}catch(e){/* as above */}});
// A name in the strip goes to the peer's row and lights it for a moment.
Properties that repeat on many rows are displayed as glyphs. Table 2 lists
Properties that repeat on many rows are displayed as glyphs. Table 2 lists
them. Point to a glyph to display its meaning.
them. Point to a glyph to display its meaning.
@@ -77,6 +88,8 @@ them. Point to a glyph to display its meaning.
| `×N` | After an AS number in an AS path: the AS appears N times in a row (prepending). |
| `×N` | After an AS number in an AS path: the AS appears N times in a row (prepending). |
| `OTC`, `AA`, `AGG`, `AS4`, `OID`, `CL`, `EXT` | Path attributes without a column of their own: Only to Customer, ATOMIC_AGGREGATE, AGGREGATOR, AS4_PATH, ORIGINATOR_ID, CLUSTER_LIST, and extended communities. |
| `OTC`, `AA`, `AGG`, `AS4`, `OID`, `CL`, `EXT` | Path attributes without a column of their own: Only to Customer, ATOMIC_AGGREGATE, AGGREGATOR, AS4_PATH, ORIGINATOR_ID, CLUSTER_LIST, and extended communities. |
| `✓`, `✗`, `–` | RPKI or ASPA validation state: valid, invalid, or not found. |
| `✓`, `✗`, `–` | RPKI or ASPA validation state: valid, invalid, or not found. |
| `▲`, `▼` | The neighbor's route count rose or fell by more than 2% between the last two refreshes. Displayed for two minutes. |
| `●` | The row changed while the browser tab was in the background: the session state changed, or the route count moved by more than 2%. Click the row to clear the mark. |
An AS path or a list of communities that runs to more than a few lines is
An AS path or a list of communities that runs to more than a few lines is
displayed shortened, with a **more** control. Click the control, or
displayed shortened, with a **more** control. Click the control, or
| `role` <span class="type">[Role](#role)</span> | None | Specifies the BGP role of the neighbor (RFC 9234). Not permitted for IBGP neighbors. Required for every EBGP neighbor if any `export` statement enables `learned_routes`. |
| `role` <span class="type">[Role](#role)</span> | None | Specifies the BGP role of the neighbor (RFC 9234). Not permitted for IBGP neighbors. Required for every EBGP neighbor if any `export` statement enables `learned_routes`. |
| `require_role` <span class="type">bool</span> | `false` | Rejects the session if the neighbor does not advertise a BGP role. Requires `role`. |
| `require_role` <span class="type">bool</span> | `false` | Rejects the session if the neighbor does not advertise a BGP role. Requires `role`. |
| `passive` <span class="type">bool</span> | `false` | Prevents Ferrix from initiating connections to the neighbor. Ferrix accepts connections from the address of `endpoint`; the port of `endpoint` is not used. See [Passive Neighbors](@/docs/guides/peers.md#passive-neighbors). |
| `passive` <span class="type">bool</span> | `false` | Prevents Ferrix from initiating connections to the neighbor. Ferrix accepts connections from the address of `endpoint`; the port of `endpoint` is not used. See [Passive Neighbors](@/docs/guides/peers.md#passive-neighbors). |
| `expected_routes` <span class="type">uint32</span> | 0 | Specifies the approximate number of routes that the neighbor advertises in normal operation. The status page marks an established session with fewer than half this number of routes as needing attention. The value does not affect route processing. 0 disables the comparison. See [Expected Route Counts](@/docs/guides/peers.md#expected-route-counts). |
| `export` <span class="type">[ExportPolicy](#exportpolicy)</span> | Top-level `export` | Specifies the export policy for the neighbor. Replaces the top-level `export` statement. |
| `export` <span class="type">[ExportPolicy](#exportpolicy)</span> | Top-level `export` | Specifies the export policy for the neighbor. Replaces the top-level `export` statement. |
| `graceful_restart` <span class="type">[GracefulRestart](#gracefulrestart)</span> | Top-level `graceful_restart` | Specifies graceful restart for the neighbor. Each statement that is set replaces the corresponding top-level statement. |
| `graceful_restart` <span class="type">[GracefulRestart](#gracefulrestart)</span> | Top-level `graceful_restart` | Specifies graceful restart for the neighbor. Each statement that is set replaces the corresponding top-level statement. |
| `route_reflector_client` <span class="type">bool</span> | `false` | Makes the IBGP neighbor a route reflector client: Ferrix advertises to it the routes learned from other IBGP neighbors, and advertises its routes to them. Permitted only for IBGP neighbors. See [Route Reflection](@/docs/guides/passing-routes-on.md#route-reflection). |
| `route_reflector_client` <span class="type">bool</span> | `false` | Makes the IBGP neighbor a route reflector client: Ferrix advertises to it the routes learned from other IBGP neighbors, and advertises its routes to them. Permitted only for IBGP neighbors. See [Route Reflection](@/docs/guides/passing-routes-on.md#route-reflection). |
| `routes` | Number of the neighbor's routes in the routing table. |
| `routes` | Number of the neighbor's routes in the routing table. |
| `rejected_routes` | Number of the neighbor's routes rejected by its import policy. |
| `rejected_routes` | Number of the neighbor's routes rejected by its import policy. |
| `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. |
| `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. |
| `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. |
| `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. |
| `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. |
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.