#![no_std]
#[macro_use]
extern crate log;
extern crate alloc;
#[cfg(test)]
extern crate std;
mod consts;
mod device;
mod general;
mod listen_table;
pub mod options;
pub mod raw;
mod router;
mod service;
mod socket;
pub(crate) mod state;
pub mod tcp;
pub mod udp;
pub mod unix;
#[cfg(feature = "vsock")]
pub mod vsock;
mod wrapper;
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use ax_driver::{AxDeviceContainer, prelude::*};
use ax_sync::Mutex;
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr};
use spin::{Lazy, Once};
use self::{
consts::{GATEWAY, IP, IP_PREFIX},
device::{EthernetDevice, LoopbackDevice},
listen_table::ListenTable,
router::{Router, Rule},
service::Service,
wrapper::SocketSetWrapper,
};
pub use self::{device::ArpEntry, socket::*};
static LISTEN_TABLE: Lazy<ListenTable> = Lazy::new(ListenTable::new);
static SOCKET_SET: Lazy<SocketSetWrapper> = Lazy::new(SocketSetWrapper::new);
static SERVICE: Once<Mutex<Service>> = Once::new();
static POLLING_INTERFACES: AtomicBool = AtomicBool::new(false);
static POLL_AGAIN: AtomicBool = AtomicBool::new(false);
const DHCP_BOOTSTRAP_ATTEMPTS: usize = 200;
const DHCP_BOOTSTRAP_POLL_INTERVAL: Duration = Duration::from_millis(10);
fn get_service() -> ax_sync::MutexGuard<'static, Service> {
SERVICE
.get()
.expect("Network service not initialized")
.lock()
}
pub fn init_network(mut net_devs: AxDeviceContainer<AxNetDevice>) {
info!("Initialize network subsystem...");
let mut router = Router::new();
let lo_dev = router.add_device(Box::new(LoopbackDevice::new()));
let lo_ip = Ipv4Cidr::new(Ipv4Address::new(127, 0, 0, 1), 8);
router.add_rule(Rule::new(
lo_ip.into(),
None,
lo_dev,
lo_ip.address().into(),
));
let static_network = !IP.is_empty() && !GATEWAY.is_empty();
let mut dhcp_dev = None;
let mut dhcp_mac = None;
let eth0_ip = if let Some(dev) = net_devs.take_one() {
info!(" use NIC 0: {:?}", dev.device_name());
let eth0_address = EthernetAddress(dev.mac_address().0);
let eth0_ip = static_network
.then(|| Ipv4Cidr::new(IP.parse().expect("Invalid IPv4 address"), IP_PREFIX));
let eth0_dev = router.add_device(Box::new(EthernetDevice::new(
"eth0".to_owned(),
dev,
eth0_ip,
)));
info!("eth0:");
info!(" mac: {}", eth0_address);
if let Some(eth0_ip) = eth0_ip {
let gateway = GATEWAY.parse().expect("Invalid gateway address");
router.add_rule(Rule::new(
Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0).into(),
Some(gateway),
eth0_dev,
eth0_ip.address().into(),
));
info!(" mode: static");
info!(" ip: {}", eth0_ip);
info!(" gw: {}", gateway);
} else {
dhcp_dev = Some(eth0_dev);
dhcp_mac = Some(eth0_address);
info!(" mode: dhcp");
}
eth0_ip
} else {
warn!(" No network device found!");
None
};
for dev in &router.devices {
info!("Device: {}", dev.name());
}
let mut service = Service::new(router);
service.iface.update_ip_addrs(|ip_addrs| {
ip_addrs.push(lo_ip.into()).unwrap();
if let Some(eth0_ip) = eth0_ip {
ip_addrs.push(eth0_ip.into()).unwrap();
}
});
if let (Some(dhcp_dev), Some(dhcp_mac)) = (dhcp_dev, dhcp_mac) {
service.enable_dhcp(dhcp_dev, dhcp_mac);
}
let dhcp_enabled = service.dhcp_enabled();
SERVICE.call_once(|| Mutex::new(service));
if dhcp_enabled {
ax_task::spawn_with_name(dhcp_bootstrap, "dhcp-bootstrap".to_owned());
}
}
#[cfg(feature = "vsock")]
pub fn init_vsock(mut vsock_devs: AxDeviceContainer<AxVsockDevice>) {
use self::device::register_vsock_device;
info!("Initialize vsock subsystem...");
if let Some(dev) = vsock_devs.take_one() {
info!(" use vsock 0: {:?}", dev.device_name());
if let Err(e) = register_vsock_device(dev) {
warn!("Failed to initialize vsock device: {:?}", e);
}
} else {
warn!(" No vsock device found!");
}
}
pub fn poll_interfaces() {
POLL_AGAIN.store(true, Ordering::Release);
loop {
if POLLING_INTERFACES
.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
.is_err()
{
return;
}
while POLL_AGAIN.swap(false, Ordering::AcqRel) {
while get_service().poll(&mut SOCKET_SET.inner.lock()) {}
}
POLLING_INTERFACES.store(false, Ordering::Release);
if !POLL_AGAIN.load(Ordering::Acquire) {
return;
}
}
}
pub fn arp_entries() -> Vec<ArpEntry> {
get_service().arp_entries()
}
fn dhcp_bootstrap() {
for _ in 0..DHCP_BOOTSTRAP_ATTEMPTS {
poll_interfaces();
if get_service().dhcp_configured() {
return;
}
ax_task::sleep(DHCP_BOOTSTRAP_POLL_INTERVAL);
}
warn!("eth0: DHCP bootstrap timed out");
}
#[cfg(test)]
pub(crate) mod test_support {
use alloc::boxed::Box;
use std::sync::Once;
use ax_sync::Mutex;
use smoltcp::wire::{IpAddress, Ipv4Address, Ipv4Cidr};
use crate::{
SERVICE,
device::LoopbackDevice,
router::{Router, Rule},
service::Service,
};
pub(crate) const LOCAL_MASK: u32 = 1 << 0;
pub(crate) const PEER_MASK: u32 = 1 << 1;
pub(crate) const LOCAL_ADDR: Ipv4Address = Ipv4Address::new(192, 0, 2, 10);
pub(crate) const PEER_ADDR: Ipv4Address = Ipv4Address::new(198, 51, 100, 20);
pub(crate) fn init_split_route_network() {
static INIT: Once = Once::new();
INIT.call_once(|| {
let mut router = Router::new();
let local_dev = router.add_device(Box::new(LoopbackDevice::new()));
let peer_dev = router.add_device(Box::new(LoopbackDevice::new()));
let local_cidr = Ipv4Cidr::new(LOCAL_ADDR, 24);
let peer_cidr = Ipv4Cidr::new(PEER_ADDR, 24);
router.add_rule(Rule::new(
local_cidr.into(),
None,
local_dev,
IpAddress::Ipv4(LOCAL_ADDR),
));
router.add_rule(Rule::new(
peer_cidr.into(),
None,
peer_dev,
IpAddress::Ipv4(PEER_ADDR),
));
let mut service = Service::new(router);
service.iface.update_ip_addrs(|ip_addrs| {
ip_addrs.push(local_cidr.into()).unwrap();
ip_addrs.push(peer_cidr.into()).unwrap();
});
SERVICE.call_once(|| Mutex::new(service));
});
}
}