use super::counters::bump;
use super::host::{Event, Host};
use super::ingress::{Arrival, Discard, Received};
use super::output::{Kind, Target};
use super::Storage;
use super::MIN_LIMIT;
use crate::message::RejectReason;
use crate::npdu::{Address, Npdu};
use crate::table::Route;
use crate::MAX_MAC_LEN;
fn source_of<'f>(at: &Arrival<'f>, npdu: &Npdu<'f>) -> Option<Address<'f>> {
npdu.source.or(Some(Address {
network: at.network,
mac: at.source,
}))
}
pub(super) const LONGEST_REJECT: usize = 2 + 2 * (3 + MAX_MAC_LEN) + 1 + 1 + 3;
const _: () = assert!(LONGEST_REJECT < MIN_LIMIT as usize);
impl<const P: usize, const R: usize, const H: usize, const D: usize, const N: usize>
Storage<P, R, H, D, N>
{
fn hop_count_spent<'f>(&mut self, npdu: &Npdu<'_>, host: &mut impl Host) -> Received<'f> {
bump(&mut self.counters.hop_count_drops);
let network = npdu
.destination
.map_or(0, |destination| destination.network);
host.observe(Event::HopCountSpent { network });
Received::Discarded(Discard::HopCount)
}
pub(super) fn forward<'f>(
&mut self,
at: &Arrival<'f>,
npdu: &Npdu<'f>,
route: &Route,
host: &mut impl Host,
) -> Received<'f> {
let last_hop = !route.is_direct() && npdu.hop_count == 1;
if npdu.hop_count == 0 || last_hop {
return self.hop_count_spent(npdu, host);
}
let Some(egress) = self.ports[..self.port_count].get(usize::from(route.port().0)) else {
bump(&mut self.counters.unknown_port);
return Received::Discarded(Discard::UnknownPort);
};
let limits = egress.max_npdu;
let (destination, hop_count, to) = if route.is_direct() {
let dadr = npdu
.destination
.map_or(&[][..], |destination| destination.mac);
(None, 0, dadr)
} else {
(
npdu.destination,
npdu.hop_count - 1,
route.next_hop().as_slice(),
)
};
let rewritten = Npdu {
destination,
source: source_of(at, npdu),
hop_count,
..*npdu
};
let limit = limits.get(to.is_empty());
let octets = rewritten.encoded_len();
if octets > limit {
bump(&mut self.counters.egress_too_long);
let dnet = npdu
.destination
.map_or(0, |destination| destination.network);
host.observe(Event::TooLong {
port: route.port(),
octets,
limit,
rejected: Some(dnet),
});
self.reject(at, npdu.source, dnet, RejectReason::MESSAGE_TOO_LONG, host);
return Received::Handled;
}
if self.write_now(&rewritten, Kind::Forward, Target::Toward(route.port(), to)) {
Received::Handled
} else {
Received::Discarded(Discard::Unwritable)
}
}
pub(super) fn forward_global(
&mut self,
at: &Arrival<'_>,
npdu: &Npdu<'_>,
host: &mut impl Host,
) {
if npdu.hop_count <= 1 {
let _ = self.hop_count_spent(npdu, host);
return;
}
let rewritten = Npdu {
source: source_of(at, npdu),
hop_count: npdu.hop_count - 1,
..*npdu
};
self.write_fan_out(&rewritten, Kind::Global, at.port, host);
}
}