diff --git a/os/Cargo.toml b/os/Cargo.toml index d8f42164..e23fe161 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -14,6 +14,7 @@ bitflags = "1.2.1" xmas-elf = "0.7.0" volatile = "0.3" virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } +lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "3f467dd" } easy-fs = { path = "../easy-fs" } embedded-graphics = "0.7.1" tinybmp = "0.3.1" diff --git a/os/Makefile b/os/Makefile index e02bc814..db3be5d8 100644 --- a/os/Makefile +++ b/os/Makefile @@ -73,6 +73,22 @@ disasm-vim: kernel run: run-inner +run-inner-none: build + @qemu-system-riscv64 \ + -M 128m \ + -machine virt \ + -bios none \ + $(GUI_OPTION) \ + -kernel $(KERNEL_ELF) \ + -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0 \ +# -device virtio-gpu-device \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000 \ + -serial stdio + run-inner: build @qemu-system-riscv64 \ -M 128m \ @@ -85,6 +101,8 @@ run-inner: build -device virtio-gpu-device \ -device virtio-keyboard-device \ -device virtio-mouse-device \ + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000 \ -serial stdio fdt: diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs index 57a15f03..e4f41724 100644 --- a/os/src/drivers/mod.rs +++ b/os/src/drivers/mod.rs @@ -3,6 +3,7 @@ pub mod bus; pub mod chardev; pub mod gpu; pub mod input; +pub mod net; pub mod plic; pub use block::BLOCK_DEVICE; @@ -10,3 +11,4 @@ pub use bus::*; pub use chardev::UART; pub use gpu::*; pub use input::*; +pub use net::*; \ No newline at end of file diff --git a/os/src/drivers/net/mod.rs b/os/src/drivers/net/mod.rs new file mode 100644 index 00000000..e1f76b00 --- /dev/null +++ b/os/src/drivers/net/mod.rs @@ -0,0 +1,41 @@ +use core::any::Any; + +use alloc::sync::Arc; +use lazy_static::*; +use virtio_drivers::{VirtIONet, VirtIOHeader}; +use crate::drivers::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; + +const VIRTIO8: usize = 0x10004000; + +lazy_static! { + pub static ref NET_DEVICE: Arc = Arc::new(VirtIONetWrapper::new()); +} + +pub trait NetDevice: Send + Sync + Any { + fn transmit(&self, data: &[u8]); + fn receive(&self, data: &mut [u8]) -> usize; +} + +pub struct VirtIONetWrapper(UPIntrFreeCell>); + +impl NetDevice for VirtIONetWrapper { + fn transmit(&self, data: &[u8]) { + self.0.exclusive_access().send(data).expect("can't send data") + } + + fn receive(&self, data: &mut [u8]) -> usize { + self.0.exclusive_access().recv(data).expect("can't receive data") + } +} + +impl VirtIONetWrapper { + pub fn new() -> Self { + unsafe { + let virtio = + VirtIONet::::new(&mut *(VIRTIO8 as *mut VirtIOHeader)) + .expect("can't create net device by virtio"); + VirtIONetWrapper(UPIntrFreeCell::new(virtio)) + } + } +} \ No newline at end of file diff --git a/os/src/main.rs b/os/src/main.rs index 1103c570..98dbcd61 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -26,6 +26,7 @@ mod syscall; mod task; mod timer; mod trap; +mod net; use crate::drivers::chardev::CharDevice; use crate::drivers::chardev::UART; diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs new file mode 100644 index 00000000..57673d3e --- /dev/null +++ b/os/src/net/mod.rs @@ -0,0 +1,92 @@ +pub mod udp; +pub mod socket; + +pub use lose_net_stack::IPv4; + +use alloc::{vec, sync::Arc}; +use lose_net_stack::{LoseStack, MacAddress, results::Packet}; + +use crate::{drivers::NET_DEVICE, sync::UPIntrFreeCell, net::socket::{get_socket, push_data}}; + +pub struct NetStack(UPIntrFreeCell); + +impl NetStack { + pub fn new() -> Self { + unsafe { + NetStack(UPIntrFreeCell::new(LoseStack::new( + IPv4::new(10, 0, 2, 15), + MacAddress::new([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]) + ))) + } + } +} + +lazy_static::lazy_static! { + static ref LOSE_NET_STACK: Arc = Arc::new(NetStack::new()); +} + + +pub fn net_interrupt_handler() { + let mut recv_buf = vec![0u8; 1024]; + + let len = NET_DEVICE.receive(&mut recv_buf); + + let packet = LOSE_NET_STACK.0.exclusive_access().analysis(&recv_buf[..len]); + + // println!("[kernel] receive a packet"); + // hexdump(&recv_buf[..len]); + + match packet { + Packet::ARP(arp_packet) => { + let lose_stack = LOSE_NET_STACK.0.exclusive_access(); + let reply_packet = arp_packet.reply_packet(lose_stack.ip, lose_stack.mac).expect("can't build reply"); + let reply_data = reply_packet.build_data(); + NET_DEVICE.transmit(&reply_data) + }, + + Packet::UDP(udp_packet) => { + let target = udp_packet.source_ip; + let lport = udp_packet.dest_port; + let rport = udp_packet.source_port; + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, udp_packet.data.to_vec()); + } + } + _ => {} + } +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("[kernel] {:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + print!("[kernel] "); + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("[kernel] {:-^1$}", " hexdump end ", PRELAND_WIDTH); +} \ No newline at end of file diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs new file mode 100644 index 00000000..4e92a00e --- /dev/null +++ b/os/src/net/socket.rs @@ -0,0 +1,93 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use lose_net_stack::IPv4; + +use crate::sync::UPIntrFreeCell; + + +// TODO: specify the protocol, TCP or UDP +pub struct Socket { + pub raddr: IPv4, // remote address + pub lport: u16, // local port + pub rport: u16, // rempote port + pub buffers: VecDeque> // datas +} + +lazy_static! { + static ref SOCKET_TABLE:UPIntrFreeCell>> = unsafe { + UPIntrFreeCell::new(Vec::new()) + }; +} + +pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + let socket_table = SOCKET_TABLE.exclusive_access(); + for i in 0..socket_table.len() { + let sock = &socket_table[i]; + if sock.is_none() { + continue; + } + + let sock = sock.as_ref().unwrap(); + if sock.raddr == raddr && sock.lport == lport && sock.rport == rport { + return Some(i) + } + } + None +} + +pub fn add_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + if get_socket(raddr, lport, rport).is_some() { + return None; + } + + let mut socket_table = SOCKET_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..socket_table.len() { + if socket_table[i].is_none() { + index = i; + break; + } + } + + let socket = Socket { + raddr, + lport, + rport, + buffers: VecDeque::new() + }; + + if index == usize::MAX { + socket_table.push(Some(socket)); + Some(socket_table.len() - 1) + } else { + socket_table[index] = Some(socket); + Some(index) + } +} + +pub fn remove_socket(index: usize) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + + socket_table[index] = None; +} + +pub fn push_data(index: usize, data: Vec) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.push_back(data); +} + +pub fn pop_data(index: usize) -> Option> { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.pop_front() +} \ No newline at end of file diff --git a/os/src/net/udp.rs b/os/src/net/udp.rs new file mode 100644 index 00000000..afa26d0e --- /dev/null +++ b/os/src/net/udp.rs @@ -0,0 +1,94 @@ +use alloc::vec; +use lose_net_stack::MacAddress; +use lose_net_stack::packets::udp::UDPPacket; +use lose_net_stack::IPv4; +use crate::fs::File; +use super::net_interrupt_handler; +use super::socket::{add_socket, remove_socket, pop_data}; +use super::LOSE_NET_STACK; +use super::NET_DEVICE; + +pub struct UDP{ + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub socket_index: usize +} + +impl UDP { + pub fn new(target: IPv4, sport: u16, dport: u16) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + socket_index: index + } + } +} + +impl File for UDP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len].copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + let udp_packet = UDPPacket::new( + lose_net_stack.ip, + lose_net_stack.mac, + self.sport, + self.target, + MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + self.dport, + len, + data.as_ref() + ); + NET_DEVICE.transmit(&udp_packet.build_data()); + len + } +} + +impl Drop for UDP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} \ No newline at end of file diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 4052cf53..a336a96f 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,4 +1,5 @@ const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -36,6 +37,7 @@ mod input; mod process; mod sync; mod thread; +mod net; use fs::*; use gui::*; @@ -43,10 +45,12 @@ use input::*; use process::*; use sync::*; use thread::*; +use net::*; pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { SYSCALL_DUP => sys_dup(args[0]), + SYSCALL_CONNECT => sys_connect(args[0] as _, args[1] as _, args[2] as _), SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), SYSCALL_CLOSE => sys_close(args[0]), SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), diff --git a/os/src/syscall/net.rs b/os/src/syscall/net.rs new file mode 100644 index 00000000..4ed4466d --- /dev/null +++ b/os/src/syscall/net.rs @@ -0,0 +1,14 @@ +use alloc::sync::Arc; +use crate::net::IPv4; +use crate::net::udp::UDP; +use crate::task::current_process; + +// just support udp +pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); + inner.fd_table[fd] = Some(Arc::new(udp_node)); + fd as isize +} \ No newline at end of file diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 87885f53..c5fb48dc 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,4 +1,5 @@ const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -48,6 +49,10 @@ pub fn sys_dup(fd: usize) -> isize { syscall(SYSCALL_DUP, [fd, 0, 0]) } +pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize { + syscall(SYSCALL_CONNECT, [dest as usize, sport as usize, dport as usize]) +} + pub fn sys_open(path: &str, flags: u32) -> isize { syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) }