diff --git a/kernel/trunk/docs/sysfuncr.txt b/kernel/trunk/docs/sysfuncr.txt index 9fbd737e34..026339df8a 100644 --- a/kernel/trunk/docs/sysfuncr.txt +++ b/kernel/trunk/docs/sysfuncr.txt @@ -2587,7 +2587,8 @@ dword- Параметры: * eax = 53 - номер функции * ebx = 0 - номер подфункции - * ecx = локальный порт (учитывается только младшее слово) + * ecx = локальный порт (учитывается только младшее слово), + ecx = 0 - предоставить системе выбор локального порта * edx = удалённый порт (учитывается только младшее слово) * esi = удалённый IP Возвращаемое значение: @@ -2659,7 +2660,8 @@ dword- Параметры: * eax = 53 - номер функции * ebx = 5 - номер подфункции - * ecx = локальный порт (учитывается только младшее слово) + * ecx = локальный порт (учитывается только младшее слово), + ecx = 0 - предоставить системе выбор локального порта * edx = удалённый порт (учитывается только младшее слово) * esi = удалённый IP * edi = режим открытия: SOCKET_PASSIVE=0 или SOCKET_ACTIVE=1 diff --git a/kernel/trunk/docs/sysfuncs.txt b/kernel/trunk/docs/sysfuncs.txt index 8fb48873f1..b13340d752 100644 --- a/kernel/trunk/docs/sysfuncs.txt +++ b/kernel/trunk/docs/sysfuncs.txt @@ -2564,7 +2564,8 @@ Returned value: Parameters: * eax = 53 - function number * ebx = 0 - subfunction number - * ecx = local port (only low word is taken into account) + * ecx = local port (only low word is taken into account), + ecx = 0 - let the system choose a port * edx = remote port (only low word is taken into account) * esi = remote IP Returned value: @@ -2637,7 +2638,8 @@ Remarks: Parameters: * eax = 53 - function number * ebx = 5 - subfunction number - * ecx = local port (only low word is taken into account) + * ecx = local port (only low word is taken into account), + ecx = 0 - let the system choose a port * edx = remote port (only low word is taken into account) * esi = remote IP * edi = open mode: SOCKET_PASSIVE=0 or SOCKET_ACTIVE=1 diff --git a/kernel/trunk/kernel.asm b/kernel/trunk/kernel.asm index 231c304ca3..62a9f4d8b1 100644 --- a/kernel/trunk/kernel.asm +++ b/kernel/trunk/kernel.asm @@ -554,6 +554,13 @@ high_code: mov [SLOT_BASE + 256 + APPDATA.dir_table], sys_pgdir - OS_BASE + stdcall kernel_alloc, 0x10000/8 + mov edi, eax + mov [network_free_ports], eax + or eax, -1 + mov ecx, 0x10000/32 + rep stosd + ; REDIRECT ALL IRQ'S TO INT'S 0x20-0x2f call rerouteirqs diff --git a/kernel/trunk/network/socket.inc b/kernel/trunk/network/socket.inc index 56127ce2b0..03571a3d79 100644 --- a/kernel/trunk/network/socket.inc +++ b/kernel/trunk/network/socket.inc @@ -66,6 +66,17 @@ SOCKET_ACTIVE = 1 SOCK_STREAM = 1 SOCK_DGRAM = 2 +; pointer to bitmap of free ports (1=free, 0=used) +uglobal +align 4 +network_free_ports dd ? +endg + +iglobal +align 4 +network_free_hint dd 1024/8 +endg + ;; Allocate memory for socket data and put new socket into the list ; Newly created socket is initialized with calling PID and number and ; put into beginning of list (which is a fastest way). @@ -153,6 +164,13 @@ proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD ;jne .next_socket ; okay, we found the correct one + ; mark local port as unused + movzx ebx, [eax + SOCKET.LocalPort] + push eax + mov eax, [network_free_ports] + xchg bl, bh +lock bts [eax], ebx + pop eax ; remove it from the list first, changing pointers mov ebx, [eax + SOCKET.NextPtr] mov eax, [eax + SOCKET.PrevPtr] @@ -253,28 +271,104 @@ endp ; @return 1 (port is free) or 0 (port is in use) in EAX ;; proc is_localport_unused stdcall - xchg bl, bh - - ; assume the return value is 'free' - xor eax, eax - inc al - - mov edx, net_sockets - - .next_socket: - mov edx, [edx + SOCKET.NextPtr] - or edx, edx - jz .exit - cmp [edx + SOCKET.LocalPort], bx - jne .next_socket - - ; return 'in use' - dec al - - .exit: + movzx ebx, bx + mov eax, [network_free_ports] + bt [eax], ebx + setc al + movzx eax, al ret endp +;====================================== +set_local_port: +;-------------------------------------- +;? Set local port in socket structure. +;-------------------------------------- +;> eax -> struct SOCKET +;> bx = local port, or 0 if the kernel must select it itself +;-------------------------------------- +;< CF set on error / cleared on success +;< [eax+SOCKET.LocalPort] filled on success +;====================================== +; 0. Prepare: save registers, make eax point to ports table, expand port to ebx. + push eax ecx + mov eax, [network_free_ports] + movzx ebx, bx +; 1. Test, whether the kernel should choose port itself. If no, proceed to 5. + test ebx, ebx + jnz .given +; 2. Yes, it should. Set ecx = limit of table, eax = start value + lea ecx, [eax+0x10000/8] + add eax, [network_free_hint] +; 3. First scan loop: from free hint to end of table. +.scan1: +; 3a. For each dword, find bit set to 1 + bsf ebx, [eax] + jz .next1 +; 3b. If such bit has been found, atomically test again and clear it. +lock btr [eax], ebx +; 3c. If the bit was still set (usual case), we have found and reserved one port. +; Proceed to 6. + jc .found +; 3d. Otherwise, someone has reserved it between bsf and btr, so retry search. + jmp .scan1 +.next1: +; 3e. All bits are cleared, so advance to next dword. + add eax, 4 +; 3f. Check limit and continue loop. + cmp eax, ecx + jb .scan1 +; 4. Second scan loop: from port 1024 (start of non-system ports) to free hint. + mov eax, [network_free_ports] + mov ecx, eax + add ecx, [network_free_hint] + add eax, 1024/8 +; 4a. Test whether there is something to scan. + cmp eax, ecx + jae .fail +; 4b. Enter the loop, the process is same as for 3. +.scan2: + bsf ebx, [eax] + jz .next2 +lock btr [eax], ebx + jc .found + jmp .scan2 +.next2: + add eax, 4 + cmp eax, ecx + jb .scan2 +; 4c. None found. Fail. +.fail: + pop ecx eax + stc + ret +; 5. No, the kernel should reserve selected port. +.given: +; 5a. Atomically test old value and clear bit. +lock btr [eax], ebx +; 5b. If the bit was set, reservation is successful. Proceed to 8. + jc .set +; 5c. Otherwise, fail. + jmp .fail +.found: +; 6. We have found the bit set to 1, convert the position to port number. + sub eax, [network_free_ports] + lea ebx, [ebx+eax*8] +; 7. Update free hint. + add eax, 4 + cmp eax, 65536/8 + jb @f + mov eax, 1024/8 +@@: + mov [network_free_hint], eax +.set: +; 8. Restore eax, set SOCKET.LocalPort and return. + pop ecx eax + xchg bl, bh ; Intel -> network byte order + mov [eax + SOCKET.LocalPort], bx + clc + ret + ;; [53.0] Open DGRAM socket (connectionless, unreliable) ; ; @param BX is local port number @@ -291,8 +385,8 @@ proc socket_open stdcall push eax - xchg bh, bl - mov [eax + SOCKET.LocalPort], bx + call set_local_port + jc .error.free xchg ch, cl mov [eax + SOCKET.RemotePort], cx mov ebx, [stack_ip] @@ -303,6 +397,9 @@ proc socket_open stdcall stdcall net_socket_addr_to_num ret + .error.free: + stdcall net_socket_free;, eax + .error: DEBUGF 1, "K : socket_open (fail)\n" or eax, -1 @@ -357,8 +454,8 @@ local sockAddr dd ? ; TODO - check this works! ;mov [eax + SOCKET.wndsizeTimer], 0 ; Reset the window timer. - xchg bh, bl - mov [eax + SOCKET.LocalPort], bx + call set_local_port + jc .error.free xchg ch, cl mov [eax + SOCKET.RemotePort], cx mov [eax + SOCKET.OrigRemotePort], cx @@ -411,6 +508,9 @@ local sockAddr dd ? stdcall net_socket_addr_to_num, [sockAddr] ret + .error.free: + stdcall net_socket_free, eax + .error: DEBUGF 1, "K : socket_open_tcp (fail)\n" or eax, -1