feat(driver/net): 实现Loopback网卡接口 (#845)

* 初步实现loopback设备
This commit is contained in:
SMALLC 2024-07-22 16:22:45 +08:00 committed by GitHub
parent ef2a79be60
commit 1ea2daad81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 613 additions and 5 deletions

View File

@ -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<u8>,
}
impl phy::RxToken for LoopbackRxToken {
/// ## 实现Rxtoken的consume函数
/// 接受一个函数 `f`,并在 `self.buffer` 上调用它。
///
/// ## 参数
/// - mut self :一个可变的 `LoopbackRxToken` 实例。
/// - f :接受一个可变的 u8 切片,并返回类型 `R` 的结果。
///
/// ## 返回值
/// 返回函数 `f` 在 `self.buffer` 上的调用结果。
fn consume<R, F>(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<R, F>(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<Vec<u8>>,
}
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<u8> {
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<u8>) {
//debug!("lo transmit!");
self.queue.push_back(buffer)
}
}
/// ## driver的包裹器
/// 为实现获得不可变引用的Interface的内部可变性故为Driver提供UnsafeCell包裹器
///
/// 参考virtio_net.rs
struct LoopbackDriverWapper(UnsafeCell<LoopbackDriver>);
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<SpinLock<Loopback>>,
}
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封装成rxtx返回给上层
///
/// ## 参数
/// - `&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<Self::TxToken<'_>> {
Some(LoopbackTxToken {
driver: self.clone(),
})
}
}
/// ## LoopbackInterface结构
/// 封装驱动包裹器和iface设置接口名称
pub struct LoopbackInterface {
driver: LoopbackDriverWapper,
iface_id: usize,
iface: SpinLock<smoltcp::iface::Interface>,
name: String,
}
impl LoopbackInterface {
/// ## `new` 是一个公共函数,用于创建一个新的 `LoopbackInterface` 实例。
/// 生成一个新的接口 ID。创建一个新的接口配置设置其硬件地址和随机种子使用接口配置和驱动器创建一个新的 `smoltcp::iface::Interface` 实例。
/// 设置接口的 IP 地址为 127.0.0.1。
/// 创建一个新的 `LoopbackDriverWapper` 实例,包装驱动器。
/// 创建一个新的 `LoopbackInterface` 实例,包含驱动器、接口 ID、接口和名称并将其封装在一个 `Arc` 中。
/// ## 参数
/// - `driver`:一个 `LoopbackDriver` 实例,用于驱动网络环回操作。
///
/// ## 返回值
/// 返回一个 `Arc<Self>`,即一个指向新创建的 `LoopbackInterface` 实例的智能指针。
pub fn new(mut driver: LoopbackDriver) -> Arc<Self> {
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<Arc<crate::filesystem::kernfs::KernFSInode>>) {
todo!()
}
fn inode(&self) -> Option<Arc<crate::filesystem::kernfs::KernFSInode>> {
todo!()
}
fn parent(&self) -> Option<alloc::sync::Weak<dyn KObject>> {
todo!()
}
fn set_parent(&self, _parent: Option<alloc::sync::Weak<dyn KObject>>) {
todo!()
}
fn kset(&self) -> Option<Arc<crate::driver::base::kset::KSet>> {
todo!()
}
fn set_kset(&self, _kset: Option<Arc<crate::driver::base::kset::KSet>>) {
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<crate::driver::base::kobject::KObjectState> {
todo!()
}
fn kobj_state_mut(
&self,
) -> crate::libs::rwlock::RwLockWriteGuard<crate::driver::base::kobject::KObjectState> {
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<Weak<dyn Bus>>) {
todo!()
}
fn set_class(&self, _class: Option<Weak<dyn Class>>) {
todo!()
}
fn driver(&self) -> Option<Arc<dyn Driver>> {
todo!()
}
fn set_driver(&self, _driver: Option<Weak<dyn Driver>>) {
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<smoltcp::iface::Interface> {
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(());
}

View File

@ -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 {

View File

@ -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(())
}

View File

@ -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);

3
user/apps/test_lo/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
Cargo.lock
/install/

View File

@ -0,0 +1,7 @@
[package]
name = "test_lo"
version = "0.1.0"
edition = "2021"
description = "测试lo网卡功能"
authors = [ "smallc <2628035541@qq.com>" ]

View File

@ -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

View File

@ -0,0 +1,7 @@
# test-lo
lo网卡功能测试程序
## 测试过程:
通过创建一个UDP套接字然后发送一条消息到本地回环地址127.0.0.1lo网卡再接收并验证这条消息以此来测试lo网卡的功能。期望发送的消息和接收到的消息是完全一样的。通过日志输出查看测试是否成功。

View File

@ -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(())
}

View File

@ -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"]
}