Skip to content

Architecture

Renato Westphal edited this page Oct 27, 2023 · 2 revisions

The core of the Holo project consists of multiple protocol implementations available as separate libraries or "crates" in Rust terminology. These protocols are implemented using the Sans I/O paradigm, where all network I/O is decoupled from the actual protocol logic. This approach has numerous benefits, including enhanced testability and flexibility, which we shall explore in subsequent sections.

In addition, Holo features a daemon named holod. This daemon offers seamless integration with all protocol libraries in a self-contained package that is easy to use. Although users have the option of writing their own daemons to fulfill specific requirements, it is anticipated that holod will provide sufficient flexibility to meet the majority of users' needs.

The following diagram illustrates how the Holo components are integrated to create a fully functional routing daemon:

arch.png

The holod daemon is responsible for initiating the northbound modules on the left and the base internal components on the right, while establishing a network of channels to enable seamless communication between them.

The northbound modules handle the communication with external management clients and expose different management interfaces. Currently, Holo supports two management interfaces, namely gRPC and gNMI, with future plans to support NETCONF/RESTCONF. Additionally, there is an external CLI that interacts indirectly with holod through gRPC.

The internal components on the right perform various functions typical of a routing protocol suite. The gray-colored components are considered to be base components and operate continuously. Other components, such as protocol instances, are initiated or stopped in response to management requests or internal events.

Asynchronous Runtime

The Holo components comprise of a set of asynchronous tasks that are managed by the Tokio runtime. The asynchronous runtime is tasked with scheduling and executing the asynchronous tasks on a dedicated thread pool, among other things. Holo's adherence to the Sans I/O paradigm ensures that adapting the code to alternate runtimes can be achieved with ease if required.

It is important to note that while all asynchronous tasks are executed within the same Linux process, Rust's robust failure isolation boundaries ensure that errors are contained and do not unintentionally propagate from one task to another.

Inter-Task communication

The communication between all asynchronous tasks is achieved through message passing. Two primary communication pathways are available:

  • Northbound channels: These channels enable bidirectional communication between management clients and internal components. Management clients can send control messages, as well as subscribe to receive streaming telemetry data.
  • Internal bus (ibus): The ibus is used for communication between internal components. These channels support a variety of messaging options, including pub-sub, broadcast, and unicast communication methods. This provides the internal components with a flexible and efficient means of communicating with one another.

Since all tasks reside within the same Linux process, the overhead associated with message passing is minimal. The process of sending and receiving a message solely involves a transfer of data ownership and does not entail any data marshalling or unmarshalling.

Internal Components

Here is a brief description of the base components:

  • holo-routing:
    • manages protocol instances
    • manages the RIB and ensure it's in sync with the FIB
    • manages route redistribution among the different protocol instances
    • implements the ietf-routing YANG module
  • holo-interface:
    • manages system interfaces and their addresses
    • implements the ietf-interfaces and ietf-ip YANG modules
  • holo-keychain:
    • manages key chains used for protocol authentication
    • implements the ietf-key-chain YANG module

Other components, such as holo-ospf and holo-rip, implement routing protocols and their corresponding YANG modules. Protocol instances use the internal bus (ibus) to communicate with the base components and with other protocol instances. Examples include querying interface addresses from holo-interface or registering a dynamic BFD peer with holo-bfd.

All protocol implementations support multiple concurrent instances, except when that doesn't make sense (such as in the case of LDP). Support for running protocol instances within the context of a VRF (Virtual Routing and Forwarding) is not implemented yet.

I/O-Free Protocol Implementations

TODO

Model-Driven Management Architecture

TODO

Data-Driven Testing

TODO

Logging

TODO