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>
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.
Design Principles
Ferrix is built on the following principles:
- Memory safety. The workspace lints forbid
unsafecode 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::policyevaluates each route against the import policy.server::rpkivalidates the route against the data that the RTR client (rpkicrate) receives.server::reachdetermines 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.