From a34468c6c14e5e5aa9aec3a74f9abdb4b2e4ddaa Mon Sep 17 00:00:00 2001 From: Samuka007 Date: Mon, 21 Apr 2025 21:11:05 +0800 Subject: [PATCH] first init --- .gitignore | 1 + Cargo.lock | 496 ++++++++++++++++++++++++++++ Cargo.toml | 28 ++ src/driver/irq.rs | 129 ++++++++ src/driver/mod.rs | 38 +++ src/driver/tap.rs | 270 ++++++++++++++++ src/event_poll.rs | 65 ++++ src/interface/mod.rs | 262 +++++++++++++++ src/interface/tap.rs | 62 ++++ src/lib.rs | 15 + src/libs/mod.rs | 3 + src/libs/rwlock.rs | 1 + src/libs/spinlock.rs | 24 ++ src/libs/wait_queue.rs | 58 ++++ src/main.rs | 16 + src/posix/family.rs | 113 +++++++ src/posix/mod.rs | 14 + src/posix/msg_flag.rs | 110 +++++++ src/posix/option.rs | 94 ++++++ src/posix/option_level.rs | 69 ++++ src/posix/posix.rs | 84 +++++ src/posix/types.rs | 21 ++ src/process/mod.rs | 11 + src/socket/common/mod.rs | 18 ++ src/socket/common/shutdown.rs | 136 ++++++++ src/socket/endpoint.rs | 44 +++ src/socket/inet/common/mod.rs | 157 +++++++++ src/socket/inet/common/port.rs | 121 +++++++ src/socket/inet/datagram/inner.rs | 167 ++++++++++ src/socket/inet/datagram/mod.rs | 306 ++++++++++++++++++ src/socket/inet/mod.rs | 40 +++ src/socket/inet/posix/mod.rs | 2 + src/socket/inet/posix/option.rs | 67 ++++ src/socket/inet/posix/proto.rs | 78 +++++ src/socket/inet/stream/inner.rs | 517 ++++++++++++++++++++++++++++++ src/socket/inet/stream/mod.rs | 507 +++++++++++++++++++++++++++++ src/socket/inet/stream/option.rs | 94 ++++++ src/socket/inet/syscall.rs | 67 ++++ src/socket/mod.rs | 148 +++++++++ 39 files changed, 4453 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/driver/irq.rs create mode 100644 src/driver/mod.rs create mode 100644 src/driver/tap.rs create mode 100644 src/event_poll.rs create mode 100644 src/interface/mod.rs create mode 100644 src/interface/tap.rs create mode 100644 src/lib.rs create mode 100644 src/libs/mod.rs create mode 100644 src/libs/rwlock.rs create mode 100644 src/libs/spinlock.rs create mode 100644 src/libs/wait_queue.rs create mode 100644 src/main.rs create mode 100644 src/posix/family.rs create mode 100644 src/posix/mod.rs create mode 100644 src/posix/msg_flag.rs create mode 100644 src/posix/option.rs create mode 100644 src/posix/option_level.rs create mode 100644 src/posix/posix.rs create mode 100644 src/posix/types.rs create mode 100644 src/process/mod.rs create mode 100644 src/socket/common/mod.rs create mode 100644 src/socket/common/shutdown.rs create mode 100644 src/socket/endpoint.rs create mode 100644 src/socket/inet/common/mod.rs create mode 100644 src/socket/inet/common/port.rs create mode 100644 src/socket/inet/datagram/inner.rs create mode 100644 src/socket/inet/datagram/mod.rs create mode 100644 src/socket/inet/mod.rs create mode 100644 src/socket/inet/posix/mod.rs create mode 100644 src/socket/inet/posix/option.rs create mode 100644 src/socket/inet/posix/proto.rs create mode 100644 src/socket/inet/stream/inner.rs create mode 100644 src/socket/inet/stream/mod.rs create mode 100644 src/socket/inet/stream/option.rs create mode 100644 src/socket/inet/syscall.rs create mode 100644 src/socket/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d6ca64a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,496 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "berkeley-socket" +version = "0.1.0" +dependencies = [ + "bitflags 2.9.0", + "hashbrown", + "lazy_static", + "libc", + "linux-errnos", + "log", + "netsock", + "num-derive", + "num-traits", + "smoltcp", + "spin", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.0.1", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.12", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[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.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-errnos" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9df90411fa4fbcb0177a4d342f93fd59aa81382dc74bd568a820903bf28ef7" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-sock-diag" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a495cb1de50560a7cd12fdcf023db70eec00e340df81be31cedbbfd4aadd6b76" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", + "smallvec", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", +] + +[[package]] +name = "netlink-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" +dependencies = [ + "bytes", + "libc", + "log", +] + +[[package]] +name = "netsock" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0619d68669a4107fe8be0e76d4e7557304c83d7e6a23d50e3111b5dab888741" +dependencies = [ + "bitflags 2.9.0", + "byteorder", + "netlink-packet-core", + "netlink-packet-sock-diag", + "netlink-packet-utils", + "netlink-sys", + "num-derive", + "num-traits", + "thiserror 2.0.12", + "windows-sys", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "smoltcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "defmt 0.3.100", + "heapless", + "log", + "managed", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f5601f8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "berkeley-socket" +version = "0.1.0" +edition = "2021" + +[dependencies] +smoltcp = { version = "0.12.0", default-features = false, features = [ + "std", + "alloc", + "log", + "medium-ethernet", + "medium-ip", + "proto-ipv4", + "socket-udp", + "socket-tcp", +]} + +spin = "0.9.4" + +linux-errnos = "*" +hashbrown = "0.15.2" +bitflags = "2.9.0" +num-derive = "0.4.2" +num-traits = "0.2.15" +log = "*" +netsock = "0.3.0" +libc = "0.2.172" +lazy_static = "1.5.0" diff --git a/src/driver/irq.rs b/src/driver/irq.rs new file mode 100644 index 0000000..837a01f --- /dev/null +++ b/src/driver/irq.rs @@ -0,0 +1,129 @@ +use std::os::unix::io::RawFd; +use std::time::Duration; +use std::{io, thread}; + +use hashbrown::HashMap; + +use crate::socket::inet::common::NET_DEVICES; + +const EPOLL_TIMEOUT_MS: i32 = 100; + +/// Start a thread that polls network devices when their tap interfaces are readable +pub fn start_network_polling_thread() -> io::Result> { + // Create an epoll instance + let epoll_fd = unsafe { libc::epoll_create1(0) }; + if epoll_fd < 0 { + return Err(io::Error::last_os_error()); + } + + let handle = thread::spawn(move || { + let mut events = Vec::with_capacity(32); + events.resize(32, libc::epoll_event { events: 0, u64: 0 }); + let mut fd_to_device_id = HashMap::new(); + + loop { + // Update the list of devices to watch + update_watched_devices(epoll_fd, &mut fd_to_device_id); + + // Wait for events + let num_events = unsafe { + libc::epoll_wait( + epoll_fd, + events.as_mut_ptr(), + events.len() as i32, + EPOLL_TIMEOUT_MS, + ) + }; + + if num_events < 0 { + let err = io::Error::last_os_error(); + // Ignore EINTR, which happens when the thread receives a signal + if err.kind() != io::ErrorKind::Interrupted { + log::error!("epoll_wait failed: {:?}", err); + } + continue; + } + + // Process events + for event in events.iter().take(num_events as usize) { + let fd = event.u64 as RawFd; + + if let Some(&device_id) = fd_to_device_id.get(&fd) { + if let Some(device) = NET_DEVICES.read().get(&device_id) { + // Poll the device that has data available + device.poll(); + } + } + } + + // Also poll all devices periodically regardless of events + // This ensures timers and other internal state are updated + for device in NET_DEVICES.read().values() { + device.poll(); + } + + // Small sleep to prevent CPU hogging + thread::sleep(Duration::from_millis(1)); + } + }); + + Ok(handle) +} + +/// Update the list of devices being watched by epoll +fn update_watched_devices(epoll_fd: RawFd, fd_to_device_id: &mut HashMap) { + let devices = NET_DEVICES.read(); + + // Find new devices to add + for (&id, device) in devices.iter() { + // Try to downcast to get TAP device + if let Some(tap_fd) = device.raw_fd() { + if !fd_to_device_id.contains_key(&tap_fd) { + // Add new device to epoll + let mut event = libc::epoll_event { + events: (libc::EPOLLIN | libc::EPOLLET) as u32, + u64: tap_fd as u64, + }; + + let result = + unsafe { libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_ADD, tap_fd, &mut event) }; + + if result == 0 { + fd_to_device_id.insert(tap_fd, id); + } else { + log::error!( + "Failed to add device fd {} to epoll: {:?}", + tap_fd, + io::Error::last_os_error() + ); + } + } + } + } + + // Find devices to remove + let mut to_remove = Vec::new(); + for &fd in fd_to_device_id.keys() { + let id = fd_to_device_id[&fd]; + if !devices.contains_key(&id) { + to_remove.push(fd); + + // Remove from epoll + let result = + unsafe { libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_DEL, fd, std::ptr::null_mut()) }; + + if result != 0 { + log::error!( + "Failed to remove device fd {} from epoll: {:?}", + fd, + io::Error::last_os_error() + ); + } + } + } + + // Actually remove the entries + for fd in to_remove { + fd_to_device_id.remove(&fd); + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs new file mode 100644 index 0000000..b40ff21 --- /dev/null +++ b/src/driver/mod.rs @@ -0,0 +1,38 @@ +use std::io; + +pub mod tap; +pub use tap::TapDesc; +pub mod irq; + +#[repr(C)] +#[derive(Debug)] +struct ifreq { + ifr_name: [libc::c_char; libc::IF_NAMESIZE], + ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ +} + +fn ifreq_for(name: &str) -> ifreq { + let mut ifreq = ifreq { + ifr_name: [0; libc::IF_NAMESIZE], + ifr_data: 0, + }; + for (i, byte) in name.as_bytes().iter().enumerate() { + ifreq.ifr_name[i] = *byte as libc::c_char + } + ifreq +} + +fn ifreq_ioctl( + lower: libc::c_int, + ifreq: &mut ifreq, + cmd: libc::c_ulong, +) -> io::Result { + unsafe { + let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq); + if res == -1 { + return Err(io::Error::last_os_error()); + } + } + + Ok(ifreq.ifr_data) +} diff --git a/src/driver/tap.rs b/src/driver/tap.rs new file mode 100644 index 0000000..8f23909 --- /dev/null +++ b/src/driver/tap.rs @@ -0,0 +1,270 @@ +use super::*; +use smoltcp::{phy::Medium, wire::EthernetFrame}; +use std::io; +use std::os::unix::io::{AsRawFd, RawFd}; + +#[derive(Debug)] +pub struct TapDesc { + lower: libc::c_int, + mtu: usize, +} + +impl AsRawFd for TapDesc { + fn as_raw_fd(&self) -> RawFd { + self.lower + } +} + +impl TapDesc { + pub fn new(name: &str, medium: Medium) -> io::Result { + let lower = unsafe { + let lower = libc::open( + c"/dev/net/tun".as_ptr() as *const libc::c_char, + libc::O_RDWR | libc::O_NONBLOCK, + ); + if lower == -1 { + return Err(io::Error::last_os_error()); + } + lower + }; + + let mut ifreq = ifreq_for(name); + Self::attach_interface_ifreq(lower, medium, &mut ifreq)?; + let mtu = Self::mtu_ifreq(medium, &mut ifreq)?; + + Ok(TapDesc { lower, mtu }) + } + + pub fn from_fd(fd: RawFd, mtu: usize) -> io::Result { + Ok(TapDesc { lower: fd, mtu }) + } + + fn attach_interface_ifreq( + lower: libc::c_int, + medium: Medium, + ifr: &mut ifreq, + ) -> io::Result<()> { + let mode = match medium { + Medium::Ip => libc::IFF_TUN, + Medium::Ethernet => libc::IFF_TAP, + }; + ifr.ifr_data = mode | libc::IFF_NO_PI; + ifreq_ioctl(lower, ifr, libc::TUNSETIFF).map(|_| ()) + } + + fn mtu_ifreq(medium: Medium, ifr: &mut ifreq) -> io::Result { + let lower = unsafe { + let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); + if lower == -1 { + return Err(io::Error::last_os_error()); + } + lower + }; + + let ip_mtu = ifreq_ioctl(lower, ifr, libc::SIOCGIFMTU).map(|mtu| mtu as usize); + + unsafe { + libc::close(lower); + } + + // Propagate error after close, to ensure we always close. + let ip_mtu = ip_mtu?; + + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + let mtu = match medium { + Medium::Ip => ip_mtu, + Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(), + // Medium::Ieee802154 => todo!(), + }; + + Ok(mtu) + } + + pub fn interface_mtu(&self) -> io::Result { + Ok(self.mtu) + } + + pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { + unsafe { + let len = libc::read( + self.lower, + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + ); + if len == -1 { + return Err(io::Error::last_os_error()); + } + Ok(len as usize) + } + } + + pub fn send(&mut self, buffer: &[u8]) -> io::Result { + unsafe { + let len = libc::write( + self.lower, + buffer.as_ptr() as *const libc::c_void, + buffer.len(), + ); + if len == -1 { + return Err(io::Error::last_os_error()); + } + Ok(len as usize) + } + } +} + +impl Drop for TapDesc { + fn drop(&mut self) { + unsafe { + libc::close(self.lower); + } + } +} + +use std::cell::RefCell; +use std::rc::Rc; +use std::vec::Vec; + +use smoltcp::phy::{self, Device, DeviceCapabilities}; +use smoltcp::time::Instant; + +/// A virtual TUN (IP) or TAP (Ethernet) interface. +#[derive(Debug)] +pub struct TapDevice { + lower: Rc>, + mtu: usize, + medium: Medium, + mac: smoltcp::wire::EthernetAddress, +} + +impl AsRawFd for TapDevice { + fn as_raw_fd(&self) -> RawFd { + self.lower.borrow().as_raw_fd() + } +} + +impl TapDevice { + /// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist. + /// + /// If `name` is a persistent interface configured with UID of the current user, + /// no special privileges are needed. Otherwise, this requires superuser privileges + /// or a corresponding capability set on the executable. + pub fn new(name: &str, medium: Medium) -> io::Result { + let lower = crate::driver::TapDesc::new(name, medium)?; + let mtu = lower.interface_mtu()?; + let mac = smoltcp::wire::EthernetAddress::from_bytes(&[ + 0x02, + 0x00, + 0x00, + std::random::random::(), + std::random::random::(), + std::random::random::(), + ]); + Ok(TapDevice { + lower: Rc::new(RefCell::new(lower)), + mtu, + medium, + mac, + }) + } + + /// Attaches to a TUN/TAP interface specified by file descriptor `fd`. + /// + /// On platforms like Android, a file descriptor to a tun interface is exposed. + /// On these platforms, a TunTapInterface cannot be instantiated with a name. + pub fn from_fd(fd: RawFd, medium: Medium, mtu: usize) -> io::Result { + let lower = crate::driver::TapDesc::from_fd(fd, mtu)?; + let mac = smoltcp::wire::EthernetAddress::from_bytes(&[ + 0x02, + 0x00, + 0x00, + std::random::random::(), + std::random::random::(), + std::random::random::(), + ]); + Ok(TapDevice { + lower: Rc::new(RefCell::new(lower)), + mtu, + medium, + mac, + }) + } + + pub fn mac(&self) -> smoltcp::wire::EthernetAddress { + self.mac + } +} + +impl Device for TapDevice { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken; + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = self.mtu; + caps.medium = self.medium; + caps + } + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let mut lower = self.lower.borrow_mut(); + let mut buffer = vec![0; self.mtu]; + match lower.recv(&mut buffer[..]) { + Ok(size) => { + buffer.resize(size, 0); + let rx = RxToken { buffer }; + let tx = TxToken { + lower: self.lower.clone(), + }; + Some((rx, tx)) + } + Err(err) if err.kind() == io::ErrorKind::WouldBlock => None, + Err(err) => panic!("{}", err), + } + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + Some(TxToken { + lower: self.lower.clone(), + }) + } +} + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl phy::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + f(&self.buffer[..]) + } +} + +#[doc(hidden)] +pub struct TxToken { + lower: Rc>, +} + +impl phy::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut lower = self.lower.borrow_mut(); + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + match lower.send(&buffer[..]) { + Ok(_) => {} + Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + log::debug!("phy: tx failed due to WouldBlock") + } + Err(err) => panic!("{}", err), + } + result + } +} diff --git a/src/event_poll.rs b/src/event_poll.rs new file mode 100644 index 0000000..a64e246 --- /dev/null +++ b/src/event_poll.rs @@ -0,0 +1,65 @@ +bitflags::bitflags! { + pub struct EPollEventType: u32 { + /// 对应的描述符有新的数据可读时会触发 + const EPOLLIN = 0x00000001; + /// 对应的描述符有紧急数据可读时会触发 + const EPOLLPRI = 0x00000002; + /// 对应的描述符可以写入数据时会触发 + const EPOLLOUT = 0x00000004; + /// 对应的描述符发生错误时会触发 + const EPOLLERR = 0x00000008; + /// 对应的描述符被挂断(连接关闭)时会触发 + const EPOLLHUP = 0x00000010; + /// 对应的描述符不是一个有效的文件描述符时会触发 + const EPOLLNVAL = 0x00000020; + /// 普通数据可读,类似于`EPOLLIN` + const EPOLLRDNORM = 0x00000040; + /// 优先级带外数据可读 + const EPOLLRDBAND = 0x00000080; + /// 普通数据可写,类似于'EPOLLOUT' + const EPOLLWRNORM = 0x00000100; + /// 优先级带外数据可写 + const EPOLLWRBAND = 0x00000200; + /// 通过消息队列收到消息时会触 + const EPOLLMSG = 0x00000400; + /// 对应的描述符被挂断(连接关闭)的一端发送了 FIN 时会触发(读关闭) + const EPOLLRDHUP = 0x00002000; + + /// 以下为额外选项 + /// + /// 特定选项,用于异步 I/O,目前未实现 + const EPOLL_URING_WAKE = 1u32 << 27; + /// 设置epoll为独占模式 + const EPOLLEXCLUSIVE = 1u32 << 28; + /// 允许在系统挂起时唤醒 epoll,通常用于通过 eventfd 或 timerfd 唤醒 epoll,(通常与电源管理相关,未实现) + const EPOLLWAKEUP = 1u32 << 29; + /// 表示只监听一次事件,之后需要重新添加 + const EPOLLONESHOT = 1u32 << 30; + + /// 启用边缘触发模式(即只有下次触发事件时才会通过epoll_wait返回), + /// 对应为水平触发(默认),水平触发模式下若这次未处理完数据,那epoll还会将其加入自己的就绪队列 + const EPOLLET = 1u32 << 31; + + /// 以下为组合码 + const EPOLLINOUT_BITS = Self::EPOLLIN.bits() | Self::EPOLLOUT.bits(); + const EPOLLEXCLUSIVE_OK_BITS = + Self::EPOLLINOUT_BITS.bits() + | Self::EPOLLERR.bits() + | Self::EPOLLHUP.bits() + | Self::EPOLLWAKEUP.bits() + | Self::EPOLLET.bits() + | Self::EPOLLEXCLUSIVE.bits(); + + const EP_PRIVATE_BITS = + Self::EPOLLWAKEUP.bits() + | Self::EPOLLONESHOT.bits() + | Self::EPOLLET.bits() + | Self::EPOLLEXCLUSIVE.bits(); + + /// 表示epoll已经被释放,但是在目前的设计中未用到 + const POLLFREE = 0x4000; + + /// listen状态的socket可以接受连接 + const EPOLL_LISTEN_CAN_ACCEPT = Self::EPOLLIN.bits() | Self::EPOLLRDNORM.bits(); + } +} diff --git a/src/interface/mod.rs b/src/interface/mod.rs new file mode 100644 index 0000000..eb3e340 --- /dev/null +++ b/src/interface/mod.rs @@ -0,0 +1,262 @@ +use linux_errnos::Errno; +use spin::Mutex; +use spin::RwLock; +use std::any::Any; +use std::fmt; +use std::fmt::Debug; +use std::sync::Arc; + +use crate::socket::inet::common::PortManager; +use crate::socket::inet::InetSocket; + +pub mod tap; + +pub trait Iface: Sync + Send + Debug + Any { + /// # `common` + /// 获取网卡的公共信息 + fn common(&self) -> &IfaceCommon; + + /// # `mac` + /// 获取网卡的MAC地址 + fn mac(&self) -> smoltcp::wire::EthernetAddress; + + // /// # `name` + // /// 获取网卡名 + // fn iface_name(&self) -> String; + + // /// # `nic_id` + // /// 获取网卡id + // fn nic_id(&self) -> usize { + // self.common().iface_id + // } + + /// # `poll` + /// 用于轮询接口的状态。 + /// ## 参数 + /// - `sockets` :一个可变引用到 `smoltcp::iface::SocketSet`,表示要轮询的套接字集 + /// ## 返回值 + /// - 成功返回 `Ok(())` + /// - 如果轮询失败,返回 `Err(Errno::EAGAIN_OR_EWOULDBLOCK)`,表示需要再次尝试或者操作会阻塞 + fn poll(&self); + + /// # `update_ip_addrs` + /// 用于更新接口的 IP 地址 + /// ## 参数 + /// - `ip_addrs` :一个包含 `smoltcp::wire::IpCidr` 的切片,表示要设置的 IP 地址和子网掩码 + /// ## 返回值 + /// - 如果 `ip_addrs` 的长度不为 1,返回 `Err(Errno::EINVAL)`,表示输入参数无效 + fn update_ip_addrs(&self, ip_addrs: &[smoltcp::wire::IpCidr]) -> Result<(), Errno> { + self.common().update_ip_addrs(ip_addrs) + } + + /// @brief 获取smoltcp的网卡接口类型 + #[inline(always)] + fn smol_iface(&self) -> &Mutex { + &self.common().smol_iface + } + // fn as_any_ref(&'static self) -> &'static dyn core::any::Any; + + /// # `sockets` + /// 获取网卡的套接字集 + fn sockets(&self) -> &Mutex> { + &self.common().sockets + } + + /// # `port_manager` + /// 用于管理网卡的端口 + fn port_manager(&self) -> &PortManager { + &self.common().port_manager + } + + /// Get the raw file descriptor if this interface has one + /// Returns None if this interface doesn't have a file descriptor + fn raw_fd(&self) -> Option { + None + } + + // fn addr_assign_type(&self) -> u8; + + // fn net_device_type(&self) -> u16; + + // fn net_state(&self) -> NetDeivceState; + + // fn set_net_state(&self, state: NetDeivceState); + + // fn operstate(&self) -> Operstate; + + // fn set_operstate(&self, state: Operstate); +} + +/// 网络设备的公共数据 +#[derive(Debug)] +pub struct NetDeviceCommonData { + /// 表示网络接口的地址分配类型 + pub addr_assign_type: u8, + /// 表示网络接口的类型 + pub net_device_type: u16, + // /// 表示网络接口的状态 + // pub state: NetDeivceState, + // /// 表示网络接口的操作状态 + // pub operstate: Operstate, +} + +impl Default for NetDeviceCommonData { + fn default() -> Self { + Self { + addr_assign_type: 0, + net_device_type: 1, + // state: NetDeivceState::empty(), + // operstate: Operstate::IF_OPER_UNKNOWN, + } + } +} + +// /// 将网络设备注册到sysfs中 +// /// 参考:https://code.dragonos.org.cn/xref/linux-2.6.39/net/core/dev.c?fi=register_netdev#5373 +// fn register_netdevice(dev: Arc) -> Result<(), Errno> { +// // 在sysfs中注册设备 +// netdev_register_kobject(dev.clone())?; + +// // 标识网络设备在系统中存在 +// dev.set_net_state(NetDeivceState::__LINK_STATE_PRESENT); + +// return Ok(()); +// } + +pub struct IfaceCommon { + iface_id: usize, + smol_iface: Mutex, + /// 存smoltcp网卡的套接字集 + sockets: Mutex>, + /// 存 kernel wrap smoltcp socket 的集合 + bounds: RwLock>>, + /// 端口管理器 + port_manager: PortManager, + /// 下次轮询的时间 + poll_at_ms: core::sync::atomic::AtomicU64, + /// 默认网卡标识 + /// TODO: 此字段设置目的是解决对bind unspecified地址的分包问题,需要在inet实现多网卡监听或路由子系统实现后移除 + default_iface: bool, +} + +impl fmt::Debug for IfaceCommon { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IfaceCommon") + .field("iface_id", &self.iface_id) + .field("sockets", &self.sockets) + // .field("bounds", &self.bounds) + .field("port_manager", &self.port_manager) + .field("poll_at_ms", &self.poll_at_ms) + .finish() + } +} + +impl IfaceCommon { + pub fn new(iface_id: usize, default_iface: bool, iface: smoltcp::iface::Interface) -> Self { + IfaceCommon { + iface_id, + smol_iface: Mutex::new(iface), + sockets: Mutex::new(smoltcp::iface::SocketSet::new(Vec::new())), + bounds: RwLock::new(Vec::new()), + port_manager: PortManager::new(), + poll_at_ms: core::sync::atomic::AtomicU64::new(0), + default_iface, + } + } + + pub fn poll(&self, device: &mut D) + where + D: smoltcp::phy::Device + ?Sized, + { + let timestamp = std::time::Instant::now().into(); + let mut sockets = self.sockets.lock(); + let mut interface = self.smol_iface.lock(); + + let (has_events, poll_at) = { + ( + matches!( + interface.poll(timestamp, device, &mut sockets), + smoltcp::iface::PollResult::SocketStateChanged + ), + loop { + let poll_at = interface.poll_at(timestamp, &sockets); + let Some(instant) = poll_at else { + break poll_at; + }; + if instant > timestamp { + break poll_at; + } + }, + ) + }; + + // drop sockets here to avoid deadlock + drop(interface); + drop(sockets); + + use core::sync::atomic::Ordering; + if let Some(instant) = poll_at { + let _old_instant = self.poll_at_ms.load(Ordering::Relaxed); + let new_instant = instant.total_millis() as u64; + self.poll_at_ms.store(new_instant, Ordering::Relaxed); + + // TODO: poll at + // if old_instant == 0 || new_instant < old_instant { + // self.polling_wait_queue.wake_all(); + // } + } else { + self.poll_at_ms.store(0, Ordering::Relaxed); + } + + self.bounds.read().iter().for_each(|bound_socket| { + // incase our inet socket missed the event, we manually notify it each time we poll + if has_events { + bound_socket.on_iface_events(); + bound_socket.wait_queue().wakeup(); + } + }); + + // TODO: remove closed sockets + // let closed_sockets = self + // .closing_sockets + // .lock_irq_disabled() + // .extract_if(|closing_socket| closing_socket.is_closed()) + // .collect::>(); + // drop(closed_sockets); + } + + pub fn update_ip_addrs(&self, ip_addrs: &[smoltcp::wire::IpCidr]) -> Result<(), Errno> { + if ip_addrs.len() != 1 { + return Err(Errno::EINVAL); + } + + self.smol_iface.lock().update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next(); + + if let Some(dest) = dest { + *dest = ip_addrs[0]; + } else { + addrs.push(ip_addrs[0]).expect("Push ipCidr failed: full"); + } + }); + Ok(()) + } + + // 需要bounds储存具体的Inet Socket信息,以提供不同种类inet socket的事件分发 + pub fn bind_socket(&self, socket: Arc) { + self.bounds.write().push(socket); + } + + pub fn unbind_socket(&self, socket: Arc) { + let mut bounds = self.bounds.write(); + if let Some(index) = bounds.iter().position(|s| Arc::ptr_eq(s, &socket)) { + bounds.remove(index); + log::debug!("unbind socket success"); + } + } + + // TODO: 需要在inet实现多网卡监听或路由子系统实现后移除 + pub fn is_default_iface(&self) -> bool { + self.default_iface + } +} diff --git a/src/interface/tap.rs b/src/interface/tap.rs new file mode 100644 index 0000000..c34a5ae --- /dev/null +++ b/src/interface/tap.rs @@ -0,0 +1,62 @@ +use std::{ops::DerefMut, sync::Arc, time::Instant}; + +use smoltcp::{ + iface::{Config, Interface}, + wire::HardwareAddress, +}; +use spin::Mutex; + +use crate::driver::tap::TapDevice; + +use super::{Iface, IfaceCommon}; + +#[derive(Debug)] +pub struct TapIface { + pub inner: Arc>, + pub common: IfaceCommon, +} + +// #[derive(Debug)] +// pub struct TapIface (Arc>); + +impl TapIface { + pub fn new(inner: Arc>) -> Self { + let mut iface_config = Config::new(HardwareAddress::Ethernet(inner.lock().mac())); + iface_config.random_seed = std::random::random(); + + let iface = Interface::new( + iface_config, + inner.lock().deref_mut(), + Instant::now().into(), + ); + let common = IfaceCommon::new(1, true, iface); + TapIface { inner, common } + } +} + +unsafe impl Send for TapIface {} +unsafe impl Sync for TapIface {} + +impl Iface for TapIface { + fn common(&self) -> &IfaceCommon { + &self.common + } + + fn mac(&self) -> smoltcp::wire::EthernetAddress { + self.inner.lock().mac() + } + + // fn iface_name(&self) -> String { + // "tap0".to_string() + // } + + // fn nic_id(&self) -> usize { + // self.inner.nic_id() + // } + + fn poll(&self) { + let mut guard = self.inner.lock(); + let reference = guard.deref_mut(); + self.common.poll(reference); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3f138c4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +#![feature(thread_id_value)] +#![feature(random)] +pub mod driver; +pub mod event_poll; +pub mod interface; +pub mod libs; +pub mod posix; +pub mod process; +pub mod socket; + +extern crate alloc; +extern crate bitflags; +extern crate num_derive; +extern crate num_traits; +extern crate smoltcp; diff --git a/src/libs/mod.rs b/src/libs/mod.rs new file mode 100644 index 0000000..519875f --- /dev/null +++ b/src/libs/mod.rs @@ -0,0 +1,3 @@ +pub mod rwlock; +pub mod spinlock; +pub mod wait_queue; diff --git a/src/libs/rwlock.rs b/src/libs/rwlock.rs new file mode 100644 index 0000000..0a9ae16 --- /dev/null +++ b/src/libs/rwlock.rs @@ -0,0 +1 @@ +pub use spin::RwLock; diff --git a/src/libs/spinlock.rs b/src/libs/spinlock.rs new file mode 100644 index 0000000..3b59ac6 --- /dev/null +++ b/src/libs/spinlock.rs @@ -0,0 +1,24 @@ +pub use spin::{Mutex as SpinLock, MutexGuard}; + +// pub struct SpinLock { +// inner: Mutex, +// } +// impl SpinLock { +// pub fn new(data: T) -> Self { +// Self { +// inner: Mutex::new(data), +// } +// } + +// pub fn lock(&self) -> MutexGuard<'_, T> { +// self.inner.lock() +// } + +// pub fn lock_irq_disabled(&self) -> MutexGuard<'_, T> { +// self.inner.lock() +// } + +// pub fn lock_irqsave(&self) -> MutexGuard<'_, T> { +// self.inner.lock() +// } +// } diff --git a/src/libs/wait_queue.rs b/src/libs/wait_queue.rs new file mode 100644 index 0000000..d539295 --- /dev/null +++ b/src/libs/wait_queue.rs @@ -0,0 +1,58 @@ +use std::{ + sync::atomic::AtomicBool, + thread::sleep, + time::Duration, +}; + +use linux_errnos::Errno; + +#[derive(Debug)] +pub struct WaitQueue { + // events: AtomicUsize, + is_scheduled: AtomicBool, +} + +pub fn wq_wait_event_interruptible bool>( + wait_queue: &WaitQueue, + should_wake: T, + _: Option, +) -> Result<(), Errno> { + // Simulate waiting for an event + if should_wake() { + wait_queue + .is_scheduled + .store(false, std::sync::atomic::Ordering::SeqCst); + return Ok(()); + } + wait_queue + .is_scheduled + .store(false, std::sync::atomic::Ordering::SeqCst); + loop { + if wait_queue + .is_scheduled + .load(std::sync::atomic::Ordering::SeqCst) + && should_wake() + { + return Ok(()); + } else { + // simulate never schedule to this process + sleep(Duration::from_millis(10)); + } + } +} + +impl WaitQueue { + pub fn wakeup(&self) { + self.is_scheduled + .store(true, std::sync::atomic::Ordering::SeqCst); + } +} + +impl Default for WaitQueue { + fn default() -> Self { + Self { + // events: AtomicUsize::new(0), + is_scheduled: AtomicBool::new(false), + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9910993 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,16 @@ +use std::sync::Arc; + +use berkeley_socket::{ + driver::{irq::start_network_polling_thread, tap::TapDevice}, + interface::tap::TapIface, + socket::inet::common::NET_DEVICES, +}; +use spin::Mutex; + +fn main() { + let device = TapDevice::new("tap0", smoltcp::phy::Medium::Ethernet).unwrap(); + let iface = Arc::new(TapIface::new(Arc::new(Mutex::new(device)))); + NET_DEVICES.write().insert(0, iface); + let _ = start_network_polling_thread(); + // TODO: add socket tests +} diff --git a/src/posix/family.rs b/src/posix/family.rs new file mode 100644 index 0000000..a8ff3d0 --- /dev/null +++ b/src/posix/family.rs @@ -0,0 +1,113 @@ +use num_derive::{FromPrimitive, ToPrimitive}; +/// # AddressFamily +/// Socket address families. +/// ## Reference +/// https://code.dragonos.org.cn/xref/linux-5.19.10/include/linux/socket.h#180 +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +pub enum AddressFamily { + /// AF_UNSPEC 表示地址族未指定 + Unspecified = 0, + /// AF_UNIX 表示Unix域的socket (与AF_LOCAL相同) + Unix = 1, + /// AF_INET 表示IPv4的socket + INet = 2, + /// AF_AX25 表示AMPR AX.25的socket + AX25 = 3, + /// AF_IPX 表示IPX的socket + IPX = 4, + /// AF_APPLETALK 表示Appletalk的socket + Appletalk = 5, + /// AF_NETROM 表示AMPR NET/ROM的socket + Netrom = 6, + /// AF_BRIDGE 表示多协议桥接的socket + Bridge = 7, + /// AF_ATMPVC 表示ATM PVCs的socket + Atmpvc = 8, + /// AF_X25 表示X.25的socket + X25 = 9, + /// AF_INET6 表示IPv6的socket + INet6 = 10, + /// AF_ROSE 表示AMPR ROSE的socket + Rose = 11, + /// AF_DECnet Reserved for DECnet project + Decnet = 12, + /// AF_NETBEUI Reserved for 802.2LLC project + Netbeui = 13, + /// AF_SECURITY 表示Security callback的伪AF + Security = 14, + /// AF_KEY 表示Key management API + Key = 15, + /// AF_NETLINK 表示Netlink的socket + Netlink = 16, + /// AF_PACKET 表示Low level packet interface + Packet = 17, + /// AF_ASH 表示Ash + Ash = 18, + /// AF_ECONET 表示Acorn Econet + Econet = 19, + /// AF_ATMSVC 表示ATM SVCs + Atmsvc = 20, + /// AF_RDS 表示Reliable Datagram Sockets + Rds = 21, + /// AF_SNA 表示Linux SNA Project + Sna = 22, + /// AF_IRDA 表示IRDA sockets + Irda = 23, + /// AF_PPPOX 表示PPPoX sockets + Pppox = 24, + /// AF_WANPIPE 表示WANPIPE API sockets + WanPipe = 25, + /// AF_LLC 表示Linux LLC + Llc = 26, + /// AF_IB 表示Native InfiniBand address + /// 介绍:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/configuring_infiniband_and_rdma_networks/index#understanding-infiniband-and-rdma_configuring-infiniband-and-rdma-networks + Ib = 27, + /// AF_MPLS 表示MPLS + Mpls = 28, + /// AF_CAN 表示Controller Area Network + Can = 29, + /// AF_TIPC 表示TIPC sockets + Tipc = 30, + /// AF_BLUETOOTH 表示Bluetooth sockets + Bluetooth = 31, + /// AF_IUCV 表示IUCV sockets + Iucv = 32, + /// AF_RXRPC 表示RxRPC sockets + Rxrpc = 33, + /// AF_ISDN 表示mISDN sockets + Isdn = 34, + /// AF_PHONET 表示Phonet sockets + Phonet = 35, + /// AF_IEEE802154 表示IEEE 802.15.4 sockets + Ieee802154 = 36, + /// AF_CAIF 表示CAIF sockets + Caif = 37, + /// AF_ALG 表示Algorithm sockets + Alg = 38, + /// AF_NFC 表示NFC sockets + Nfc = 39, + /// AF_VSOCK 表示vSockets + Vsock = 40, + /// AF_KCM 表示Kernel Connection Multiplexor + Kcm = 41, + /// AF_QIPCRTR 表示Qualcomm IPC Router + Qipcrtr = 42, + /// AF_SMC 表示SMC-R sockets. + /// reserve number for PF_SMC protocol family that reuses AF_INET address family + Smc = 43, + /// AF_XDP 表示XDP sockets + Xdp = 44, + /// AF_MCTP 表示Management Component Transport Protocol + Mctp = 45, + /// AF_MAX 表示最大的地址族 + Max = 46, +} + +impl core::convert::TryFrom for AddressFamily { + type Error = linux_errnos::Errno; + fn try_from(x: u16) -> Result { + use num_traits::FromPrimitive; + // this will return EINVAL but still works, idk why + ::from_u16(x).ok_or(Self::Error::EINVAL) + } +} diff --git a/src/posix/mod.rs b/src/posix/mod.rs new file mode 100644 index 0000000..5311b45 --- /dev/null +++ b/src/posix/mod.rs @@ -0,0 +1,14 @@ +// posix socket and arguments definitions +// now all posix definitions are with P front like MSG -> PMSG, +// for better understanding and avoiding conflicts with other definitions +pub mod family; +mod msg_flag; +mod option; +mod option_level; +pub mod posix; +mod types; + +pub use msg_flag::MessageFlag as PMSG; // Socket message flags MSG_* +pub use option::Options as PSO; // Socket options SO_* +pub use option_level::OptionLevel as PSOL; // Socket options level SOL_* +pub use types::SOCK; // Socket types SOCK_* diff --git a/src/posix/msg_flag.rs b/src/posix/msg_flag.rs new file mode 100644 index 0000000..fcc69ad --- /dev/null +++ b/src/posix/msg_flag.rs @@ -0,0 +1,110 @@ +bitflags::bitflags! { + /// # Message Flags + /// Flags we can use with send/ and recv. \ + /// Added those for 1003.1g not all are supported yet + /// ## Reference + /// - [Linux Socket Flags](https://code.dragonos.org.cn/xref/linux-6.6.21/include/linux/socket.h#299) + pub struct MessageFlag: u32 { + /// `MSG_OOB` + /// `0b0000_0001`\ + /// Process out-of-band data. + const OOB = 1; + /// `MSG_PEEK` + /// `0b0000_0010`\ + /// Peek at an incoming message. + const PEEK = 2; + /// `MSG_DONTROUTE` + /// `0b0000_0100`\ + /// Don't use routing tables. + const DONTROUTE = 4; + /// `MSG_TRYHARD` + /// `0b0000_0100`\ + /// `MSG_TRYHARD` is not defined in the standard, but it is used in Linux. + const TRYHARD = 4; + /// `MSG_CTRUNC` + /// `0b0000_1000`\ + /// Control data lost before delivery. + const CTRUNC = 8; + /// `MSG_PROBE` + /// `0b0001_0000`\ + const PROBE = 0x10; + /// `MSG_TRUNC` + /// `0b0010_0000`\ + /// Data truncated before delivery. + const TRUNC = 0x20; + /// `MSG_DONTWAIT` + /// `0b0100_0000`\ + /// This flag is used to make the socket non-blocking. + const DONTWAIT = 0x40; + /// `MSG_EOR` + /// `0b1000_0000`\ + /// End of record. + const EOR = 0x80; + /// `MSG_WAITALL` + /// `0b0001_0000_0000`\ + /// Wait for full request or error. + const WAITALL = 0x100; + /// `MSG_FIN` + /// `0b0010_0000_0000`\ + /// Terminate the connection. + const FIN = 0x200; + /// `MSG_SYN` + /// `0b0100_0000_0000`\ + /// Synchronize sequence numbers. + const SYN = 0x400; + /// `MSG_CONFIRM` + /// `0b1000_0000_0000`\ + /// Confirm path validity. + const CONFIRM = 0x800; + /// `MSG_RST` + /// `0b0001_0000_0000_0000`\ + /// Reset the connection. + const RST = 0x1000; + /// `MSG_ERRQUEUE` + /// `0b0010_0000_0000_0000`\ + /// Fetch message from error queue. + const ERRQUEUE = 0x2000; + /// `MSG_NOSIGNAL` + /// `0b0100_0000_0000_0000`\ + /// Do not generate a signal. + const NOSIGNAL = 0x4000; + /// `MSG_MORE` + /// `0b1000_0000_0000_0000`\ + /// Sender will send more. + const MORE = 0x8000; + /// `MSG_WAITFORONE` + /// `0b0001_0000_0000_0000_0000`\ + /// For nonblocking operation. + const WAITFORONE = 0x10000; + /// `MSG_SENDPAGE_NOPOLICY` + /// `0b0010_0000_0000_0000_0000`\ + /// Sendpage: do not apply policy. + const SENDPAGE_NOPOLICY = 0x10000; + /// `MSG_BATCH` + /// `0b0100_0000_0000_0000_0000`\ + /// Sendpage: next message is batch. + const BATCH = 0x40000; + /// `MSG_EOF` + const EOF = Self::FIN.bits(); + /// `MSG_NO_SHARED_FRAGS` + const NO_SHARED_FRAGS = 0x80000; + /// `MSG_SENDPAGE_DECRYPTED` + const SENDPAGE_DECRYPTED = 0x10_0000; + + /// `MSG_ZEROCOPY` + const ZEROCOPY = 0x400_0000; + /// `MSG_SPLICE_PAGES` + const SPLICE_PAGES = 0x800_0000; + /// `MSG_FASTOPEN` + const FASTOPEN = 0x2000_0000; + /// `MSG_CMSG_CLOEXEC` + const CMSG_CLOEXEC = 0x4000_0000; + /// `MSG_CMSG_COMPAT` + // if define CONFIG_COMPAT + // const CMSG_COMPAT = 0x8000_0000; + const CMSG_COMPAT = 0; + /// `MSG_INTERNAL_SENDMSG_FLAGS` + const INTERNAL_SENDMSG_FLAGS + = Self::SPLICE_PAGES.bits() | Self::SENDPAGE_NOPOLICY.bits() | Self::SENDPAGE_DECRYPTED.bits(); + } +} diff --git a/src/posix/option.rs b/src/posix/option.rs new file mode 100644 index 0000000..ed16fdb --- /dev/null +++ b/src/posix/option.rs @@ -0,0 +1,94 @@ +use num_derive::{FromPrimitive, ToPrimitive}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +#[allow(non_camel_case_types)] +pub enum Options { + DEBUG = 1, + REUSEADDR = 2, + TYPE = 3, + ERROR = 4, + DONTROUTE = 5, + BROADCAST = 6, + SNDBUF = 7, + RCVBUF = 8, + SNDBUFFORCE = 32, + RCVBUFFORCE = 33, + KEEPALIVE = 9, + OOBINLINE = 10, + NO_CHECK = 11, + PRIORITY = 12, + LINGER = 13, + BSDCOMPAT = 14, + REUSEPORT = 15, + PASSCRED = 16, + PEERCRED = 17, + RCVLOWAT = 18, + SNDLOWAT = 19, + RCVTIMEO_OLD = 20, + SNDTIMEO_OLD = 21, + SECURITY_AUTHENTICATION = 22, + SECURITY_ENCRYPTION_TRANSPORT = 23, + SECURITY_ENCRYPTION_NETWORK = 24, + BINDTODEVICE = 25, + /// 与GET_FILTER相同 + ATTACH_FILTER = 26, + DETACH_FILTER = 27, + PEERNAME = 28, + ACCEPTCONN = 30, + PEERSEC = 31, + PASSSEC = 34, + MARK = 36, + PROTOCOL = 38, + DOMAIN = 39, + RXQ_OVFL = 40, + /// 与SCM_WIFI_STATUS相同 + WIFI_STATUS = 41, + PEEK_OFF = 42, + /* Instruct lower device to use last 4-bytes of skb data as FCS */ + NOFCS = 43, + LOCK_FILTER = 44, + SELECT_ERR_QUEUE = 45, + BUSY_POLL = 46, + MAX_PACING_RATE = 47, + BPF_EXTENSIONS = 48, + INCOMING_CPU = 49, + ATTACH_BPF = 50, + // DETACH_BPF = DETACH_FILTER, + ATTACH_REUSEPORT_CBPF = 51, + ATTACH_REUSEPORT_EBPF = 52, + CNX_ADVICE = 53, + SCM_TIMESTAMPING_OPT_STATS = 54, + MEMINFO = 55, + INCOMING_NAPI_ID = 56, + COOKIE = 57, + SCM_TIMESTAMPING_PKTINFO = 58, + PEERGROUPS = 59, + ZEROCOPY = 60, + /// 与SCM_TXTIME相同 + TXTIME = 61, + BINDTOIFINDEX = 62, + TIMESTAMP_OLD = 29, + TIMESTAMPNS_OLD = 35, + TIMESTAMPING_OLD = 37, + TIMESTAMP_NEW = 63, + TIMESTAMPNS_NEW = 64, + TIMESTAMPING_NEW = 65, + RCVTIMEO_NEW = 66, + SNDTIMEO_NEW = 67, + DETACH_REUSEPORT_BPF = 68, + PREFER_BUSY_POLL = 69, + BUSY_POLL_BUDGET = 70, + NETNS_COOKIE = 71, + BUF_LOCK = 72, + RESERVE_MEM = 73, + TXREHASH = 74, + RCVMARK = 75, +} + +impl TryFrom for Options { + type Error = linux_errnos::Errno; + fn try_from(x: u32) -> Result { + use num_traits::FromPrimitive; + ::from_u32(x).ok_or(Self::Error::EINVAL) + } +} diff --git a/src/posix/option_level.rs b/src/posix/option_level.rs new file mode 100644 index 0000000..48a97fd --- /dev/null +++ b/src/posix/option_level.rs @@ -0,0 +1,69 @@ +use num_derive::{FromPrimitive, ToPrimitive}; + +/// # SOL (Socket Option Level) +/// Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx +/// ## Reference +/// - [Setsockoptions(2) level](https://code.dragonos.org.cn/xref/linux-6.6.21/include/linux/socket.h#345) +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +#[allow(non_camel_case_types)] +pub enum OptionLevel { + IP = 0, + SOCKET = 1, + // ICMP = 1, No-no-no! Due to Linux :-) we cannot + TCP = 6, + UDP = 17, + IPV6 = 41, + ICMPV6 = 58, + SCTP = 132, + UDPLITE = 136, // UDP-Lite (RFC 3828) + RAW = 255, + IPX = 256, + AX25 = 257, + ATALK = 258, + NETROM = 259, + ROSE = 260, + DECNET = 261, + X25 = 262, + PACKET = 263, + ATM = 264, // ATM layer (cell level) + AAL = 265, // ATM Adaption Layer (packet level) + IRDA = 266, + NETBEUI = 267, + LLC = 268, + DCCP = 269, + NETLINK = 270, + TIPC = 271, + RXRPC = 272, + PPPOL2TP = 273, + BLUETOOTH = 274, + PNPIPE = 275, + RDS = 276, + IUCV = 277, + CAIF = 278, + ALG = 279, + NFC = 280, + KCM = 281, + TLS = 282, + XDP = 283, + MPTCP = 284, + MCTP = 285, + SMC = 286, + VSOCK = 287, +} + +impl TryFrom for OptionLevel { + type Error = linux_errnos::Errno; + + fn try_from(value: u32) -> Result { + match ::from_u32(value) { + Some(p) => Ok(p), + None => Err(linux_errnos::Errno::EPROTONOSUPPORT), + } + } +} + +impl From for u32 { + fn from(value: OptionLevel) -> Self { + ::to_u32(&value).unwrap() + } +} diff --git a/src/posix/posix.rs b/src/posix/posix.rs new file mode 100644 index 0000000..d682c4b --- /dev/null +++ b/src/posix/posix.rs @@ -0,0 +1,84 @@ +// +// posix.rs 记录了系统调用时用到的结构 +// +bitflags::bitflags! { + // #[derive(PartialEq, Eq, Debug, Clone, Copy)] + pub struct PosixArgsSocketType: u32 { + const DGRAM = 1; // 0b0000_0001 + const STREAM = 2; // 0b0000_0010 + const RAW = 3; // 0b0000_0011 + const RDM = 4; // 0b0000_0100 + const SEQPACKET = 5; // 0b0000_0101 + const DCCP = 6; // 0b0000_0110 + const PACKET = 10; // 0b0000_1010 + + const NONBLOCK = 0b0000_1000; // 0x8000_0000 + const CLOEXEC = 0b0000_0100; // 0x4000_0000 + } +} + +impl PosixArgsSocketType { + #[inline(always)] + pub(super) fn types(&self) -> PosixArgsSocketType { + PosixArgsSocketType::from_bits(self.bits() & 0b_1111).unwrap() + } + + #[inline(always)] + pub fn is_nonblock(&self) -> bool { + self.contains(PosixArgsSocketType::NONBLOCK) + } + + #[inline(always)] + pub fn is_cloexec(&self) -> bool { + self.contains(PosixArgsSocketType::CLOEXEC) + } +} + +use super::family::AddressFamily; + +// use super::socket::{endpoint::Endpoint, AddressFamily}; + +// 参考资料: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_in.h.html#tag_13_32 +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SockAddrIn { + pub sin_family: u16, + pub sin_port: u16, + pub sin_addr: u32, + pub sin_zero: [u8; 8], +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SockAddrUn { + pub sun_family: u16, + pub sun_path: [u8; 108], +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SockAddrLl { + pub sll_family: u16, + pub sll_protocol: u16, + pub sll_ifindex: u32, + pub sll_hatype: u16, + pub sll_pkttype: u8, + pub sll_halen: u8, + pub sll_addr: [u8; 8], +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SockAddrNl { + pub nl_family: AddressFamily, + pub nl_pad: u16, + pub nl_pid: u32, + pub nl_groups: u32, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct SockAddrPlaceholder { + pub family: u16, + pub data: [u8; 14], +} diff --git a/src/posix/types.rs b/src/posix/types.rs new file mode 100644 index 0000000..e736df8 --- /dev/null +++ b/src/posix/types.rs @@ -0,0 +1,21 @@ +use linux_errnos::Errno; +use num_derive::{FromPrimitive, ToPrimitive}; +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +pub enum SOCK { + Stream = 1, + Datagram = 2, + Raw = 3, + RDM = 4, + SeqPacket = 5, + DCCP = 6, + Packet = 10, +} + +use super::posix::PosixArgsSocketType; +impl TryFrom for SOCK { + type Error = Errno; + fn try_from(x: PosixArgsSocketType) -> Result { + use num_traits::FromPrimitive; + ::from_u32(x.types().bits()).ok_or(Self::Error::EINVAL) + } +} diff --git a/src/process/mod.rs b/src/process/mod.rs new file mode 100644 index 0000000..2b812fd --- /dev/null +++ b/src/process/mod.rs @@ -0,0 +1,11 @@ +// To make compatible +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Pid(usize); + +pub struct ProcessManager {} + +impl ProcessManager { + pub fn current_pid() -> Pid { + Pid(std::thread::current().id().as_u64().get() as usize) + } +} diff --git a/src/socket/common/mod.rs b/src/socket/common/mod.rs new file mode 100644 index 0000000..d3b181f --- /dev/null +++ b/src/socket/common/mod.rs @@ -0,0 +1,18 @@ +// pub mod poll_unit; +// mod epoll_items; + +pub mod shutdown; +// pub use epoll_items::EPollItems; + +// /// @brief 在trait Socket的metadata函数中返回该结构体供外部使用 +// #[derive(Debug, Clone)] +// pub struct Metadata { +// /// 接收缓冲区的大小 +// pub rx_buf_size: usize, +// /// 发送缓冲区的大小 +// pub tx_buf_size: usize, +// /// 元数据的缓冲区的大小 +// pub metadata_buf_size: usize, +// /// socket的选项 +// pub options: SocketOptions, +// } diff --git a/src/socket/common/shutdown.rs b/src/socket/common/shutdown.rs new file mode 100644 index 0000000..365e61d --- /dev/null +++ b/src/socket/common/shutdown.rs @@ -0,0 +1,136 @@ +// TODO: 其他模块需要实现shutdown的具体逻辑 +#![allow(dead_code)] +use core::sync::atomic::AtomicU8; + +use linux_errnos::Errno; + +bitflags::bitflags! { + /// @brief 用于指定socket的关闭类型 + /// 参考:https://code.dragonos.org.cn/xref/linux-6.1.9/include/net/sock.h?fi=SHUTDOWN_MASK#1573 + #[derive(Debug, PartialEq, Eq)] + pub struct ShutdownBit: u8 { + const SHUT_RD = 0; + const SHUT_WR = 1; + const SHUT_RDWR = 2; + } +} + +const RCV_SHUTDOWN: u8 = 0x01; +const SEND_SHUTDOWN: u8 = 0x02; +const SHUTDOWN_MASK: u8 = 0x03; + +#[derive(Debug, Default)] +pub struct Shutdown { + bit: AtomicU8, +} + +impl From for Shutdown { + fn from(shutdown_bit: ShutdownBit) -> Self { + match shutdown_bit { + ShutdownBit::SHUT_RD => Shutdown { + bit: AtomicU8::new(RCV_SHUTDOWN), + }, + ShutdownBit::SHUT_WR => Shutdown { + bit: AtomicU8::new(SEND_SHUTDOWN), + }, + ShutdownBit::SHUT_RDWR => Shutdown { + bit: AtomicU8::new(SHUTDOWN_MASK), + }, + _ => Shutdown::default(), + } + } +} + +impl Shutdown { + pub fn new() -> Self { + Self { + bit: AtomicU8::new(0), + } + } + + pub fn recv_shutdown(&self) { + self.bit + .fetch_or(RCV_SHUTDOWN, core::sync::atomic::Ordering::SeqCst); + } + + pub fn send_shutdown(&self) { + self.bit + .fetch_or(SEND_SHUTDOWN, core::sync::atomic::Ordering::SeqCst); + } + + pub fn is_recv_shutdown(&self) -> bool { + self.bit.load(core::sync::atomic::Ordering::SeqCst) & RCV_SHUTDOWN != 0 + } + + pub fn is_send_shutdown(&self) -> bool { + self.bit.load(core::sync::atomic::Ordering::SeqCst) & SEND_SHUTDOWN != 0 + } + + pub fn is_both_shutdown(&self) -> bool { + self.bit.load(core::sync::atomic::Ordering::SeqCst) & SHUTDOWN_MASK == SHUTDOWN_MASK + } + + pub fn is_empty(&self) -> bool { + self.bit.load(core::sync::atomic::Ordering::SeqCst) == 0 + } + + pub fn from_how(how: usize) -> Self { + Self::from(ShutdownBit::from_bits_truncate(how as u8)) + } + + pub fn get(&self) -> ShutdownTemp { + ShutdownTemp { + bit: self.bit.load(core::sync::atomic::Ordering::SeqCst), + } + } +} + +pub struct ShutdownTemp { + bit: u8, +} + +impl ShutdownTemp { + pub fn is_recv_shutdown(&self) -> bool { + self.bit & RCV_SHUTDOWN != 0 + } + + pub fn is_send_shutdown(&self) -> bool { + self.bit & SEND_SHUTDOWN != 0 + } + + pub fn is_both_shutdown(&self) -> bool { + self.bit & SHUTDOWN_MASK == SHUTDOWN_MASK + } + + pub fn is_empty(&self) -> bool { + self.bit == 0 + } + + pub fn bits(&self) -> ShutdownBit { + ShutdownBit::from_bits_truncate(self.bit) + } +} + +impl From for ShutdownTemp { + fn from(shutdown_bit: ShutdownBit) -> Self { + match shutdown_bit { + ShutdownBit::SHUT_RD => Self { bit: RCV_SHUTDOWN }, + ShutdownBit::SHUT_WR => Self { bit: SEND_SHUTDOWN }, + ShutdownBit::SHUT_RDWR => Self { bit: SHUTDOWN_MASK }, + _ => Self { bit: 0 }, + } + } +} + +impl TryFrom for ShutdownTemp { + type Error = Errno; + + fn try_from(value: usize) -> Result { + match value { + 0..2 => Ok(ShutdownTemp { + bit: value as u8 + 1, + }), + _ => Err(Errno::EINVAL), + } + } +} diff --git a/src/socket/endpoint.rs b/src/socket/endpoint.rs new file mode 100644 index 0000000..fba1b17 --- /dev/null +++ b/src/socket/endpoint.rs @@ -0,0 +1,44 @@ +// use crate::{filesystem::vfs::InodeId, net::socket}; +// use alloc::{string::String, sync::Arc}; + +pub use smoltcp::wire::IpEndpoint; + +// use super::unix::ns::abs::AbsHandle; + +#[derive(Debug, Clone)] +pub enum Endpoint { + // /// 链路层端点 + // LinkLayer(LinkLayerEndpoint), + /// 网络层端点 + Ip(IpEndpoint), + // /// inode端点,Unix实际保存的端点 + // Inode((Arc, String)), + // /// Unix传递id索引和path所用的端点 + // Unixpath((InodeId, String)), + // /// Unix抽象端点 + // Abspath((AbsHandle, String)), +} + +// /// @brief 链路层端点 +// #[derive(Debug, Clone)] +// pub struct LinkLayerEndpoint { +// /// 网卡的接口号 +// pub interface: usize, +// } + +// impl LinkLayerEndpoint { +// /// @brief 创建一个链路层端点 +// /// +// /// @param interface 网卡的接口号 +// /// +// /// @return 返回创建的链路层端点 +// pub fn new(interface: usize) -> Self { +// Self { interface } +// } +// } + +impl From for Endpoint { + fn from(endpoint: IpEndpoint) -> Self { + Self::Ip(endpoint) + } +} diff --git a/src/socket/inet/common/mod.rs b/src/socket/inet/common/mod.rs new file mode 100644 index 0000000..dd77a40 --- /dev/null +++ b/src/socket/inet/common/mod.rs @@ -0,0 +1,157 @@ +use std::collections::BTreeMap; + +// use crate::net::{Iface, NET_DEVICES}; +use crate::interface::Iface; +use alloc::sync::Arc; + +pub mod port; +use linux_errnos::Errno as SystemError; +pub use port::PortManager; +use spin::RwLock; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Types { + Raw, + Icmp, + Udp, + Tcp, + Dhcpv4, + Dns, +} + +lazy_static::lazy_static! { + /// # 所有网络接口的列表 + /// 这个列表在中断上下文会使用到,因此需要irqsave + pub static ref NET_DEVICES: RwLock>> = RwLock::new(BTreeMap::new()); +} + +/** + * 目前,以下设计仍然没有考虑多网卡的listen问题,仅只解决了socket在绑定单网卡下的问题。 + */ +#[derive(Debug)] +pub struct BoundInner { + handle: smoltcp::iface::SocketHandle, + iface: Arc, + // inner: Vec<(smoltcp::iface::SocketHandle, Arc)> + // address: smoltcp::wire::IpAddress, +} + +impl BoundInner { + /// # `bind` + /// 将socket绑定到指定的地址上,置入指定的网络接口中 + pub fn bind( + socket: T, + // socket_type: Types, + address: &smoltcp::wire::IpAddress, + ) -> Result + where + T: smoltcp::socket::AnySocket<'static>, + { + if address.is_unspecified() { + // 强绑VirtualIO + let iface = NET_DEVICES + .read() + .iter() + .find_map(|(_, v)| { + if v.common().is_default_iface() { + Some(v.clone()) + } else { + None + } + }) + .expect("No default interface"); + + let handle = iface.sockets().lock().add(socket); + Ok(Self { handle, iface }) + } else { + let iface = get_iface_to_bind(address).ok_or(SystemError::ENODEV)?; + let handle = iface.sockets().lock().add(socket); + Ok(Self { handle, iface }) + } + } + + pub fn bind_ephemeral( + socket: T, + // socket_type: Types, + remote: smoltcp::wire::IpAddress, + ) -> Result<(Self, smoltcp::wire::IpAddress), SystemError> + where + T: smoltcp::socket::AnySocket<'static>, + { + let (iface, address) = get_ephemeral_iface(&remote); + // let bound_port = iface.port_manager().bind_ephemeral_port(socket_type)?; + let handle = iface.sockets().lock().add(socket); + // let endpoint = smoltcp::wire::IpEndpoint::new(local_addr, bound_port); + Ok((Self { handle, iface }, address)) + } + + pub fn port_manager(&self) -> &PortManager { + self.iface.port_manager() + } + + pub fn with_mut, R, F: FnMut(&mut T) -> R>( + &self, + mut f: F, + ) -> R { + f(self.iface.sockets().lock().get_mut::(self.handle)) + } + + pub fn with, R, F: Fn(&T) -> R>(&self, f: F) -> R { + f(self.iface.sockets().lock().get::(self.handle)) + } + + pub fn iface(&self) -> &Arc { + &self.iface + } + + pub fn release(&self) { + self.iface.sockets().lock().remove(self.handle); + } +} + +#[inline] +pub fn get_iface_to_bind(ip_addr: &smoltcp::wire::IpAddress) -> Option> { + // log::debug!("get_iface_to_bind: {:?}", ip_addr); + // if ip_addr.is_unspecified() + NET_DEVICES + .read() + .iter() + .find(|(_, iface)| { + let guard = iface.smol_iface().lock(); + // log::debug!("iface name: {}, ip: {:?}", iface.iface_name(), guard.ip_addrs()); + guard.has_ip_addr(*ip_addr) + }) + .map(|(_, iface)| iface.clone()) +} + +/// Get a suitable iface to deal with sendto/connect request if the socket is not bound to an iface. +/// If the remote address is the same as that of some iface, we will use the iface. +/// Otherwise, we will use a default interface. +fn get_ephemeral_iface( + remote_ip_addr: &smoltcp::wire::IpAddress, +) -> (Arc, smoltcp::wire::IpAddress) { + get_iface_to_bind(remote_ip_addr) + .map(|iface| (iface, *remote_ip_addr)) + .or({ + let ifaces = NET_DEVICES.read(); + ifaces.iter().find_map(|(_, iface)| { + iface + .smol_iface() + .lock() + .ip_addrs() + .iter() + .find(|cidr| cidr.contains_addr(remote_ip_addr)) + .map(|cidr| (iface.clone(), cidr.address())) + }) + }) + .or({ + NET_DEVICES.read().values().next().map(|iface| { + ( + iface.clone(), + iface.smol_iface().lock().ip_addrs()[0].address(), + ) + }) + }) + .expect("No network interface") +} diff --git a/src/socket/inet/common/port.rs b/src/socket/inet/common/port.rs new file mode 100644 index 0000000..a3a203a --- /dev/null +++ b/src/socket/inet/common/port.rs @@ -0,0 +1,121 @@ +use hashbrown::HashMap; +use libc::rand; +use linux_errnos::Errno as SystemError; + +use crate::{ + // arch::rand::rand, + libs::spinlock::SpinLock, + process::{Pid, ProcessManager}, +}; + +use super::Types::{self, *}; + +/// # TCP 和 UDP 的端口管理器。 +/// 如果 TCP/UDP 的 socket 绑定了某个端口,它会在对应的表中记录,以检测端口冲突。 +#[derive(Debug)] +pub struct PortManager { + // TCP 端口记录表 + tcp_port_table: SpinLock>, + // UDP 端口记录表 + udp_port_table: SpinLock>, +} + +impl Default for PortManager { + fn default() -> Self { + Self::new() + } +} + +impl PortManager { + pub fn new() -> Self { + Self { + tcp_port_table: SpinLock::new(HashMap::new()), + udp_port_table: SpinLock::new(HashMap::new()), + } + } + + /// @brief 自动分配一个相对应协议中未被使用的PORT,如果动态端口均已被占用,返回错误码 EADDRINUSE + pub fn get_ephemeral_port(&self, socket_type: Types) -> Result { + // TODO: selects non-conflict high port + + static mut EPHEMERAL_PORT: u16 = 0; + unsafe { + if EPHEMERAL_PORT == 0 { + EPHEMERAL_PORT = (49152 + rand() % (65536 - 49152)) as u16; + } + } + + let mut remaining = 65536 - 49152; // 剩余尝试分配端口次数 + let mut port: u16; + while remaining > 0 { + unsafe { + if EPHEMERAL_PORT == 65535 { + EPHEMERAL_PORT = 49152; + } else { + EPHEMERAL_PORT += 1; + } + port = EPHEMERAL_PORT; + } + + // 使用 ListenTable 检查端口是否被占用 + let listen_table_guard = match socket_type { + Udp => self.udp_port_table.lock(), + Tcp => self.tcp_port_table.lock(), + _ => panic!("{:?} cann't get a port", socket_type), + }; + if listen_table_guard.get(&port).is_none() { + drop(listen_table_guard); + return Ok(port); + } + remaining -= 1; + } + Err(SystemError::EADDRINUSE) + } + + #[inline] + pub fn bind_ephemeral_port(&self, socket_type: Types) -> Result { + let port = self.get_ephemeral_port(socket_type)?; + self.bind_port(socket_type, port)?; + Ok(port) + } + + /// @brief 检测给定端口是否已被占用,如果未被占用则在 TCP/UDP 对应的表中记录 + /// + /// TODO: 增加支持端口复用的逻辑 + pub fn bind_port(&self, socket_type: Types, port: u16) -> Result<(), SystemError> { + if port > 0 { + match socket_type { + Udp => { + let mut guard = self.udp_port_table.lock(); + if guard.get(&port).is_some() { + return Err(SystemError::EADDRINUSE); + } + guard.insert(port, ProcessManager::current_pid()); + } + Tcp => { + let mut guard = self.tcp_port_table.lock(); + if guard.get(&port).is_some() { + return Err(SystemError::EADDRINUSE); + } + guard.insert(port, ProcessManager::current_pid()); + } + _ => {} + }; + } + Ok(()) + } + + /// @brief 在对应的端口记录表中将端口和 socket 解绑 + /// should call this function when socket is closed or aborted + pub fn unbind_port(&self, socket_type: Types, port: u16) { + match socket_type { + Udp => { + self.udp_port_table.lock().remove(&port); + } + Tcp => { + self.tcp_port_table.lock().remove(&port); + } + _ => {} + }; + } +} diff --git a/src/socket/inet/datagram/inner.rs b/src/socket/inet/datagram/inner.rs new file mode 100644 index 0000000..2d537b9 --- /dev/null +++ b/src/socket/inet/datagram/inner.rs @@ -0,0 +1,167 @@ +use linux_errnos::Errno as SystemError; +use smoltcp; + +use crate::{ + libs::spinlock::SpinLock, + socket::inet::common::{BoundInner, Types as InetTypes}, +}; + +pub type SmolUdpSocket = smoltcp::socket::udp::Socket<'static>; + +pub const DEFAULT_METADATA_BUF_SIZE: usize = 1024; +pub const DEFAULT_RX_BUF_SIZE: usize = 64 * 1024; +pub const DEFAULT_TX_BUF_SIZE: usize = 64 * 1024; + +#[derive(Debug)] +pub struct UnboundUdp { + socket: SmolUdpSocket, +} + +impl Default for UnboundUdp { + fn default() -> Self { + Self::new() + } +} + +impl UnboundUdp { + pub fn new() -> Self { + let rx_buffer = smoltcp::socket::udp::PacketBuffer::new( + vec![smoltcp::socket::udp::PacketMetadata::EMPTY; DEFAULT_METADATA_BUF_SIZE], + vec![0; DEFAULT_RX_BUF_SIZE], + ); + let tx_buffer = smoltcp::socket::udp::PacketBuffer::new( + vec![smoltcp::socket::udp::PacketMetadata::EMPTY; DEFAULT_METADATA_BUF_SIZE], + vec![0; DEFAULT_TX_BUF_SIZE], + ); + let socket = SmolUdpSocket::new(rx_buffer, tx_buffer); + + Self { socket } + } + + pub fn bind(self, local_endpoint: smoltcp::wire::IpEndpoint) -> Result { + let inner = BoundInner::bind(self.socket, &local_endpoint.addr)?; + let bind_addr = local_endpoint.addr; + let bind_port = if local_endpoint.port == 0 { + inner.port_manager().bind_ephemeral_port(InetTypes::Udp)? + } else { + inner + .port_manager() + .bind_port(InetTypes::Udp, local_endpoint.port)?; + local_endpoint.port + }; + + if bind_addr.is_unspecified() { + if inner + .with_mut::(|socket| socket.bind(bind_port)) + .is_err() + { + return Err(SystemError::EINVAL); + } + } else if inner + .with_mut::(|socket| { + socket.bind(smoltcp::wire::IpEndpoint::new(bind_addr, bind_port)) + }) + .is_err() + { + return Err(SystemError::EINVAL); + } + Ok(BoundUdp { + inner, + remote: SpinLock::new(None), + }) + } + + pub fn bind_ephemeral(self, remote: smoltcp::wire::IpAddress) -> Result { + // let (addr, port) = (remote.addr, remote.port); + let (inner, address) = BoundInner::bind_ephemeral(self.socket, remote)?; + let bound_port = inner.port_manager().bind_ephemeral_port(InetTypes::Udp)?; + let endpoint = smoltcp::wire::IpEndpoint::new(address, bound_port); + Ok(BoundUdp { + inner, + remote: SpinLock::new(Some(endpoint)), + }) + } +} + +#[derive(Debug)] +pub struct BoundUdp { + inner: BoundInner, + remote: SpinLock>, +} + +impl BoundUdp { + pub fn with_mut_socket(&self, f: F) -> T + where + F: FnMut(&mut SmolUdpSocket) -> T, + { + self.inner.with_mut(f) + } + + pub fn with_socket(&self, f: F) -> T + where + F: Fn(&SmolUdpSocket) -> T, + { + self.inner.with(f) + } + + pub fn endpoint(&self) -> smoltcp::wire::IpListenEndpoint { + self.inner + .with::(|socket| socket.endpoint()) + } + + pub fn connect(&self, remote: smoltcp::wire::IpEndpoint) { + self.remote.lock().replace(remote); + } + + #[inline] + pub fn try_recv( + &self, + buf: &mut [u8], + ) -> Result<(usize, smoltcp::wire::IpEndpoint), SystemError> { + self.with_mut_socket(|socket| { + if socket.can_recv() { + if let Ok((size, metadata)) = socket.recv_slice(buf) { + return Ok((size, metadata.endpoint)); + } + } + Err(SystemError::EAGAIN) + }) + } + + pub fn try_send( + &self, + buf: &[u8], + to: Option, + ) -> Result { + let remote = to.or(*self.remote.lock()).ok_or(SystemError::ENOTCONN)?; + + self.with_mut_socket(|socket| { + if socket.can_send() && socket.send_slice(buf, remote).is_ok() { + log::debug!("send {} bytes", buf.len()); + return Ok(buf.len()); + } + Err(SystemError::ENOBUFS) + }) + } + + pub fn inner(&self) -> &BoundInner { + &self.inner + } + + pub fn close(&self) { + self.inner + .iface() + .port_manager() + .unbind_port(InetTypes::Udp, self.endpoint().port); + self.with_mut_socket(|socket| { + socket.close(); + }); + } +} + +// Udp Inner 负责其内部资源管理 +#[derive(Debug)] +pub enum UdpInner { + Unbound(UnboundUdp), + Bound(BoundUdp), +} diff --git a/src/socket/inet/datagram/mod.rs b/src/socket/inet/datagram/mod.rs new file mode 100644 index 0000000..4dd58ae --- /dev/null +++ b/src/socket/inet/datagram/mod.rs @@ -0,0 +1,306 @@ +use inner::{UdpInner, UnboundUdp}; +use linux_errnos::Errno as SystemError; +use smoltcp; + +use crate::event_poll::EPollEventType; +use crate::libs::wait_queue::{wq_wait_event_interruptible, WaitQueue}; +use crate::socket::{Socket, PMSG}; +use crate::{libs::rwlock::RwLock, socket::endpoint::Endpoint}; +use alloc::sync::{Arc, Weak}; +use core::sync::atomic::AtomicBool; + +use super::InetSocket; + +pub mod inner; + +type EP = EPollEventType; + +// Udp Socket 负责提供状态切换接口、执行状态切换 +#[derive(Debug)] +pub struct UdpSocket { + inner: RwLock>, + nonblock: AtomicBool, + wait_queue: WaitQueue, + self_ref: Weak, +} + +impl UdpSocket { + pub fn new(nonblock: bool) -> Arc { + Arc::new_cyclic(|me| Self { + inner: RwLock::new(Some(UdpInner::Unbound(UnboundUdp::new()))), + nonblock: AtomicBool::new(nonblock), + wait_queue: WaitQueue::default(), + self_ref: me.clone(), + }) + } + + pub fn is_nonblock(&self) -> bool { + self.nonblock.load(core::sync::atomic::Ordering::Relaxed) + } + + pub fn do_bind(&self, local_endpoint: smoltcp::wire::IpEndpoint) -> Result<(), SystemError> { + let mut inner = self.inner.write(); + if let Some(UdpInner::Unbound(unbound)) = inner.take() { + let bound = unbound.bind(local_endpoint)?; + + bound + .inner() + .iface() + .common() + .bind_socket(self.self_ref.upgrade().unwrap()); + *inner = Some(UdpInner::Bound(bound)); + return Ok(()); + } + Err(SystemError::EINVAL) + } + + pub fn bind_emphemeral(&self, remote: smoltcp::wire::IpAddress) -> Result<(), SystemError> { + let mut inner_guard = self.inner.write(); + let bound = match inner_guard.take().expect("Udp inner is None") { + UdpInner::Bound(inner) => inner, + UdpInner::Unbound(inner) => inner.bind_ephemeral(remote)?, + }; + inner_guard.replace(UdpInner::Bound(bound)); + Ok(()) + } + + pub fn is_bound(&self) -> bool { + let inner = self.inner.read(); + if let Some(UdpInner::Bound(_)) = &*inner { + return true; + } + false + } + + pub fn close(&self) { + let mut inner = self.inner.write(); + if let Some(UdpInner::Bound(bound)) = &mut *inner { + bound.close(); + inner.take(); + } + // unbound socket just drop (only need to free memory) + } + + pub fn try_recv( + &self, + buf: &mut [u8], + ) -> Result<(usize, smoltcp::wire::IpEndpoint), SystemError> { + match self.inner.read().as_ref().expect("Udp Inner is None") { + UdpInner::Bound(bound) => { + let ret = bound.try_recv(buf); + bound.inner().iface().poll(); + ret + } + _ => Err(SystemError::ENOTCONN), + } + } + + #[inline] + pub fn can_recv(&self) -> bool { + self.event().contains(EP::EPOLLIN) + } + + #[inline] + #[allow(dead_code)] + pub fn can_send(&self) -> bool { + self.event().contains(EP::EPOLLOUT) + } + + pub fn try_send( + &self, + buf: &[u8], + to: Option, + ) -> Result { + { + let mut inner_guard = self.inner.write(); + let inner = match inner_guard.take().expect("Udp Inner is None") { + UdpInner::Bound(bound) => bound, + UdpInner::Unbound(unbound) => { + unbound.bind_ephemeral(to.ok_or(SystemError::EADDRNOTAVAIL)?.addr)? + } + }; + // size = inner.try_send(buf, to)?; + inner_guard.replace(UdpInner::Bound(inner)); + }; + // Optimize: 拿两次锁的平均效率是否比一次长时间的读锁效率要高? + let result = match self.inner.read().as_ref().expect("Udp Inner is None") { + UdpInner::Bound(bound) => { + let ret = bound.try_send(buf, to); + bound.inner().iface().poll(); + ret + } + _ => Err(SystemError::ENOTCONN), + }; + result + } + + pub fn event(&self) -> EPollEventType { + let mut event = EPollEventType::empty(); + match self.inner.read().as_ref().unwrap() { + UdpInner::Unbound(_) => { + event.insert(EP::EPOLLOUT | EP::EPOLLWRNORM | EP::EPOLLWRBAND); + } + UdpInner::Bound(bound) => { + let (can_recv, can_send) = + bound.with_socket(|socket| (socket.can_recv(), socket.can_send())); + + if can_recv { + event.insert(EP::EPOLLIN | EP::EPOLLRDNORM); + } + + if can_send { + event.insert(EP::EPOLLOUT | EP::EPOLLWRNORM | EP::EPOLLWRBAND); + } + } + } + event + } +} + +impl Socket for UdpSocket { + fn wait_queue(&self) -> &WaitQueue { + &self.wait_queue + } + + fn poll(&self) -> usize { + self.event().bits() as usize + } + + fn bind(&self, local_endpoint: Endpoint) -> Result<(), SystemError> { + if let Endpoint::Ip(local_endpoint) = local_endpoint { + return self.do_bind(local_endpoint); + } + Err(SystemError::EAFNOSUPPORT) + } + + fn send_buffer_size(&self) -> usize { + match self.inner.read().as_ref().unwrap() { + UdpInner::Bound(bound) => bound.with_socket(|socket| socket.payload_send_capacity()), + _ => inner::DEFAULT_TX_BUF_SIZE, + } + } + + fn recv_buffer_size(&self) -> usize { + match self.inner.read().as_ref().unwrap() { + UdpInner::Bound(bound) => bound.with_socket(|socket| socket.payload_recv_capacity()), + _ => inner::DEFAULT_RX_BUF_SIZE, + } + } + + fn connect(&self, endpoint: Endpoint) -> Result<(), SystemError> { + if let Endpoint::Ip(remote) = endpoint { + if !self.is_bound() { + self.bind_emphemeral(remote.addr)?; + } + if let UdpInner::Bound(inner) = self.inner.read().as_ref().expect("UDP Inner disappear") + { + inner.connect(remote); + return Ok(()); + } else { + panic!(""); + } + } + Err(SystemError::EAFNOSUPPORT) + } + + fn send(&self, buffer: &[u8], flags: PMSG) -> Result { + if flags.contains(PMSG::DONTWAIT) { + log::warn!("Nonblock send is not implemented yet"); + } + + self.try_send(buffer, None) + } + + fn send_to(&self, buffer: &[u8], flags: PMSG, address: Endpoint) -> Result { + if flags.contains(PMSG::DONTWAIT) { + log::warn!("Nonblock send is not implemented yet"); + } + + if let Endpoint::Ip(remote) = address { + return self.try_send(buffer, Some(remote)); + } + + Err(SystemError::EINVAL) + } + + fn recv(&self, buffer: &mut [u8], flags: PMSG) -> Result { + if self.is_nonblock() || flags.contains(PMSG::DONTWAIT) { + self.try_recv(buffer) + } else { + loop { + match self.try_recv(buffer) { + Err(SystemError::EAGAIN) => { + wq_wait_event_interruptible(&self.wait_queue, || self.can_recv(), None)?; + } + result => break result, + } + } + } + .map(|(len, _)| len) + } + + fn recv_from( + &self, + buffer: &mut [u8], + flags: PMSG, + address: Option, + ) -> Result<(usize, Endpoint), SystemError> { + // could block io + if let Some(endpoint) = address { + self.connect(endpoint)?; + } + + if self.is_nonblock() || flags.contains(PMSG::DONTWAIT) { + self.try_recv(buffer) + } else { + loop { + match self.try_recv(buffer) { + Err(SystemError::EAGAIN) => { + wq_wait_event_interruptible(&self.wait_queue, || self.can_recv(), None)?; + log::debug!("UdpSocket::recv_from: wake up"); + } + result => break result, + } + } + } + .map(|(len, remote)| (len, Endpoint::Ip(remote))) + } + + fn close(&self) -> Result<(), SystemError> { + self.close(); + Ok(()) + } +} + +impl InetSocket for UdpSocket { + fn on_iface_events(&self) { + } +} + +bitflags::bitflags! { + pub struct UdpSocketOptions: u32 { + const ZERO = 0; /* No UDP options */ + const UDP_CORK = 1; /* Never send partially complete segments */ + const UDP_ENCAP = 100; /* Set the socket to accept encapsulated packets */ + const UDP_NO_CHECK6_TX = 101; /* Disable sending checksum for UDP6X */ + const UDP_NO_CHECK6_RX = 102; /* Disable accepting checksum for UDP6 */ + const UDP_SEGMENT = 103; /* Set GSO segmentation size */ + const UDP_GRO = 104; /* This socket can receive UDP GRO packets */ + + const UDPLITE_SEND_CSCOV = 10; /* sender partial coverage (as sent) */ + const UDPLITE_RECV_CSCOV = 11; /* receiver partial coverage (threshold ) */ + } +} + +bitflags::bitflags! { + pub struct UdpEncapTypes: u8 { + const ZERO = 0; + const ESPINUDP_NON_IKE = 1; // draft-ietf-ipsec-nat-t-ike-00/01 + const ESPINUDP = 2; // draft-ietf-ipsec-udp-encaps-06 + const L2TPINUDP = 3; // rfc2661 + const GTP0 = 4; // GSM TS 09.60 + const GTP1U = 5; // 3GPP TS 29.060 + const RXRPC = 6; + const ESPINTCP = 7; // Yikes, this is really xfrm encap types. + } +} diff --git a/src/socket/inet/mod.rs b/src/socket/inet/mod.rs new file mode 100644 index 0000000..45937d6 --- /dev/null +++ b/src/socket/inet/mod.rs @@ -0,0 +1,40 @@ +use smoltcp; + +// pub mod raw; +// pub mod icmp; +pub mod common; +pub mod datagram; +// pub mod stream; +pub mod posix; +pub mod syscall; + +pub use common::BoundInner; +pub use common::Types; +// pub use raw::RawSocket; +pub use datagram::UdpSocket; + +use smoltcp::wire::IpAddress; +use smoltcp::wire::IpEndpoint; +use smoltcp::wire::Ipv4Address; +// use smoltcp::wire::Ipv6Address; + +// pub use stream::TcpSocket; +// pub use syscall::Inet; + +use super::Socket; + +/// A local endpoint, which indicates that the local endpoint is unspecified. +/// +/// According to the Linux man pages and the Linux implementation, `getsockname()` will _not_ fail +/// even if the socket is unbound. Instead, it will return an unspecified socket address. This +/// unspecified endpoint helps with that. +const UNSPECIFIED_LOCAL_ENDPOINT_V4: IpEndpoint = + IpEndpoint::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); +// const UNSPECIFIED_LOCAL_ENDPOINT_V6: IpEndpoint = +// IpEndpoint::new(IpAddress::Ipv6(Ipv6Address::UNSPECIFIED), 0); + +pub trait InetSocket: Socket { + /// `on_iface_events` + /// 通知socket发生的事件 + fn on_iface_events(&self); +} diff --git a/src/socket/inet/posix/mod.rs b/src/socket/inet/posix/mod.rs new file mode 100644 index 0000000..de45d94 --- /dev/null +++ b/src/socket/inet/posix/mod.rs @@ -0,0 +1,2 @@ +pub mod option; +pub mod proto; diff --git a/src/socket/inet/posix/option.rs b/src/socket/inet/posix/option.rs new file mode 100644 index 0000000..8a3872d --- /dev/null +++ b/src/socket/inet/posix/option.rs @@ -0,0 +1,67 @@ +bitflags::bitflags! { + pub struct IpOptions: u32 { + const IP_TOS = 1; // Type of service + const IP_TTL = 2; // Time to live + const IP_HDRINCL = 3; // Header compression + const IP_OPTIONS = 4; // IP options + const IP_ROUTER_ALERT = 5; // Router alert + const IP_RECVOPTS = 6; // Receive options + const IP_RETOPTS = 7; // Return options + const IP_PKTINFO = 8; // Packet information + const IP_PKTOPTIONS = 9; // Packet options + const IP_MTU_DISCOVER = 10; // MTU discovery + const IP_RECVERR = 11; // Receive errors + const IP_RECVTTL = 12; // Receive time to live + const IP_RECVTOS = 13; // Receive type of service + const IP_MTU = 14; // MTU + const IP_FREEBIND = 15; // Freebind + const IP_IPSEC_POLICY = 16; // IPsec policy + const IP_XFRM_POLICY = 17; // IPipsec transform policy + const IP_PASSSEC = 18; // Pass security + const IP_TRANSPARENT = 19; // Transparent + + const IP_RECVRETOPTS = 20; // Receive return options (deprecated) + + const IP_ORIGDSTADDR = 21; // Originate destination address (used by TProxy) + const IP_RECVORIGDSTADDR = 21; // Receive originate destination address + + const IP_MINTTL = 22; // Minimum time to live + const IP_NODEFRAG = 23; // Don't fragment (used by TProxy) + const IP_CHECKSUM = 24; // Checksum offload (used by TProxy) + const IP_BIND_ADDRESS_NO_PORT = 25; // Bind to address without port (used by TProxy) + const IP_RECVFRAGSIZE = 26; // Receive fragment size + const IP_RECVERR_RFC4884 = 27; // Receive ICMPv6 error notifications + + const IP_PMTUDISC_DONT = 28; // Don't send DF frames + const IP_PMTUDISC_DO = 29; // Always DF + const IP_PMTUDISC_PROBE = 30; // Ignore dst pmtu + const IP_PMTUDISC_INTERFACE = 31; // Always use interface mtu (ignores dst pmtu) + const IP_PMTUDISC_OMIT = 32; // Weaker version of IP_PMTUDISC_INTERFACE + + const IP_MULTICAST_IF = 33; // Multicast interface + const IP_MULTICAST_TTL = 34; // Multicast time to live + const IP_MULTICAST_LOOP = 35; // Multicast loopback + const IP_ADD_MEMBERSHIP = 36; // Add multicast group membership + const IP_DROP_MEMBERSHIP = 37; // Drop multicast group membership + const IP_UNBLOCK_SOURCE = 38; // Unblock source + const IP_BLOCK_SOURCE = 39; // Block source + const IP_ADD_SOURCE_MEMBERSHIP = 40; // Add source multicast group membership + const IP_DROP_SOURCE_MEMBERSHIP = 41; // Drop source multicast group membership + const IP_MSFILTER = 42; // Multicast source filter + + const MCAST_JOIN_GROUP = 43; // Join a multicast group + const MCAST_BLOCK_SOURCE = 44; // Block a multicast source + const MCAST_UNBLOCK_SOURCE = 45; // Unblock a multicast source + const MCAST_LEAVE_GROUP = 46; // Leave a multicast group + const MCAST_JOIN_SOURCE_GROUP = 47; // Join a multicast source group + const MCAST_LEAVE_SOURCE_GROUP = 48; // Leave a multicast source group + const MCAST_MSFILTER = 49; // Multicast source filter + + const IP_MULTICAST_ALL = 50; // Multicast all + const IP_UNICAST_IF = 51; // Unicast interface + const IP_LOCAL_PORT_RANGE = 52; // Local port range + const IP_PROTOCOL = 53; // Protocol + + // ... other flags ... + } +} diff --git a/src/socket/inet/posix/proto.rs b/src/socket/inet/posix/proto.rs new file mode 100644 index 0000000..ec3d398 --- /dev/null +++ b/src/socket/inet/posix/proto.rs @@ -0,0 +1,78 @@ +use num_derive::{FromPrimitive, ToPrimitive}; + +pub const SOL_SOCKET: u16 = 1; + +#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive, PartialEq, Eq)] +pub enum IPProtocol { + /// Dummy protocol for TCP. + IP = 0, + /// Internet Control Message Protocol. + ICMP = 1, + /// Internet Group Management Protocol. + IGMP = 2, + /// IPIP tunnels (older KA9Q tunnels use 94). + IPIP = 4, + /// Transmission Control Protocol. + TCP = 6, + /// Exterior Gateway Protocol. + EGP = 8, + /// PUP protocol. + PUP = 12, + /// User Datagram Protocol. + UDP = 17, + /// XNS IDP protocol. + IDP = 22, + /// SO Transport Protocol Class 4. + TP = 29, + /// Datagram Congestion Control Protocol. + DCCP = 33, + /// IPv6-in-IPv4 tunnelling. + IPv6 = 41, + /// RSVP Protocol. + RSVP = 46, + /// Generic Routing Encapsulation. (Cisco GRE) (rfc 1701, 1702) + GRE = 47, + /// Encapsulation Security Payload protocol + ESP = 50, + /// Authentication Header protocol + AH = 51, + /// Multicast Transport Protocol. + MTP = 92, + /// IP option pseudo header for BEET + BEETPH = 94, + /// Encapsulation Header. + ENCAP = 98, + /// Protocol Independent Multicast. + PIM = 103, + /// Compression Header Protocol. + COMP = 108, + /// Stream Control Transport Protocol + SCTP = 132, + /// UDP-Lite protocol (RFC 3828) + UDPLITE = 136, + /// MPLS in IP (RFC 4023) + MPLSINIP = 137, + /// Ethernet-within-IPv6 Encapsulation + ETHERNET = 143, + /// Raw IP packets + RAW = 255, + /// Multipath TCP connection + MPTCP = 262, +} + +impl TryFrom for IPProtocol { + type Error = linux_errnos::Errno; + + fn try_from(value: u16) -> Result { + match ::from_u16(value) { + Some(p) => Ok(p), + None => Err(Self::Error::EPROTONOSUPPORT), + } + } +} + +impl From for u16 { + fn from(value: IPProtocol) -> Self { + ::to_u16(&value).unwrap() + } +} diff --git a/src/socket/inet/stream/inner.rs b/src/socket/inet/stream/inner.rs new file mode 100644 index 0000000..10d02f5 --- /dev/null +++ b/src/socket/inet/stream/inner.rs @@ -0,0 +1,517 @@ +use core::sync::atomic::AtomicUsize; + +use crate::libs::rwlock::RwLock; +// use crate::net::socket::EPollEventType; +use crate::socket::{self, inet::Types}; +use alloc::boxed::Box; +use alloc::vec::Vec; +use smoltcp; +use smoltcp::socket::tcp; +use linux_errnos::Errno as SystemError; + +// pub const DEFAULT_METADATA_BUF_SIZE: usize = 1024; +pub const DEFAULT_RX_BUF_SIZE: usize = 512 * 1024; +pub const DEFAULT_TX_BUF_SIZE: usize = 512 * 1024; + +fn new_smoltcp_socket() -> smoltcp::socket::tcp::Socket<'static> { + let rx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0; DEFAULT_RX_BUF_SIZE]); + let tx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0; DEFAULT_TX_BUF_SIZE]); + smoltcp::socket::tcp::Socket::new(rx_buffer, tx_buffer) +} + +fn new_listen_smoltcp_socket(local_endpoint: T) -> smoltcp::socket::tcp::Socket<'static> +where + T: Into, +{ + let mut socket = new_smoltcp_socket(); + socket.listen(local_endpoint).unwrap(); + socket +} + +#[derive(Debug)] +pub enum Init { + Unbound( + ( + Box>, + smoltcp::wire::IpVersion, + ), + ), + Bound((socket::inet::BoundInner, smoltcp::wire::IpEndpoint)), +} + +impl Init { + pub(super) fn new(ver: smoltcp::wire::IpVersion) -> Self { + Init::Unbound((Box::new(new_smoltcp_socket()), ver)) + } + + /// 传入一个已经绑定的socket + pub(super) fn new_bound(inner: socket::inet::BoundInner) -> Self { + let endpoint = inner.with::(|socket| { + socket + .local_endpoint() + .expect("A Bound Socket Must Have A Local Endpoint") + }); + Init::Bound((inner, endpoint)) + } + + pub(super) fn bind( + self, + local_endpoint: smoltcp::wire::IpEndpoint, + ) -> Result { + match self { + Init::Unbound((socket, _)) => { + let bound = socket::inet::BoundInner::bind(*socket, &local_endpoint.addr)?; + bound + .port_manager() + .bind_port(Types::Tcp, local_endpoint.port)?; + // bound.iface().common().bind_socket() + Ok(Init::Bound((bound, local_endpoint))) + } + Init::Bound(_) => { + log::debug!("Already Bound"); + Err(SystemError::EINVAL) + } + } + } + + pub(super) fn bind_to_ephemeral( + self, + remote_endpoint: smoltcp::wire::IpEndpoint, + ) -> Result<(socket::inet::BoundInner, smoltcp::wire::IpEndpoint), (Self, SystemError)> { + match self { + Init::Unbound((socket, ver)) => { + let (bound, address) = + socket::inet::BoundInner::bind_ephemeral(*socket, remote_endpoint.addr) + .map_err(|err| (Self::new(ver), err))?; + let bound_port = bound + .port_manager() + .bind_ephemeral_port(Types::Tcp) + .map_err(|err| (Self::new(ver), err))?; + let endpoint = smoltcp::wire::IpEndpoint::new(address, bound_port); + Ok((bound, endpoint)) + } + Init::Bound(_) => Err((self, SystemError::EINVAL)), + } + } + + pub(super) fn connect( + self, + remote_endpoint: smoltcp::wire::IpEndpoint, + ) -> Result { + let (inner, local) = match self { + Init::Unbound(_) => self.bind_to_ephemeral(remote_endpoint)?, + Init::Bound(inner) => inner, + }; + if local.addr.is_unspecified() { + return Err((Init::Bound((inner, local)), SystemError::EINVAL)); + } + let result = inner.with_mut::(|socket| { + socket + .connect( + inner.iface().smol_iface().lock().context(), + remote_endpoint, + local, + ) + .map_err(|_| SystemError::ECONNREFUSED) + }); + match result { + Ok(_) => Ok(Connecting::new(inner)), + Err(err) => Err((Init::Bound((inner, local)), err)), + } + } + + /// # `listen` + pub(super) fn listen(self, backlog: usize) -> Result { + let (inner, local) = match self { + Init::Unbound(_) => { + return Err((self, SystemError::EINVAL)); + } + Init::Bound(inner) => inner, + }; + let listen_addr = if local.addr.is_unspecified() { + smoltcp::wire::IpListenEndpoint::from(local.port) + } else { + smoltcp::wire::IpListenEndpoint::from(local) + }; + log::debug!("listen at {:?}", listen_addr); + let mut inners = Vec::new(); + if let Err(err) = || -> Result<(), SystemError> { + for _ in 0..(backlog - 1) { + // -1 because the first one is already bound + let new_listen = socket::inet::BoundInner::bind( + new_listen_smoltcp_socket(listen_addr), + listen_addr + .addr + .as_ref() + .unwrap_or(&smoltcp::wire::IpAddress::from( + smoltcp::wire::Ipv4Address::UNSPECIFIED, + )), + )?; + inners.push(new_listen); + } + Ok(()) + }() { + return Err((Init::Bound((inner, local)), err)); + } + + if let Err(err) = inner.with_mut::(|socket| { + socket + .listen(listen_addr) + .map_err(|_| SystemError::ECONNREFUSED) + }) { + return Err((Init::Bound((inner, local)), err)); + } + + inners.push(inner); + return Ok(Listening { + inners, + connect: AtomicUsize::new(0), + listen_addr, + }); + } + + pub(super) fn close(&self) { + match self { + Init::Unbound(_) => {} + Init::Bound((inner, endpoint)) => { + inner.port_manager().unbind_port(Types::Tcp, endpoint.port); + inner.with_mut::(|socket| socket.close()); + } + } + } +} + +#[derive(Debug, Default, Clone, Copy)] +enum ConnectResult { + Connected, + #[default] + Connecting, + Refused, +} + +#[derive(Debug)] +pub struct Connecting { + inner: socket::inet::BoundInner, + result: RwLock, +} + +impl Connecting { + fn new(inner: socket::inet::BoundInner) -> Self { + Connecting { + inner, + result: RwLock::new(ConnectResult::Connecting), + } + } + + pub fn with_mut) -> R>( + &self, + f: F, + ) -> R { + self.inner.with_mut(f) + } + + pub fn into_result(self) -> (Inner, Result<(), SystemError>) { + let result = *self.result.read(); + match result { + ConnectResult::Connecting => ( + Inner::Connecting(self), + Err(SystemError::EAGAIN_OR_EWOULDBLOCK), + ), + ConnectResult::Connected => ( + Inner::Established(Established { inner: self.inner }), + Ok(()), + ), + ConnectResult::Refused => ( + Inner::Init(Init::new_bound(self.inner)), + Err(SystemError::ECONNREFUSED), + ), + } + } + + pub unsafe fn into_established(self) -> Established { + Established { inner: self.inner } + } + + /// Returns `true` when `conn_result` becomes ready, which indicates that the caller should + /// invoke the `into_result()` method as soon as possible. + /// + /// Since `into_result()` needs to be called only once, this method will return `true` + /// _exactly_ once. The caller is responsible for not missing this event. + #[must_use] + pub(super) fn update_io_events(&self) -> bool { + // if matches!(*self.result.read_irqsave(), ConnectResult::Connecting) { + // return false; + // } + + self.inner + .with_mut(|socket: &mut smoltcp::socket::tcp::Socket| { + let mut result = self.result.write(); + if matches!(*result, ConnectResult::Refused | ConnectResult::Connected) { + return false; // Already connected or refused + } + + // Connected + if socket.can_send() { + log::debug!("can send"); + *result = ConnectResult::Connected; + return true; + } + // Connecting + if socket.is_open() { + log::debug!("connecting"); + *result = ConnectResult::Connecting; + return false; + } + // Refused + *result = ConnectResult::Refused; + return true; + }) + } + + pub fn get_name(&self) -> smoltcp::wire::IpEndpoint { + self.inner + .with::(|socket| { + socket + .local_endpoint() + .expect("A Connecting Tcp With No Local Endpoint") + }) + } + + pub fn get_peer_name(&self) -> smoltcp::wire::IpEndpoint { + self.inner + .with::(|socket| { + socket + .remote_endpoint() + .expect("A Connecting Tcp With No Remote Endpoint") + }) + } +} + +#[derive(Debug)] +pub struct Listening { + inners: Vec, + connect: AtomicUsize, + listen_addr: smoltcp::wire::IpListenEndpoint, +} + +impl Listening { + pub fn accept(&mut self) -> Result<(Established, smoltcp::wire::IpEndpoint), SystemError> { + let connected: &mut socket::inet::BoundInner = self + .inners + .get_mut(self.connect.load(core::sync::atomic::Ordering::Relaxed)) + .unwrap(); + + if connected.with::(|socket| !socket.is_active()) { + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + + let remote_endpoint = connected.with::(|socket| { + socket + .remote_endpoint() + .expect("A Connected Tcp With No Remote Endpoint") + }); + + // log::debug!("local at {:?}", local_endpoint); + + let mut new_listen = socket::inet::BoundInner::bind( + new_listen_smoltcp_socket(self.listen_addr), + self.listen_addr + .addr + .as_ref() + .unwrap_or(&smoltcp::wire::IpAddress::from( + smoltcp::wire::Ipv4Address::UNSPECIFIED, + )), + )?; + + // swap the connected socket with the new_listen socket + // TODO is smoltcp socket swappable? + core::mem::swap(&mut new_listen, connected); + + return Ok((Established { inner: new_listen }, remote_endpoint)); + } + + pub fn update_io_events(&self, pollee: &AtomicUsize) { + let position = self.inners.iter().position(|inner| { + inner.with::(|socket| socket.is_active()) + }); + + if let Some(position) = position { + self.connect + .store(position, core::sync::atomic::Ordering::Relaxed); + pollee.fetch_or( + EPollEventType::EPOLLIN.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } else { + pollee.fetch_and( + !EPollEventType::EPOLLIN.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } + } + + pub fn get_name(&self) -> smoltcp::wire::IpEndpoint { + smoltcp::wire::IpEndpoint::new( + self.listen_addr + .addr + .unwrap_or(smoltcp::wire::IpAddress::from( + smoltcp::wire::Ipv4Address::UNSPECIFIED, + )), + self.listen_addr.port, + ) + } + + pub fn close(&self) { + // log::debug!("Close Listening Socket"); + let port = self.get_name().port; + for inner in self.inners.iter() { + inner.with_mut::(|socket| socket.close()); + } + self.inners[0] + .iface() + .port_manager() + .unbind_port(Types::Tcp, port); + } + + pub fn release(&self) { + // log::debug!("Release Listening Socket"); + for inner in self.inners.iter() { + inner.release(); + } + } +} + +#[derive(Debug)] +pub struct Established { + inner: socket::inet::BoundInner, +} + +impl Established { + pub fn with_mut) -> R>( + &self, + f: F, + ) -> R { + self.inner.with_mut(f) + } + + pub fn close(&self) { + self.inner + .with_mut::(|socket| socket.close()); + self.inner.iface().poll(); + } + + pub fn release(&self) { + self.inner.release(); + } + + pub fn get_name(&self) -> smoltcp::wire::IpEndpoint { + self.inner + .with::(|socket| socket.local_endpoint()) + .unwrap() + } + + pub fn get_peer_name(&self) -> smoltcp::wire::IpEndpoint { + self.inner + .with::(|socket| socket.remote_endpoint().unwrap()) + } + + pub fn recv_slice(&self, buf: &mut [u8]) -> Result { + self.inner + .with_mut::(|socket| { + if socket.can_send() { + match socket.recv_slice(buf) { + Ok(size) => Ok(size), + Err(tcp::RecvError::InvalidState) => { + log::error!("TcpSocket::try_recv: InvalidState"); + Err(SystemError::ENOTCONN) + } + Err(tcp::RecvError::Finished) => Ok(0), + } + } else { + Err(SystemError::ENOBUFS) + } + }) + } + + pub fn send_slice(&self, buf: &[u8]) -> Result { + self.inner + .with_mut::(|socket| { + if socket.can_send() { + socket + .send_slice(buf) + .map_err(|_| SystemError::ECONNABORTED) + } else { + Err(SystemError::ENOBUFS) + } + }) + } + + pub fn update_io_events(&self, pollee: &AtomicUsize) { + self.inner + .with_mut::(|socket| { + if socket.can_send() { + pollee.fetch_or( + EPollEventType::EPOLLOUT.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } else { + pollee.fetch_and( + !EPollEventType::EPOLLOUT.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } + if socket.can_recv() { + pollee.fetch_or( + EPollEventType::EPOLLIN.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } else { + pollee.fetch_and( + !EPollEventType::EPOLLIN.bits() as usize, + core::sync::atomic::Ordering::Relaxed, + ); + } + }) + } +} + +#[derive(Debug)] +pub enum Inner { + Init(Init), + Connecting(Connecting), + Listening(Listening), + Established(Established), +} + +impl Inner { + pub fn send_buffer_size(&self) -> usize { + match self { + Inner::Init(_) => DEFAULT_TX_BUF_SIZE, + Inner::Connecting(conn) => conn.with_mut(|socket| socket.send_capacity()), + // only the first socket in the list is used for sending + Inner::Listening(listen) => listen.inners[0] + .with_mut::(|socket| socket.send_capacity()), + Inner::Established(est) => est.with_mut(|socket| socket.send_capacity()), + } + } + + pub fn recv_buffer_size(&self) -> usize { + match self { + Inner::Init(_) => DEFAULT_RX_BUF_SIZE, + Inner::Connecting(conn) => conn.with_mut(|socket| socket.recv_capacity()), + // only the first socket in the list is used for receiving + Inner::Listening(listen) => listen.inners[0] + .with_mut::(|socket| socket.recv_capacity()), + Inner::Established(est) => est.with_mut(|socket| socket.recv_capacity()), + } + } + + pub fn iface(&self) -> Option<&alloc::sync::Arc> { + match self { + Inner::Init(_) => None, + Inner::Connecting(conn) => Some(conn.inner.iface()), + Inner::Listening(listen) => Some(listen.inners[0].iface()), + Inner::Established(est) => Some(est.inner.iface()), + } + } +} diff --git a/src/socket/inet/stream/mod.rs b/src/socket/inet/stream/mod.rs new file mode 100644 index 0000000..4447079 --- /dev/null +++ b/src/socket/inet/stream/mod.rs @@ -0,0 +1,507 @@ +use alloc::sync::{Arc, Weak}; +use core::sync::atomic::{AtomicBool, AtomicUsize}; +use system_error::SystemError; + +use crate::libs::wait_queue::WaitQueue; +// use crate::event_poll::EPollEventType; +use crate::socket::common::shutdown::{ShutdownBit, ShutdownTemp}; +use crate::socket::endpoint::Endpoint; +use crate::socket::{Socket, PMSG, PSOL}; +// use crate::sched::SchedMode; +use crate::{libs::rwlock::RwLock, net::socket::common::shutdown::Shutdown}; +use smoltcp; + +mod inner; + +mod option; +pub use option::Options as TcpOption; + +use super::{InetSocket, UNSPECIFIED_LOCAL_ENDPOINT_V4}; + +type EP = EPollEventType; +#[derive(Debug)] +pub struct TcpSocket { + inner: RwLock>, + #[allow(dead_code)] + shutdown: Shutdown, // TODO set shutdown status + nonblock: AtomicBool, + wait_queue: WaitQueue, + self_ref: Weak, + pollee: AtomicUsize, +} + +impl TcpSocket { + pub fn new(_nonblock: bool, ver: smoltcp::wire::IpVersion) -> Arc { + Arc::new_cyclic(|me| Self { + inner: RwLock::new(Some(inner::Inner::Init(inner::Init::new(ver)))), + shutdown: Shutdown::new(), + nonblock: AtomicBool::new(false), + wait_queue: WaitQueue::default(), + self_ref: me.clone(), + pollee: AtomicUsize::new(0_usize), + }) + } + + pub fn new_established(inner: inner::Established, nonblock: bool) -> Arc { + Arc::new_cyclic(|me| Self { + inner: RwLock::new(Some(inner::Inner::Established(inner))), + shutdown: Shutdown::new(), + nonblock: AtomicBool::new(nonblock), + wait_queue: WaitQueue::default(), + self_ref: me.clone(), + pollee: AtomicUsize::new((EP::EPOLLIN.bits() | EP::EPOLLOUT.bits()) as usize), + }) + } + + pub fn is_nonblock(&self) -> bool { + self.nonblock.load(core::sync::atomic::Ordering::Relaxed) + } + + pub fn do_bind(&self, local_endpoint: smoltcp::wire::IpEndpoint) -> Result<(), SystemError> { + let mut writer = self.inner.write(); + match writer.take().expect("Tcp inner::Inner is None") { + inner::Inner::Init(inner) => { + let bound = inner.bind(local_endpoint)?; + if let inner::Init::Bound((ref bound, _)) = bound { + bound + .iface() + .common() + .bind_socket(self.self_ref.upgrade().unwrap()); + } + writer.replace(inner::Inner::Init(bound)); + Ok(()) + } + any => { + writer.replace(any); + log::error!("TcpSocket::do_bind: not Init"); + Err(SystemError::EINVAL) + } + } + } + + pub fn do_listen(&self, backlog: usize) -> Result<(), SystemError> { + let mut writer = self.inner.write(); + let inner = writer.take().expect("Tcp inner::Inner is None"); + let (listening, err) = match inner { + inner::Inner::Init(init) => { + let listen_result = init.listen(backlog); + match listen_result { + Ok(listening) => (inner::Inner::Listening(listening), None), + Err((init, err)) => (inner::Inner::Init(init), Some(err)), + } + } + _ => (inner, Some(SystemError::EINVAL)), + }; + writer.replace(listening); + drop(writer); + + if let Some(err) = err { + return Err(err); + } + return Ok(()); + } + + pub fn try_accept(&self) -> Result<(Arc, smoltcp::wire::IpEndpoint), SystemError> { + match self + .inner + .write() + .as_mut() + .expect("Tcp inner::Inner is None") + { + inner::Inner::Listening(listening) => listening.accept().map(|(stream, remote)| { + ( + TcpSocket::new_established(stream, self.is_nonblock()), + remote, + ) + }), + _ => Err(SystemError::EINVAL), + } + } + + // SHOULD refactor + pub fn start_connect( + &self, + remote_endpoint: smoltcp::wire::IpEndpoint, + ) -> Result<(), SystemError> { + let mut writer = self.inner.write(); + let inner = writer.take().expect("Tcp inner::Inner is None"); + let (init, result) = match inner { + inner::Inner::Init(init) => { + let conn_result = init.connect(remote_endpoint); + match conn_result { + Ok(connecting) => ( + inner::Inner::Connecting(connecting), + if !self.is_nonblock() { + Ok(()) + } else { + Err(SystemError::EINPROGRESS) + }, + ), + Err((init, err)) => (inner::Inner::Init(init), Err(err)), + } + } + inner::Inner::Connecting(connecting) if self.is_nonblock() => ( + inner::Inner::Connecting(connecting), + Err(SystemError::EALREADY), + ), + inner::Inner::Connecting(connecting) => (inner::Inner::Connecting(connecting), Ok(())), + inner::Inner::Listening(inner) => { + (inner::Inner::Listening(inner), Err(SystemError::EISCONN)) + } + inner::Inner::Established(inner) => { + (inner::Inner::Established(inner), Err(SystemError::EISCONN)) + } + }; + + match result { + Ok(()) | Err(SystemError::EINPROGRESS) => { + init.iface().unwrap().poll(); + } + _ => {} + } + + writer.replace(init); + return result; + } + + // for irq use + pub fn finish_connect(&self) -> Result<(), SystemError> { + let mut writer = self.inner.write(); + let inner::Inner::Connecting(conn) = writer.take().expect("Tcp inner::Inner is None") + else { + log::error!("TcpSocket::finish_connect: not Connecting"); + return Err(SystemError::EINVAL); + }; + + let (inner, result) = conn.into_result(); + writer.replace(inner); + drop(writer); + + result + } + + pub fn check_connect(&self) -> Result<(), SystemError> { + self.update_events(); + let mut write_state = self.inner.write(); + let inner = write_state.take().expect("Tcp inner::Inner is None"); + let (replace, result) = match inner { + inner::Inner::Connecting(conn) => conn.into_result(), + inner::Inner::Established(es) => { + log::warn!("TODO: check new established"); + (inner::Inner::Established(es), Ok(())) + } // TODO check established + _ => { + log::warn!("TODO: connecting socket error options"); + (inner, Err(SystemError::EINVAL)) + } // TODO socket error options + }; + write_state.replace(replace); + result + } + + pub fn try_recv(&self, buf: &mut [u8]) -> Result { + self.inner + .read() + .as_ref() + .map(|inner| { + inner.iface().unwrap().poll(); + let result = match inner { + inner::Inner::Established(inner) => inner.recv_slice(buf), + _ => Err(SystemError::EINVAL), + }; + inner.iface().unwrap().poll(); + result + }) + .unwrap() + } + + pub fn try_send(&self, buf: &[u8]) -> Result { + // TODO: add nonblock check of connecting socket + let sent = match self + .inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + { + inner::Inner::Established(inner) => inner.send_slice(buf), + _ => Err(SystemError::EINVAL), + }; + self.inner.read().as_ref().unwrap().iface().unwrap().poll(); + sent + } + + fn update_events(&self) -> bool { + match self + .inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + { + inner::Inner::Init(_) => false, + inner::Inner::Connecting(connecting) => connecting.update_io_events(), + inner::Inner::Established(established) => { + established.update_io_events(&self.pollee); + false + } + inner::Inner::Listening(listening) => { + listening.update_io_events(&self.pollee); + false + } + } + } + + fn incoming(&self) -> bool { + EP::from_bits_truncate(self.poll() as u32).contains(EP::EPOLLIN) + } +} + +impl Socket for TcpSocket { + fn wait_queue(&self) -> &WaitQueue { + &self.wait_queue + } + + fn get_name(&self) -> Result { + match self + .inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + { + inner::Inner::Init(inner::Init::Unbound((_, ver))) => Ok(Endpoint::Ip(match ver { + smoltcp::wire::IpVersion::Ipv4 => UNSPECIFIED_LOCAL_ENDPOINT_V4, + // smoltcp::wire::IpVersion::Ipv6 => UNSPECIFIED_LOCAL_ENDPOINT_V6, + })), + inner::Inner::Init(inner::Init::Bound((_, local))) => Ok(Endpoint::Ip(*local)), + inner::Inner::Connecting(connecting) => Ok(Endpoint::Ip(connecting.get_name())), + inner::Inner::Established(established) => Ok(Endpoint::Ip(established.get_name())), + inner::Inner::Listening(listening) => Ok(Endpoint::Ip(listening.get_name())), + } + } + + fn get_peer_name(&self) -> Result { + match self + .inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + { + inner::Inner::Init(_) => Err(SystemError::ENOTCONN), + inner::Inner::Connecting(connecting) => Ok(Endpoint::Ip(connecting.get_peer_name())), + inner::Inner::Established(established) => Ok(Endpoint::Ip(established.get_peer_name())), + inner::Inner::Listening(_) => Err(SystemError::ENOTCONN), + } + } + + fn bind(&self, endpoint: Endpoint) -> Result<(), SystemError> { + if let Endpoint::Ip(addr) = endpoint { + return self.do_bind(addr); + } + log::debug!("TcpSocket::bind: invalid endpoint"); + return Err(SystemError::EINVAL); + } + + fn connect(&self, endpoint: Endpoint) -> Result<(), SystemError> { + let Endpoint::Ip(endpoint) = endpoint else { + log::debug!("TcpSocket::connect: invalid endpoint"); + return Err(SystemError::EINVAL); + }; + self.start_connect(endpoint)?; // Only Nonblock or error will return error. + + return loop { + match self.check_connect() { + Err(SystemError::EAGAIN_OR_EWOULDBLOCK) => {} + result => break result, + } + }; + } + + fn poll(&self) -> usize { + self.pollee.load(core::sync::atomic::Ordering::SeqCst) + } + + fn listen(&self, backlog: usize) -> Result<(), SystemError> { + self.do_listen(backlog) + } + + fn accept(&self) -> Result<(Arc, Endpoint), SystemError> { + if self.is_nonblock() { + self.try_accept() + } else { + loop { + match self.try_accept() { + Err(SystemError::EAGAIN_OR_EWOULDBLOCK) => { + wq_wait_event_interruptible!(self.wait_queue, self.incoming(), {})?; + } + result => break result, + } + } + } + .map(|(inner, endpoint)| (SocketInode::new(inner), Endpoint::Ip(endpoint))) + } + + fn recv(&self, buffer: &mut [u8], _flags: PMSG) -> Result { + self.try_recv(buffer) + } + + fn send(&self, buffer: &[u8], _flags: PMSG) -> Result { + self.try_send(buffer) + } + + fn send_buffer_size(&self) -> usize { + self.inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + .send_buffer_size() + } + + fn recv_buffer_size(&self) -> usize { + self.inner + .read() + .as_ref() + .expect("Tcp inner::Inner is None") + .recv_buffer_size() + } + + fn shutdown(&self, how: ShutdownTemp) -> Result<(), SystemError> { + let self_shutdown = self.shutdown.get().bits(); + let diff = how.bits().difference(self_shutdown); + match diff.is_empty() { + true => return Ok(()), + false => { + if diff.contains(ShutdownBit::SHUT_RD) { + self.shutdown.recv_shutdown(); + // TODO 协议栈处理 + } + if diff.contains(ShutdownBit::SHUT_WR) { + self.shutdown.send_shutdown(); + // TODO 协议栈处理 + } + } + } + Ok(()) + } + + fn close(&self) -> Result<(), SystemError> { + let Some(inner) = self.inner.write().take() else { + log::warn!("TcpSocket::close: already closed, unexpected"); + return Ok(()); + }; + if let Some(iface) = inner.iface() { + iface + .common() + .unbind_socket(self.self_ref.upgrade().unwrap()); + } + + match inner { + // complete connecting socket close logic + inner::Inner::Connecting(conn) => { + let conn = unsafe { conn.into_established() }; + conn.close(); + conn.release(); + } + inner::Inner::Established(es) => { + es.close(); + es.release(); + } + inner::Inner::Listening(ls) => { + ls.close(); + ls.release(); + } + inner::Inner::Init(init) => { + init.close(); + } + }; + + Ok(()) + } + + fn set_option(&self, level: PSOL, name: usize, val: &[u8]) -> Result<(), SystemError> { + if level != PSOL::TCP { + // return Err(SystemError::EINVAL); + log::debug!("TcpSocket::set_option: not TCP"); + return Ok(()); + } + use option::Options::{self, *}; + let option_name = Options::try_from(name as i32)?; + log::debug!("TCP Option: {:?}, value = {:?}", option_name, val); + match option_name { + NoDelay => { + let nagle_enabled = val[0] != 0; + let mut writer = self.inner.write(); + let inner = writer.take().expect("Tcp inner::Inner is None"); + match inner { + inner::Inner::Established(established) => { + established.with_mut(|socket| { + socket.set_nagle_enabled(nagle_enabled); + }); + writer.replace(inner::Inner::Established(established)); + } + _ => { + writer.replace(inner); + return Err(SystemError::EINVAL); + } + } + } + KeepIntvl => { + if val.len() == 4 { + let mut writer = self.inner.write(); + let inner = writer.take().expect("Tcp inner::Inner is None"); + match inner { + inner::Inner::Established(established) => { + let interval = u32::from_ne_bytes([val[0], val[1], val[2], val[3]]); + established.with_mut(|socket| { + socket.set_keep_alive(Some(smoltcp::time::Duration::from_secs( + interval as u64, + ))); + }); + writer.replace(inner::Inner::Established(established)); + } + _ => { + writer.replace(inner); + return Err(SystemError::EINVAL); + } + } + } else { + return Err(SystemError::EINVAL); + } + } + KeepCnt => { + // if val.len() == 4 { + // let mut writer = self.inner.write(); + // let inner = writer.take().expect("Tcp inner::Inner is None"); + // match inner { + // inner::Inner::Established(established) => { + // let count = u32::from_ne_bytes([val[0], val[1], val[2], val[3]]); + // established.with_mut(|socket| { + // socket.set_keep_alive_count(count); + // }); + // writer.replace(inner::Inner::Established(established)); + // } + // _ => { + // writer.replace(inner); + // return Err(SystemError::EINVAL); + // } + // } + // } else { + // return Err(SystemError::EINVAL); + // } + } + KeepIdle => {} + _ => { + log::debug!("TcpSocket::set_option: not supported"); + // return Err(ENOPROTOOPT); + } + } + Ok(()) + } +} + +impl InetSocket for TcpSocket { + fn on_iface_events(&self) { + if self.update_events() { + let _result = self.finish_connect(); + // set error + } + } +} diff --git a/src/socket/inet/stream/option.rs b/src/socket/inet/stream/option.rs new file mode 100644 index 0000000..09ccbc1 --- /dev/null +++ b/src/socket/inet/stream/option.rs @@ -0,0 +1,94 @@ +use linux_errnos::Errno; +use num_derive::{FromPrimitive, ToPrimitive}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +pub enum Options { + /// Turn off Nagle's algorithm. + NoDelay = 1, + /// Limit MSS. + MaxSegment = 2, + /// Never send partially complete segments. + Cork = 3, + /// Start keeplives after this period. + KeepIdle = 4, + /// Interval between keepalives. + KeepIntvl = 5, + /// Number of keepalives before death. + KeepCnt = 6, + /// Number of SYN retransmits. + Syncnt = 7, + /// Lifetime for orphaned FIN-WAIT-2 state. + Linger2 = 8, + /// Wake up listener only when data arrive. + DeferAccept = 9, + /// Bound advertised window + WindowClamp = 10, + /// Information about this connection. + Info = 11, + /// Block/reenable quick acks. + QuickAck = 12, + /// Congestion control algorithm. + Congestion = 13, + /// TCP MD5 Signature (RFC2385). + Md5Sig = 14, + /// Use linear timeouts for thin streams + ThinLinearTimeouts = 16, + /// Fast retrans. after 1 dupack. + ThinDupack = 17, + /// How long for loss retry before timeout. + UserTimeout = 18, + /// TCP sock is under repair right now. + Repair = 19, + RepairQueue = 20, + QueueSeq = 21, + #[allow(clippy::enum_variant_names)] + RepairOptions = 22, + /// Enable FastOpen on listeners + FastOpen = 23, + Timestamp = 24, + /// Limit number of unsent bytes in write queue. + NotSentLowat = 25, + /// Get Congestion Control (optional) info. + CCInfo = 26, + /// Record SYN headers for new connections. + SaveSyn = 27, + /// Get SYN headers recorded for connection. + SavedSyn = 28, + /// Get/set window parameters. + RepairWindow = 29, + /// Attempt FastOpen with connect. + FastOpenConnect = 30, + /// Attach a ULP to a TCP connection. + ULP = 31, + /// TCP MD5 Signature with extensions. + Md5SigExt = 32, + /// Set the key for Fast Open(cookie). + FastOpenKey = 33, + /// Enable TFO without a TFO cookie. + FastOpenNoCookie = 34, + ZeroCopyReceive = 35, + /// Notify bytes available to read as a cmsg on read. + /// 与TCP_CM_INQ相同 + INQ = 36, + /// delay outgoing packets by XX usec + TxDelay = 37, +} + +impl TryFrom for Options { + type Error = Errno; + + fn try_from(value: i32) -> Result { + use num_traits::FromPrimitive; + match ::from_i32(value) { + Some(p) => Ok(p), + None => Err(Self::Error::EINVAL), + } + } +} + +impl From for i32 { + fn from(val: Options) -> Self { + use num_traits::ToPrimitive; + ::to_i32(&val).unwrap() + } +} diff --git a/src/socket/inet/syscall.rs b/src/socket/inet/syscall.rs new file mode 100644 index 0000000..f9fd6fa --- /dev/null +++ b/src/socket/inet/syscall.rs @@ -0,0 +1,67 @@ +use alloc::sync::Arc; +use linux_errnos::Errno as SystemError; +use smoltcp::{self, wire::IpProtocol}; + +use crate::{ + posix::SOCK, + socket::{ + inet::UdpSocket, + Family, + Socket, // SocketInode, + }, +}; + +fn create_inet_socket( + version: smoltcp::wire::IpVersion, + socket_type: SOCK, + protocol: smoltcp::wire::IpProtocol, +) -> Result, SystemError> { + // log::debug!("type: {:?}, protocol: {:?}", socket_type, protocol); + match socket_type { + SOCK::Datagram => match protocol { + IpProtocol::HopByHop | IpProtocol::Udp => { + Ok(UdpSocket::new(false)) + } + _ => { + Err(SystemError::EPROTONOSUPPORT) + } + }, + // SOCK::Stream => match protocol { + // IpProtocol::HopByHop | IpProtocol::Tcp => { + // log::debug!("create tcp socket"); + // return Ok(TcpSocket::new(false, version)); + // } + // _ => { + // return Err(SystemError::EPROTONOSUPPORT); + // } + // }, + SOCK::Raw => { + todo!("raw") + } + _ => { + Err(SystemError::EPROTONOSUPPORT) + } + } +} + +pub struct Inet; +impl Family for Inet { + fn socket(stype: SOCK, protocol: u32) -> Result, SystemError> { + create_inet_socket( + smoltcp::wire::IpVersion::Ipv4, + stype, + smoltcp::wire::IpProtocol::from(protocol as u8), + ) + } +} + +// pub struct Inet6; +// impl Family for Inet6 { +// fn socket(stype: PSOCK, protocol: u32) -> Result, SystemError> { +// create_inet_socket( +// smoltcp::wire::IpVersion::Ipv6, +// stype, +// smoltcp::wire::IpProtocol::from(protocol as u8), +// ) +// } +// } diff --git a/src/socket/mod.rs b/src/socket/mod.rs new file mode 100644 index 0000000..3d7a7ce --- /dev/null +++ b/src/socket/mod.rs @@ -0,0 +1,148 @@ +pub mod common; +pub mod endpoint; +pub mod inet; + +use crate::{libs::wait_queue::WaitQueue, posix::SOCK}; +use core::any::Any; +use core::fmt::Debug; +use linux_errnos::Errno as SystemError; +use std::sync::Arc; + +use super::{ + posix::{PMSG, PSOL}, + // SocketInode, +}; +use common::shutdown::ShutdownTemp; +use endpoint::Endpoint; + +/// # `Socket` methods +/// ## Reference +/// - [Posix standard](https://pubs.opengroup.org/onlinepubs/9699919799/) +#[allow(unused_variables)] +pub trait Socket: Sync + Send + Debug + Any { + /// # `wait_queue` + /// 获取socket的wait queue + fn wait_queue(&self) -> &WaitQueue; + /// # `socket_poll` + /// 获取socket的事件。 + fn poll(&self) -> usize; + + fn send_buffer_size(&self) -> usize; + fn recv_buffer_size(&self) -> usize; + // /// # `accept` + // /// 接受连接,仅用于listening stream socket + // /// ## Block + // /// 如果没有连接到来,会阻塞 + // fn accept(&self) -> Result<(Arc, Endpoint), SystemError> { + // Err(SystemError::ENOSYS) + // } + /// # `bind` + /// 对应于POSIX的bind函数,用于绑定到本机指定的端点 + fn bind(&self, endpoint: Endpoint) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + /// # `close` + /// 关闭socket + fn close(&self) -> Result<(), SystemError> { + Ok(()) + } + /// # `connect` + /// 对应于POSIX的connect函数,用于连接到指定的远程服务器端点 + fn connect(&self, endpoint: Endpoint) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + // fnctl + // freeaddrinfo + // getaddrinfo + // getnameinfo + /// # `get_peer_name` + /// 获取对端的地址 + fn get_peer_name(&self) -> Result { + Err(SystemError::ENOSYS) + } + /// # `get_name` + /// 获取socket的地址 + fn get_name(&self) -> Result { + Err(SystemError::ENOSYS) + } + /// # `get_option` + /// 对应于 Posix `getsockopt` ,获取socket选项 + fn get_option(&self, level: PSOL, name: usize, value: &mut [u8]) -> Result { + log::warn!("getsockopt is not implemented"); + Ok(0) + } + /// # `listen` + /// 监听socket,仅用于stream socket + fn listen(&self, backlog: usize) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + // poll + // pselect + /// # `read` + fn read(&self, buffer: &mut [u8]) -> Result { + self.recv(buffer, PMSG::empty()) + } + /// # `recv` + /// 接收数据,`read` = `recv` with flags = 0 + fn recv(&self, buffer: &mut [u8], flags: PMSG) -> Result { + Err(SystemError::ENOSYS) + } + /// # `recv_from` + fn recv_from( + &self, + buffer: &mut [u8], + flags: PMSG, + address: Option, + ) -> Result<(usize, Endpoint), SystemError> { + Err(SystemError::ENOSYS) + } + // /// # `recv_msg` + // fn recv_msg(&self, msg: &mut MsgHdr, flags: PMSG) -> Result { + // Err(SystemError::ENOSYS) + // } + // select + /// # `send` + fn send(&self, buffer: &[u8], flags: PMSG) -> Result { + Err(SystemError::ENOSYS) + } + // /// # `send_msg` + // fn send_msg(&self, msg: &MsgHdr, flags: PMSG) -> Result { + // Err(SystemError::ENOSYS) + // } + /// # `send_to` + fn send_to(&self, buffer: &[u8], flags: PMSG, address: Endpoint) -> Result { + Err(SystemError::ENOSYS) + } + /// # `set_option` + /// Posix `setsockopt` ,设置socket选项 + /// ## Parameters + /// - level 选项的层次 + /// - name 选项的名称 + /// - value 选项的值 + /// ## Reference + /// https://code.dragonos.org.cn/s?refs=sk_setsockopt&project=linux-6.6.21 + fn set_option(&self, level: PSOL, name: usize, val: &[u8]) -> Result<(), SystemError> { + log::warn!("setsockopt is not implemented"); + Ok(()) + } + /// # `shutdown` + fn shutdown(&self, how: ShutdownTemp) -> Result<(), SystemError> { + // TODO 构建shutdown系统调用 + // set shutdown bit + Err(SystemError::ENOSYS) + } + // sockatmark + // socket + // socketpair + /// # `write` + fn write(&self, buffer: &[u8]) -> Result { + self.send(buffer, PMSG::empty()) + } + // fn write_buffer(&self, _buf: &[u8]) -> Result { + // todo!() + // } +} + +pub trait Family { + fn socket(stype: SOCK, protocol: u32) -> Result, SystemError>; +}