mod config;
mod control;
mod counters;
mod forward;
mod host;
mod ingress;
mod init;
mod output;
mod reject;
mod tasks;
use core::borrow::BorrowMut;
use core::fmt;
pub use config::{ConfigError, Limits, Options, PortConfig};
pub use counters::Counters;
pub use host::{Claim, ControlClass, Event, Group, Host, Permissive, Targets};
pub use ingress::{Discard, Frame, Local, Received};
pub use output::{Action, Cause, Dest, Kind, Send, SendRefusal};
use counters::bump;
use output::Outputs;
use tasks::Task;
use crate::discovery::Discovery;
use crate::table::Table;
use crate::{Instant, PortId, MAX_NPDU};
pub(crate) const MIN_LIMIT: u16 = Limits::MIN;
pub struct Storage<
const P: usize,
const R: usize,
const H: usize,
const D: usize = 16,
const N: usize = MAX_NPDU,
> {
ports: [PortConfig; P],
port_count: usize,
table: Table<R, H>,
discovery: Discovery<D>,
scratch: [u8; N],
out: Outputs,
counters: Counters,
}
impl<const P: usize, const R: usize, const H: usize, const D: usize, const N: usize>
Storage<P, R, H, D, N>
{
#[must_use]
pub const fn new() -> Self {
const { assert!(P <= 255, "a router has at most 255 ports") };
const { assert!(N >= MAX_NPDU, "the scratch holds at least MAX_NPDU") };
Self {
ports: [PortConfig::ZERO; P],
port_count: 0,
table: Table::new(),
discovery: Discovery::new(),
scratch: [0; N],
out: Outputs::NEW,
counters: Counters::ZERO,
}
}
}
impl<const P: usize, const R: usize, const H: usize, const D: usize, const N: usize> Default
for Storage<P, R, H, D, N>
{
fn default() -> Self {
Self::new()
}
}
impl<const P: usize, const R: usize, const H: usize, const D: usize, const N: usize> fmt::Debug
for Storage<P, R, H, D, N>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Storage")
.field("ports", &&self.ports[..self.port_count])
.field("table", &self.table)
.field("discovery", &self.discovery)
.field("pending", &self.out.pending(self.port_count))
.field("counters", &self.counters)
.finish_non_exhaustive()
}
}
pub struct Router<
S,
const P: usize,
const R: usize,
const H: usize,
const D: usize = 16,
const N: usize = MAX_NPDU,
> {
storage: S,
options: Options,
next_sweep: Instant,
latest: Instant,
}
impl<S, const P: usize, const R: usize, const H: usize, const D: usize, const N: usize> fmt::Debug
for Router<S, P, R, H, D, N>
where
S: BorrowMut<Storage<P, R, H, D, N>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Router")
.field("storage", self.storage.borrow())
.field("options", &self.options)
.field("next_sweep", &self.next_sweep)
.field("latest", &self.latest)
.finish()
}
}
impl<S, const P: usize, const R: usize, const H: usize, const D: usize, const N: usize>
Router<S, P, R, H, D, N>
where
S: BorrowMut<Storage<P, R, H, D, N>>,
{
pub fn new(
mut storage: S,
ports: &[PortConfig],
options: Options,
now: Instant,
) -> Result<Self, ConfigError> {
if ports.is_empty() {
return Err(ConfigError::NoPorts);
}
if ports.len() > P.min(255) {
return Err(ConfigError::TooManyPorts {
count: ports.len(),
capacity: P.min(255),
});
}
for (index, port) in ports.iter().enumerate() {
if ports[..index]
.iter()
.any(|other| other.network == port.network)
{
return Err(ConfigError::DuplicateNetwork(port.network));
}
let limits = [port.max_npdu.unicast, port.max_npdu.broadcast];
if limits
.iter()
.any(|&limit| limit < MIN_LIMIT || usize::from(limit) > N)
{
return Err(ConfigError::Limit(PortId(index as u8)));
}
}
if usize::from(options.route_cap) > R {
return Err(ConfigError::RouteCap {
cap: options.route_cap,
capacity: R,
});
}
if options.sweep.as_nanos() == 0 {
return Err(ConfigError::ZeroSweep);
}
let store = storage.borrow_mut();
store.table.clear();
store.table.set_convergence(options.convergence);
store.discovery.clear();
store.out = Outputs::NEW;
store.counters = Counters::ZERO;
store.ports[..ports.len()].copy_from_slice(ports);
store.port_count = ports.len();
for (index, port) in ports.iter().enumerate() {
let reserved = port.network == 0 || port.network == 0xFFFF;
if !store.table.add_direct(port.network, PortId(index as u8)) && !reserved {
return Err(ConfigError::TableTooSmall);
}
}
Ok(Self {
storage,
options,
next_sweep: now + options.sweep,
latest: now,
})
}
fn store(&mut self) -> &mut Storage<P, R, H, D, N> {
self.storage.borrow_mut()
}
fn clock(&mut self, now: Instant) -> Instant {
self.latest = self.latest.max(now);
self.latest
}
pub fn start(&mut self) {
let store = self.store();
store.drop_undrained();
store.out.push(Task::announce());
}
pub fn receive<'f>(
&mut self,
frame: Frame<'f>,
now: Instant,
host: &mut impl Host,
) -> Received<'f> {
let now = self.clock(now);
let options = self.options;
self.store().receive(frame, now, &options, host)
}
pub fn poll_action(&mut self) -> Action<'_> {
self.store().poll()
}
pub fn refused(&mut self, why: SendRefusal) {
let store = self.store();
if why == SendRefusal::TooLong {
bump(&mut store.counters.egress_too_long);
}
store.refused();
}
pub fn next_deadline(&self) -> Option<Instant> {
Some(self.next_sweep)
}
pub fn on_deadline(&mut self, now: Instant, host: &mut impl Host) {
let now = self.clock(now);
if now < self.next_sweep {
return;
}
let options = self.options;
let store = self.storage.borrow_mut();
store
.table
.purge_stale(now, options.learned_age, |network| {
host.observe(Event::Purged { network });
});
store.table.clear_expired_busy(now);
store.table.expire_pending(now, &options.timing, |network| {
host.observe(Event::PendingExpired { network });
});
store
.discovery
.expire(now, options.discovery_age, |network| {
host.observe(Event::DiscoveryExpired { network });
});
self.next_sweep += options.sweep;
if self.next_sweep <= now {
self.next_sweep = now + options.sweep;
}
}
pub fn cancel_discovery(&mut self) {
self.store().discovery.cancel();
}
pub fn table(&self) -> &Table<R, H> {
&self.storage.borrow().table
}
pub fn table_mut(&mut self) -> &mut Table<R, H> {
&mut self.store().table
}
pub fn discovery(&self) -> &Discovery<D> {
&self.storage.borrow().discovery
}
pub fn counters(&self) -> Counters {
self.storage.borrow().counters
}
pub fn ports(&self) -> &[PortConfig] {
let store = self.storage.borrow();
&store.ports[..store.port_count]
}
pub const fn options(&self) -> &Options {
&self.options
}
}