增加 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
9 changed files with 716 additions and 51 deletions

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