net: add loopback interface

This commit is contained in:
Mark Poliakov 2024-01-26 18:53:07 +02:00
parent 0124003130
commit f6617da3d6
9 changed files with 167 additions and 27 deletions

View File

@ -21,6 +21,7 @@ device-api-macros = { path = "lib/device-api/macros" }
# Drivers
ygg_driver_block = { path = "driver/block/core" }
ygg_driver_net_core = { path = "driver/net/core" }
ygg_driver_net_loopback = { path = "driver/net/loopback" }
kernel-fs = { path = "driver/fs/kernel-fs" }
memfs = { path = "driver/fs/memfs" }

View File

@ -1,6 +1,6 @@
use core::{
mem::size_of,
sync::atomic::{AtomicU32, Ordering},
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
};
use alloc::{boxed::Box, collections::BTreeMap, format, sync::Arc};
@ -8,6 +8,7 @@ use alloc::{boxed::Box, collections::BTreeMap, format, sync::Arc};
use kernel_util::{
mem::PageBox,
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
util::OneTimeInit,
};
use yggdrasil_abi::{
error::Error,
@ -32,13 +33,28 @@ pub struct NetworkInterface {
pub(crate) id: u32,
}
static ETH_INTERFACES: IrqSafeRwLock<BTreeMap<u32, Arc<NetworkInterface>>> =
#[derive(PartialEq, Eq)]
pub enum NetworkInterfaceType {
Ethernet,
Loopback,
}
static INTERFACES: IrqSafeRwLock<BTreeMap<u32, Arc<NetworkInterface>>> =
IrqSafeRwLock::new(BTreeMap::new());
static ETH_INTERFACE_ID: AtomicU32 = AtomicU32::new(1);
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> {
ETH_INTERFACES
INTERFACES
.read()
.get(&id)
.cloned()
@ -46,7 +62,7 @@ impl NetworkInterface {
}
pub fn query_by_name(name: &str) -> Result<Arc<Self>, Error> {
ETH_INTERFACES
INTERFACES
.read()
.iter()
.find_map(|(_, iface)| {
@ -60,7 +76,7 @@ impl NetworkInterface {
}
pub fn list_ref() -> IrqSafeRwLockReadGuard<'static, BTreeMap<u32, Arc<NetworkInterface>>> {
ETH_INTERFACES.read()
INTERFACES.read()
}
pub fn set_address(&self, address: IpAddr) {
@ -94,11 +110,23 @@ impl NetworkInterface {
}
}
pub fn register_interface(dev: &'static dyn NetworkDevice) -> u32 {
let mac = dev.read_hardware_address();
pub fn register_interface(
ty: NetworkInterfaceType,
dev: &'static 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 id = ETH_INTERFACE_ID.fetch_add(1, Ordering::SeqCst);
let name = format!("eth{}", id).into_boxed_str();
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,
@ -107,11 +135,14 @@ pub fn register_interface(dev: &'static dyn NetworkDevice) -> u32 {
address: IrqSafeRwLock::new(None),
id,
};
log::info!("Registered network interface #{}: {}", id, mac);
let interface = Arc::new(iface);
ETH_INTERFACES.write().insert(id, interface);
INTERFACES.write().insert(id, interface.clone());
id
if ty == NetworkInterfaceType::Loopback {
LOOPBACK.init(interface.clone());
}
interface
}

View File

@ -70,6 +70,23 @@ impl Route {
}
pub fn lookup(address: IpAddr) -> Option<(u32, Option<IpAddr>)> {
// TODO sort routes based on their "specificity"?
// Check for local route
for (_, interface) in NetworkInterface::list_ref().iter() {
if interface
.address
.read()
.map(|addr| addr == address)
.unwrap_or(false)
{
// This is the address of loopback, return it
return Some((
NetworkInterface::loopback().id,
Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
));
}
}
let routes = ROUTES.read();
for route in routes.iter() {
if route.subnet.contains(&address) {

View File

@ -669,6 +669,7 @@ pub async fn handle(packet: L3Packet) -> Result<(), Error> {
.await
} else {
// RST+ACK
log::warn!("SYN {} -> {}: port not listening", remote, local);
let window_size = u16::from_network_order(tcp_frame.window_size);
send(
local,

View File

@ -136,6 +136,23 @@ impl<T: Socket> SocketTable<T> {
}
}
pub fn try_insert_with_ephemeral_port<F: FnMut(u16) -> Result<Arc<T>, Error>>(
&mut self,
local: IpAddr,
mut with: F,
) -> Result<Arc<T>, Error> {
for port in 32768..u16::MAX - 1 {
let local = SocketAddr::new(local, port);
match self.try_insert_with(local, || with(port)) {
Ok(socket) => return Ok(socket),
Err(Error::AddrInUse) => continue,
Err(error) => return Err(error),
}
}
Err(Error::AddrInUse)
}
pub fn try_insert_with<F: FnOnce() -> Result<Arc<T>, Error>>(
&mut self,
address: SocketAddr,
@ -189,19 +206,35 @@ static TCP_LISTENERS: IrqSafeRwLock<SocketTable<TcpListener>> =
impl UdpSocket {
pub fn bind(address: SocketAddr) -> Result<Arc<UdpSocket>, Error> {
let mut sockets = UDP_SOCKETS.write();
sockets.try_insert_with(address, move || {
let socket = Arc::new(Self {
local: address,
remote: None,
broadcast: AtomicBool::new(false),
receive_queue: Mutex::new(RingBuffer::try_with_capacity(128)?),
receive_notify: QueueWaker::new(),
});
if address.port() == 0 {
sockets.try_insert_with_ephemeral_port(address.ip(), |port| {
let socket = Arc::new(Self {
local: SocketAddr::new(address.ip(), port),
remote: None,
broadcast: AtomicBool::new(false),
receive_queue: Mutex::new(RingBuffer::try_with_capacity(128)?),
receive_notify: QueueWaker::new(),
});
log::debug!("UDP socket opened: {}", address);
log::debug!("UDP socket opened: *{}", socket.local);
Ok(socket)
})
Ok(socket)
})
} else {
sockets.try_insert_with(address, move || {
let socket = Arc::new(Self {
local: address,
remote: None,
broadcast: AtomicBool::new(false),
receive_queue: Mutex::new(RingBuffer::try_with_capacity(128)?),
receive_notify: QueueWaker::new(),
});
log::debug!("UDP socket opened: {}", address);
Ok(socket)
})
}
}
pub fn connect(&self, _address: SocketAddr) -> Result<(), Error> {

View File

@ -0,0 +1,12 @@
[package]
name = "ygg_driver_net_loopback"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-util = { path = "../../../lib/kernel-util" }
ygg_driver_net_core = { path = "../../net/core" }

View File

@ -0,0 +1,41 @@
#![no_std]
use kernel_util::{mem::PageBox, util::OneTimeInit};
use ygg_driver_net_core::{
interface::{NetworkDevice, NetworkInterfaceType},
Packet,
};
use yggdrasil_abi::{
error::Error,
net::{IpAddr, Ipv4Addr, MacAddress},
};
struct LoopbackDevice;
impl NetworkDevice for LoopbackDevice {
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error> {
let packet = Packet::new(packet, 0, *LOOPBACK_ID.get());
ygg_driver_net_core::receive_packet(packet)
}
fn packet_prefix_size(&self) -> usize {
0
}
fn read_hardware_address(&self) -> MacAddress {
MacAddress::UNSPECIFIED
}
}
static LOOPBACK: OneTimeInit<LoopbackDevice> = OneTimeInit::new();
static LOOPBACK_ID: OneTimeInit<u32> = OneTimeInit::new();
pub fn init() {
let loopback = LOOPBACK.init(LoopbackDevice);
let interface =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Loopback, loopback);
LOOPBACK_ID.init(interface.id());
interface.set_address(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
}

View File

@ -18,7 +18,10 @@ use kernel_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock, IrqSafeSpinlockGuard},
util::OneTimeInit,
};
use ygg_driver_net_core::{interface::NetworkDevice, Packet};
use ygg_driver_net_core::{
interface::{NetworkDevice, NetworkInterfaceType},
Packet,
};
use ygg_driver_pci::{
capability::{MsiXCapability, MsiXVectorTable},
PciConfigurationSpace, PciDeviceInfo,
@ -258,8 +261,8 @@ impl<T: Transport + 'static> Device for VirtioNet<T> {
self.finish_init(status);
let id = ygg_driver_net_core::register_interface(self);
self.interface_id.init(id);
let iface = ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self);
self.interface_id.init(iface.id());
self.listen(64);
Ok(())

View File

@ -28,6 +28,7 @@ fn setup_root() -> Result<NodeRef, Error> {
pub fn kinit() -> Result<(), Error> {
infoln!("In main");
ygg_driver_net_loopback::init();
ygg_driver_net_core::start_network_tasks()?;
#[cfg(feature = "fb_console")]