Files
ferrix/docs/content/docs/development/architecture.md
T
Rayhaan JaufeerallyandClaude Opus 5.5 dc275f99d7
ci / check (push) Canceled after 0s
ci / docs (push) Canceled after 0s
ci / interop (push) Canceled after 0s
ci / fuzz (push) Canceled after 0s
Add a documentation website, built with Zola
The documentation moves from READMEs into a site in docs/: getting
started, a user guide, operations, a reference, and development pages,
written in the style of network vendors' documentation (docs/STYLE.md).
bin/bgp/README.md and docs/RFCs.md are folded into it.

- A theme of its own in the International Typographic Style, in blues,
  with self-hosted Inter and JetBrains Mono, search over Zola's index,
  dark mode, and no resources loaded from other hosts.
- Example configurations in docs/examples/, shown on the pages and
  checked with `bgp --check` in CI.
- A test that fails when a field of config.proto is missing from the
  configuration reference.
- A CI job that builds the site; Zola fails on broken links and anchors.
- A hardened systemd unit, tried under systemd with kernel routes on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:12:43 +02:00

9.5 KiB

+++ title = "Architecture Overview" description = "Describes the internal components of Ferrix and how a route is processed from reception to kernel installation and advertisement." weight = 1

[extra] nav = "Architecture" +++

This topic describes the internal components of Ferrix and how they process a route. It is intended for developers who work on the source code. Figure 1 shows the components and the path of a route through them.

UPDATE messages to neighbors, through the session output queue Neighbors TCP, port 179 Sessions one task each Import policy · RPKI Routing table shards best paths journal Exporters one per session Kernel netlink Status, API HTTP, metrics RTR client RPKI caches
Figure 1: Route processing in Ferrix

Design Principles

Ferrix is built on the following principles:

  • Memory safety. The workspace lints forbid unsafe code in every crate.
  • No central routing table task. Each session task applies the routes of an UPDATE message to the routing table directly, and locks only the shards that contain the affected prefixes. Sessions that update different shards do not wait for each other. A session that must wait is slowed down, which provides back-pressure without an unbounded queue in front of the routing table.
  • Independent consumers. The components that act on best path changes (the export task of each session, kernel route installation, and the status page) read the changes from a journal at their own rate. A slow consumer does not delay the routing table or other consumers.
  • Fault isolation. A panic ends only the task in which it occurs. If a session task panics, Ferrix closes the session, notifies the neighbor, and removes the neighbor's routes. Shared tables use locks that are not poisoned by a panic. The daemon exits only if it loses a component that it cannot operate without.
  • Resistance to neighbor-triggered load. Ferrix rate-limits log messages that a neighbor can trigger, closes connections from unconfigured addresses before reading from them, and places flapping neighbors in hold-down.

Inbound Route Processing

Connection Management

The dialer (server::dialer) initiates a connection to each configured neighbor and retries with exponential backoff. The listener accepts connections from the addresses of configured neighbors only. The collision module (server::collision) retains one connection for each neighbor when both speakers initiate a connection (RFC 4271 Section 6.8).

BGP Finite State Machine

The BGP finite state machine (server::fsm) implements RFC 4271 Section 8 without input or output. It receives events and returns the actions to perform, so that each state transition can be tested with a function call. The connection driver (server::connection) converts received bytes and timer expirations into events, and performs the returned actions.

Message Decoding

The packet crate decodes and encodes BGP messages. Errors in UPDATE messages are handled as specified in RFC 7606: depending on the error, the attribute is discarded or the routes are treated as withdrawn. All parsers are fuzz-tested.

Import Processing

The server::routes module applies the routes of an UPDATE message to the routing table, according to the neighbor's import policy:

  • server::policy evaluates each route against the import policy.
  • server::rpki validates the route against the data that the RTR client (rpki crate) receives.
  • server::reach determines whether the next hop is resolvable.

A route that is rejected for a reason that can change without a new UPDATE (RPKI invalid, ASPA invalid, or an unresolvable next hop) is stored as a held route. Ferrix reevaluates held routes when the RPKI data or the interfaces change.

Routing Table

The routing table (rib::table) combines the Adj-RIBs-In and the Loc-RIB (RFC 4271 Section 3.2) in one structure, organized by prefix. The entry for a prefix contains the routes from all neighbors and identifies the best path. After a change, best path selection examines only the entry for the affected prefix. The routing table is designed for hundreds of full routing tables:

  • A route occupies 16 bytes: a pointer to its path attribute set, and one word that encodes the neighbor and the first steps of best path selection as a single comparable value.
  • Path attribute sets are stored once and shared by all routes with identical attributes. Unused sets are removed periodically.
  • The table is divided into shards by a hash of the prefix. Each shard has its own lock.

Outbound Route Processing

Change Journal

Each shard of the routing table maintains a journal of the prefixes whose best path changed. Each consumer reads the journal through its own reader. The journal records that the best path of a prefix changed, not the new best path; the reader retrieves the current best path when it processes the entry. A reader that falls too far behind compares its state with the routing table instead. Changes made together carry the same table-wide sequence stamp, so that a reader processes them together and they are advertised in the same UPDATE messages.

Route Advertisement

The server::advertise module runs an export task for each session. The task advertises the originated routes and, if the export policy enables learned_routes, the best paths learned from other neighbors, as the server::export module permits for each route. The task records the routes that it has advertised, groups routes with identical attributes into one UPDATE message, and waits when the session's output queue is full.

Kernel Route Installation

The fib crate reads best path changes and maintains the kernel routing table over rtnetlink, with one route for each prefix through the next hop of its best path. It also receives the router's interfaces and addresses from the kernel, which determine whether a next hop is resolvable.

HTTP Interface

The web crate provides read-only access to the state owned by the server. It does not store routing state.

Crates

Table 1 lists the crates of the workspace.

Crate Contents
crates/primitives Prefixes, address families, and parsing utilities
crates/packet BGP message encoding and decoding
crates/rib The routing table, best path selection, and the change journal
crates/rpki The RTR client, route origin validation, and ASPA verification
crates/fib Kernel route installation over netlink
crates/server Sessions, the finite state machine, policy, export, and configuration
crates/web The status page, HTTP API, and metrics
crates/integration-tests Tests that run the server against in-process BGP peers
crates/bench Benchmarks, with a synthetic Internet routing table
bin/bgp The daemon

The API documentation of each crate (cargo doc --open) describes its modules. Module-level comments document the reasons for each design.