diff --git a/kernel/src/driver/net/loopback.rs b/kernel/src/driver/net/loopback.rs new file mode 100644 index 00000000..475c9e32 --- /dev/null +++ b/kernel/src/driver/net/loopback.rs @@ -0,0 +1,484 @@ +use crate::arch::rand::rand; +use crate::driver::base::class::Class; +use crate::driver::base::device::bus::Bus; +use crate::driver::base::device::driver::Driver; +use crate::driver::base::device::{Device, DeviceType, IdTable}; +use crate::driver::base::kobject::{KObjType, KObject, KObjectState}; +use crate::init::initcall::INITCALL_DEVICE; +use crate::libs::spinlock::SpinLock; +use crate::net::{generate_iface_id, NET_DEVICES}; +use crate::time::Instant; +use alloc::collections::VecDeque; +use alloc::fmt::Debug; +use alloc::string::{String, ToString}; +use alloc::sync::{Arc, Weak}; +use alloc::vec::Vec; +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use log::debug; +use smoltcp::wire::HardwareAddress; +use smoltcp::{ + phy::{self}, + wire::{IpAddress, IpCidr}, +}; +use system_error::SystemError; +use unified_init::macros::unified_init; + +use super::NetDevice; + +const DEVICE_NAME: &str = "loopback"; + +/// ## 环回接收令牌 +/// 用于储存lo网卡接收到的数据 +pub struct LoopbackRxToken { + buffer: Vec, +} + +impl phy::RxToken for LoopbackRxToken { + /// ## 实现Rxtoken的consume函数 + /// 接受一个函数 `f`,并在 `self.buffer` 上调用它。 + /// + /// ## 参数 + /// - mut self :一个可变的 `LoopbackRxToken` 实例。 + /// - f :接受一个可变的 u8 切片,并返回类型 `R` 的结果。 + /// + /// ## 返回值 + /// 返回函数 `f` 在 `self.buffer` 上的调用结果。 + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self.buffer.as_mut_slice()) + } +} + +/// ## 环回发送令牌 +/// 返回驱动用于操作lo设备 +pub struct LoopbackTxToken { + driver: LoopbackDriver, +} + +impl phy::TxToken for LoopbackTxToken { + /// ## 实现TxToken的consume函数 + /// 向lo的队列推入待发送的数据报,实现环回 + /// + /// ## 参数 + /// - self + /// - len:数据包的长度 + /// - f:接受一个可变的 u8 切片,并返回类型 `R` 的结果。 + /// + /// ## 返回值 + /// 返回f对数据包操纵的结果 + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(buffer.as_mut_slice()); + let mut device = self.driver.inner.lock(); + device.loopback_transmit(buffer); + result + } +} + +/// ## Loopback设备 +/// 成员是一个队列,用来存放接受到的数据包。 +/// 当使用lo发送数据包时,不会把数据包传到link层,而是直接发送到该队列,实现环回。 +pub struct Loopback { + //回环设备的缓冲区,接受的数据包会存放在这里,发送的数据包也会发送到这里,实现环回 + queue: VecDeque>, +} + +impl Loopback { + /// ## Loopback创建函数 + /// 创建lo设备 + pub fn new() -> Self { + let queue = VecDeque::new(); + Loopback { queue } + } + /// ## Loopback处理接受到的数据包函数 + /// Loopback接受到数据后会调用这个函数来弹出接收的数据,返回给协议栈 + /// + /// ## 参数 + /// - &mut self :自身可变引用 + /// + /// ## 返回值 + /// - queue的头部数据包 + pub fn loopback_receive(&mut self) -> Vec { + let buffer = self.queue.pop_front(); + match buffer { + Some(buffer) => { + //debug!("lo receive:{:?}", buffer); + return buffer; + } + None => { + return Vec::new(); + } + } + } + /// ## Loopback发送数据包的函数 + /// Loopback发送数据包给自己的接收队列,实现环回 + /// + /// ## 参数 + /// - &mut self:自身可变引用 + /// - buffer:需要发送的数据包 + pub fn loopback_transmit(&mut self, buffer: Vec) { + //debug!("lo transmit!"); + self.queue.push_back(buffer) + } +} + +/// ## driver的包裹器 +/// 为实现获得不可变引用的Interface的内部可变性,故为Driver提供UnsafeCell包裹器 +/// +/// 参考virtio_net.rs +struct LoopbackDriverWapper(UnsafeCell); +unsafe impl Send for LoopbackDriverWapper {} +unsafe impl Sync for LoopbackDriverWapper {} + +/// ## deref 方法返回一个指向 `LoopbackDriver` 的引用。 +impl Deref for LoopbackDriverWapper { + type Target = LoopbackDriver; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0.get() } + } +} +/// ## `deref_mut` 方法返回一个指向可变 `LoopbackDriver` 的引用。 +impl DerefMut for LoopbackDriverWapper { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.0.get() } + } +} + +impl LoopbackDriverWapper { + /// ## force_get_mut返回一个指向可变 `LoopbackDriver` 的引用。 + #[allow(clippy::mut_from_ref)] + #[allow(clippy::mut_from_ref)] + fn force_get_mut(&self) -> &mut LoopbackDriver { + unsafe { &mut *self.0.get() } + } +} + +/// ## Loopback驱动 +/// 负责操作Loopback设备实现基本的网卡功能 +pub struct LoopbackDriver { + pub inner: Arc>, +} + +impl LoopbackDriver { + /// ## LoopbackDriver创建函数 + pub fn new() -> Self { + let inner = Arc::new(SpinLock::new(Loopback::new())); + LoopbackDriver { inner } + } +} + +impl Clone for LoopbackDriver { + fn clone(&self) -> Self { + LoopbackDriver { + inner: self.inner.clone(), + } + } +} + +impl phy::Device for LoopbackDriver { + type RxToken<'a> = LoopbackRxToken where Self: 'a; + type TxToken<'a> = LoopbackTxToken where Self: 'a; + /// ## 返回设备的物理层特性。 + /// lo设备的最大传输单元为65535,最大突发大小为1,传输介质默认为Ethernet + fn capabilities(&self) -> phy::DeviceCapabilities { + let mut result = phy::DeviceCapabilities::default(); + result.max_transmission_unit = 65535; + result.max_burst_size = Some(1); + result.medium = smoltcp::phy::Medium::Ethernet; + return result; + } + /// ## Loopback驱动处理接受数据事件 + /// 驱动调用Loopback的receive函数,处理buffer封装成(rx,tx)返回给上层 + /// + /// ## 参数 + /// - `&mut self` :自身可变引用 + /// - `_timestamp` + /// + /// ## 返回值 + /// - None: 如果接收队列为空,返回 `None`,以通知上层没有可以接收的包 + /// - Option::Some((rx, tx)):如果接收队列不为空,返回 `Some`,其中包含一个接收令牌 `rx` 和一个发送令牌 `tx` + fn receive( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let buffer = self.inner.lock().loopback_receive(); + //receive队列为为空,返回NONE值以通知上层没有可以receive的包 + if buffer.is_empty() { + return Option::None; + } + let rx = LoopbackRxToken { buffer }; + let tx = LoopbackTxToken { + driver: self.clone(), + }; + return Option::Some((rx, tx)); + } + /// ## Loopback驱动处理发送数据包事件 + /// Loopback驱动在需要发送数据时会调用这个函数来获取一个发送令牌。 + /// + /// ## 参数 + /// - `&mut self` :自身可变引用 + /// - `_timestamp` + /// + /// ## 返回值 + /// - 返回一个 `Some`,其中包含一个发送令牌,该令牌包含一个对自身的克隆引用 + fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + Some(LoopbackTxToken { + driver: self.clone(), + }) + } +} + +/// ## LoopbackInterface结构 +/// 封装驱动包裹器和iface,设置接口名称 +pub struct LoopbackInterface { + driver: LoopbackDriverWapper, + iface_id: usize, + iface: SpinLock, + name: String, +} + +impl LoopbackInterface { + /// ## `new` 是一个公共函数,用于创建一个新的 `LoopbackInterface` 实例。 + /// 生成一个新的接口 ID。创建一个新的接口配置,设置其硬件地址和随机种子,使用接口配置和驱动器创建一个新的 `smoltcp::iface::Interface` 实例。 + /// 设置接口的 IP 地址为 127.0.0.1。 + /// 创建一个新的 `LoopbackDriverWapper` 实例,包装驱动器。 + /// 创建一个新的 `LoopbackInterface` 实例,包含驱动器、接口 ID、接口和名称,并将其封装在一个 `Arc` 中。 + /// ## 参数 + /// - `driver`:一个 `LoopbackDriver` 实例,用于驱动网络环回操作。 + /// + /// ## 返回值 + /// 返回一个 `Arc`,即一个指向新创建的 `LoopbackInterface` 实例的智能指针。 + pub fn new(mut driver: LoopbackDriver) -> Arc { + let iface_id = generate_iface_id(); + let mut iface_config = smoltcp::iface::Config::new(HardwareAddress::Ethernet( + smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]), + )); + iface_config.random_seed = rand() as u64; + + let mut iface = + smoltcp::iface::Interface::new(iface_config, &mut driver, Instant::now().into()); + //设置网卡地址为127.0.0.1 + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + }); + let driver = LoopbackDriverWapper(UnsafeCell::new(driver)); + Arc::new(LoopbackInterface { + driver, + iface_id, + iface: SpinLock::new(iface), + name: "lo".to_string(), + }) + } +} + +impl Debug for LoopbackInterface { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LoopbackInterface") + .field("iface_id", &self.iface_id) + .field("iface", &"smtoltcp::iface::Interface") + .field("name", &self.name) + .finish() + } +} +//TODO: 向sysfs注册lo设备 +impl KObject for LoopbackInterface { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn set_inode(&self, _inode: Option>) { + todo!() + } + + fn inode(&self) -> Option> { + todo!() + } + + fn parent(&self) -> Option> { + todo!() + } + + fn set_parent(&self, _parent: Option>) { + todo!() + } + + fn kset(&self) -> Option> { + todo!() + } + + fn set_kset(&self, _kset: Option>) { + todo!() + } + + fn kobj_type(&self) -> Option<&'static dyn crate::driver::base::kobject::KObjType> { + todo!() + } + + fn name(&self) -> String { + self.name.clone() + } + + fn set_name(&self, _name: String) { + todo!() + } + + fn kobj_state( + &self, + ) -> crate::libs::rwlock::RwLockReadGuard { + todo!() + } + + fn kobj_state_mut( + &self, + ) -> crate::libs::rwlock::RwLockWriteGuard { + todo!() + } + + fn set_kobj_state(&self, _state: KObjectState) { + todo!() + } + + fn set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>) { + todo!() + } +} + +impl Device for LoopbackInterface { + fn dev_type(&self) -> DeviceType { + DeviceType::Net + } + + fn id_table(&self) -> IdTable { + IdTable::new(DEVICE_NAME.to_string(), None) + } + + fn set_bus(&self, _bus: Option>) { + todo!() + } + + fn set_class(&self, _class: Option>) { + todo!() + } + + fn driver(&self) -> Option> { + todo!() + } + + fn set_driver(&self, _driver: Option>) { + todo!() + } + + fn is_dead(&self) -> bool { + todo!() + } + + fn can_match(&self) -> bool { + todo!() + } + + fn set_can_match(&self, _can_match: bool) { + todo!() + } + + fn state_synced(&self) -> bool { + true + } +} + +impl NetDevice for LoopbackInterface { + /// 由于lo网卡设备不是实际的物理设备,其mac地址需要手动设置为一个默认值,这里默认为0200000001 + fn mac(&self) -> smoltcp::wire::EthernetAddress { + let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01]; + smoltcp::wire::EthernetAddress(mac) + } + + #[inline] + fn nic_id(&self) -> usize { + self.iface_id + } + + #[inline] + fn name(&self) -> String { + self.name.clone() + } + /// ## `update_ip_addrs` 用于更新接口的 IP 地址。 + /// + /// ## 参数 + /// - `&self` :自身引用 + /// - `ip_addrs` :一个包含 `smoltcp::wire::IpCidr` 的切片,表示要设置的 IP 地址和子网掩码 + /// + /// ## 返回值 + /// - 如果 `ip_addrs` 的长度不为 1,返回 `Err(SystemError::EINVAL)`,表示输入参数无效 + /// - 如果更新成功,返回 `Ok(())` + fn update_ip_addrs( + &self, + ip_addrs: &[smoltcp::wire::IpCidr], + ) -> Result<(), system_error::SystemError> { + if ip_addrs.len() != 1 { + return Err(SystemError::EINVAL); + } + + self.iface.lock().update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next(); + + if let Some(dest) = dest { + *dest = ip_addrs[0]; + } else { + addrs.push(ip_addrs[0]).expect("Push ipCidr failed: full"); + } + }); + return Ok(()); + } + /// ## `poll` 用于轮询接口的状态。 + /// + /// ## 参数 + /// - `&self` :自身引用 + /// - `sockets` :一个可变引用到 `smoltcp::iface::SocketSet`,表示要轮询的套接字集 + /// + /// ## 返回值 + /// - 如果轮询成功,返回 `Ok(())` + /// - 如果轮询失败,返回 `Err(SystemError::EAGAIN_OR_EWOULDBLOCK)`,表示需要再次尝试或者操作会阻塞 + fn poll(&self, sockets: &mut smoltcp::iface::SocketSet) -> Result<(), SystemError> { + let timestamp: smoltcp::time::Instant = Instant::now().into(); + let mut guard = self.iface.lock(); + let poll_res = guard.poll(timestamp, self.driver.force_get_mut(), sockets); + if poll_res { + return Ok(()); + } + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + + #[inline(always)] + fn inner_iface(&self) -> &SpinLock { + return &self.iface; + } +} + +pub fn loopback_probe() { + loopback_driver_init(); +} +/// ## lo网卡设备初始化函数 +/// 创建驱动和iface,初始化一个lo网卡,添加到全局NET_DEVICES中 +pub fn loopback_driver_init() { + let driver = LoopbackDriver::new(); + let iface = LoopbackInterface::new(driver); + + NET_DEVICES + .write_irqsave() + .insert(iface.iface_id, iface.clone()); +} + +/// ## lo网卡设备的注册函数 +#[unified_init(INITCALL_DEVICE)] +pub fn loopback_init() -> Result<(), SystemError> { + loopback_probe(); + return Ok(()); +} diff --git a/kernel/src/driver/net/mod.rs b/kernel/src/driver/net/mod.rs index ce1a149b..deb81020 100644 --- a/kernel/src/driver/net/mod.rs +++ b/kernel/src/driver/net/mod.rs @@ -11,6 +11,7 @@ use system_error::SystemError; mod dma; pub mod e1000e; pub mod irq_handle; +pub mod loopback; pub mod virtio_net; pub trait NetDevice: Device { diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 4059b59c..839024fc 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -35,10 +35,8 @@ fn kernel_init() -> Result<(), SystemError> { #[cfg(target_arch = "x86_64")] crate::driver::disk::ahci::ahci_init().expect("Failed to initialize AHCI"); - virtio_probe(); mount_root_fs().expect("Failed to mount root fs"); - e1000e_init(); net_init().unwrap_or_else(|err| { error!("Failed to initialize network: {:?}", err); @@ -97,7 +95,6 @@ fn try_to_run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), } return Err(e); } - Ok(()) } @@ -107,6 +104,5 @@ fn run_init_process(path: String, trap_frame: &mut TrapFrame) -> Result<(), Syst compiler_fence(Ordering::SeqCst); Syscall::do_execve(path, argv, envp, trap_frame)?; - Ok(()) } diff --git a/kernel/src/net/net_core.rs b/kernel/src/net/net_core.rs index 7a36e3fe..eeb74148 100644 --- a/kernel/src/net/net_core.rs +++ b/kernel/src/net/net_core.rs @@ -43,7 +43,9 @@ pub fn net_init() -> Result<(), SystemError> { fn dhcp_query() -> Result<(), SystemError> { let binding = NET_DEVICES.write_irqsave(); - let net_face = binding.get(&0).ok_or(SystemError::ENODEV)?.clone(); + //由于现在os未实现在用户态为网卡动态分配内存,而lo网卡的id最先分配且ip固定不能被分配 + //所以特判取用id为1的网卡(也就是virto_net) + let net_face = binding.get(&1).ok_or(SystemError::ENODEV)?.clone(); drop(binding); diff --git a/user/apps/test_lo/.gitignore b/user/apps/test_lo/.gitignore new file mode 100644 index 00000000..1ac35461 --- /dev/null +++ b/user/apps/test_lo/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test_lo/Cargo.toml b/user/apps/test_lo/Cargo.toml new file mode 100644 index 00000000..cb9613c1 --- /dev/null +++ b/user/apps/test_lo/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "test_lo" +version = "0.1.0" +edition = "2021" +description = "测试lo网卡功能" +authors = [ "smallc <2628035541@qq.com>" ] + diff --git a/user/apps/test_lo/Makefile b/user/apps/test_lo/Makefile new file mode 100644 index 00000000..7522ea16 --- /dev/null +++ b/user/apps/test_lo/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN= +RUSTFLAGS= + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_lo/README.md b/user/apps/test_lo/README.md new file mode 100644 index 00000000..cd96a78e --- /dev/null +++ b/user/apps/test_lo/README.md @@ -0,0 +1,7 @@ +# test-lo + +lo网卡功能测试程序 + +## 测试过程: + +通过创建一个UDP套接字,然后发送一条消息到本地回环地址127.0.0.1(lo网卡),再接收并验证这条消息,以此来测试lo网卡的功能。期望发送的消息和接收到的消息是完全一样的。通过日志输出查看测试是否成功。 \ No newline at end of file diff --git a/user/apps/test_lo/src/main.rs b/user/apps/test_lo/src/main.rs new file mode 100644 index 00000000..a9249373 --- /dev/null +++ b/user/apps/test_lo/src/main.rs @@ -0,0 +1,25 @@ +use std::net::UdpSocket; +use std::str; + +fn main() -> std::io::Result<()> { + let socket = UdpSocket::bind("127.0.0.1:34254")?; + socket.connect("127.0.0.1:34254")?; + + let msg = "Hello, loopback!"; + socket.send(msg.as_bytes())?; + + let mut buf = [0; 1024]; + let (amt, _src) = socket.recv_from(&mut buf)?; + + let received_msg = str::from_utf8(&buf[..amt]).expect("Could not read buffer as UTF-8"); + + println!("Sent: {}", msg); + println!("Received: {}", received_msg); + + assert_eq!( + msg, received_msg, + "The sent and received messages do not match!" + ); + + Ok(()) +} diff --git a/user/dadk/config/test_lo_0_1_0.dadk b/user/dadk/config/test_lo_0_1_0.dadk new file mode 100644 index 00000000..2eed4571 --- /dev/null +++ b/user/dadk/config/test_lo_0_1_0.dadk @@ -0,0 +1,27 @@ +{ + "name": "test_lo", + "version": "0.1.0", + "description": "test for lo interface", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_lo" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [], + "build_once": false, + "install_once": false, + "target_arch": ["x86_64"] +} \ No newline at end of file