milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use smoltcp::phy::{Device, DeviceCapabilities, Medium};
|
||||
use smoltcp::storage::PacketMetadata;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::IpAddress;
|
||||
|
||||
use self::route_table::RouteTable;
|
||||
use crate::link::DeviceList;
|
||||
use crate::scheme::Smolnetd;
|
||||
|
||||
pub mod route_table;
|
||||
|
||||
pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
|
||||
|
||||
pub struct Router {
|
||||
rx_buffer: PacketBuffer,
|
||||
tx_buffer: PacketBuffer,
|
||||
devices: Rc<RefCell<DeviceList>>,
|
||||
route_table: Rc<RefCell<RouteTable>>,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
pub fn new(devices: Rc<RefCell<DeviceList>>, route_table: Rc<RefCell<RouteTable>>) -> Self {
|
||||
let rx_buffer = PacketBuffer::new(
|
||||
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
let tx_buffer = PacketBuffer::new(
|
||||
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
Self {
|
||||
rx_buffer,
|
||||
tx_buffer,
|
||||
devices,
|
||||
route_table,
|
||||
}
|
||||
}
|
||||
|
||||
pub const MTU: usize = 1486;
|
||||
|
||||
pub fn can_recv(&self) -> bool {
|
||||
let mut can_recv = false;
|
||||
for dev in self.devices.borrow().iter() {
|
||||
can_recv |= dev.can_recv();
|
||||
}
|
||||
can_recv
|
||||
}
|
||||
|
||||
pub fn poll(&mut self, now: Instant) {
|
||||
for dev in self.devices.borrow_mut().iter_mut() {
|
||||
if self.rx_buffer.is_full() {
|
||||
break;
|
||||
}
|
||||
|
||||
loop {
|
||||
if self.rx_buffer.is_full() {
|
||||
break;
|
||||
}
|
||||
|
||||
let Some(buf) = dev.recv(now) else {
|
||||
break;
|
||||
};
|
||||
|
||||
self.rx_buffer
|
||||
.enqueue(buf.len(), ())
|
||||
.expect("We checked if it was full")
|
||||
.copy_from_slice(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch(&mut self, now: Instant) {
|
||||
while let Ok(((), packet)) = self.tx_buffer.dequeue() {
|
||||
if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) {
|
||||
let dst_addr = IpAddress::Ipv4(packet.dst_addr());
|
||||
if packet.dst_addr().is_broadcast() {
|
||||
let buf = packet.into_inner();
|
||||
for dev in self.devices.borrow_mut().iter_mut() {
|
||||
dev.send(dst_addr, buf, now)
|
||||
}
|
||||
} else {
|
||||
let route_table = self.route_table.borrow();
|
||||
let Some(rule) = route_table.lookup_rule(&dst_addr) else {
|
||||
warn!("No route found for destination: {}", dst_addr);
|
||||
continue;
|
||||
};
|
||||
|
||||
let next_hop = match rule.via {
|
||||
Some(via) => via,
|
||||
None => dst_addr,
|
||||
};
|
||||
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let Some(dev) = devices.get_mut(&rule.dev) else {
|
||||
warn!("Device {} not found", rule.dev);
|
||||
// TODO: Remove route if device doesn't exist anymore ?
|
||||
continue;
|
||||
};
|
||||
|
||||
let IpAddress::Ipv4(src) = rule.src;
|
||||
if src != packet.src_addr() {
|
||||
packet.set_src_addr(src);
|
||||
packet.fill_checksum()
|
||||
}
|
||||
|
||||
dev.send(next_hop, packet.into_inner(), now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Router {
|
||||
type RxToken<'a> = RxToken<'a>;
|
||||
|
||||
type TxToken<'a> = TxToken<'a>;
|
||||
|
||||
fn receive(
|
||||
&mut self,
|
||||
_timestamp: smoltcp::time::Instant,
|
||||
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
if self.rx_buffer.is_empty() || self.tx_buffer.is_full() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
RxToken {
|
||||
rx_buffer: &mut self.rx_buffer,
|
||||
},
|
||||
TxToken {
|
||||
tx_buffer: &mut self.tx_buffer,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
||||
if self.tx_buffer.is_full() {
|
||||
None
|
||||
} else {
|
||||
Some(TxToken {
|
||||
tx_buffer: &mut self.tx_buffer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.medium = Medium::Ip;
|
||||
caps.max_transmission_unit = Router::MTU;
|
||||
caps.max_burst_size = Some(Smolnetd::SOCKET_BUFFER_SIZE);
|
||||
caps
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxToken<'a> {
|
||||
tx_buffer: &'a mut PacketBuffer,
|
||||
}
|
||||
|
||||
impl smoltcp::phy::TxToken for TxToken<'_> {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
f(self
|
||||
.tx_buffer
|
||||
.enqueue(len, ())
|
||||
.expect("This was checked before creating the TxToken"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxToken<'a> {
|
||||
rx_buffer: &'a mut PacketBuffer,
|
||||
}
|
||||
|
||||
impl<'a> smoltcp::phy::RxToken for RxToken<'a> {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[u8]) -> R,
|
||||
{
|
||||
let ((), buf) = self
|
||||
.rx_buffer
|
||||
.dequeue()
|
||||
.expect("This was checked before creating the RxToken");
|
||||
|
||||
f(buf)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
use smoltcp::wire::{IpAddress, IpCidr};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rule {
|
||||
pub filter: IpCidr,
|
||||
pub via: Option<IpAddress>,
|
||||
pub dev: Rc<str>,
|
||||
pub src: IpAddress,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
pub fn new(filter: IpCidr, via: Option<IpAddress>, dev: Rc<str>, src: IpAddress) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
via,
|
||||
dev,
|
||||
src,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rule {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.filter.prefix_len() == 0 {
|
||||
write!(f, "default")?;
|
||||
} else {
|
||||
write!(f, "{} ", self.filter)?;
|
||||
}
|
||||
|
||||
if let Some(via) = self.via {
|
||||
write!(f, " via {}", via)?;
|
||||
}
|
||||
|
||||
write!(f, " dev {}", self.dev)?;
|
||||
write!(f, " src {}", self.src)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RouteTable {
|
||||
rules: Vec<Rule>,
|
||||
}
|
||||
|
||||
impl RouteTable {
|
||||
pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> {
|
||||
self.rules
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|rule| rule.filter.contains_addr(dst))
|
||||
}
|
||||
|
||||
pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option<IpAddress> {
|
||||
Some(self.lookup_rule(dst)?.src)
|
||||
}
|
||||
|
||||
pub fn lookup_gateway(&self, dst: &IpAddress) -> Option<IpAddress> {
|
||||
self.lookup_rule(dst)?.via
|
||||
}
|
||||
|
||||
pub fn lookup_device(&self, dst: &IpAddress) -> Option<Rc<str>> {
|
||||
Some(self.lookup_rule(dst)?.dev.clone())
|
||||
}
|
||||
|
||||
pub fn insert_rule(&mut self, new_rule: Rule) {
|
||||
let i = match self
|
||||
.rules
|
||||
.binary_search_by_key(&new_rule.filter.prefix_len(), |rule| {
|
||||
rule.filter.prefix_len()
|
||||
}) {
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
self.rules.insert(i, new_rule);
|
||||
}
|
||||
|
||||
pub fn remove_rule(&mut self, filter: IpCidr) {
|
||||
self.rules.retain(|rule| rule.filter != filter);
|
||||
}
|
||||
|
||||
pub fn change_src(&mut self, old_src: IpAddress, new_src: IpAddress) {
|
||||
for rule in self.rules.iter_mut().filter(|rule| rule.src == old_src) {
|
||||
rule.src = new_src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RouteTable {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for rule in self.rules.iter() {
|
||||
writeln!(f, "{}", rule)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user