diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 916c19a28..fb1a97e37 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,13 +12,17 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.rust-toolchain }} components: rust-src, clippy, rustfmt + - name: Install gcc multilib + run: sudo apt update && sudo apt install -y gcc-multilib - name: Clippy for the default target - run: make clippy + run: make clippy - name: Clippy for x86_64 run: make clippy ARCH=x86_64 - name: Clippy for riscv64 @@ -37,6 +41,8 @@ jobs: arch: [x86_64, riscv64, aarch64] steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -47,6 +53,8 @@ jobs: crate: cargo-binutils version: latest use-tool-cache: true + - name: Install gcc multilib + run: sudo apt update && sudo apt install -y gcc-multilib # musl toolchain is also needed in non-musl apps # because we need to build opensbi under riscv64 - uses: ./.github/workflows/actions/setup-musl diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dee04f8fa..68e607319 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,10 +16,14 @@ jobs: default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.rust-toolchain }} + - name: Install gcc multilib + run: sudo apt update && sudo apt install -y gcc-multilib - name: Build docs continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} run: make doc_check_missing diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c546118f..1cdb06752 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,11 +11,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.rust-toolchain }} - components: rust-src + components: rust-src, llvm-tools-preview - name: Run unit tests run: make unittest_no_fail_fast @@ -28,16 +30,20 @@ jobs: arch: [x86_64, riscv64, aarch64] steps: - uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ env.rust-toolchain }} - components: rust-src + components: rust-src, llvm-tools-preview - uses: actions-rs/install@v0.1 with: crate: cargo-binutils version: latest use-tool-cache: true + - name: Install gcc multilib + run: sudo apt update && sudo apt install -y gcc-multilib - uses: ./.github/workflows/actions/setup-qemu with: qemu-version: ${{ env.qemu-version }} diff --git a/.gitmodules b/.gitmodules index 633fd15b6..18e97ea37 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "patches/opensbi"] path = patches/opensbi url = https://github.com/Sssssaltyfish/opensbi.git +[submodule "crates/lwip_rust/depend/lwip"] + path = crates/lwip_rust/depend/lwip + url = https://github.com/lwip-tcpip/lwip.git diff --git a/Cargo.lock b/Cargo.lock index 5b6fae1c2..1a922b93c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,15 +201,19 @@ version = "0.1.0" dependencies = [ "axerrno", "axio", + "axlog", "axsync", "cfg-if", + "cty", "driver_net", "lazy_init", "log", + "lwip_rust", + "printf-compat", "ruxdriver", "ruxhal", "ruxtask", - "smoltcp", + "smoltcp 0.11.0", "spin 0.9.8", ] @@ -229,6 +233,7 @@ name = "axsync" version = "0.1.0" dependencies = [ "axsync", + "log", "rand", "ruxtask", "spinlock", @@ -257,6 +262,29 @@ dependencies = [ "volatile 0.2.7", ] +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.32", + "which", +] + [[package]] name = "bindgen" version = "0.66.1" @@ -356,6 +384,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -513,7 +542,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -534,7 +563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -592,6 +621,22 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "cstr_core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" +dependencies = [ + "cty", + "memchr", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "defmt" version = "0.3.5" @@ -862,6 +907,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.0" @@ -875,12 +929,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ "atomic-polyfill", - "hash32", + "hash32 0.2.1", "rustc_version 0.4.0", "spin 0.9.8", "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "hermit-abi" version = "0.3.2" @@ -931,6 +995,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.5" @@ -954,10 +1027,19 @@ dependencies = [ "bit_field", "core_detect", "log", - "smoltcp", + "smoltcp 0.10.0", "volatile 0.3.0", ] +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -1038,6 +1120,14 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "lwip_rust" +version = "0.1.0" +dependencies = [ + "bindgen 0.65.1", + "cc", +] + [[package]] name = "managed" version = "0.8.0" @@ -1269,6 +1359,18 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "printf-compat" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b002af28ffe3d3d67202ae717810a28125a494d5396debc43de01ee136ac404" +dependencies = [ + "bitflags 1.3.2", + "cstr_core", + "cty", + "itertools 0.9.0", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1486,6 +1588,7 @@ version = "0.1.0" dependencies = [ "axfs_vfs", "axnet", + "axsync", "driver_9p", "driver_common", "log", @@ -1628,7 +1731,7 @@ version = "0.1.0" dependencies = [ "axerrno", "axio", - "bindgen", + "bindgen 0.66.1", "ruxfeat", "ruxos_posix_api", ] @@ -1674,7 +1777,7 @@ dependencies = [ "axlog", "axnet", "axsync", - "bindgen", + "bindgen 0.66.1", "bitflags 2.4.0", "cfg-if", "crate_interface", @@ -1855,7 +1958,21 @@ dependencies = [ "byteorder", "cfg-if", "defmt", - "heapless", + "heapless 0.7.16", + "log", + "managed", +] + +[[package]] +name = "smoltcp" +version = "0.11.0" +source = "git+https://github.com/smoltcp-rs/smoltcp?rev=ce42011#ce420118efff83b47767389500ef1562f5074b55" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "defmt", + "heapless 0.8.0", "log", "managed", ] diff --git a/Cargo.toml b/Cargo.toml index 99145f43c..8cc5f12db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "crates/flatten_objects", "crates/lazy_init", "crates/linked_list", + "crates/lwip_rust", "crates/page_table", "crates/page_table_entry", "crates/percpu", @@ -58,7 +59,7 @@ members = [ "apps/display/basic_painting", "apps/display/draw_map", - "apps/fs/shell", + "apps/fs/shell", "crates/lwip_rust", ] [profile.release] diff --git a/Makefile b/Makefile index 8e4cceddb..47dfe1a35 100644 --- a/Makefile +++ b/Makefile @@ -121,19 +121,24 @@ ifeq ($(ARCH), x86_64) ACCEL ?= $(if $(findstring -microsoft, $(shell uname -r | tr '[:upper:]' '[:lower:]')),n,y) PLATFORM_NAME ?= x86_64-qemu-q35 TARGET := x86_64-unknown-none + TARGET_CFLAGS := -mno-sse BUS := pci else ifeq ($(ARCH), riscv64) ACCEL ?= n PLATFORM_NAME ?= riscv64-qemu-virt TARGET := riscv64gc-unknown-none-elf + TARGET_CFLAGS := -mabi=lp64d else ifeq ($(ARCH), aarch64) ACCEL ?= n PLATFORM_NAME ?= aarch64-qemu-virt TARGET := aarch64-unknown-none-softfloat + TARGET_CFLAGS := -mgeneral-regs-only else $(error "ARCH" must be one of "x86_64", "riscv64", or "aarch64") endif +export TARGET_CC = $(CC) +export TARGET_CFLAGS export RUX_ARCH=$(ARCH) export RUX_PLATFORM=$(PLATFORM_NAME) export RUX_SMP=$(SMP) diff --git a/api/arceos_api/Cargo.toml b/api/arceos_api/Cargo.toml index 9be613762..360ff40ee 100644 --- a/api/arceos_api/Cargo.toml +++ b/api/arceos_api/Cargo.toml @@ -19,6 +19,7 @@ paging = ["alloc", "ruxfeat/paging"] multitask = ["ruxtask/multitask", "ruxfeat/multitask"] fs = ["dep:ruxfs", "ruxfeat/fs"] net = ["dep:axnet", "ruxfeat/net"] +lwip = ["ruxfeat/lwip"] display = ["dep:ruxdisplay", "ruxfeat/display"] myfs = ["ruxfeat/myfs"] diff --git a/api/arceos_api/src/imp/net.rs b/api/arceos_api/src/imp/net.rs index 16920dc7a..7cf4e2546 100644 --- a/api/arceos_api/src/imp/net.rs +++ b/api/arceos_api/src/imp/net.rs @@ -47,7 +47,7 @@ pub fn ax_tcp_bind(socket: &AxTcpSocketHandle, addr: SocketAddr) -> AxResult { socket.0.bind(addr) } -pub fn ax_tcp_listen(socket: &AxTcpSocketHandle, _backlog: usize) -> AxResult { +pub fn ax_tcp_listen(socket: &mut AxTcpSocketHandle, _backlog: usize) -> AxResult { socket.0.listen() } diff --git a/api/arceos_api/src/lib.rs b/api/arceos_api/src/lib.rs index dc829d4b2..0e2e0e1f6 100644 --- a/api/arceos_api/src/lib.rs +++ b/api/arceos_api/src/lib.rs @@ -262,7 +262,7 @@ pub mod net { /// Binds the TCP socket to the given address and port. pub fn ax_tcp_bind(socket: &AxTcpSocketHandle, addr: SocketAddr) -> AxResult; /// Starts listening on the bound address and port. - pub fn ax_tcp_listen(socket: &AxTcpSocketHandle, _backlog: usize) -> AxResult; + pub fn ax_tcp_listen(socket: &mut AxTcpSocketHandle, _backlog: usize) -> AxResult; /// Accepts a new connection on the TCP socket. /// /// This function will block the calling thread until a new TCP connection diff --git a/api/ruxfeat/Cargo.toml b/api/ruxfeat/Cargo.toml index f4aaf4d56..8f34b7054 100644 --- a/api/ruxfeat/Cargo.toml +++ b/api/ruxfeat/Cargo.toml @@ -48,6 +48,8 @@ myfs = ["ruxfs?/myfs"] # Networking net = ["alloc", "ruxdriver/virtio-net", "dep:axnet", "ruxruntime/net"] +lwip = ["axnet/lwip"] +smoltcp = ["axnet/smoltcp"] # Display display = ["alloc", "ruxdriver/virtio-gpu", "dep:ruxdisplay", "ruxruntime/display"] diff --git a/api/ruxos_posix_api/src/imp/fd_ops.rs b/api/ruxos_posix_api/src/imp/fd_ops.rs index bb5ca1e43..72c330be7 100644 --- a/api/ruxos_posix_api/src/imp/fd_ops.rs +++ b/api/ruxos_posix_api/src/imp/fd_ops.rs @@ -63,7 +63,7 @@ pub fn close_file_like(fd: c_int) -> LinuxResult { /// Close a file by `fd`. pub fn sys_close(fd: c_int) -> c_int { - debug!("sys_close <= {}", fd); + info!("sys_close <= {}", fd); if (0..=2).contains(&fd) { return 0; // stdin, stdout, stderr } diff --git a/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs b/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs index 68c7b8a50..e7dbf0ee4 100644 --- a/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs +++ b/api/ruxos_posix_api/src/imp/io_mpx/epoll.rs @@ -215,7 +215,6 @@ pub unsafe fn sys_epoll_wait( loop { #[cfg(feature = "net")] axnet::poll_interfaces(); - let poll_all_res = epoll_instance.poll_all(events); let mut events_num = 0; match poll_all_res { @@ -237,8 +236,8 @@ pub unsafe fn sys_epoll_wait( } Err(_) => {} } - if events_num > 0 { + //info!("lhw debug in sys_epoll wait normal return"); return Ok(events_num as c_int); } diff --git a/api/ruxos_posix_api/src/imp/net.rs b/api/ruxos_posix_api/src/imp/net.rs index 56cb8b6a1..165b4f7f6 100644 --- a/api/ruxos_posix_api/src/imp/net.rs +++ b/api/ruxos_posix_api/src/imp/net.rs @@ -123,14 +123,14 @@ impl Socket { fn shutdown(&self) -> LinuxResult { match self { Socket::Udp(udpsocket) => { - let udpsocket = udpsocket.lock(); + let mut udpsocket = udpsocket.lock(); udpsocket.peer_addr()?; udpsocket.shutdown()?; Ok(()) } Socket::Tcp(tcpsocket) => { - let tcpsocket = tcpsocket.lock(); + let mut tcpsocket = tcpsocket.lock(); tcpsocket.peer_addr()?; tcpsocket.shutdown()?; Ok(()) @@ -433,7 +433,7 @@ pub fn sys_listen( socket_fd: c_int, backlog: c_int, // currently not used ) -> c_int { - debug!("sys_listen <= {} {}", socket_fd, backlog); + info!("sys_listen <= {} {}", socket_fd, backlog); syscall_body!(sys_listen, { Socket::from_fd(socket_fd)?.listen()?; Ok(0) diff --git a/crates/driver_net/src/ixgbe.rs b/crates/driver_net/src/ixgbe.rs index 27fd4363a..3fe98b4c6 100644 --- a/crates/driver_net/src/ixgbe.rs +++ b/crates/driver_net/src/ixgbe.rs @@ -15,7 +15,7 @@ use driver_common::{BaseDriverOps, DevError, DevResult, DeviceType}; use ixgbe_driver::{IxgbeDevice, IxgbeError, IxgbeNetBuf, MemPool, NicDevice}; pub use ixgbe_driver::{IxgbeHal, PhysAddr, INTEL_82599, INTEL_VEND}; -use crate::{EthernetAddress, NetBufPtr, NetDriverOps}; +use crate::{EthernetAddress, NetBuf, NetBufPool, NetBufPtr, NetDriverOps}; extern crate alloc; @@ -88,6 +88,16 @@ impl NetDriverOps for IxgbeNic) -> DevResult { + Ok(()) + } + + ///TODO + fn prepare_tx_buffer(&self, _: &mut NetBuf, _: usize) -> DevResult { + Ok(()) + } + fn recycle_rx_buffer(&mut self, rx_buf: NetBufPtr) -> DevResult { let rx_buf = ixgbe_ptr_to_buf(rx_buf, &self.mem_pool)?; drop(rx_buf); diff --git a/crates/driver_net/src/lib.rs b/crates/driver_net/src/lib.rs index 6ceb95e47..6fd20c600 100644 --- a/crates/driver_net/src/lib.rs +++ b/crates/driver_net/src/lib.rs @@ -14,6 +14,9 @@ #![feature(const_slice_from_raw_parts_mut)] #![feature(box_into_inner)] +extern crate alloc; +use alloc::sync::Arc; + #[cfg(feature = "ixgbe")] /// ixgbe NIC device driver. pub mod ixgbe; @@ -46,6 +49,16 @@ pub trait NetDriverOps: BaseDriverOps { /// Size of the transmit queue. fn tx_queue_size(&self) -> usize; + /// Fills the receive queue with buffers. + /// + /// It should be called once when the driver is initialized. + fn fill_rx_buffers(&mut self, buf_pool: &Arc) -> DevResult; + + /// Prepares a buffer for transmitting. + /// + /// e.g., fill the header of the packet. + fn prepare_tx_buffer(&self, tx_buf: &mut NetBuf, packet_len: usize) -> DevResult; + /// Gives back the `rx_buf` to the receive queue for later receiving. /// /// `rx_buf` should be the same as the one returned by diff --git a/crates/driver_virtio/src/net.rs b/crates/driver_virtio/src/net.rs index 3569738c1..afef6b0b1 100644 --- a/crates/driver_virtio/src/net.rs +++ b/crates/driver_virtio/src/net.rs @@ -81,6 +81,7 @@ impl VirtIoNetDev { } } +use log::info; impl const BaseDriverOps for VirtIoNetDev { fn device_name(&self) -> &str { "virtio-net" @@ -117,6 +118,21 @@ impl NetDriverOps for VirtIoNetDev) -> DevResult { + for (i, rx_buf_place) in self.rx_buffers.iter_mut().enumerate() { + let mut rx_buf = buf_pool.alloc_boxed().ok_or(DevError::NoMemory)?; + // Safe because the buffer lives as long as the queue. + let token = unsafe { + self.inner + .receive_begin(rx_buf.raw_buf_mut()) + .map_err(as_dev_err)? + }; + assert_eq!(token, i as u16); + *rx_buf_place = Some(rx_buf); + } + Ok(()) + } + fn recycle_rx_buffer(&mut self, rx_buf: NetBufPtr) -> DevResult { let mut rx_buf = unsafe { NetBuf::from_buf_ptr(rx_buf) }; // Safe because we take the ownership of `rx_buf` back to `rx_buffers`, @@ -135,6 +151,19 @@ impl NetDriverOps for VirtIoNetDev DevResult { + let hdr_len = self + .inner + .fill_buffer_header(tx_buf.raw_buf_mut()) + .or(Err(DevError::InvalidParam))?; + if hdr_len + pkt_len > tx_buf.capacity() { + return Err(DevError::InvalidParam); + } + tx_buf.set_header_len(hdr_len); + tx_buf.set_packet_len(pkt_len); + Ok(()) + } + fn recycle_tx_buffers(&mut self) -> DevResult { while let Some(token) = self.inner.poll_transmit() { let tx_buf = self.tx_buffers[token as usize] diff --git a/crates/lwip_rust/.gitignore b/crates/lwip_rust/.gitignore new file mode 100644 index 000000000..e2862f469 --- /dev/null +++ b/crates/lwip_rust/.gitignore @@ -0,0 +1,2 @@ +/target +/src/bindings.rs \ No newline at end of file diff --git a/crates/lwip_rust/Cargo.toml b/crates/lwip_rust/Cargo.toml new file mode 100644 index 000000000..0bba752c1 --- /dev/null +++ b/crates/lwip_rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lwip_rust" +version = "0.1.0" +edition = "2021" +authors = ["Haixuan Tong "] +description = "Rust wrapper of lwip net stack" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/rcore-os/arceos" +repository = "https://github.com/rcore-os/arceos/tree/main/crates/lwip_rust" +documentation = "https://rcore-os.github.io/arceos/lwip_rust/index.html" + +[build-dependencies] +bindgen = "0.65.1" +cc = { version = "1.0.79", features = ["parallel"] } diff --git a/crates/lwip_rust/README.md b/crates/lwip_rust/README.md new file mode 100644 index 000000000..4afb4bf30 --- /dev/null +++ b/crates/lwip_rust/README.md @@ -0,0 +1,5 @@ +# lwip_rust + +## dependency + +`sudo apt install libclang-dev` diff --git a/crates/lwip_rust/build.rs b/crates/lwip_rust/build.rs new file mode 100644 index 000000000..3e5572ae9 --- /dev/null +++ b/crates/lwip_rust/build.rs @@ -0,0 +1,96 @@ +use std::path::PathBuf; + +fn main() { + println!("cargo:rustc-link-lib=lwip"); + println!("cargo:rerun-if-changed=custom"); + println!("cargo:rerun-if-changed=depend"); + println!("cargo:rerun-if-changed=wrapper.h"); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let clippy_args = std::env::var("CLIPPY_ARGS"); + + // Not build with clippy or doc + if target_os == "none" && clippy_args.is_err() { + compile_lwip(); + } + generate_lwip_bindings(); +} + +fn generate_lwip_bindings() { + let bindings = bindgen::Builder::default() + .use_core() + .header("wrapper.h") + .clang_arg("--target=aarch64-unknown-none-softfloat") + .clang_arg("-I./depend/lwip/src/include") + .clang_arg("-I./custom") + .clang_arg("-Wno-everything") + .layout_tests(false) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from("src"); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn compile_lwip() { + let mut base_config = cc::Build::new(); + + base_config + .include("depend/lwip/src/include") + .include("custom"); + + base_config + .file("depend/lwip/src/api/err.c") + .file("depend/lwip/src/core/init.c") + .file("depend/lwip/src/core/def.c") + .file("depend/lwip/src/core/dns.c") + .file("depend/lwip/src/core/inet_chksum.c") + .file("depend/lwip/src/core/ip.c") + .file("depend/lwip/src/core/mem.c") + .file("depend/lwip/src/core/memp.c") + .file("depend/lwip/src/core/netif.c") + .file("depend/lwip/src/core/pbuf.c") + .file("depend/lwip/src/core/raw.c") + .file("depend/lwip/src/core/stats.c") + .file("depend/lwip/src/core/sys.c") + .file("depend/lwip/src/core/altcp.c") + .file("depend/lwip/src/core/altcp_alloc.c") + .file("depend/lwip/src/core/altcp_tcp.c") + .file("depend/lwip/src/core/tcp.c") + .file("depend/lwip/src/core/tcp_in.c") + .file("depend/lwip/src/core/tcp_out.c") + .file("depend/lwip/src/core/timeouts.c") + .file("depend/lwip/src/core/udp.c") + .file("depend/lwip/src/core/ipv4/autoip.c") + .file("depend/lwip/src/core/ipv4/dhcp.c") + .file("depend/lwip/src/core/ipv4/etharp.c") + .file("depend/lwip/src/core/ipv4/icmp.c") + .file("depend/lwip/src/core/ipv4/igmp.c") + .file("depend/lwip/src/core/ipv4/ip4_frag.c") + .file("depend/lwip/src/core/ipv4/ip4.c") + .file("depend/lwip/src/core/ipv4/ip4_addr.c") + .file("depend/lwip/src/core/ipv6/dhcp6.c") + .file("depend/lwip/src/core/ipv6/ethip6.c") + .file("depend/lwip/src/core/ipv6/icmp6.c") + .file("depend/lwip/src/core/ipv6/inet6.c") + .file("depend/lwip/src/core/ipv6/ip6.c") + .file("depend/lwip/src/core/ipv6/ip6_addr.c") + .file("depend/lwip/src/core/ipv6/ip6_frag.c") + .file("depend/lwip/src/core/ipv6/mld6.c") + .file("depend/lwip/src/core/ipv6/nd6.c") + .file("depend/lwip/src/netif/ethernet.c") + // .file("depend/lwip/src/apps/lwiperf/lwiperf.c") + .file("custom/custom_pool.c") + .file("custom/sys_arch.c"); + + base_config + .warnings(true) + .flag("-static") + .flag("-no-pie") + .flag("-fno-builtin") + .flag("-ffreestanding") + .compile("liblwip.a"); +} diff --git a/crates/lwip_rust/custom/arch/cc.h b/crates/lwip_rust/custom/arch/cc.h new file mode 100644 index 000000000..64b0c6730 --- /dev/null +++ b/crates/lwip_rust/custom/arch/cc.h @@ -0,0 +1,44 @@ +#ifndef __ARCH_CC_H__ +#define __ARCH_CC_H__ + +#ifndef _SSIZE_T_DEFINED +typedef long ssize_t; +#define _SSIZE_T_DEFINED +#endif + +#define LWIP_NO_INTTYPES_H 1 +#define U8_F "hhu" +#define S8_F "hhd" +#define X8_F "hhx" +#define U16_F "hu" +#define S16_F "hd" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "zu" + +#define LWIP_NO_LIMITS_H 1 +#define LWIP_NO_CTYPE_H 1 + +#define SSIZE_MAX INT_MAX +#define LWIP_NO_UNISTD_H 1 + +extern int lwip_print(const char *fmt, ...); +extern void lwip_abort(void); +extern unsigned int lwip_rand_u32(void); + +#define LWIP_PLATFORM_DIAG(x) \ + do { \ + lwip_print x; \ + } while (0) + +#define LWIP_PLATFORM_ASSERT(x) \ + do { \ + lwip_print("Assert \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); \ + lwip_abort(); \ + } while (0) + +#define LWIP_RAND() (lwip_rand_u32()) + +#endif /* __ARCH_CC_H__ */ \ No newline at end of file diff --git a/crates/lwip_rust/custom/arch/perf.h b/crates/lwip_rust/custom/arch/perf.h new file mode 100644 index 000000000..29cabc691 --- /dev/null +++ b/crates/lwip_rust/custom/arch/perf.h @@ -0,0 +1,7 @@ +#ifndef __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ \ No newline at end of file diff --git a/crates/lwip_rust/custom/arch/sys_arch.h b/crates/lwip_rust/custom/arch/sys_arch.h new file mode 100644 index 000000000..4d3d5acce --- /dev/null +++ b/crates/lwip_rust/custom/arch/sys_arch.h @@ -0,0 +1,12 @@ +#ifndef __ARCH_SYS_ARCH_H__ +#define __ARCH_SYS_ARCH_H__ + +#define SYS_MBOX_NULL NULL +#define SYS_SEM_NULL NULL + +#define isspace(a) ((a == ' ' || (unsigned)a - '\t' < 5)) +#define isdigit(a) (((unsigned)(a) - '0') < 10) + +int strcmp(const char *l, const char *r); + +#endif /* __ARCH_SYS_ARCH_H__ */ \ No newline at end of file diff --git a/crates/lwip_rust/custom/custom_pool.c b/crates/lwip_rust/custom/custom_pool.c new file mode 100644 index 000000000..da2181b58 --- /dev/null +++ b/crates/lwip_rust/custom/custom_pool.c @@ -0,0 +1,30 @@ +#include "custom_pool.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" + +#define RX_POOL_SIZE 128 + +LWIP_MEMPOOL_DECLARE(RX_POOL, RX_POOL_SIZE, sizeof(rx_custom_pbuf_t), "Zero-copy RX PBUF pool") + +void rx_custom_pbuf_init(void) +{ + LWIP_MEMPOOL_INIT(RX_POOL); +} + +struct pbuf *rx_custom_pbuf_alloc(pbuf_free_custom_fn custom_free_function, void *buf, void *dev, + u16_t length, void *payload_mem, u16_t payload_mem_len) +{ + rx_custom_pbuf_t *my_pbuf = (rx_custom_pbuf_t *)LWIP_MEMPOOL_ALLOC(RX_POOL); + my_pbuf->p.custom_free_function = custom_free_function; + my_pbuf->buf = buf; + my_pbuf->dev = dev; + struct pbuf *p = + pbuf_alloced_custom(PBUF_RAW, length, PBUF_REF, &my_pbuf->p, payload_mem, payload_mem_len); + return p; +} + +void rx_custom_pbuf_free(rx_custom_pbuf_t *p) +{ + LWIP_MEMPOOL_FREE(RX_POOL, p); +} diff --git a/crates/lwip_rust/custom/custom_pool.h b/crates/lwip_rust/custom/custom_pool.h new file mode 100644 index 000000000..66ccde732 --- /dev/null +++ b/crates/lwip_rust/custom/custom_pool.h @@ -0,0 +1,18 @@ + +#ifndef __CUSTOM_POOL_H__ +#define __CUSTOM_POOL_H__ + +#include "lwip/pbuf.h" + +typedef struct rx_custom_pbuf_t { + struct pbuf_custom p; + void *buf; + void *dev; +} rx_custom_pbuf_t; + +void rx_custom_pbuf_init(void); +struct pbuf *rx_custom_pbuf_alloc(pbuf_free_custom_fn custom_free_function, void *buf, void *dev, + u16_t length, void *payload_mem, u16_t payload_mem_len); +void rx_custom_pbuf_free(rx_custom_pbuf_t *p); + +#endif /* __CUSTOM_POOL_H__ */ \ No newline at end of file diff --git a/crates/lwip_rust/custom/lwipopts.h b/crates/lwip_rust/custom/lwipopts.h new file mode 100644 index 000000000..b9abfcdc3 --- /dev/null +++ b/crates/lwip_rust/custom/lwipopts.h @@ -0,0 +1,140 @@ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +#define NO_SYS 1 +// #define LWIP_TIMERS 1 +// #define NO_SYS_NO_TIMERS 1 + +/* + ------------------------------------ + ------------ Functions ------------ + ------------------------------------ +*/ +#define IP_DEFAULT_TTL 64 +#define LWIP_ETHERNET 1 +#define LWIP_ARP 1 +#define ARP_QUEUEING 0 +#define IP_FORWARD 0 +#define LWIP_ICMP 1 +#define LWIP_RAW 0 +#define LWIP_DHCP 0 +#define LWIP_AUTOIP 0 +#define LWIP_SNMP 0 +#define LWIP_IGMP 0 +#define LWIP_DNS 1 +#define LWIP_UDP 1 +#define LWIP_UDPLITE 0 +#define LWIP_TCP 1 +#define LWIP_CALLBACK_API 1 +#define LWIP_NETIF_API 0 +#define LWIP_NETIF_LOOPBACK 0 +#define LWIP_HAVE_LOOPIF 1 +#define LWIP_HAVE_SLIPIF 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define PPP_SUPPORT 0 +#define LWIP_IPV4 1 +#define LWIP_IPV6 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_AUTOCONFIG 1 + +// Enable SO_REUSEADDR +#define SO_REUSE 1 + +/* + ------------------------------------ + ------ Memory and Performance ------ + ------------------------------------ +*/ + +// Important performance options +// Smaller values increase performance +// Larger values increase simultaneously active TCP connections limit +#define MEMP_NUM_TCP_PCB 5 + +// Memory options +#define MEM_SIZE (1 * 1024 * 1024) +#define MEMP_NUM_TCP_SEG 128 +#define MEMP_NUM_PBUF 32 +#define PBUF_POOL_SIZE 32 + +// Tcp options +#define TCP_MSS 1460 +#define TCP_WND (32 * TCP_MSS) +#define TCP_SND_BUF (16 * TCP_MSS) + +// Disable checksum checks +#define CHECKSUM_CHECK_IP 0 +#define CHECKSUM_CHECK_UDP 0 +#define CHECKSUM_CHECK_TCP 0 +#define CHECKSUM_CHECK_ICMP 0 +#define CHECKSUM_CHECK_ICMP6 0 + +// Other performance options +#define LWIP_CHECKSUM_ON_COPY 1 +#define SYS_LIGHTWEIGHT_PROT 0 + +// needed on 64-bit systems, enable it always so that the same configuration +// is used regardless of the platform +#define IPV6_FRAG_COPYHEADER 1 + +/* + ------------------------------------ + ---------- Debug options ---------- + ------------------------------------ +*/ + +#define LWIP_DEBUG 0 +#define LWIP_DBG_TYPES_ON LWIP_DBG_OFF +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_ON +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_ON +#define API_MSG_DEBUG LWIP_DBG_ON +#define SOCKETS_DEBUG LWIP_DBG_ON +#define ICMP_DEBUG LWIP_DBG_ON +#define IGMP_DEBUG LWIP_DBG_ON +#define INET_DEBUG LWIP_DBG_ON +#define IP_DEBUG LWIP_DBG_ON +#define IP_REASS_DEBUG LWIP_DBG_ON +#define RAW_DEBUG LWIP_DBG_ON +#define MEM_DEBUG LWIP_DBG_ON +#define MEMP_DEBUG LWIP_DBG_ON +#define SYS_DEBUG LWIP_DBG_ON +#define TIMERS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_ON +#define TCP_INPUT_DEBUG LWIP_DBG_ON +#define TCP_FR_DEBUG LWIP_DBG_ON +#define TCP_RTO_DEBUG LWIP_DBG_ON +#define TCP_CWND_DEBUG LWIP_DBG_ON +#define TCP_WND_DEBUG LWIP_DBG_ON +#define TCP_OUTPUT_DEBUG LWIP_DBG_ON +#define TCP_RST_DEBUG LWIP_DBG_ON +#define TCP_QLEN_DEBUG LWIP_DBG_ON +#define UDP_DEBUG LWIP_DBG_ON +#define TCPIP_DEBUG LWIP_DBG_ON +#define SLIP_DEBUG LWIP_DBG_ON +#define DHCP_DEBUG LWIP_DBG_ON +#define AUTOIP_DEBUG LWIP_DBG_ON +#define ACD_DEBUG LWIP_DBG_ON +#define DNS_DEBUG LWIP_DBG_ON +#define IP6_DEBUG LWIP_DBG_ON +#define DHCP6_DEBUG LWIP_DBG_ON + +#define LWIP_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define LWIP_PERF 0 + +/* + ------------------------------------ + ----------- Memory check ----------- + ------------------------------------ +*/ +#define MEMP_OVERFLOW_CHECK 0 +#define MEMP_SANITY_CHECK 0 +#define MEM_OVERFLOW_CHECK 0 +#define MEM_SANITY_CHECK 0 + +#endif /* __LWIPOPTS_H__ */ \ No newline at end of file diff --git a/crates/lwip_rust/custom/sys_arch.c b/crates/lwip_rust/custom/sys_arch.c new file mode 100644 index 000000000..3c5bcc3be --- /dev/null +++ b/crates/lwip_rust/custom/sys_arch.c @@ -0,0 +1,10 @@ +#include "arch/sys_arch.h" +#include "lwip/opt.h" +#include "lwip/sys.h" + +int strcmp(const char *l, const char *r) +{ + for (; *l == *r && *l; l++, r++) + ; + return *(unsigned char *)l - *(unsigned char *)r; +} \ No newline at end of file diff --git a/crates/lwip_rust/depend/lwip b/crates/lwip_rust/depend/lwip new file mode 160000 index 000000000..86c9f7999 --- /dev/null +++ b/crates/lwip_rust/depend/lwip @@ -0,0 +1 @@ +Subproject commit 86c9f7999150199374d7697fd2eed62dcd9b1afa diff --git a/crates/lwip_rust/src/lib.rs b/crates/lwip_rust/src/lib.rs new file mode 100644 index 000000000..77a366662 --- /dev/null +++ b/crates/lwip_rust/src/lib.rs @@ -0,0 +1,7 @@ +#![no_std] +#![allow(dead_code)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(missing_docs)] +pub mod bindings; diff --git a/crates/lwip_rust/wrapper.h b/crates/lwip_rust/wrapper.h new file mode 100644 index 000000000..078ec137a --- /dev/null +++ b/crates/lwip_rust/wrapper.h @@ -0,0 +1,18 @@ +#include "depend/lwip/src/include/lwip/def.h" +#include "depend/lwip/src/include/lwip/dns.h" +#include "depend/lwip/src/include/lwip/etharp.h" +#include "depend/lwip/src/include/lwip/ethip6.h" +#include "depend/lwip/src/include/lwip/init.h" +#include "depend/lwip/src/include/lwip/ip4_addr.h" +#include "depend/lwip/src/include/lwip/ip_addr.h" +#include "depend/lwip/src/include/lwip/netif.h" +#include "depend/lwip/src/include/lwip/tcp.h" +#include "depend/lwip/src/include/lwip/timeouts.h" +#include "depend/lwip/src/include/lwip/udp.h" +#include "depend/lwip/src/include/netif/ethernet.h" + +// Custom +#include "custom/custom_pool.h" + +// APP +// #include "depend/lwip/src/include/lwip/apps/lwiperf.h" \ No newline at end of file diff --git a/modules/axnet/Cargo.toml b/modules/axnet/Cargo.toml index 7f40b6bf0..b8addb684 100644 --- a/modules/axnet/Cargo.toml +++ b/modules/axnet/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/rcore-os/arceos/tree/main/modules/axnet" documentation = "https://rcore-os.github.io/arceos/axnet/index.html" [features] +lwip = ["dep:printf-compat", "dep:lwip_rust", "cty" ] smoltcp = [] default = ["smoltcp"] @@ -19,16 +20,22 @@ cfg-if = "1.0" spin = "0.9" driver_net = { path = "../../crates/driver_net" } lazy_init = { path = "../../crates/lazy_init" } +lwip_rust = { path = "../../crates/lwip_rust", optional = true } +printf-compat = { version = "0.1", default-features = false, optional = true } axerrno = { path = "../../crates/axerrno" } ruxhal = { path = "../ruxhal" } axsync = { path = "../axsync" } +axlog = { path = "../axlog" } ruxtask = { path = "../ruxtask" } ruxdriver = { path = "../ruxdriver", features = ["net"] } +cty = { version = "0.2.2", optional = true } axio = { path = "../../crates/axio" } [dependencies.smoltcp] -git = "https://github.com/rcore-os/smoltcp.git" -rev = "2ade274" +git = "https://github.com/smoltcp-rs/smoltcp" +rev = "ce42011" +#git = "https://github.com/rcore-os/smoltcp.git" +#rev = "2ade274" default-features = false features = [ "alloc", "log", # no std diff --git a/modules/axnet/src/lib.rs b/modules/axnet/src/lib.rs index e744facda..df5cf1720 100644 --- a/modules/axnet/src/lib.rs +++ b/modules/axnet/src/lib.rs @@ -27,18 +27,28 @@ //! [smoltcp]: https://github.com/smoltcp-rs/smoltcp #![no_std] +#![feature(c_variadic)] #![feature(ip_in_core)] #![feature(new_uninit)] +#![feature(inherent_associated_types)] #[macro_use] extern crate log; extern crate alloc; cfg_if::cfg_if! { - if #[cfg(feature = "smoltcp")] { + if #[cfg(feature = "lwip")] { + mod lwip_impl; + use lwip_impl as net_impl; + pub use lwip_impl::{IpAddr, Ipv4Addr, SocketAddr}; + } + else if #[cfg(feature = "smoltcp")] { mod smoltcp_impl; use smoltcp_impl as net_impl; } + else { + error!("No network stack is selected"); + } } pub use self::net_impl::TcpSocket; @@ -54,5 +64,14 @@ pub fn init_network(mut net_devs: AxDeviceContainer) { let dev = net_devs.take_one().expect("No NIC device found!"); info!(" use NIC 0: {:?}", dev.device_name()); + cfg_if::cfg_if! { + if #[cfg(feature = "lwip")] { + info!(" net stack: lwip"); + } else if #[cfg(feature = "smoltcp")] { + info!(" net stack: smoltcp"); + } else { + compile_error!("No network stack is selected"); + } + } net_impl::init(dev); } diff --git a/modules/axnet/src/lwip_impl/addr.rs b/modules/axnet/src/lwip_impl/addr.rs new file mode 100644 index 000000000..2e74c4aac --- /dev/null +++ b/modules/axnet/src/lwip_impl/addr.rs @@ -0,0 +1,414 @@ +use core::{ + fmt::{self, Error}, + str::FromStr, +}; + +use lwip_rust::bindings::{ + ip4_addr_t, ip6_addr_t, ip_addr__bindgen_ty_1, ip_addr_t, lwip_ip_addr_type_IPADDR_TYPE_V4, + lwip_ip_addr_type_IPADDR_TYPE_V6, +}; + +use core::net::{ + Ipv4Addr as CoreIpv4Addr, Ipv6Addr as CoreIpv6Addr, SocketAddr as CoreSocketAddr, SocketAddrV4, + SocketAddrV6, +}; + +/// Mac Address +#[derive(Clone, Copy, Debug, Default)] +pub struct MacAddr(pub [u8; 6]); + +/// IP Address, either IPv4 or IPv6 +#[derive(Clone, Copy, Debug)] +pub enum IpAddr { + /// IPv4 Address + Ipv4(Ipv4Addr), + + /// IPv6 Address + Ipv6(Ipv6Addr), +} + +/// IPv4 Address (host byte order) +#[derive(Clone, Copy, Debug, Default)] +pub struct Ipv4Addr(pub u32); + +/// IPv6 Address +#[derive(Clone, Copy, Debug, Default)] +pub struct Ipv6Addr { + /// Address in host byte order + pub addr: [u32; 4usize], + + /// Zone identifier + pub zone: u8, +} + +/// Socket Address (IP Address + Port) +#[derive(Clone, Copy, Debug)] +pub struct SocketAddr { + /// IP Address + pub addr: IpAddr, + + /// Port + pub port: u16, +} + +impl MacAddr { + /// Create a new MacAddr from a byte array + pub fn from_bytes(bytes: &[u8]) -> MacAddr { + let mut addr = [0u8; 6]; + addr.copy_from_slice(bytes); + MacAddr(addr) + } +} + +impl fmt::Display for MacAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5] + ) + } +} + +impl IpAddr { + /// Get the IP Address as a byte array + pub fn as_bytes(&self) -> &[u8] { + match self { + IpAddr::Ipv4(Ipv4Addr(addr)) => unsafe { &*(addr as *const u32 as *const [u8; 4]) }, + IpAddr::Ipv6(Ipv6Addr { addr, .. }) => unsafe { + &*(addr as *const u32 as *const [u8; 16]) + }, + } + } +} + +impl From for IpAddr { + fn from(addr: Ipv4Addr) -> IpAddr { + IpAddr::Ipv4(addr) + } +} + +impl From for IpAddr { + fn from(addr: Ipv6Addr) -> IpAddr { + IpAddr::Ipv6(addr) + } +} + +impl FromStr for IpAddr { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut parts = s.split('.'); + let mut addr: u32 = 0; + for i in 0..4 { + let part = parts.next().ok_or(())?; + let part = part.parse::().map_err(|_| ())?; + addr |= (part as u32) << (8 * i); + } + Ok(IpAddr::Ipv4(Ipv4Addr(addr))) + } +} + +impl From for ip_addr_t { + fn from(val: IpAddr) -> Self { + match val { + IpAddr::Ipv4(Ipv4Addr(addr)) => ip_addr_t { + u_addr: ip_addr__bindgen_ty_1 { + ip4: ip4_addr_t { addr }, + }, + type_: lwip_ip_addr_type_IPADDR_TYPE_V4 as u8, + }, + IpAddr::Ipv6(Ipv6Addr { addr, zone }) => ip_addr_t { + u_addr: ip_addr__bindgen_ty_1 { + ip6: ip6_addr_t { addr, zone }, + }, + type_: lwip_ip_addr_type_IPADDR_TYPE_V6 as u8, + }, + } + } +} + +impl From for IpAddr { + #[allow(non_upper_case_globals)] + fn from(addr: ip_addr_t) -> IpAddr { + match addr.type_ as u32 { + lwip_ip_addr_type_IPADDR_TYPE_V4 => { + IpAddr::Ipv4(Ipv4Addr(unsafe { addr.u_addr.ip4.addr })) + } + lwip_ip_addr_type_IPADDR_TYPE_V6 => IpAddr::Ipv6(Ipv6Addr { + addr: unsafe { addr.u_addr.ip6.addr }, + zone: unsafe { addr.u_addr.ip6.zone }, + }), + _ => panic!("unsupported ip type"), + } + } +} + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + IpAddr::Ipv4(ipv4_addr) => write!(f, "{ipv4_addr}"), + IpAddr::Ipv6(ipv6_addr) => write!(f, "{ipv6_addr}"), + } + } +} + +impl Ipv4Addr { + /// Construct an IPv4 address from parts. + pub fn new(a0: u8, a1: u8, a2: u8, a3: u8) -> Ipv4Addr { + Self::from_bytes(&[a0, a1, a2, a3]) + } + + /// Create a new Ipv4Addr from a byte array + pub fn from_bytes(bytes: &[u8]) -> Ipv4Addr { + let mut addr: u32 = 0; + for (i, &b) in bytes.iter().enumerate().take(4) { + addr |= (b as u32) << (8 * i); + } + Ipv4Addr(addr) + } +} + +impl fmt::Display for Ipv4Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let bytes = self.0; + write!( + f, + "{}.{}.{}.{}", + bytes & 0xff, + (bytes >> 8) & 0xff, + (bytes >> 16) & 0xff, + (bytes >> 24) & 0xff + ) + } +} + +// Reference: https://github.com/smoltcp-rs/smoltcp/blob/9027825c16c9c3fbadb7663e56d64b590fc95d5a/src/wire/ipv6.rs#L247-L306 +// Modified to use [u32; 4] instead of [u8; 16] +impl fmt::Display for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The string representation of an IPv6 address should + // collapse a series of 16 bit sections that evaluate + // to 0 to "::" + // + // See https://tools.ietf.org/html/rfc4291#section-2.2 + // for details. + enum State { + Head, + HeadBody, + Tail, + TailBody, + } + let mut words = [0u16; 8]; + for i in 0..4 { + words[i * 2] = ((self.addr[i] & 0xffff) as u16).swap_bytes(); + words[i * 2 + 1] = (((self.addr[i] >> 16) & 0xffff) as u16).swap_bytes(); + } + let mut state = State::Head; + for word in words.iter() { + state = match (*word, &state) { + // Once a u16 equal to zero write a double colon and + // skip to the next non-zero u16. + (0, &State::Head) | (0, &State::HeadBody) => { + write!(f, "::")?; + State::Tail + } + // Continue iterating without writing any characters until + // we hit a non-zero value. + (0, &State::Tail) => State::Tail, + // When the state is Head or Tail write a u16 in hexadecimal + // without the leading colon if the value is not 0. + (_, &State::Head) => { + write!(f, "{word:x}")?; + State::HeadBody + } + (_, &State::Tail) => { + write!(f, "{word:x}")?; + State::TailBody + } + // Write the u16 with a leading colon when parsing a value + // that isn't the first in a section + (_, &State::HeadBody) | (_, &State::TailBody) => { + write!(f, ":{word:x}")?; + state + } + } + } + Ok(()) + } +} + +/*impl Into for SocketAddr { + fn into(self) -> CoreSocketAddr { + match self.addr { + IpAddr::ipv4(addr) => { + let ipv4_addr = Ipv4Addr::new(addr); + CoreSocketAddr::V4(SocketAddrV4::new(ipv4_addr, self.port as u8)) + } + IpAddr::ipv6(addr) => { + let ipv6_addr = Ipv6Addr::new(addr); + CoreSocketAddr::V6(SocketAddrV6::new(ipv6_addr, self.port, 0, 0)) + } + } + } +}*/ + +impl From for CoreSocketAddr { + fn from(socket_addr: SocketAddr) -> Self { + match socket_addr.addr { + IpAddr::Ipv4(ipv4_addr) => { + let octets = ipv4_addr.0.to_be_bytes(); + let std_ipv4 = CoreIpv4Addr::new(octets[0], octets[1], octets[2], octets[3]); + CoreSocketAddr::V4(SocketAddrV4::new(std_ipv4, socket_addr.port)) + } + IpAddr::Ipv6(ipv6_addr) => { + let segments = [ + (ipv6_addr.addr[0] >> 16) as u16, + (ipv6_addr.addr[0] & 0xFFFF) as u16, + (ipv6_addr.addr[1] >> 16) as u16, + (ipv6_addr.addr[1] & 0xFFFF) as u16, + (ipv6_addr.addr[2] >> 16) as u16, + (ipv6_addr.addr[2] & 0xFFFF) as u16, + (ipv6_addr.addr[3] >> 16) as u16, + (ipv6_addr.addr[3] & 0xFFFF) as u16, + ]; + let std_ipv6 = CoreIpv6Addr::new( + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7], + ); + CoreSocketAddr::V6(SocketAddrV6::new( + std_ipv6, + socket_addr.port, + 0, + ipv6_addr.zone.into(), + )) + } + } + } +} + +impl From for SocketAddr { + fn from(std_socket_addr: CoreSocketAddr) -> Self { + match std_socket_addr { + CoreSocketAddr::V4(v4_addr) => { + let octets = v4_addr.ip().octets(); + let ipv4_addr = Ipv4Addr(u32::from_be_bytes(octets)); + SocketAddr { + addr: IpAddr::Ipv4(ipv4_addr), + port: v4_addr.port(), + } + } + CoreSocketAddr::V6(v6_addr) => { + let segments = v6_addr.ip().segments(); + let ipv6_addr = Ipv6Addr { + addr: [ + ((segments[0] as u32) << 16) | (segments[1] as u32), + ((segments[2] as u32) << 16) | (segments[3] as u32), + ((segments[4] as u32) << 16) | (segments[5] as u32), + ((segments[6] as u32) << 16) | (segments[7] as u32), + ], + zone: v6_addr.scope_id() as u8, + }; + SocketAddr { + addr: IpAddr::Ipv6(ipv6_addr), + port: v6_addr.port(), + } + } + } + } +} + +impl FromStr for SocketAddr { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut parts = s.split(':'); + let addr = parts.next().ok_or(())?.parse::()?; + let port = parts.next().ok_or(())?.parse::().map_err(|_| ())?; + Ok(SocketAddr { addr, port }) + } +} + +impl SocketAddr { + /// Create a new SocketAddr from an IpAddr and a port + pub fn new(addr: IpAddr, port: u16) -> SocketAddr { + SocketAddr { addr, port } + } +} + +impl From<(IpAddr, u16)> for SocketAddr { + fn from((addr, port): (IpAddr, u16)) -> SocketAddr { + SocketAddr { addr, port } + } +} + +impl From<(Ipv4Addr, u16)> for SocketAddr { + fn from((addr, port): (Ipv4Addr, u16)) -> SocketAddr { + SocketAddr { + addr: IpAddr::Ipv4(addr), + port, + } + } +} + +impl From<(Ipv6Addr, u16)> for SocketAddr { + fn from((addr, port): (Ipv6Addr, u16)) -> SocketAddr { + SocketAddr { + addr: IpAddr::Ipv6(addr), + port, + } + } +} + +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.addr, self.port) + } +} + +/// Convert a mask to a prefix length +pub fn mask_to_prefix(mask: IpAddr) -> Result { + match mask { + IpAddr::Ipv4(Ipv4Addr(mask)) => { + let mut mask = mask.swap_bytes(); + let mut prefix = 0; + while mask & (1 << 31) != 0 { + prefix += 1; + mask <<= 1; + } + if mask != 0 { + Err(Error) + } else { + Ok(prefix) + } + } + IpAddr::Ipv6(Ipv6Addr { addr, .. }) => { + let mut prefix = 0; + let mut finish = false; + for mask in &addr { + let mut mask = mask.swap_bytes(); + for _ in 0..32 { + if finish { + if mask != 0 { + return Err(Error); + } else { + break; + } + } else if mask & (1 << 31) != 0 { + prefix += 1; + } else { + finish = true; + } + mask <<= 1; + } + } + Ok(prefix) + } + } +} diff --git a/modules/axnet/src/lwip_impl/cbindings.rs b/modules/axnet/src/lwip_impl/cbindings.rs new file mode 100644 index 000000000..2323244c7 --- /dev/null +++ b/modules/axnet/src/lwip_impl/cbindings.rs @@ -0,0 +1,52 @@ +use alloc::string::String; +use axlog::ax_print; +use core::ffi::{c_int, c_uchar, c_uint}; +use core::sync::atomic::{AtomicU64, Ordering::SeqCst}; +use ruxhal::{cpu::this_cpu_id, time::current_time}; + +#[no_mangle] +unsafe extern "C" fn lwip_print(str: *const c_uchar, mut args: ...) -> c_int { + use printf_compat::{format, output}; + let mut s = String::new(); + let bytes_written = format( + str as *const cty::c_char, + args.as_va_list(), + output::fmt_write(&mut s), + ); + let now = current_time(); + let cpu_id = this_cpu_id(); + ax_print!( + "[{:>3}.{:06} {}] {}", + now.as_secs(), + now.subsec_micros(), + cpu_id, + s + ); + bytes_written +} + +#[no_mangle] +extern "C" fn lwip_abort() { + panic!("lwip_abort"); +} + +#[no_mangle] +extern "C" fn sys_now() -> c_uint { + current_time().as_millis() as c_uint +} + +static SEED: AtomicU64 = AtomicU64::new(0xa2ce_a2ce); + +/// Sets the seed for the random number generator. +#[no_mangle] +extern "C" fn lwip_srand(seed: u32) { + SEED.store(seed.wrapping_sub(1) as u64, SeqCst); +} + +/// Returns a 32-bit unsigned pseudo random interger. +#[no_mangle] +extern "C" fn lwip_rand_u32() -> u32 { + let new_seed = SEED.load(SeqCst).wrapping_mul(6364136223846793005) + 1; + SEED.store(new_seed, SeqCst); + (new_seed >> 33) as u32 +} diff --git a/modules/axnet/src/lwip_impl/dns.rs b/modules/axnet/src/lwip_impl/dns.rs new file mode 100644 index 000000000..2c8ab681d --- /dev/null +++ b/modules/axnet/src/lwip_impl/dns.rs @@ -0,0 +1,104 @@ +use super::driver::lwip_loop_once; +use crate::{IpAddr, Ipv4Addr}; +use alloc::{ffi::CString, vec, vec::Vec}; +use axerrno::{ax_err, AxResult}; +use core::{ + ffi::{c_char, c_void, CStr}, + str::FromStr, +}; +use lwip_rust::bindings::{ + dns_gethostbyname, dns_setserver, err_enum_t_ERR_ARG, err_enum_t_ERR_INPROGRESS, + err_enum_t_ERR_OK, err_enum_t_ERR_VAL, ip_addr_t, +}; +use ruxtask::yield_now; + +use super::LWIP_MUTEX; + +struct DnsQueryEntry { + ipaddr: Option, + finished: bool, +} + +extern "C" fn dns_found_callback( + name: *const c_char, + ipaddr: *const ip_addr_t, + callback_arg: *mut c_void, +) { + trace!( + "[dns_found_callback]: name_ptr={:?} ipaddr_ptr={:?}", + name, + ipaddr + ); + let res = callback_arg as *mut DnsQueryEntry; + unsafe { + (*res).finished = true; + (*res).ipaddr = if ipaddr.is_null() { + None + } else { + debug!( + "DNS found: name={} ipaddr={}", + CStr::from_ptr(name as *mut c_char).to_str().unwrap(), + IpAddr::from(*ipaddr) + ); + Some((*ipaddr).into()) + }; + } +} + +/// Public function for DNS query. +pub fn resolve_socket_addr(name: &str) -> AxResult> { + let guard = LWIP_MUTEX.lock(); + unsafe { + dns_setserver( + 0, + &IpAddr::from_str("8.8.8.8").unwrap().into() as *const ip_addr_t, + ) + }; + + let mut addr: ip_addr_t = IpAddr::Ipv4(Ipv4Addr(0)).into(); + let mut query_entry = DnsQueryEntry { + ipaddr: None, + finished: false, + }; + let name = CString::new(name).unwrap(); + let res = unsafe { + dns_gethostbyname( + name.as_ptr(), + &mut addr as *mut ip_addr_t, + Some(dns_found_callback), + &mut query_entry as *mut DnsQueryEntry as *mut c_void, + ) as i32 + }; + drop(guard); + + #[allow(non_upper_case_globals)] + match res { + err_enum_t_ERR_OK => Ok(vec![addr.into()]), + err_enum_t_ERR_INPROGRESS => loop { + lwip_loop_once(); + if query_entry.finished { + break if query_entry.ipaddr.is_some() { + Ok(vec![query_entry.ipaddr.unwrap()]) + } else { + ax_err!(NotFound, "LWIP dns not found") + }; + } + yield_now(); + }, + err_enum_t_ERR_ARG => ax_err!( + InvalidInput, + "LWIP dns client not initialized or invalid hostname" + ), + err_enum_t_ERR_VAL => ax_err!( + InvalidInput, + "LWIP dns client error, perhaps dns server not configured" + ), + _ => ax_err!(InvalidInput, "LWIP dns client error"), + } +} + +/// Public function for DNS query. +pub fn dns_query(name: &str) -> AxResult> { + let empty_vec = alloc::vec::Vec::new(); + Ok(empty_vec) +} diff --git a/modules/axnet/src/lwip_impl/driver.rs b/modules/axnet/src/lwip_impl/driver.rs new file mode 100644 index 000000000..5a1381335 --- /dev/null +++ b/modules/axnet/src/lwip_impl/driver.rs @@ -0,0 +1,271 @@ +use super::LWIP_MUTEX; +use crate::{ + net_impl::addr::{mask_to_prefix, MacAddr}, + IpAddr, +}; +use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; +#[cfg(feature = "irq")] +use axdriver::register_interrupt_handler; +use axsync::Mutex; +use core::{cell::RefCell, ffi::c_void}; +use driver_net::{DevError, NetBuf, NetBufBox, NetBufPool, NetBufPtr}; +use lazy_init::LazyInit; +use lwip_rust::bindings::{ + err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_t, etharp_output, ethernet_input, ethip6_output, + ip4_addr_t, lwip_htonl, lwip_init, netif, netif_add, netif_create_ip6_linklocal_address, + netif_set_default, netif_set_link_up, netif_set_up, pbuf, pbuf_free, rx_custom_pbuf_alloc, + rx_custom_pbuf_free, rx_custom_pbuf_init, rx_custom_pbuf_t, sys_check_timeouts, + NETIF_FLAG_BROADCAST, NETIF_FLAG_ETHARP, NETIF_FLAG_ETHERNET, +}; +use ruxdriver::prelude::*; + +const RX_BUF_QUEUE_SIZE: usize = 64; + +const NET_BUF_LEN: usize = 1526; +const NET_BUF_POOL_SIZE: usize = 128; + +//static NET_BUF_POOL: LazyInit> = LazyInit::new(); + +struct NetifWrapper(netif); +unsafe impl Send for NetifWrapper {} + +struct DeviceWrapper { + inner: RefCell, // use `RefCell` is enough since it's wrapped in `Mutex` in `InterfaceWrapper`. + rx_buf_queue: VecDeque, +} + +impl DeviceWrapper { + fn new(inner: AxNetDevice) -> Self { + Self { + inner: RefCell::new(inner), + rx_buf_queue: VecDeque::with_capacity(RX_BUF_QUEUE_SIZE), + } + } + + fn poll(&mut self) { + while self.rx_buf_queue.len() < RX_BUF_QUEUE_SIZE { + match self.inner.borrow_mut().receive() { + Ok(bufptr) => unsafe { + self.rx_buf_queue.push_back(NetBuf::from_buf_ptr(bufptr)); + }, + Err(DevError::Again) => break, // TODO: better method to avoid error type conversion + Err(err) => { + warn!("receive failed: {:?}", err); + break; + } + } + } + } + + fn receive(&mut self) -> Option { + self.rx_buf_queue.pop_front() + } + + #[cfg(feature = "irq")] + fn ack_interrupt(&mut self) -> bool { + unsafe { self.inner.as_ptr().as_mut().unwrap().ack_interrupt() } + } +} + +struct InterfaceWrapper { + name: &'static str, + dev: Arc>, + netif: Mutex, +} + +impl InterfaceWrapper { + pub fn name(&self) -> &str { + self.name + } + + pub fn poll(&self) { + self.dev.lock().poll(); + loop { + let buf_receive = self.dev.lock().receive(); + if let Some(buf) = buf_receive { + trace!("RECV {} bytes: {:02X?}", buf.packet().len(), buf.packet()); + + let length = buf.packet().len(); + let payload_mem = buf.packet().as_ptr() as *mut _; + let payload_mem_len = buf.capacity() as u16; + let p = unsafe { + rx_custom_pbuf_alloc( + Some(pbuf_free_custom), + Box::into_raw(buf) as *mut _, + Arc::into_raw(self.dev.clone()) as *mut _, + length as u16, + payload_mem, + payload_mem_len, + ) + }; + + debug!("ethernet_input"); + let mut netif = self.netif.lock(); + unsafe { + let res = netif.0.input.unwrap()(p, &mut netif.0); + if (res as i32) != err_enum_t_ERR_OK { + warn!("ethernet_input failed: {:?}", res); + pbuf_free(p); + } + } + } else { + break; + } + } + } + + #[cfg(feature = "irq")] + pub fn ack_interrupt(&self) { + unsafe { &mut *self.dev.as_mut_ptr() }.ack_interrupt(); + } +} + +extern "C" fn pbuf_free_custom(p: *mut pbuf) { + debug!("pbuf_free_custom: {:x?}", p); + let p = p as *mut rx_custom_pbuf_t; + let buf = unsafe { Box::from_raw((*p).buf as *mut NetBuf) }; + let dev = unsafe { Arc::from_raw((*p).dev as *const Mutex) }; + match dev + .lock() + .inner + .borrow_mut() + .recycle_rx_buffer(NetBuf::into_buf_ptr(buf)) + { + Ok(_) => (), + Err(err) => { + warn!("recycle_rx_buffer failed: {:?}", err); + } + }; + unsafe { + rx_custom_pbuf_free(p); + }; +} + +extern "C" fn ethif_init(netif: *mut netif) -> err_t { + debug!("ethif_init"); + unsafe { + (*netif).name[0] = 'e' as i8; + (*netif).name[1] = 'n' as i8; + (*netif).num = 0; + + (*netif).output = Some(etharp_output); + (*netif).output_ip6 = Some(ethip6_output); + (*netif).linkoutput = Some(ethif_output); + + (*netif).mtu = 1500; + (*netif).flags = 0; + (*netif).flags = (NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET) as u8; + } + err_enum_t_ERR_OK as err_t +} + +extern "C" fn ethif_output(netif: *mut netif, p: *mut pbuf) -> err_t { + debug!("ethif_output"); + let ethif = unsafe { &mut *((*netif).state as *mut _ as *mut InterfaceWrapper) }; + let dev_wrapper = ethif.dev.lock(); + let mut dev = dev_wrapper.inner.borrow_mut(); + + if dev.can_transmit() { + unsafe { + let tot_len = unsafe { (*p).tot_len }; + //let mut tx_buf = NET_BUF_POOL.alloc().unwrap(); + let mut tx_buf = *NetBuf::from_buf_ptr(dev.alloc_tx_buffer(tot_len.into()).unwrap()); + dev.prepare_tx_buffer(&mut tx_buf, tot_len.into()).unwrap(); + + // Copy pbuf chain to tx_buf + let mut offset = 0; + let mut q = p; + while !q.is_null() { + let len = unsafe { (*q).len } as usize; + let payload = unsafe { (*q).payload }; + let payload = unsafe { core::slice::from_raw_parts(payload as *const u8, len) }; + tx_buf.packet_mut()[offset..offset + len].copy_from_slice(payload); + offset += len; + q = unsafe { (*q).next }; + } + + trace!( + "SEND {} bytes: {:02X?}", + tx_buf.packet().len(), + tx_buf.packet() + ); + dev.transmit(NetBuf::into_buf_ptr(Box::new(tx_buf))) + .unwrap(); + err_enum_t_ERR_OK as err_t + } + } else { + error!("[ethif_output] dev can't transmit"); + err_enum_t_ERR_MEM as err_t + } +} + +static ETH0: LazyInit = LazyInit::new(); + +fn ip4_addr_gen(a: u8, b: u8, c: u8, d: u8) -> ip4_addr_t { + ip4_addr_t { + addr: unsafe { + lwip_htonl(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32)) + }, + } +} + +pub fn init(mut net_dev: AxNetDevice) { + LWIP_MUTEX.init_by(Mutex::new(0)); + let _guard = LWIP_MUTEX.lock(); + + let ipaddr: ip4_addr_t = ip4_addr_gen(10, 0, 2, 15); // QEMU user networking default IP + let netmask: ip4_addr_t = ip4_addr_gen(255, 255, 255, 0); + let gw: ip4_addr_t = ip4_addr_gen(10, 0, 2, 2); // QEMU user networking gateway + + let dev = net_dev; + let mut netif: netif = unsafe { core::mem::zeroed() }; + netif.hwaddr_len = 6; + netif.hwaddr = dev.mac_address().0; + + ETH0.init_by(InterfaceWrapper { + name: "eth0", + dev: Arc::new(Mutex::new(DeviceWrapper::new(dev))), + netif: Mutex::new(NetifWrapper(netif)), + }); + + unsafe { + lwip_init(); + rx_custom_pbuf_init(); + netif_add( + &mut ETH0.netif.lock().0, + &ipaddr, + &netmask, + &gw, + Ð0 as *const _ as *mut c_void, + Some(ethif_init), + Some(ethernet_input), + ); + netif_create_ip6_linklocal_address(&mut ETH0.netif.lock().0, 1); + netif_set_link_up(&mut ETH0.netif.lock().0); + netif_set_up(&mut ETH0.netif.lock().0); + netif_set_default(&mut ETH0.netif.lock().0); + } + + info!("created net interface {:?}:", ETH0.name()); + info!( + " ether: {}", + MacAddr::from_bytes(Ð0.netif.lock().0.hwaddr) + ); + let ip = IpAddr::from(ETH0.netif.lock().0.ip_addr); + let mask = mask_to_prefix(IpAddr::from(ETH0.netif.lock().0.netmask)).unwrap(); + info!(" ip: {}/{}", ip, mask); + info!(" gateway: {}", IpAddr::from(ETH0.netif.lock().0.gw)); + info!( + " ip6: {}", + IpAddr::from(ETH0.netif.lock().0.ip6_addr[0]) + ); +} + +pub fn lwip_loop_once() { + let guard = LWIP_MUTEX.lock(); + unsafe { + ETH0.poll(); + sys_check_timeouts(); + } + drop(guard); +} diff --git a/modules/axnet/src/lwip_impl/mod.rs b/modules/axnet/src/lwip_impl/mod.rs new file mode 100644 index 000000000..feb1bf275 --- /dev/null +++ b/modules/axnet/src/lwip_impl/mod.rs @@ -0,0 +1,32 @@ +mod addr; +mod cbindings; +mod dns; +mod driver; +mod tcp; +mod udp; + +pub use self::addr::{IpAddr, Ipv4Addr, SocketAddr}; +pub use self::dns::{dns_query, resolve_socket_addr}; +pub use self::driver::init; +pub use self::tcp::TcpSocket; +pub use self::udp::UdpSocket; + +use axsync::Mutex; +use lazy_init::LazyInit; + +static LWIP_MUTEX: LazyInit> = LazyInit::new(); + +const RECV_QUEUE_LEN: usize = 16; +const ACCEPT_QUEUE_LEN: usize = 16; + +/// Benchmark raw socket receive bandwidth. +pub fn bench_receive() {} + +/// Benchmark raw socket transmit bandwidth. +pub fn bench_transmit() {} + +/// Poll the network stack. +/// +/// It may receive packets from the NIC and process them, and transmit queued +/// packets to the NIC. +pub fn poll_interfaces() {} diff --git a/modules/axnet/src/lwip_impl/tcp.rs b/modules/axnet/src/lwip_impl/tcp.rs new file mode 100644 index 000000000..cdba54bcf --- /dev/null +++ b/modules/axnet/src/lwip_impl/tcp.rs @@ -0,0 +1,502 @@ +use crate::{ + net_impl::{driver::lwip_loop_once, ACCEPT_QUEUE_LEN, RECV_QUEUE_LEN}, + IpAddr, SocketAddr, +}; +use alloc::{boxed::Box, collections::VecDeque}; +use axerrno::{ax_err, AxError, AxResult}; +use axio::PollState; +use axsync::Mutex; +use core::cell::UnsafeCell; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::{ffi::c_void, pin::Pin, ptr::null_mut}; +use lwip_rust::bindings::{ + err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_enum_t_ERR_USE, err_enum_t_ERR_VAL, err_t, + ip_addr_t, pbuf, pbuf_free, tcp_accept, tcp_arg, tcp_bind, tcp_close, tcp_connect, + tcp_listen_with_backlog, tcp_new, tcp_output, tcp_pcb, tcp_recv, tcp_recved, tcp_state_CLOSED, + tcp_state_LISTEN, tcp_write, TCP_DEFAULT_LISTEN_BACKLOG, TCP_MSS, +}; +use ruxtask::yield_now; + +use super::LWIP_MUTEX; + +struct TcpPcbPointer(Mutex<*mut tcp_pcb>); + +unsafe impl Sync for TcpPcbPointer {} + +unsafe impl Send for TcpPcbPointer {} +impl TcpPcbPointer { + unsafe fn get(&self) -> *mut tcp_pcb { + *self.0.lock() + } + + unsafe fn set(&self, new_pcb: *mut tcp_pcb) { + *self.0.lock() = new_pcb; + } +} +struct PbuffPointer(*mut pbuf); +unsafe impl Send for PbuffPointer {} + +struct TcpSocketInner { + nonblock: AtomicBool, + remote_closed: bool, + connect_result: UnsafeCell, + // (pcb, offset) + recv_queue: Mutex>, + accept_queue: Mutex>, +} + +/// A TCP socket that provides POSIX-like APIs. +pub struct TcpSocket { + pcb: TcpPcbPointer, + inner: Pin>, +} + +extern "C" fn connect_callback(arg: *mut c_void, _tpcb: *mut tcp_pcb, err: err_t) -> err_t { + debug!("[TcpSocket] connect_callback: {:#?}", err); + let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; + socket_inner.connect_result = err.into(); + err +} + +extern "C" fn recv_callback( + arg: *mut c_void, + _tpcb: *mut tcp_pcb, + p: *mut pbuf, + err: err_t, +) -> err_t { + debug!("[TcpSocket] recv_callback: {:#?}", err); + if err != 0 { + error!("[TcpSocket][recv_callback] err: {:#?}", err); + return err; + } + let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; + if p.is_null() { + debug!("[TcpSocket][recv_callback] p is null, remote close"); + socket_inner.remote_closed = true; + } else { + debug!( + "[TcpSocket][recv_callback] p is not null, len: {}, tot_len: {}", + unsafe { (*p).len }, + unsafe { (*p).tot_len } + ); + socket_inner + .recv_queue + .lock() + .push_back((PbuffPointer(p), 0)); + debug!( + "[TcpSocket][recv_callback] recv_queue len: {}", + socket_inner.recv_queue.lock().len() + ); + } + 0 +} + +extern "C" fn accept_callback(arg: *mut c_void, newpcb: *mut tcp_pcb, err: err_t) -> err_t { + debug!("[TcpSocket] accept_callback: {:#?}", err); + if err != 0 { + debug!("[TcpSocket][accept_callback] err: {:#?}", err); + return err; + } + let socket_inner = unsafe { &mut *(arg as *mut TcpSocketInner) }; + let mut socket = TcpSocket { + pcb: TcpPcbPointer(Mutex::new(newpcb)), + inner: Box::pin(TcpSocketInner { + nonblock: AtomicBool::new(false), + remote_closed: false, + connect_result: 0.into(), + recv_queue: Mutex::new(VecDeque::with_capacity(RECV_QUEUE_LEN)), + accept_queue: Mutex::new(VecDeque::new()), + }), + }; + unsafe { + tcp_arg( + socket.pcb.get(), + socket.inner.as_mut().get_mut() as *mut _ as *mut c_void, + ); + tcp_recv(socket.pcb.get(), Some(recv_callback)); + } + socket_inner.accept_queue.lock().push_back(socket); + debug!( + "[TcpSocket][accept_callback] accept_queue len: {}", + socket_inner.accept_queue.lock().len() + ); + 0 +} + +impl TcpSocket { + /// Creates a new TCP socket. + pub fn new() -> Self { + debug!("[TcpSocket] new"); + let guard = LWIP_MUTEX.lock(); + let mut socket = Self { + pcb: TcpPcbPointer(Mutex::new(unsafe { tcp_new() })), + inner: Box::pin(TcpSocketInner { + nonblock: AtomicBool::new(false), + remote_closed: false, + connect_result: 0.into(), + recv_queue: Mutex::new(VecDeque::new()), + accept_queue: Mutex::new(VecDeque::with_capacity(ACCEPT_QUEUE_LEN)), + }), + }; + unsafe { + tcp_arg( + socket.pcb.get(), + socket.inner.as_mut().get_mut() as *mut _ as *mut c_void, + ); + } + drop(guard); + socket + } + + /// Returns the local address and port, or + /// [`Err(NotConnected)`](AxError::NotConnected) if not connected. + pub fn local_addr(&self) -> AxResult { + if unsafe { self.pcb.get().is_null() } { + Err(AxError::NotConnected) + } else { + let guard = LWIP_MUTEX.lock(); + let addr = unsafe { (*self.pcb.get()).local_ip }; + let port = unsafe { (*self.pcb.get()).local_port }; + drop(guard); + trace!( + "[TcpSocket] local_addr: {:#?}:{:#?}", + IpAddr::from(addr), + port + ); + Ok(SocketAddr { + addr: addr.into(), + port, + } + .into()) + } + } + + /// Returns the remote address and port, or + /// [`Err(NotConnected)`](AxError::NotConnected) if not connected. + pub fn peer_addr(&self) -> AxResult { + if unsafe { self.pcb.get().is_null() } { + Err(AxError::NotConnected) + } else { + let guard = LWIP_MUTEX.lock(); + let addr = unsafe { (*self.pcb.get()).remote_ip }; + let port = unsafe { (*self.pcb.get()).remote_port }; + drop(guard); + trace!( + "[TcpSocket] peer_addr: {:#?}:{:#?}", + IpAddr::from(addr), + port + ); + Ok(SocketAddr { + addr: addr.into(), + port, + } + .into()) + } + } + + /// Returns whether this socket is in nonblocking mode. + #[inline] + pub fn is_nonblocking(&self) -> bool { + self.inner.nonblock.load(Ordering::Acquire) + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// This will result in `read`, `write`, `recv` and `send` operations + /// becoming nonblocking, i.e., immediately returning from their calls. + /// If the IO operation is successful, `Ok` is returned and no further + /// action is required. If the IO operation could not be completed and needs + /// to be retried, an error with kind [`Err(WouldBlock)`](AxError::WouldBlock) is + /// returned. + pub fn set_nonblocking(&self, nonblocking: bool) { + self.inner.nonblock.store(nonblocking, Ordering::Release); + } + + /// Connects to the given address and port. + /// + /// The local port is generated automatically. + pub fn connect(&self, caddr: core::net::SocketAddr) -> AxResult { + let addr = SocketAddr::from(caddr); + debug!("[TcpSocket] connect to {:#?}", addr); + let ip_addr: ip_addr_t = addr.addr.into(); + unsafe { + self.inner.connect_result.get().write(1); + } + + // lock lwip + let guard = LWIP_MUTEX.lock(); + unsafe { + debug!("[TcpSocket] set recv_callback"); + tcp_recv(self.pcb.get(), Some(recv_callback)); + + debug!("[TcpSocket] tcp_connect"); + #[allow(non_upper_case_globals)] + match tcp_connect(self.pcb.get(), &ip_addr, addr.port, Some(connect_callback)) as i32 { + err_enum_t_ERR_OK => {} + err_enum_t_ERR_VAL => { + return ax_err!(InvalidInput, "LWIP [tcp_connect] Invalid input."); + } + _ => { + return ax_err!(Unsupported, "LWIP [tcp_connect] Failed."); + } + }; + } + drop(guard); + + // wait for connect + debug!("[TcpSocket] wait for connect"); + lwip_loop_once(); + #[allow(clippy::while_immutable_condition)] + while unsafe { self.inner.connect_result.get().read() == 1 } { + yield_now(); + lwip_loop_once(); + } + debug!("[TcpSocket] connect result: {}", unsafe { + self.inner.connect_result.get().read() + }); + + if unsafe { self.inner.connect_result.get().read() == 0 } { + Ok(()) + } else { + ax_err!(Unsupported, "LWIP [connect_result] Unsupported") + } + } + + /// Binds an unbound socket to the given address and port. + /// + /// If the given port is 0, it generates one automatically. + /// + /// It's must be called before [`listen`](Self::listen) and + /// [`accept`](Self::accept). + pub fn bind(&self, caddr: core::net::SocketAddr) -> AxResult { + let addr = SocketAddr::from(caddr); + debug!("[TcpSocket] bind to {:#?}", addr); + let guard = LWIP_MUTEX.lock(); + unsafe { + #[allow(non_upper_case_globals)] + match tcp_bind(self.pcb.get(), &addr.addr.into(), addr.port) as i32 { + err_enum_t_ERR_OK => {} + err_enum_t_ERR_USE => { + return ax_err!(AddrInUse, "LWIP [tcp_bind] Port already in use."); + } + err_enum_t_ERR_VAL => { + return ax_err!( + InvalidInput, + "LWIP [tcp_bind] The PCB is not in a valid state." + ); + } + _ => { + return ax_err!(Unsupported, "LWIP [tcp_bind] Failed."); + } + }; + } + drop(guard); + Ok(()) + } + + /// Starts listening on the bound address and port. + /// + /// It's must be called after [`bind`](Self::bind) and before + /// [`accept`](Self::accept). + pub fn listen(&mut self) -> AxResult { + debug!("[TcpSocket] listen"); + let guard = LWIP_MUTEX.lock(); + unsafe { + if (*self.pcb.get()).state == tcp_state_CLOSED { + let listen_res = + tcp_listen_with_backlog(self.pcb.get(), TCP_DEFAULT_LISTEN_BACKLOG as u8); + self.pcb.set(listen_res); + tcp_arg( + self.pcb.get(), + self.inner.as_mut().get_mut() as *mut _ as *mut c_void, + ); + tcp_accept(self.pcb.get(), Some(accept_callback)); + } + } + drop(guard); + // TODO: check if listen failed + Ok(()) + } + + /// Accepts a new connection. + /// + /// This function will block the calling thread until a new TCP connection + /// is established. When established, a new [`TcpSocket`] is returned. + /// + /// It's must be called after [`bind`](Self::bind) and [`listen`](Self::listen). + pub fn accept(&self) -> AxResult { + debug!("[TcpSocket] accept"); + loop { + lwip_loop_once(); + let mut accept_queue = self.inner.accept_queue.lock(); + if accept_queue.len() != 0 { + return Ok(accept_queue.pop_front().unwrap()); + } + drop(accept_queue); + if self.is_nonblocking() { + return Err(AxError::WouldBlock); + } else { + yield_now(); + } + } + } + + /// Close the connection. + pub fn shutdown(&self) -> AxResult { + if unsafe { !self.pcb.get().is_null() } { + info!("tcp shutdown"); + unsafe { + let _guard = LWIP_MUTEX.lock(); + tcp_arg(self.pcb.get(), null_mut()); + if (*self.pcb.get()).state == tcp_state_LISTEN { + tcp_accept(self.pcb.get(), None); + } else { + tcp_recv(self.pcb.get(), None); + } + + warn!("[TcpSocket] tcp_close"); + #[allow(non_upper_case_globals)] + match tcp_close(self.pcb.get()) as i32 { + err_enum_t_ERR_OK => {} + e => { + error!("LWIP tcp_close failed: {}", e); + return ax_err!(Unsupported, "LWIP [tcp_close] failed"); + } + } + } + unsafe { + self.pcb.set(null_mut()); + } + lwip_loop_once(); + Ok(()) + } else { + Err(AxError::NotConnected) + } + } + + /// Receives data from the socket, stores it in the given buffer. + pub fn recv(&self, buf: &mut [u8], flags: i32) -> AxResult { + trace!("[TcpSocket] recv"); + loop { + if self.inner.remote_closed { + return Ok(0); + } + lwip_loop_once(); + let mut recv_queue = self.inner.recv_queue.lock(); + let res = if recv_queue.len() == 0 { + Ok(0) + } else { + let (p, offset) = recv_queue.pop_front().unwrap(); + let p = p.0; + let len = unsafe { (*p).len as usize }; + let tot_len = unsafe { (*p).tot_len as usize }; + if len != tot_len { + // TODO: pbuf chain + error!("[TcpSocket] recv pbuf len != tot_len"); + return ax_err!(Unsupported, "LWIP [recv] pbuf len != tot_len"); + } + let payload = unsafe { (*p).payload }; + let payload = unsafe { core::slice::from_raw_parts_mut(payload as *mut u8, len) }; + + let copy_len = core::cmp::min(len - offset, buf.len()); + buf[0..copy_len].copy_from_slice(&payload[offset..offset + copy_len]); + if offset + copy_len < len { + recv_queue.push_front((PbuffPointer(p), offset + copy_len)); + } else { + let guard = LWIP_MUTEX.lock(); + unsafe { + pbuf_free(p); + tcp_recved(self.pcb.get(), len as u16); + } + drop(guard); + } + + Ok(copy_len) + }; + drop(recv_queue); + match res { + Ok(0) => { + if self.is_nonblocking() { + return Err(AxError::WouldBlock); + } else { + yield_now(); + } + } + Ok(len) => { + trace!("[TcpSocket] recv done (len: {}): {:?}", len, &buf[0..len]); + return Ok(len); + } + Err(e) => { + return Err(e); + } + }; + } + } + + /// Transmits data in the given buffer. + pub fn send(&self, buf: &[u8]) -> AxResult { + trace!("[TcpSocket] send (len = {})", buf.len()); + let copy_len = core::cmp::min(buf.len(), TCP_MSS as usize); + unsafe { + let _guard = LWIP_MUTEX.lock(); + trace!("[TcpSocket] tcp_write"); + #[allow(non_upper_case_globals)] + match tcp_write(self.pcb.get(), buf.as_ptr() as *const _, copy_len as u16, 0) as i32 { + err_enum_t_ERR_OK => {} + err_enum_t_ERR_MEM => { + return ax_err!(NoMemory, "LWIP [tcp_write] Out of memory."); + } + _ => { + return ax_err!(Unsupported, "LWIP [tcp_write] Failed."); + } + } + trace!("[TcpSocket] tcp_output"); + #[allow(non_upper_case_globals)] + match tcp_output(self.pcb.get()) as i32 { + err_enum_t_ERR_OK => {} + _ => { + return ax_err!(Unsupported, "LWIP [tcp_output] Failed."); + } + } + }; + lwip_loop_once(); + trace!("[TcpSocket] send done (len: {})", copy_len); + Ok(copy_len) + } + + /// Detect whether the socket needs to receive/can send. + /// + /// Return is + pub fn poll(&self) -> AxResult { + trace!("poll pcbstate: {:?}", unsafe { (*self.pcb.get()).state }); + lwip_loop_once(); + if unsafe { (*self.pcb.get()).state } == tcp_state_LISTEN { + let test = self.inner.accept_queue.lock().len(); + // listener + Ok(PollState { + readable: self.inner.accept_queue.lock().len() != 0, + writable: false, + }) + } else { + let test = self.inner.recv_queue.lock().len(); + // stream + Ok(PollState { + readable: self.inner.recv_queue.lock().len() != 0, + writable: true, + }) + } + } +} + +impl Drop for TcpSocket { + fn drop(&mut self) { + debug!("[TcpSocket] drop"); + self.shutdown().unwrap(); + } +} + +impl Default for TcpSocket { + fn default() -> Self { + Self::new() + } +} diff --git a/modules/axnet/src/lwip_impl/udp.rs b/modules/axnet/src/lwip_impl/udp.rs new file mode 100644 index 000000000..d682b903d --- /dev/null +++ b/modules/axnet/src/lwip_impl/udp.rs @@ -0,0 +1,358 @@ +use crate::{ + net_impl::{driver::lwip_loop_once, RECV_QUEUE_LEN}, + IpAddr, SocketAddr, +}; +use alloc::{boxed::Box, collections::VecDeque}; +use axerrno::{ax_err, AxError, AxResult}; +use axio::PollState; +use axsync::Mutex; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::{ffi::c_void, pin::Pin, ptr::null_mut}; +use lwip_rust::bindings::{ + err_enum_t_ERR_MEM, err_enum_t_ERR_OK, err_enum_t_ERR_RTE, err_enum_t_ERR_USE, + err_enum_t_ERR_VAL, ip_addr_t, pbuf, pbuf_alloc, pbuf_free, pbuf_layer_PBUF_TRANSPORT, + pbuf_type_PBUF_RAM, u16_t, udp_bind, udp_connect, udp_new, udp_pcb, udp_recv, udp_remove, + udp_sendto, +}; +use ruxtask::yield_now; + +use super::LWIP_MUTEX; + +struct UdpPcbPointer(*mut udp_pcb); +unsafe impl Send for UdpPcbPointer {} +struct PbuffPointer(*mut pbuf); +unsafe impl Send for PbuffPointer {} + +struct UdpSocketInner { + nonblock: AtomicBool, + // (pbuf, offser, addr) + recv_queue: Mutex>, +} + +/// A UDP socket that provides POSIX-like APIs. +pub struct UdpSocket { + pcb: UdpPcbPointer, + inner: Pin>, +} + +extern "C" fn udp_recv_callback( + arg: *mut ::core::ffi::c_void, + _pcb: *mut udp_pcb, + p: *mut pbuf, + addr: *const ip_addr_t, + port: u16_t, +) { + let socket_inner = unsafe { &mut *(arg as *mut UdpSocketInner) }; + if p.is_null() { + error!("[UdpSocket][udp_recv_callback] p is null"); + } else { + debug!( + "[UdpSocket][udp_recv_callback] p is not null, len: {}, tot_len: {}", + unsafe { (*p).len }, + unsafe { (*p).tot_len } + ); + socket_inner.recv_queue.lock().push_back(( + PbuffPointer(p), + 0, + SocketAddr::new(unsafe { *addr }.into(), port).into(), + )); + } +} + +impl UdpSocket { + /// Creates a new UDP socket. + pub fn new() -> Self { + debug!("[UdpSocket] new"); + let _guard = LWIP_MUTEX.lock(); + let mut socket = Self { + pcb: UdpPcbPointer(unsafe { udp_new() }), + inner: Box::pin(UdpSocketInner { + nonblock: AtomicBool::new(false), + recv_queue: Mutex::new(VecDeque::with_capacity(RECV_QUEUE_LEN)), + }), + }; + unsafe { + udp_recv( + socket.pcb.0, + Some(udp_recv_callback), + socket.inner.as_mut().get_mut() as *mut _ as *mut c_void, + ); + } + socket + } + + /// Returns the local address and port, or + /// [`Err(NotConnected)`](AxError::NotConnected) if not connected. + pub fn local_addr(&self) -> AxResult { + if self.pcb.0.is_null() { + Err(AxError::NotConnected) + } else { + let _guard = LWIP_MUTEX.lock(); + let addr = unsafe { (*self.pcb.0).local_ip }; + let port = unsafe { (*self.pcb.0).local_port }; + trace!( + "[UdpSocket] local_addr: {:#?}:{:#?}", + IpAddr::from(addr), + port + ); + Ok(SocketAddr { + addr: addr.into(), + port, + } + .into()) + } + } + + /// Returns the remote address and port, or + /// [`Err(NotConnected)`](AxError::NotConnected) if not connected. + pub fn peer_addr(&self) -> AxResult { + if self.pcb.0.is_null() { + Err(AxError::NotConnected) + } else { + let _guard = LWIP_MUTEX.lock(); + let addr = unsafe { (*self.pcb.0).remote_ip }; + let port = unsafe { (*self.pcb.0).remote_port }; + trace!( + "[UdpSocket] peer_addr: {:#?}:{:#?}", + IpAddr::from(addr), + port + ); + Ok(SocketAddr { + addr: addr.into(), + port, + } + .into()) + } + } + + /// Returns whether this socket is in nonblocking mode. + #[inline] + pub fn is_nonblocking(&self) -> bool { + self.inner.nonblock.load(Ordering::Acquire) + } + + /// Moves this UDP socket into or out of nonblocking mode. + /// + /// This will result in `recv`, `recv_from`, `send`, and `send_to` + /// operations becoming nonblocking, i.e., immediately returning from their + /// calls. If the IO operation is successful, `Ok` is returned and no + /// further action is required. If the IO operation could not be completed + /// and needs to be retried, an error with kind + /// [`Err(WouldBlock)`](AxError::WouldBlock) is returned. + #[inline] + pub fn set_nonblocking(&self, nonblocking: bool) { + self.inner.nonblock.store(nonblocking, Ordering::Release); + } + + /// Binds an unbound socket to the given address and port. + /// + /// It's must be called before [`send_to`](Self::send_to) and + /// [`recv_from`](Self::recv_from). + pub fn bind(&self, caddr: core::net::SocketAddr) -> AxResult { + let addr = SocketAddr::from(caddr); + debug!("[UdpSocket] bind to {:#?}", addr); + let mut addr = addr; + if addr.port == 0 { + addr.port = get_ephemeral_port()?; + } + let _guard = LWIP_MUTEX.lock(); + unsafe { + #[allow(non_upper_case_globals)] + match udp_bind(self.pcb.0, &addr.addr.into(), addr.port) as i32 { + err_enum_t_ERR_OK => Ok(()), + err_enum_t_ERR_USE => { + ax_err!(AlreadyExists, "LWIP [udp_bind] Port already in use.") + } + _ => ax_err!(InvalidInput, "LWIP [udp_bind] Failed."), + } + } + } + + /// Transmits data in the given buffer to the given address. + pub fn send_to(&self, buf: &[u8], caddr: core::net::SocketAddr) -> AxResult { + let addr = SocketAddr::from(caddr); + trace!("[UdpSocket] send (len = {})", buf.len()); + let copy_len = core::cmp::min(buf.len(), 1472); + unsafe { + let _guard = LWIP_MUTEX.lock(); + let p = pbuf_alloc( + pbuf_layer_PBUF_TRANSPORT, + copy_len as u16, + pbuf_type_PBUF_RAM, + ); + if p.is_null() { + return ax_err!(NoMemory, "LWIP Out of memory."); + } + let payload = (*p).payload; + let payload = core::slice::from_raw_parts_mut(payload as *mut u8, copy_len); + payload.copy_from_slice(buf); + (*p).len = copy_len as u16; + (*p).tot_len = copy_len as u16; + + trace!("[UdpSocket] udp_sendto"); + + #[allow(non_upper_case_globals)] + match udp_sendto(self.pcb.0, p, &addr.addr.into(), addr.port) as i32 { + err_enum_t_ERR_OK => {} + err_enum_t_ERR_MEM => return ax_err!(NoMemory, "LWIP Out of memory."), + err_enum_t_ERR_RTE => { + return ax_err!( + BadState, + "LWIP Could not find route to destination address." + ) + } + err_enum_t_ERR_VAL => { + return ax_err!(InvalidInput, "LWIP No PCB or PCB is dual-stack.") + } + _ => return ax_err!(InvalidInput, "LWIP Invalid input."), + } + } + lwip_loop_once(); + Ok(copy_len) + } + + /// Receives data from the socket, stores it in the given buffer. + pub fn recv_from(&self, buf: &mut [u8]) -> AxResult<(usize, core::net::SocketAddr)> { + trace!("[UdpSocket] recvfrom"); + loop { + lwip_loop_once(); + let mut recv_queue = self.inner.recv_queue.lock(); + let res: Result<(usize, SocketAddr), AxError> = if recv_queue.len() == 0 { + Err(AxError::WouldBlock) + } else { + let (p, offset, caddr) = recv_queue.pop_front().unwrap(); + let addr = SocketAddr::from(caddr); + let p: *mut pbuf = p.0; + let len = unsafe { (*p).len as usize }; + let tot_len = unsafe { (*p).tot_len as usize }; + if len != tot_len { + // TODO: pbuf chain + error!("[TcpSocket] recv pbuf len != tot_len"); + return ax_err!(Unsupported, "LWIP [recv] pbuf len != tot_len"); + } + let payload = unsafe { (*p).payload }; + let payload = unsafe { core::slice::from_raw_parts_mut(payload as *mut u8, len) }; + + let copy_len = core::cmp::min(len - offset, buf.len()); + buf[0..copy_len].copy_from_slice(&payload[offset..offset + copy_len]); + if offset + copy_len < len { + recv_queue.push_front((PbuffPointer(p), offset + copy_len, addr.into())); + } else { + let guard = LWIP_MUTEX.lock(); + unsafe { + pbuf_free(p); + } + drop(guard); + } + + Ok((copy_len, addr.into())) + }; + drop(recv_queue); + match res { + Ok((len, addr)) => { + trace!("[UdpSocket] recv done (len: {}): {:?}", len, &buf[0..len]); + return Ok((len, addr.into())); + } + Err(AxError::WouldBlock) => { + if self.is_nonblocking() { + return Err(AxError::WouldBlock); + } else { + yield_now(); + } + } + Err(e) => { + return Err(e); + } + }; + } + } + + /// Connects to the given address and port. + /// + /// The local port will be generated automatically if the socket is not bound. + /// It's must be called before [`send`](Self::send) and + /// [`recv`](Self::recv). + pub fn connect(&self, caddr: core::net::SocketAddr) -> AxResult { + let addr = SocketAddr::from(caddr); + debug!("[UdpSocket] connect to {:#?}", addr); + let ip_addr: ip_addr_t = addr.addr.into(); + let _guard = LWIP_MUTEX.lock(); + unsafe { + #[allow(non_upper_case_globals)] + match udp_connect(self.pcb.0, &ip_addr, addr.port) as i32 { + err_enum_t_ERR_OK => Ok(()), + _ => ax_err!(InvalidInput, "LWIP [udp_connect] Failed."), + } + } + } + + /// Transmits data in the given buffer to the remote address to which it is connected. + pub fn send(&self, _buf: &[u8]) -> AxResult { + ax_err!(Unsupported, "LWIP Unsupported UDP send") + } + + /// Recv data in the given buffer from the remote address to which it is connected. + pub fn recv(&self, _buf: &mut [u8]) -> AxResult { + ax_err!(Unsupported, "LWIP Unsupported UDP recv") + } + + /// Close the socket. + pub fn shutdown(&mut self) -> AxResult { + if !self.pcb.0.is_null() { + let _guard = LWIP_MUTEX.lock(); + unsafe { + udp_recv(self.pcb.0, None, null_mut()); + udp_remove(self.pcb.0); + } + self.pcb.0 = null_mut(); + lwip_loop_once(); + Ok(()) + } else { + ax_err!(InvalidInput) + } + } + + /// Receives data from the socket, stores it in the given buffer, without removing it from the queue. + pub fn peek_from(&self, _buf: &mut [u8]) -> AxResult<(usize, core::net::SocketAddr)> { + ax_err!(Unsupported, "LWIP Unsupported UDP peek_from") + } + + /// Detect whether the socket needs to receive/can send. + /// + /// Return is + pub fn poll(&self) -> AxResult { + lwip_loop_once(); + Ok(PollState { + readable: self.inner.recv_queue.lock().len() != 0, + writable: true, + }) + } +} + +impl Drop for UdpSocket { + fn drop(&mut self) { + debug!("[UdpSocket] drop"); + self.shutdown().unwrap(); + } +} + +impl Default for UdpSocket { + fn default() -> Self { + Self::new() + } +} + +fn get_ephemeral_port() -> AxResult { + const PORT_START: u16 = 0xc000; + const PORT_END: u16 = 0xffff; + static CURR: Mutex = Mutex::new(PORT_START); + let mut curr = CURR.lock(); + + let port = *curr; + if *curr == PORT_END { + *curr = PORT_START; + } else { + *curr += 1; + } + Ok(port) +} diff --git a/modules/axnet/src/smoltcp_impl/mod.rs b/modules/axnet/src/smoltcp_impl/mod.rs index da911f2f7..5504023b0 100644 --- a/modules/axnet/src/smoltcp_impl/mod.rs +++ b/modules/axnet/src/smoltcp_impl/mod.rs @@ -247,11 +247,13 @@ impl Device for DeviceWrapper { struct AxNetRxToken<'a>(&'a RefCell, NetBufPtr); struct AxNetTxToken<'a>(&'a RefCell); -impl<'a> RxToken for AxNetRxToken<'a> { +impl<'a> AxNetRxToken<'a> { fn preprocess(&self, sockets: &mut SocketSet<'_>) { snoop_tcp_packet(self.1.packet(), sockets).ok(); } +} +impl<'a> RxToken for AxNetRxToken<'a> { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -319,7 +321,12 @@ pub fn bench_receive() { ETH0.dev.lock().bench_receive_bandwidth(); } +//use driver_net::NetBufPool; +//use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; pub(crate) fn init(net_dev: AxNetDevice) { + info!("lhw debug in smoltcp init"); + //let pool = NetBufPool::new(128, 1526).unwrap(); + //net_dev.fill_rx_buffers(&pool).unwrap(); let ether_addr = EthernetAddress(net_dev.mac_address().0); let eth0 = InterfaceWrapper::new("eth0", net_dev, ether_addr); diff --git a/modules/axnet/src/smoltcp_impl/tcp.rs b/modules/axnet/src/smoltcp_impl/tcp.rs index de9c14fd5..241f2a00f 100644 --- a/modules/axnet/src/smoltcp_impl/tcp.rs +++ b/modules/axnet/src/smoltcp_impl/tcp.rs @@ -219,7 +219,9 @@ impl TcpSocket { /// It's must be called after [`bind`](Self::bind) and before /// [`accept`](Self::accept). pub fn listen(&self) -> AxResult { + info!("smoltcp listen"); self.update_state(STATE_BUSY, STATE_LISTENING, || { + info!("smoltcp real listen"); let bound_endpoint = self.bound_endpoint()?; unsafe { (*self.local_addr.get()).port = bound_endpoint.port; diff --git a/modules/axsync/Cargo.toml b/modules/axsync/Cargo.toml index c3359fdd0..6dfc6e4f6 100644 --- a/modules/axsync/Cargo.toml +++ b/modules/axsync/Cargo.toml @@ -14,6 +14,7 @@ multitask = ["ruxtask/multitask"] default = [] [dependencies] +log = "0.4" spinlock = { path = "../../crates/spinlock" } ruxtask = { path = "../ruxtask" } diff --git a/modules/axsync/src/mutex.rs b/modules/axsync/src/mutex.rs index 25eb18815..f1cdbcf48 100644 --- a/modules/axsync/src/mutex.rs +++ b/modules/axsync/src/mutex.rs @@ -61,6 +61,7 @@ impl Mutex { } } +use log::info; impl Mutex { /// Returns `true` if the lock is currently held. /// @@ -101,10 +102,11 @@ impl Mutex { } } } - MutexGuard { + let ret = MutexGuard { lock: self, data: unsafe { &mut *self.data.get() }, - } + }; + ret } /// Try to lock this [`Mutex`], returning a lock guard if successful. diff --git a/modules/rux9p/Cargo.toml b/modules/rux9p/Cargo.toml index e24320b2a..ed4a7243e 100644 --- a/modules/rux9p/Cargo.toml +++ b/modules/rux9p/Cargo.toml @@ -18,6 +18,7 @@ log = "0.4" spin = "0.9" driver_9p = { path = "../../crates/driver_9p"} axfs_vfs = { path = "../../crates/axfs_vfs"} +axsync = { path = "../axsync" } driver_common = { path = "../../crates/driver_common", optional = true} ruxfs = { path = "../ruxfs"} diff --git a/modules/rux9p/src/netdev.rs b/modules/rux9p/src/netdev.rs index 07c78865b..9ebb5a855 100644 --- a/modules/rux9p/src/netdev.rs +++ b/modules/rux9p/src/netdev.rs @@ -7,13 +7,14 @@ * See the Mulan PSL v2 for more details. */ use axnet::TcpSocket; +use axsync::Mutex; use core::net::{IpAddr, Ipv4Addr, SocketAddr}; use driver_9p::_9pDriverOps; use driver_common::{BaseDriverOps, DeviceType}; use log::*; pub struct Net9pDev { - socket: TcpSocket, + socket: Mutex, srv_addr: SocketAddr, } @@ -27,7 +28,7 @@ impl Net9pDev { } }; Self { - socket: TcpSocket::new(), + socket: Mutex::new(TcpSocket::new()), srv_addr: SocketAddr::new(ip_addr, port), } } @@ -47,7 +48,7 @@ impl _9pDriverOps for Net9pDev { // initialize self(e.g. setup TCP connection) fn init(&self) -> Result<(), u8> { info!("9P client connecting to {:?}", self.srv_addr); - match self.socket.connect(self.srv_addr) { + match self.socket.lock().connect(self.srv_addr) { Ok(_) => { info!("net9p connected successfully"); Ok(()) @@ -61,7 +62,7 @@ impl _9pDriverOps for Net9pDev { // send bytes of inputs as request and receive get answer in outputs fn send_with_recv(&mut self, inputs: &[u8], outputs: &mut [u8]) -> Result { - match self.socket.send(inputs) { + match self.socket.lock().send(inputs) { Ok(length) => { debug!("net9p send successfully,length = {}", length); } @@ -70,7 +71,7 @@ impl _9pDriverOps for Net9pDev { return Err(0); } } - match self.socket.recv(outputs, 0) { + match self.socket.lock().recv(outputs, 0) { Ok(length) => { debug!("net9p recv successfully,length = {}", length); Ok(length as u32) diff --git a/modules/ruxdriver/src/dummy.rs b/modules/ruxdriver/src/dummy.rs index ba13b8f13..f048ef60c 100644 --- a/modules/ruxdriver/src/dummy.rs +++ b/modules/ruxdriver/src/dummy.rs @@ -13,6 +13,7 @@ #![allow(dead_code)] use super::prelude::*; +use alloc::sync::Arc; use cfg_if::cfg_if; cfg_if! { @@ -34,6 +35,8 @@ cfg_if! { fn can_receive(&self) -> bool { false } fn rx_queue_size(&self) -> usize { 0 } fn tx_queue_size(&self) -> usize { 0 } + fn fill_rx_buffers(&mut self, _: &Arc) -> DevResult { Err(DevError::Unsupported) } + fn prepare_tx_buffer(&self, _: &mut NetBuf, _: usize) -> DevResult { Err(DevError::Unsupported) } fn recycle_rx_buffer(&mut self, _: NetBufPtr) -> DevResult { Err(DevError::Unsupported) } fn recycle_tx_buffers(&mut self) -> DevResult { Err(DevError::Unsupported) } fn transmit(&mut self, _: NetBufPtr) -> DevResult { Err(DevError::Unsupported) } diff --git a/modules/ruxdriver/src/lib.rs b/modules/ruxdriver/src/lib.rs index 32343e0e7..ad3d32ce8 100644 --- a/modules/ruxdriver/src/lib.rs +++ b/modules/ruxdriver/src/lib.rs @@ -71,8 +71,6 @@ #[macro_use] extern crate log; - -#[cfg(feature = "dyn")] extern crate alloc; #[macro_use] diff --git a/scripts/make/test.mk b/scripts/make/test.mk index c49273405..b132654a6 100644 --- a/scripts/make/test.mk +++ b/scripts/make/test.mk @@ -3,7 +3,7 @@ define unit_test $(call run_cmd,cargo test,-p percpu $(1) -- --nocapture) $(call run_cmd,cargo test,-p ruxfs $(1) --features "myfs" -- --nocapture) - $(call run_cmd,cargo test,--workspace --exclude "arceos-*" --exclude "ruxos-*" $(1) -- --nocapture) + $(call run_cmd,cargo test,--workspace --exclude lwip_rust --exclude "arceos-*" --exclude "ruxos-*" $(1) -- --nocapture) endef test_app := diff --git a/ulib/axstd/src/net/tcp.rs b/ulib/axstd/src/net/tcp.rs index 21b14bddb..ccd132664 100644 --- a/ulib/axstd/src/net/tcp.rs +++ b/ulib/axstd/src/net/tcp.rs @@ -102,9 +102,9 @@ impl TcpListener { super::each_addr(addr, |addr: io::Result<&SocketAddr>| { let addr = addr?; let backlog = 128; - let socket = api::ax_tcp_socket(); + let mut socket = api::ax_tcp_socket(); api::ax_tcp_bind(&socket, *addr)?; - api::ax_tcp_listen(&socket, backlog)?; + api::ax_tcp_listen(&mut socket, backlog)?; Ok(TcpListener(socket)) }) }