From 821bb9a2dcfd28f9878d53ba722bdf164cf00f69 Mon Sep 17 00:00:00 2001 From: Xshine Date: Fri, 28 Jul 2023 17:51:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20ListenTable=20=E6=9D=A5?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E7=AB=AF=E5=8F=A3=E5=8D=A0=E7=94=A8=20(#291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加 ListenTable 来检测端口占用 * 使用Arc封装GlobalSocketHandle * 删除 listen 处的端口检测逻辑,延至实现端口复用时完成 * 设立两张表,分别记录TCP和UDP的端口占用 * 实现 meatadata 相关逻辑 * 实现socket关闭时,端口在表中移除 * 使用端口管理器重构端口记录表 * 修正与RawSocket相关的端口管理逻辑 * 补充测试文件 * 修正 unbind_port 在逻辑错误 * 修正格式问题 --------- Co-authored-by: longjin --- kernel/src/filesystem/vfs/core.rs | 2 +- kernel/src/io/device.rs | 2 +- kernel/src/mm/allocator/buddy.rs | 4 +- kernel/src/net/socket.rs | 230 ++++++++++++++++++++----- kernel/src/process/process.rs | 1 - user/apps/test_bind/Makefile | 27 +++ user/apps/test_bind/link.lds | 239 ++++++++++++++++++++++++++ user/apps/test_bind/main.c | 234 +++++++++++++++++++++++++ user/dadk/config/test_bind-0.1.0.dadk | 28 +++ 9 files changed, 716 insertions(+), 51 deletions(-) create mode 100644 user/apps/test_bind/Makefile create mode 100644 user/apps/test_bind/link.lds create mode 100644 user/apps/test_bind/main.c create mode 100644 user/dadk/config/test_bind-0.1.0.dadk diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index 9a79388d..0c3af33e 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -228,7 +228,7 @@ pub fn do_mkdir(path: &str, _mode: FileMode) -> Result { return Ok(0); } -/// @breif 删除文件夹 +/// @brief 删除文件夹 pub fn do_remove_dir(path: &str) -> Result { // 文件名过长 if path.len() > PAGE_4K_SIZE as usize { diff --git a/kernel/src/io/device.rs b/kernel/src/io/device.rs index 96a98764..96d2f12f 100644 --- a/kernel/src/io/device.rs +++ b/kernel/src/io/device.rs @@ -81,7 +81,7 @@ pub trait BlockDevice: Any + Send + Sync + Debug { /// @brief: 同步磁盘信息,把所有的dirty数据写回硬盘 - 待实现 fn sync(&self) -> Result<(), SystemError>; - /// @breif: 每个块设备都必须固定自己块大小,而且该块大小必须是2的幂次 + /// @brief: 每个块设备都必须固定自己块大小,而且该块大小必须是2的幂次 /// @return: 返回一个固定量,硬编码(编程的时候固定的常量). fn blk_size_log2(&self) -> u8; diff --git a/kernel/src/mm/allocator/buddy.rs b/kernel/src/mm/allocator/buddy.rs index 23d77f06..23647948 100644 --- a/kernel/src/mm/allocator/buddy.rs +++ b/kernel/src/mm/allocator/buddy.rs @@ -70,9 +70,9 @@ pub struct BuddyAllocator { impl BuddyAllocator { const BUDDY_ENTRIES: usize = - // 定义一个变量记录buddy表的大小 + // 定义一个变量记录buddy表的大小 (A::PAGE_SIZE - mem::size_of::>()) / mem::size_of::(); - + pub unsafe fn new(mut bump_allocator: BumpAllocator) -> Option { let initial_free_pages = bump_allocator.usage().free(); kdebug!("Free pages before init buddy: {:?}", initial_free_pages); diff --git a/kernel/src/net/socket.rs b/kernel/src/net/socket.rs index daee14da..c5e4951d 100644 --- a/kernel/src/net/socket.rs +++ b/kernel/src/net/socket.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use hashbrown::HashMap; use smoltcp::{ iface::{SocketHandle, SocketSet}, socket::{raw, tcp, udp}, @@ -25,6 +26,100 @@ lazy_static! { /// TODO: 优化这里,自己实现SocketSet!!!现在这样的话,不管全局有多少个网卡,每个时间点都只会有1个进程能够访问socket pub static ref SOCKET_SET: SpinLock> = SpinLock::new(SocketSet::new(vec![])); pub static ref SOCKET_WAITQUEUE: WaitQueue = WaitQueue::INIT; + /// 端口管理器 + pub static ref PORT_MANAGER: PortManager = PortManager::new(); +} + +/// @brief TCP 和 UDP 的端口管理器。 +/// 如果 TCP/UDP 的 socket 绑定了某个端口,它会在对应的表中记录,以检测端口冲突。 +pub struct PortManager { + // TCP 端口记录表 + tcp_port_table: SpinLock>>, + // UDP 端口记录表 + udp_port_table: SpinLock>>, +} + +impl PortManager { + pub fn new() -> Self { + return 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: SocketType) -> 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 = EPHEMERAL_PORT + 1; + } + port = EPHEMERAL_PORT; + } + + // 使用 ListenTable 检查端口是否被占用 + let listen_table_guard = match socket_type { + SocketType::UdpSocket => self.udp_port_table.lock(), + SocketType::TcpSocket => self.tcp_port_table.lock(), + SocketType::RawSocket => todo!(), + }; + if let None = listen_table_guard.get(&port) { + drop(listen_table_guard); + return Ok(port); + } + remaining -= 1; + } + return Err(SystemError::EADDRINUSE); + } + + /// @brief 检测给定端口是否已被占用,如果未被占用则在 TCP/UDP 对应的表中记录 + /// + /// TODO: 增加支持端口复用的逻辑 + pub fn get_port( + &self, + socket_type: SocketType, + port: u16, + handle: Arc, + ) -> Result<(), SystemError> { + if port > 0 { + let mut listen_table_guard = match socket_type { + SocketType::UdpSocket => self.udp_port_table.lock(), + SocketType::TcpSocket => self.tcp_port_table.lock(), + SocketType::RawSocket => panic!("RawSocket cann't bind a port"), + }; + match listen_table_guard.get(&port) { + Some(_) => return Err(SystemError::EADDRINUSE), + None => listen_table_guard.insert(port, handle), + }; + drop(listen_table_guard); + } + return Ok(()); + } + + /// @brief 在对应的端口记录表中将端口和 socket 解绑 + pub fn unbind_port(&self, socket_type: SocketType, port: u16) -> Result<(), SystemError> { + let mut listen_table_guard = match socket_type { + SocketType::UdpSocket => self.udp_port_table.lock(), + SocketType::TcpSocket => self.tcp_port_table.lock(), + SocketType::RawSocket => return Ok(()), + }; + listen_table_guard.remove(&port); + drop(listen_table_guard); + return Ok(()); + } } /* For setsockopt(2) */ @@ -38,8 +133,8 @@ pub const SOL_SOCKET: u8 = 1; pub struct GlobalSocketHandle(SocketHandle); impl GlobalSocketHandle { - pub fn new(handle: SocketHandle) -> Self { - Self(handle) + pub fn new(handle: SocketHandle) -> Arc { + return Arc::new(Self(handle)); } } @@ -59,7 +154,7 @@ impl Drop for GlobalSocketHandle { } /// @brief socket的类型 -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum SocketType { /// 原始的socket RawSocket, @@ -86,7 +181,7 @@ bitflags! { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// @brief 在trait Socket的metadata函数中返回该结构体供外部使用 pub struct SocketMetadata { /// socket的类型 @@ -101,18 +196,36 @@ pub struct SocketMetadata { pub options: SocketOptions, } +impl SocketMetadata { + fn new( + socket_type: SocketType, + send_buf_size: usize, + recv_buf_size: usize, + metadata_buf_size: usize, + options: SocketOptions, + ) -> Self { + Self { + socket_type, + send_buf_size, + recv_buf_size, + metadata_buf_size, + options, + } + } +} + /// @brief 表示原始的socket。原始套接字绕过传输层协议(如 TCP 或 UDP)并提供对网络层协议(如 IP)的直接访问。 /// /// ref: https://man7.org/linux/man-pages/man7/raw.7.html #[derive(Debug, Clone)] pub struct RawSocket { - handle: GlobalSocketHandle, + handle: Arc, /// 用户发送的数据包是否包含了IP头. /// 如果是true,用户发送的数据包,必须包含IP头。(即用户要自行设置IP头+数据) /// 如果是false,用户发送的数据包,不包含IP头。(即用户只要设置数据) header_included: bool, - /// socket的选项 - options: SocketOptions, + /// socket的metadata + metadata: SocketMetadata, } impl RawSocket { @@ -147,12 +260,21 @@ impl RawSocket { ); // 把socket添加到socket集合中,并得到socket的句柄 - let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + let handle: Arc = + GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + + let metadata = SocketMetadata::new( + SocketType::RawSocket, + Self::DEFAULT_RX_BUF_SIZE, + Self::DEFAULT_TX_BUF_SIZE, + Self::DEFAULT_METADATA_BUF_SIZE, + options, + ); return Self { handle, header_included: false, - options, + metadata, }; } } @@ -177,7 +299,7 @@ impl Socket for RawSocket { ); } Err(smoltcp::socket::raw::RecvError::Exhausted) => { - if !self.options.contains(SocketOptions::BLOCK) { + if !self.metadata.options.contains(SocketOptions::BLOCK) { // 如果是非阻塞的socket,就返回错误 return (Err(SystemError::EAGAIN_OR_EWOULDBLOCK), Endpoint::Ip(None)); } @@ -271,7 +393,7 @@ impl Socket for RawSocket { } fn metadata(&self) -> Result { - todo!() + Ok(self.metadata.clone()) } fn box_clone(&self) -> alloc::boxed::Box { @@ -284,9 +406,9 @@ impl Socket for RawSocket { /// https://man7.org/linux/man-pages/man7/udp.7.html #[derive(Debug, Clone)] pub struct UdpSocket { - pub handle: GlobalSocketHandle, + pub handle: Arc, remote_endpoint: Option, // 记录远程endpoint提供给connect(), 应该使用IP地址。 - options: SocketOptions, + metadata: SocketMetadata, } impl UdpSocket { @@ -315,17 +437,29 @@ impl UdpSocket { let socket = udp::Socket::new(tx_buffer, rx_buffer); // 把socket添加到socket集合中,并得到socket的句柄 - let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + let handle: Arc = + GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + + let metadata = SocketMetadata::new( + SocketType::UdpSocket, + Self::DEFAULT_RX_BUF_SIZE, + Self::DEFAULT_TX_BUF_SIZE, + Self::DEFAULT_METADATA_BUF_SIZE, + options, + ); return Self { handle, remote_endpoint: None, - options, + metadata, }; } fn do_bind(&self, socket: &mut udp::Socket, endpoint: Endpoint) -> Result<(), SystemError> { if let Endpoint::Ip(Some(ip)) = endpoint { + // 检测端口是否已被占用 + PORT_MANAGER.get_port(self.metadata.socket_type, ip.port, self.handle.clone())?; + let bind_res = if ip.addr.is_unspecified() { socket.bind(ip.port) } else { @@ -388,7 +522,7 @@ impl Socket for UdpSocket { // kdebug!("is open()={}", socket.is_open()); // kdebug!("socket endpoint={:?}", socket.endpoint()); if socket.endpoint().port == 0 { - let temp_port = get_ephemeral_port(); + let temp_port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; let local_ep = match remote_endpoint.addr { // 远程remote endpoint使用什么协议,发送的时候使用的协议是一样的吧 @@ -461,7 +595,7 @@ impl Socket for UdpSocket { todo!() } fn metadata(&self) -> Result { - todo!() + Ok(self.metadata.clone()) } fn box_clone(&self) -> alloc::boxed::Box { @@ -499,10 +633,10 @@ impl Socket for UdpSocket { /// https://man7.org/linux/man-pages/man7/tcp.7.html #[derive(Debug, Clone)] pub struct TcpSocket { - handle: GlobalSocketHandle, + handle: Arc, local_endpoint: Option, // save local endpoint for bind() is_listening: bool, - options: SocketOptions, + metadata: SocketMetadata, } impl TcpSocket { @@ -525,13 +659,22 @@ impl TcpSocket { let socket = tcp::Socket::new(tx_buffer, rx_buffer); // 把socket添加到socket集合中,并得到socket的句柄 - let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + let handle: Arc = + GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); + + let metadata = SocketMetadata::new( + SocketType::TcpSocket, + Self::DEFAULT_RX_BUF_SIZE, + Self::DEFAULT_TX_BUF_SIZE, + Self::DEFAULT_METADATA_BUF_SIZE, + options, + ); return Self { handle, local_endpoint: None, is_listening: false, - options, + metadata, }; } fn do_listen( @@ -546,7 +689,7 @@ impl TcpSocket { // kdebug!("Tcp Socket Listen on {local_endpoint}"); socket.listen(local_endpoint) }; - // todo: 增加端口占用检查 + // TODO: 增加端口占用检查 return match listen_result { Ok(()) => { // kdebug!( @@ -668,7 +811,7 @@ impl Socket for TcpSocket { let socket = sockets.get_mut::(self.handle.0); if let Endpoint::Ip(Some(ip)) = endpoint { - let temp_port = get_ephemeral_port(); + let temp_port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; // kdebug!("temp_port: {}", temp_port); let iface: Arc = NET_DRIVERS.write().get(&0).unwrap().clone(); let mut inner_iface = iface.inner_iface().lock(); @@ -737,9 +880,12 @@ impl Socket for TcpSocket { fn bind(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { if let Endpoint::Ip(Some(mut ip)) = endpoint { if ip.port == 0 { - ip.port = get_ephemeral_port(); + ip.port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; } + // 检测端口是否已被占用 + PORT_MANAGER.get_port(self.metadata.socket_type, ip.port, self.handle.clone())?; + self.local_endpoint = Some(ip); self.is_listening = false; return Ok(()); @@ -785,11 +931,19 @@ impl Socket for TcpSocket { let new_handle = GlobalSocketHandle::new(sockets.add(tcp_socket)); let old_handle = ::core::mem::replace(&mut self.handle, new_handle); + let metadata = SocketMetadata { + socket_type: SocketType::TcpSocket, + send_buf_size: Self::DEFAULT_RX_BUF_SIZE, + recv_buf_size: Self::DEFAULT_TX_BUF_SIZE, + metadata_buf_size: Self::DEFAULT_METADATA_BUF_SIZE, + options: self.metadata.options, + }; + Box::new(TcpSocket { handle: old_handle, local_endpoint: self.local_endpoint, is_listening: false, - options: self.options, + metadata, }) }; // kdebug!("tcp accept: new socket: {:?}", new_socket); @@ -825,7 +979,7 @@ impl Socket for TcpSocket { } fn metadata(&self) -> Result { - todo!() + Ok(self.metadata.clone()) } fn box_clone(&self) -> alloc::boxed::Box { @@ -833,26 +987,6 @@ impl Socket for TcpSocket { } } -/// @breif 自动分配一个未被使用的PORT -/// -/// TODO: 增加ListenTable, 用于检查端口是否被占用 -pub fn get_ephemeral_port() -> u16 { - // 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; - } - if EPHEMERAL_PORT == 65535 { - EPHEMERAL_PORT = 49152; - } else { - EPHEMERAL_PORT = EPHEMERAL_PORT + 1; - } - EPHEMERAL_PORT - } -} - /// @brief 地址族的枚举 /// /// 参考:https://opengrok.ringotek.cn/xref/linux-5.19.10/include/linux/socket.h#180 @@ -1012,6 +1146,10 @@ impl IndexNode for SocketInode { &self, _data: &mut crate::filesystem::vfs::FilePrivateData, ) -> Result<(), SystemError> { + let socket = self.0.lock(); + if let Some(Endpoint::Ip(Some(ip))) = socket.endpoint() { + PORT_MANAGER.unbind_port(socket.metadata().unwrap().socket_type, ip.port)?; + } return Ok(()); } diff --git a/kernel/src/process/process.rs b/kernel/src/process/process.rs index 084ee52d..275fb954 100644 --- a/kernel/src/process/process.rs +++ b/kernel/src/process/process.rs @@ -348,7 +348,6 @@ impl process_control_block { } } - /// @brief 初始化pid=1的进程的stdio pub fn init_stdio() -> Result<(), SystemError> { if current_pcb().pid != 1 { diff --git a/user/apps/test_bind/Makefile b/user/apps/test_bind/Makefile new file mode 100644 index 00000000..8aaedcd6 --- /dev/null +++ b/user/apps/test_bind/Makefile @@ -0,0 +1,27 @@ +CC=$(DragonOS_GCC)/x86_64-elf-gcc +LD=ld +OBJCOPY=objcopy +# 修改这里,把它改为你的relibc的sysroot路径 +RELIBC_OPT=$(DADK_BUILD_CACHE_DIR_RELIBC_0_1_0) +CFLAGS=-I $(RELIBC_OPT)/include + +tmp_output_dir=$(ROOT_PATH)/bin/tmp/user +output_dir=$(DADK_BUILD_CACHE_DIR_HTTP_SERVER_0_1_0) + +LIBC_OBJS:=$(shell find $(RELIBC_OPT)/lib -name "*.o" | sort ) +LIBC_OBJS+=$(RELIBC_OPT)/lib/libc.a + +all: main.o + mkdir -p $(tmp_output_dir) + + $(LD) -b elf64-x86-64 -z muldefs -o $(tmp_output_dir)/test_bind $(shell find . -name "*.o") $(LIBC_OBJS) -T link.lds + + $(OBJCOPY) -I elf64-x86-64 -R ".eh_frame" -R ".comment" -O elf64-x86-64 $(tmp_output_dir)/test_bind $(output_dir)/test_bind.elf + + mv $(output_dir)/test_bind.elf $(output_dir)/test_bind + +main.o: main.c + $(CC) $(CFLAGS) -c main.c -o main.o + +clean: + rm -f *.o \ No newline at end of file diff --git a/user/apps/test_bind/link.lds b/user/apps/test_bind/link.lds new file mode 100644 index 00000000..1f2e57e4 --- /dev/null +++ b/user/apps/test_bind/link.lds @@ -0,0 +1,239 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", + "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/user/apps/test_bind/main.c b/user/apps/test_bind/main.c new file mode 100644 index 00000000..b5feb11e --- /dev/null +++ b/user/apps/test_bind/main.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 12580 +#define MAX_REQUEST_SIZE 1500 +#define MAX_RESPONSE_SIZE 1500 +#define EXIT_CODE 1 +#define min(a, b) ((a) < (b) ? (a) : (b)) + +struct sockaddr_in address; +int addrlen = sizeof(address); +char buffer[MAX_REQUEST_SIZE] = {0}; +int opt = 1; + +void test_tcp_bind() +{ + int tcp_sk_fd1, tcp_sk_fd2, tcp_sk_fd3; + + // create tcp sockets + if ((tcp_sk_fd1 = socket(AF_INET, SOCK_STREAM, 0)) == 0) + { + perror("tcp socket (1) failed"); + exit(EXIT_CODE); + } + if ((tcp_sk_fd2 = socket(AF_INET, SOCK_STREAM, 0)) == 0) + { + perror("tcp socket (2) failed"); + exit(EXIT_CODE); + } + if ((tcp_sk_fd3 = socket(AF_INET, SOCK_STREAM, 0)) == 0) + { + perror("tcp socket (3) failed"); + exit(EXIT_CODE); + } + + // TEST tcp bind diff ports + if (bind(tcp_sk_fd1, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("tcp bind (1) failed"); + exit(EXIT_CODE); + } + address.sin_port = htons(PORT+1); + if (bind(tcp_sk_fd2, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("tcp bind (2) failed"); + exit(EXIT_CODE); + } + printf("===TEST 4 PASSED===\n"); + + // TEST tcp bind same ports + address.sin_port = htons(PORT); + if (bind(tcp_sk_fd3, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("tcp bind (3) failed"); + // exit(EXIT_CODE); + } + printf("===TEST 5 PASSED===\n"); + + if (close(tcp_sk_fd1) < 0) + { + perror("tcp close (1) failed"); + exit(EXIT_CODE); + } + if (close(tcp_sk_fd2) < 0) + { + perror("tcp close (2) failed"); + exit(EXIT_CODE); + } + if (close(tcp_sk_fd3) < 0) + { + perror("tcp close (3) failed"); + exit(EXIT_CODE); + } + printf("===TEST 6 PASSED===\n"); +} + +void test_udp_bind() +{ + int udp_sk_fd1, udp_sk_fd2, udp_sk_fd3; + + // create tcp sockets + if ((udp_sk_fd1 = socket(AF_INET, SOCK_DGRAM, 0)) == 0) + { + perror("udp socket (1) failed"); + exit(EXIT_CODE); + } + if ((udp_sk_fd2 = socket(AF_INET, SOCK_DGRAM, 0)) == 0) + { + perror("udp socket (2) failed"); + exit(EXIT_CODE); + } + if ((udp_sk_fd3 = socket(AF_INET, SOCK_DGRAM, 0)) == 0) + { + perror("udp socket (3) failed"); + exit(EXIT_CODE); + } + + // TEST udp bind diff ports + if (bind(udp_sk_fd1, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("udp bind (1) failed"); + exit(EXIT_CODE); + } + address.sin_port = htons(PORT+1); + if (bind(udp_sk_fd2, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("udp bind (2) failed"); + exit(EXIT_CODE); + } + printf("===TEST 7 PASSED===\n"); + + // TEST udp bind same ports + address.sin_port = htons(PORT); + if (bind(udp_sk_fd3, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("udp bind (3) failed"); + // exit(EXIT_CODE); + } + printf("===TEST 8 PASSED===\n"); + + if (close(udp_sk_fd1) < 0) + { + perror("udp close (1) failed"); + exit(EXIT_CODE); + } + if (close(udp_sk_fd2) < 0) + { + perror("udp close (2) failed"); + exit(EXIT_CODE); + } + if (close(udp_sk_fd3) < 0) + { + perror("udp close (3) failed"); + exit(EXIT_CODE); + } + printf("===TEST 9 PASSED===\n"); +} + +void test_all_ports() { + int count = 0; + + while (1) { + int tcp_fd; + if ((tcp_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) + { + perror("socket failed"); + exit(EXIT_CODE); + } + + address.sin_port = htons(0); + if (bind(tcp_fd, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("bind failed"); + // exit(EXIT_CODE); + break; + } + + count++; + } + printf("===TEST 10===\n"); + printf("count: %d\n", count); +} + +int main(int argc, char const *argv[]) +{ + int server_fd; + int udp_sk_fd; + + // 创建socket + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) + { + perror("tcp socket failed"); + exit(EXIT_CODE); + } + + if ((udp_sk_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) + { + perror("udp socket failed"); + exit(EXIT_CODE); + } + + // 设置地址和端口 + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(PORT); + + // TEST socket's bind + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("tcp bind failed"); + exit(EXIT_CODE); + } + address.sin_port = htons(PORT); + if (bind(udp_sk_fd, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("udp bind failed"); + exit(EXIT_CODE); + } + printf("===TEST 1 PASSED===\n"); + + // TEST socket's listen + if (listen(server_fd, 3) < 0) + { + perror("listen failed"); + exit(EXIT_CODE); + } + printf("===TEST 2 PASSED===\n"); + + // TEST socket's close + if (close(server_fd) < 0) + { + perror("tcp close failed"); + exit(EXIT_CODE); + } + if (close(udp_sk_fd) < 0) + { + perror("udp close failed"); + exit(EXIT_CODE); + } + printf("===TEST 3 PASSED===\n"); + + + test_tcp_bind(); + test_udp_bind(); + test_all_ports(); + + return 0; +} diff --git a/user/dadk/config/test_bind-0.1.0.dadk b/user/dadk/config/test_bind-0.1.0.dadk new file mode 100644 index 00000000..c3356fef --- /dev/null +++ b/user/dadk/config/test_bind-0.1.0.dadk @@ -0,0 +1,28 @@ +{ + "name": "test_bind", + "version": "0.1.0", + "description": "一个简单的test bind", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_bind" + } + } + }, + "depends": [ + { + "name": "relibc", + "version": "0.1.0" + } + ], + "build": { + "build_command": "make" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [] +} \ No newline at end of file