158 lines
4.3 KiB
Rust
158 lines
4.3 KiB
Rust
use core::{
|
|
mem::{size_of, MaybeUninit},
|
|
net::IpAddr,
|
|
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
|
|
};
|
|
|
|
use alloc::{boxed::Box, collections::BTreeMap, format, sync::Arc};
|
|
use libk::dma::DmaBuffer;
|
|
// TODO: link state management?
|
|
use libk_util::{
|
|
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
|
|
OneTimeInit,
|
|
};
|
|
use yggdrasil_abi::{
|
|
error::Error,
|
|
net::{link::LinkState, protocols::EthernetFrame, MacAddress},
|
|
};
|
|
|
|
use crate::{
|
|
l3::{arp::ArpTable, Route},
|
|
TxPacketBuilder,
|
|
};
|
|
|
|
pub trait NetworkDevice: Sync + Send {
|
|
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error>;
|
|
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error>;
|
|
|
|
fn packet_prefix_size(&self) -> usize;
|
|
|
|
fn read_hardware_address(&self) -> MacAddress;
|
|
fn link_state(&self) -> LinkState {
|
|
LinkState::Other { up: true }
|
|
}
|
|
}
|
|
|
|
pub struct NetworkInterface {
|
|
pub(crate) name: Box<str>,
|
|
pub(crate) device: Arc<dyn NetworkDevice>,
|
|
pub(crate) mac: MacAddress,
|
|
|
|
pub(crate) address: IrqSafeRwLock<Option<IpAddr>>,
|
|
pub(crate) id: u32,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
pub enum NetworkInterfaceType {
|
|
Ethernet,
|
|
Loopback,
|
|
}
|
|
|
|
static INTERFACES: IrqSafeRwLock<BTreeMap<u32, Arc<NetworkInterface>>> =
|
|
IrqSafeRwLock::new(BTreeMap::new());
|
|
static LAST_INTERFACE_ID: AtomicU32 = AtomicU32::new(1);
|
|
static LOOPBACK: OneTimeInit<Arc<NetworkInterface>> = OneTimeInit::new();
|
|
|
|
impl NetworkInterface {
|
|
pub fn id(&self) -> u32 {
|
|
self.id
|
|
}
|
|
|
|
pub fn loopback() -> &'static Arc<Self> {
|
|
LOOPBACK.get()
|
|
}
|
|
|
|
pub fn get(id: u32) -> Result<Arc<Self>, Error> {
|
|
INTERFACES
|
|
.read()
|
|
.get(&id)
|
|
.cloned()
|
|
.ok_or(Error::DoesNotExist)
|
|
}
|
|
|
|
pub fn query_by_name(name: &str) -> Result<Arc<Self>, Error> {
|
|
INTERFACES
|
|
.read()
|
|
.iter()
|
|
.find_map(|(_, iface)| {
|
|
if iface.name.as_ref() == name {
|
|
Some(iface.clone())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.ok_or(Error::DoesNotExist)
|
|
}
|
|
|
|
pub fn list_ref() -> IrqSafeRwLockReadGuard<'static, BTreeMap<u32, Arc<NetworkInterface>>> {
|
|
INTERFACES.read()
|
|
}
|
|
|
|
pub fn set_address(&self, address: IpAddr) {
|
|
// Flush routes associated with the interface
|
|
{
|
|
let mut routes = Route::list_mut();
|
|
routes.retain(|route| route.interface != self.id);
|
|
}
|
|
|
|
let mut addr = self.address.write();
|
|
// Flush owned ARP entries related to the old address
|
|
if let Some(address) = *addr {
|
|
ArpTable::flush_address(self.id, address);
|
|
}
|
|
|
|
log::info!("Assigned address: {:?} -> {:?}", self.name, address);
|
|
addr.replace(address);
|
|
|
|
ArpTable::insert_address(self.id, self.mac, address, true);
|
|
}
|
|
|
|
pub fn link_state(&self) -> LinkState {
|
|
self.device.link_state()
|
|
}
|
|
|
|
pub fn send_l2(&self, l2_frame: &EthernetFrame, l2_data: &[u8]) -> Result<(), Error> {
|
|
let mut builder = TxPacketBuilder::new(self, size_of::<EthernetFrame>() + l2_data.len())?;
|
|
builder.push_bytes(bytemuck::bytes_of(l2_frame))?;
|
|
builder.push_bytes(l2_data)?;
|
|
builder.transmit(None)
|
|
}
|
|
}
|
|
|
|
pub fn register_interface(
|
|
ty: NetworkInterfaceType,
|
|
dev: Arc<dyn NetworkDevice>,
|
|
) -> Arc<NetworkInterface> {
|
|
let name = match ty {
|
|
NetworkInterfaceType::Ethernet => {
|
|
static LAST_ETHERNET_ID: AtomicUsize = AtomicUsize::new(0);
|
|
let eth_id = LAST_ETHERNET_ID.fetch_add(1, Ordering::SeqCst);
|
|
format!("eth{eth_id}").into_boxed_str()
|
|
}
|
|
NetworkInterfaceType::Loopback => "lo".into(),
|
|
};
|
|
|
|
let mac = dev.read_hardware_address();
|
|
let id = LAST_INTERFACE_ID.fetch_add(1, Ordering::SeqCst);
|
|
|
|
log::info!("Register network interface {} (#{}): {}", name, id, mac);
|
|
|
|
let iface = NetworkInterface {
|
|
name,
|
|
device: dev,
|
|
mac,
|
|
address: IrqSafeRwLock::new(None),
|
|
id,
|
|
};
|
|
|
|
let interface = Arc::new(iface);
|
|
|
|
INTERFACES.write().insert(id, interface.clone());
|
|
|
|
if ty == NetworkInterfaceType::Loopback {
|
|
LOOPBACK.init(interface.clone());
|
|
}
|
|
|
|
interface
|
|
}
|