增加 ListenTable 来检测端口占用 (#291)

* 增加 ListenTable 来检测端口占用


* 使用Arc封装GlobalSocketHandle

* 删除 listen 处的端口检测逻辑,延至实现端口复用时完成

* 设立两张表,分别记录TCP和UDP的端口占用

* 实现 meatadata 相关逻辑

* 实现socket关闭时,端口在表中移除

* 使用端口管理器重构端口记录表

* 修正与RawSocket相关的端口管理逻辑

* 补充测试文件

* 修正 unbind_port 在逻辑错误

* 修正格式问题

---------

Co-authored-by: longjin <longjin@RinGoTek.cn>
This commit is contained in:
Xshine 2023-07-28 17:51:05 +08:00 committed by GitHub
parent 7cc4a02c7f
commit 821bb9a2dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 716 additions and 51 deletions

View File

@ -228,7 +228,7 @@ pub fn do_mkdir(path: &str, _mode: FileMode) -> Result<u64, SystemError> {
return Ok(0); return Ok(0);
} }
/// @breif 删除文件夹 /// @brief 删除文件夹
pub fn do_remove_dir(path: &str) -> Result<u64, SystemError> { pub fn do_remove_dir(path: &str) -> Result<u64, SystemError> {
// 文件名过长 // 文件名过长
if path.len() > PAGE_4K_SIZE as usize { if path.len() > PAGE_4K_SIZE as usize {

View File

@ -81,7 +81,7 @@ pub trait BlockDevice: Any + Send + Sync + Debug {
/// @brief: 同步磁盘信息把所有的dirty数据写回硬盘 - 待实现 /// @brief: 同步磁盘信息把所有的dirty数据写回硬盘 - 待实现
fn sync(&self) -> Result<(), SystemError>; fn sync(&self) -> Result<(), SystemError>;
/// @breif: 每个块设备都必须固定自己块大小而且该块大小必须是2的幂次 /// @brief: 每个块设备都必须固定自己块大小而且该块大小必须是2的幂次
/// @return: 返回一个固定量,硬编码(编程的时候固定的常量). /// @return: 返回一个固定量,硬编码(编程的时候固定的常量).
fn blk_size_log2(&self) -> u8; fn blk_size_log2(&self) -> u8;

View File

@ -1,5 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloc::{boxed::Box, sync::Arc, vec::Vec};
use hashbrown::HashMap;
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
socket::{raw, tcp, udp}, socket::{raw, tcp, udp},
@ -25,6 +26,100 @@ lazy_static! {
/// TODO: 优化这里自己实现SocketSet现在这样的话不管全局有多少个网卡每个时间点都只会有1个进程能够访问socket /// TODO: 优化这里自己实现SocketSet现在这样的话不管全局有多少个网卡每个时间点都只会有1个进程能够访问socket
pub static ref SOCKET_SET: SpinLock<SocketSet<'static >> = SpinLock::new(SocketSet::new(vec![])); pub static ref SOCKET_SET: SpinLock<SocketSet<'static >> = SpinLock::new(SocketSet::new(vec![]));
pub static ref SOCKET_WAITQUEUE: WaitQueue = WaitQueue::INIT; 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<HashMap<u16, Arc<GlobalSocketHandle>>>,
// UDP 端口记录表
udp_port_table: SpinLock<HashMap<u16, Arc<GlobalSocketHandle>>>,
}
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<u16, SystemError> {
// 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<GlobalSocketHandle>,
) -> 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) */ /* For setsockopt(2) */
@ -38,8 +133,8 @@ pub const SOL_SOCKET: u8 = 1;
pub struct GlobalSocketHandle(SocketHandle); pub struct GlobalSocketHandle(SocketHandle);
impl GlobalSocketHandle { impl GlobalSocketHandle {
pub fn new(handle: SocketHandle) -> Self { pub fn new(handle: SocketHandle) -> Arc<Self> {
Self(handle) return Arc::new(Self(handle));
} }
} }
@ -59,7 +154,7 @@ impl Drop for GlobalSocketHandle {
} }
/// @brief socket的类型 /// @brief socket的类型
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum SocketType { pub enum SocketType {
/// 原始的socket /// 原始的socket
RawSocket, RawSocket,
@ -86,7 +181,7 @@ bitflags! {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// @brief 在trait Socket的metadata函数中返回该结构体供外部使用 /// @brief 在trait Socket的metadata函数中返回该结构体供外部使用
pub struct SocketMetadata { pub struct SocketMetadata {
/// socket的类型 /// socket的类型
@ -101,18 +196,36 @@ pub struct SocketMetadata {
pub options: SocketOptions, 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的直接访问。 /// @brief 表示原始的socket。原始套接字绕过传输层协议如 TCP 或 UDP并提供对网络层协议如 IP的直接访问。
/// ///
/// ref: https://man7.org/linux/man-pages/man7/raw.7.html /// ref: https://man7.org/linux/man-pages/man7/raw.7.html
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RawSocket { pub struct RawSocket {
handle: GlobalSocketHandle, handle: Arc<GlobalSocketHandle>,
/// 用户发送的数据包是否包含了IP头. /// 用户发送的数据包是否包含了IP头.
/// 如果是true用户发送的数据包必须包含IP头。即用户要自行设置IP头+数据) /// 如果是true用户发送的数据包必须包含IP头。即用户要自行设置IP头+数据)
/// 如果是false用户发送的数据包不包含IP头。即用户只要设置数据 /// 如果是false用户发送的数据包不包含IP头。即用户只要设置数据
header_included: bool, header_included: bool,
/// socket的选项 /// socket的metadata
options: SocketOptions, metadata: SocketMetadata,
} }
impl RawSocket { impl RawSocket {
@ -147,12 +260,21 @@ impl RawSocket {
); );
// 把socket添加到socket集合中并得到socket的句柄 // 把socket添加到socket集合中并得到socket的句柄
let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); let handle: Arc<GlobalSocketHandle> =
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 { return Self {
handle, handle,
header_included: false, header_included: false,
options, metadata,
}; };
} }
} }
@ -177,7 +299,7 @@ impl Socket for RawSocket {
); );
} }
Err(smoltcp::socket::raw::RecvError::Exhausted) => { Err(smoltcp::socket::raw::RecvError::Exhausted) => {
if !self.options.contains(SocketOptions::BLOCK) { if !self.metadata.options.contains(SocketOptions::BLOCK) {
// 如果是非阻塞的socket就返回错误 // 如果是非阻塞的socket就返回错误
return (Err(SystemError::EAGAIN_OR_EWOULDBLOCK), Endpoint::Ip(None)); return (Err(SystemError::EAGAIN_OR_EWOULDBLOCK), Endpoint::Ip(None));
} }
@ -271,7 +393,7 @@ impl Socket for RawSocket {
} }
fn metadata(&self) -> Result<SocketMetadata, SystemError> { fn metadata(&self) -> Result<SocketMetadata, SystemError> {
todo!() Ok(self.metadata.clone())
} }
fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> { fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> {
@ -284,9 +406,9 @@ impl Socket for RawSocket {
/// https://man7.org/linux/man-pages/man7/udp.7.html /// https://man7.org/linux/man-pages/man7/udp.7.html
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct UdpSocket { pub struct UdpSocket {
pub handle: GlobalSocketHandle, pub handle: Arc<GlobalSocketHandle>,
remote_endpoint: Option<Endpoint>, // 记录远程endpoint提供给connect() 应该使用IP地址。 remote_endpoint: Option<Endpoint>, // 记录远程endpoint提供给connect() 应该使用IP地址。
options: SocketOptions, metadata: SocketMetadata,
} }
impl UdpSocket { impl UdpSocket {
@ -315,17 +437,29 @@ impl UdpSocket {
let socket = udp::Socket::new(tx_buffer, rx_buffer); let socket = udp::Socket::new(tx_buffer, rx_buffer);
// 把socket添加到socket集合中并得到socket的句柄 // 把socket添加到socket集合中并得到socket的句柄
let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); let handle: Arc<GlobalSocketHandle> =
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 { return Self {
handle, handle,
remote_endpoint: None, remote_endpoint: None,
options, metadata,
}; };
} }
fn do_bind(&self, socket: &mut udp::Socket, endpoint: Endpoint) -> Result<(), SystemError> { fn do_bind(&self, socket: &mut udp::Socket, endpoint: Endpoint) -> Result<(), SystemError> {
if let Endpoint::Ip(Some(ip)) = endpoint { 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() { let bind_res = if ip.addr.is_unspecified() {
socket.bind(ip.port) socket.bind(ip.port)
} else { } else {
@ -388,7 +522,7 @@ impl Socket for UdpSocket {
// kdebug!("is open()={}", socket.is_open()); // kdebug!("is open()={}", socket.is_open());
// kdebug!("socket endpoint={:?}", socket.endpoint()); // kdebug!("socket endpoint={:?}", socket.endpoint());
if socket.endpoint().port == 0 { 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 { let local_ep = match remote_endpoint.addr {
// 远程remote endpoint使用什么协议发送的时候使用的协议是一样的吧 // 远程remote endpoint使用什么协议发送的时候使用的协议是一样的吧
@ -461,7 +595,7 @@ impl Socket for UdpSocket {
todo!() todo!()
} }
fn metadata(&self) -> Result<SocketMetadata, SystemError> { fn metadata(&self) -> Result<SocketMetadata, SystemError> {
todo!() Ok(self.metadata.clone())
} }
fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> { fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> {
@ -499,10 +633,10 @@ impl Socket for UdpSocket {
/// https://man7.org/linux/man-pages/man7/tcp.7.html /// https://man7.org/linux/man-pages/man7/tcp.7.html
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TcpSocket { pub struct TcpSocket {
handle: GlobalSocketHandle, handle: Arc<GlobalSocketHandle>,
local_endpoint: Option<wire::IpEndpoint>, // save local endpoint for bind() local_endpoint: Option<wire::IpEndpoint>, // save local endpoint for bind()
is_listening: bool, is_listening: bool,
options: SocketOptions, metadata: SocketMetadata,
} }
impl TcpSocket { impl TcpSocket {
@ -525,13 +659,22 @@ impl TcpSocket {
let socket = tcp::Socket::new(tx_buffer, rx_buffer); let socket = tcp::Socket::new(tx_buffer, rx_buffer);
// 把socket添加到socket集合中并得到socket的句柄 // 把socket添加到socket集合中并得到socket的句柄
let handle: GlobalSocketHandle = GlobalSocketHandle::new(SOCKET_SET.lock().add(socket)); let handle: Arc<GlobalSocketHandle> =
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 { return Self {
handle, handle,
local_endpoint: None, local_endpoint: None,
is_listening: false, is_listening: false,
options, metadata,
}; };
} }
fn do_listen( fn do_listen(
@ -546,7 +689,7 @@ impl TcpSocket {
// kdebug!("Tcp Socket Listen on {local_endpoint}"); // kdebug!("Tcp Socket Listen on {local_endpoint}");
socket.listen(local_endpoint) socket.listen(local_endpoint)
}; };
// todo: 增加端口占用检查 // TODO: 增加端口占用检查
return match listen_result { return match listen_result {
Ok(()) => { Ok(()) => {
// kdebug!( // kdebug!(
@ -668,7 +811,7 @@ impl Socket for TcpSocket {
let socket = sockets.get_mut::<tcp::Socket>(self.handle.0); let socket = sockets.get_mut::<tcp::Socket>(self.handle.0);
if let Endpoint::Ip(Some(ip)) = endpoint { 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); // kdebug!("temp_port: {}", temp_port);
let iface: Arc<dyn NetDriver> = NET_DRIVERS.write().get(&0).unwrap().clone(); let iface: Arc<dyn NetDriver> = NET_DRIVERS.write().get(&0).unwrap().clone();
let mut inner_iface = iface.inner_iface().lock(); let mut inner_iface = iface.inner_iface().lock();
@ -737,9 +880,12 @@ impl Socket for TcpSocket {
fn bind(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { fn bind(&mut self, endpoint: Endpoint) -> Result<(), SystemError> {
if let Endpoint::Ip(Some(mut ip)) = endpoint { if let Endpoint::Ip(Some(mut ip)) = endpoint {
if ip.port == 0 { 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.local_endpoint = Some(ip);
self.is_listening = false; self.is_listening = false;
return Ok(()); return Ok(());
@ -785,11 +931,19 @@ impl Socket for TcpSocket {
let new_handle = GlobalSocketHandle::new(sockets.add(tcp_socket)); let new_handle = GlobalSocketHandle::new(sockets.add(tcp_socket));
let old_handle = ::core::mem::replace(&mut self.handle, new_handle); 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 { Box::new(TcpSocket {
handle: old_handle, handle: old_handle,
local_endpoint: self.local_endpoint, local_endpoint: self.local_endpoint,
is_listening: false, is_listening: false,
options: self.options, metadata,
}) })
}; };
// kdebug!("tcp accept: new socket: {:?}", new_socket); // kdebug!("tcp accept: new socket: {:?}", new_socket);
@ -825,7 +979,7 @@ impl Socket for TcpSocket {
} }
fn metadata(&self) -> Result<SocketMetadata, SystemError> { fn metadata(&self) -> Result<SocketMetadata, SystemError> {
todo!() Ok(self.metadata.clone())
} }
fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> { fn box_clone(&self) -> alloc::boxed::Box<dyn Socket> {
@ -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 地址族的枚举 /// @brief 地址族的枚举
/// ///
/// 参考https://opengrok.ringotek.cn/xref/linux-5.19.10/include/linux/socket.h#180 /// 参考https://opengrok.ringotek.cn/xref/linux-5.19.10/include/linux/socket.h#180
@ -1012,6 +1146,10 @@ impl IndexNode for SocketInode {
&self, &self,
_data: &mut crate::filesystem::vfs::FilePrivateData, _data: &mut crate::filesystem::vfs::FilePrivateData,
) -> Result<(), SystemError> { ) -> 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(()); return Ok(());
} }

View File

@ -348,7 +348,6 @@ impl process_control_block {
} }
} }
/// @brief 初始化pid=1的进程的stdio /// @brief 初始化pid=1的进程的stdio
pub fn init_stdio() -> Result<(), SystemError> { pub fn init_stdio() -> Result<(), SystemError> {
if current_pcb().pid != 1 { if current_pcb().pid != 1 {

View File

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

View File

@ -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_*) }
}

234
user/apps/test_bind/main.c Normal file
View File

@ -0,0 +1,234 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

View File

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