use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use pktkit::{Frame, L2Device, L2Handler};
use crate::core::sync::{LockRank, Mutex};
use super::link::{MacAddr, NetLink, NetPort};
pub struct PktkitLink {
inbox: Arc<NetPort>,
handler: Mutex<Option<L2Handler>>,
}
impl fmt::Debug for PktkitLink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PktkitLink")
.field("mac", &self.inbox.mac())
.field("attached", &self.handler.lock().is_some())
.field("inbox", &self.inbox)
.finish()
}
}
impl PktkitLink {
#[must_use]
pub fn new() -> Arc<PktkitLink> {
PktkitLink::with_inbox(Arc::new(NetPort::new()))
}
#[must_use]
pub fn with_inbox(inbox: Arc<NetPort>) -> Arc<PktkitLink> {
Arc::new(PktkitLink {
inbox,
handler: Mutex::with_rank(LockRank::LEAF, None),
})
}
#[must_use]
pub fn inbox(&self) -> &Arc<NetPort> {
&self.inbox
}
#[must_use]
pub fn attached(&self) -> bool {
self.handler.lock().is_some()
}
}
impl L2Device for PktkitLink {
fn set_handler(&self, h: L2Handler) {
*self.handler.lock() = Some(h);
}
fn send(&self, frame: &Frame) -> pktkit::Result<()> {
self.inbox.deliver(frame.as_bytes());
Ok(())
}
fn hw_addr(&self) -> pktkit::MacAddr {
pktkit::MacAddr(self.inbox.mac().octets())
}
fn close(&self) -> pktkit::Result<()> {
*self.handler.lock() = None;
self.inbox.set_link(false);
Ok(())
}
}
impl NetLink for PktkitLink {
fn transmit(&self, _now: u64, frame: &[u8]) {
if frame.len() < 14 {
return;
}
let handler = self.handler.lock().clone();
if let Some(handler) = handler {
let _ = handler(Frame::from_slice(frame));
}
}
fn receive(&self, now: u64) -> Option<Vec<u8>> {
self.inbox.receive(now)
}
fn next_arrival(&self) -> Option<u64> {
self.inbox.next_arrival()
}
fn link_up(&self) -> bool {
self.inbox.link_up()
}
fn set_mac(&self, mac: MacAddr) {
self.inbox.set_mac(mac);
}
}
impl From<MacAddr> for pktkit::MacAddr {
fn from(mac: MacAddr) -> pktkit::MacAddr {
pktkit::MacAddr(mac.octets())
}
}
#[must_use]
pub fn from_pktkit_mac(mac: pktkit::MacAddr) -> MacAddr {
MacAddr::new(mac.0)
}
#[cfg(test)]
mod tests {
use super::*;
use pktkit::{L2Hub, MacAddr as PktMac};
fn frame(dst: [u8; 6], src: [u8; 6]) -> Vec<u8> {
let mut f = Vec::with_capacity(60);
f.extend_from_slice(&dst);
f.extend_from_slice(&src);
f.extend_from_slice(&[0x08, 0x00]);
f.resize(60, 0xab);
f
}
#[test]
fn a_frame_the_network_pushes_waits_for_the_machine_to_come_and_get_it() {
let link = PktkitLink::new();
let sent = frame([0xff; 6], [0x52, 0x54, 0, 1, 2, 3]);
link.send(Frame::from_slice(&sent)).unwrap();
assert_eq!(link.next_arrival(), Some(0), "queued for the next look");
assert_eq!(link.receive(1234).as_deref(), Some(&sent[..]));
assert_eq!(link.receive(1234), None);
}
#[test]
fn two_stations_on_a_hub_hear_each_other() {
let hub = Arc::new(L2Hub::new());
let left = PktkitLink::new();
let right = PktkitLink::new();
left.set_mac(MacAddr::new([0x52, 0x54, 0, 0, 0, 1]));
right.set_mac(MacAddr::new([0x52, 0x54, 0, 0, 0, 2]));
let _a = hub.connect_arc(Arc::clone(&left) as Arc<dyn L2Device>);
let _b = hub.connect_arc(Arc::clone(&right) as Arc<dyn L2Device>);
assert!(left.attached(), "the hub installed its handler");
let broadcast = frame([0xff; 6], [0x52, 0x54, 0, 0, 0, 1]);
left.transmit(0, &broadcast);
assert_eq!(right.receive(0).as_deref(), Some(&broadcast[..]));
assert_eq!(left.receive(0), None, "and not back to the sender");
let unicast = frame([0x52, 0x54, 0, 0, 0, 1], [0x52, 0x54, 0, 0, 0, 2]);
right.transmit(0, &unicast);
assert_eq!(left.receive(0).as_deref(), Some(&unicast[..]));
}
#[test]
fn a_live_arrival_is_recorded_through_the_seam_rather_than_by_the_port() {
use crate::core::clock::GlobalTime;
use crate::core::record::Recorder;
use crate::dev::net::link::ports;
let hosts = crate::core::hosts::HostObjects::new();
let inbox = ports::open(&hosts, "net0").unwrap();
let link = PktkitLink::with_inbox(Arc::clone(&inbox));
let recorder = Recorder::recording();
recorder
.register(ports::channel("net0"), ports::sink(&inbox))
.unwrap();
let one = frame([0xff; 6], [0, 0, 0, 0, 0, 1]);
link.send(Frame::from_slice(&one)).unwrap();
assert!(link.receive(700).is_some());
assert!(recorder.log().is_empty(), "the port writes nothing down");
let two = frame([0xff; 6], [0, 0, 0, 0, 0, 2]);
recorder.post(&ports::channel("net0"), &two).unwrap();
recorder.deliver(GlobalTime::from_nanos(9_000)).unwrap();
assert_eq!(link.receive(0).as_deref(), Some(&two[..]));
let log = recorder.log();
assert_eq!(log.len(), 1);
assert_eq!(log.events()[0].at, GlobalTime::from_nanos(9_000));
}
#[test]
fn a_station_with_no_cable_swallows_what_the_guest_transmits() {
let link = PktkitLink::new();
link.transmit(0, &frame([0xff; 6], [0; 6]));
assert_eq!(link.receive(0), None);
link.transmit(0, &[1, 2, 3, 4]);
}
#[test]
fn closing_the_station_drops_the_carrier() {
let link = PktkitLink::new();
let hub = Arc::new(L2Hub::new());
let _h = hub.connect_arc(Arc::clone(&link) as Arc<dyn L2Device>);
assert!(link.link_up());
link.close().unwrap();
assert!(!link.attached());
assert!(!link.link_up(), "a NIC transmitting now loses carrier");
}
#[test]
fn the_station_reports_the_address_the_guest_programmed() {
let link = PktkitLink::new();
let ours = MacAddr::parse("52:54:00:12:34:56").unwrap();
link.set_mac(ours);
assert_eq!(link.hw_addr().0, ours.octets());
let theirs: PktMac = ours.into();
assert_eq!(theirs.0, ours.octets());
assert_eq!(from_pktkit_mac(theirs), ours);
}
}