/kernel/branches/net/applications/downloader/downloader.asm |
---|
0,0 → 1,1201 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2009-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; downloader.asm - HTTP client for KolibriOS ;; |
;; ;; |
;; Based on HTTPC.asm for menuetos by ville turjanmaa ;; |
;; ;; |
;; Programmers: Barsuk, Clevermouse, Marat Zakiyanov, ;; |
;; Kirill Lipatov, dunkaist, HidnPlayr ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
URLMAXLEN = 1024 |
primary_buffer_size = 4096 |
__DEBUG__ = 1 |
__DEBUG_LEVEL__ = 1 |
format binary as "" |
use32 |
org 0x0 |
db 'MENUET01' ; header |
dd 0x01 ; header version |
dd START ; entry point |
dd IM_END ; image size |
dd I_END ; required memory |
dd I_END ; esp |
dd params ; I_PARAM |
dd 0x0 ; I_Path |
include '../macros.inc' |
include '../proc32.inc' |
include '../network.inc' |
include '../../../../../programs/develop/libraries/box_lib/trunk/box_lib.mac' |
include '../dll.inc' |
include '../debug-fdo.inc' |
START: |
mcall 68, 11 ; init heap so we can allocate memory dynamically |
mcall 40, EV_STACK |
; load libraries |
stdcall dll.Load, @IMPORT |
test eax, eax |
jnz exit |
; prepare webAddr area |
mov al, ' ' |
mov edi, webAddr |
mov ecx, URLMAXLEN |
rep stosb |
xor eax, eax |
stosb |
; prepare document area |
mov al, '/' |
mov edi, document |
stosb |
mov al, ' ' |
mov ecx, URLMAXLEN-1 |
rep stosb |
call load_settings |
cmp byte[params], 0 |
je prepare_event ;red |
; we have an url |
mov edi, document_user |
mov al, ' ' |
mov ecx, URLMAXLEN |
rep stosb |
mov esi, params |
mov edi, document_user |
; copy untill space or 0 |
.copy_param: |
mov al, [esi] |
test al, al |
jz .done |
cmp al, ' ' |
jz .done_inc |
mov [edi], al |
inc esi |
inc edi |
jmp .copy_param |
.done_inc: |
; url is followed by shared memory name. |
inc esi |
.done: |
mov [shared_name], esi |
mov ah, 22 ; strange way to tell that socket should be opened... |
call socket_commands |
jmp still |
prepare_event: |
; Report events |
; Stack 8 + defaults |
mcall 40, 10100111b |
red: ; redraw |
call draw_window |
still: |
mcall 23, 1 ; wait here for event |
cmp eax, 1 ; redraw request ? |
je red |
cmp eax, 2 ; key in buffer ? |
je key |
cmp eax, 3 ; button in buffer ? |
je button |
cmp eax, 6 ; mouse in buffer ? |
je mouse |
; Get the web page data from the remote server |
cmp eax, 8 |
jne still |
call read_incoming_data |
cmp [status], 4 |
je .no_send |
mov [onoff], 1 |
call send_request |
.no_send: |
call print_status |
cmp [prev_status], 4 |
jne no_close |
cmp [status], 4 ; connection closed by server |
jbe no_close ; respond to connection close command |
; draw page |
call read_incoming_data |
mcall close, [socketnum] |
mov [onoff], 0 |
no_close: |
jmp still |
key: |
mcall 2 ; read key |
stdcall [edit_box_key], dword edit1 |
shr eax, 8 |
cmp eax, 13 |
je retkey |
jmp still |
button: |
mcall 17 ; get id |
cmp ah, 26 |
je save |
cmp ah, 1 ; button id=1 ? |
jne noclose |
; DEBUGF 1, "Closing socket before exit...\n" |
close_end_exit: |
exit: |
or eax, -1 ; close this program |
mcall |
mouse: |
stdcall [edit_box_mouse], edit1 |
jmp still |
save: |
DEBUGF 1, "file saved\n" |
mcall 70, fileinfo |
mov ecx, [sc.work_text] |
or ecx, 0x80000000 |
mcall 4, <10, 93>, , download_complete |
jmp still |
noclose: |
cmp ah, 31 |
jne noup |
sub [display_from], 20 |
jmp still |
noup: |
cmp ah, 32 |
jne nourl |
add [display_from], 20 |
jmp still |
retkey: |
mov ah, 22 ; start load |
nourl: |
call socket_commands ; opens or closes the connection |
jmp still |
;**************************************************************************** |
; Function |
; send_request |
; |
; Description |
; Transmits the GET request to the server. |
; This is done as GET then URL then HTTP/1.1', 13, 10, 13, 10 in 3 packets |
; |
;**************************************************************************** |
send_request: |
pusha |
mov esi, string0 |
mov edi, request |
movsd |
; If proxy is used, make absolute URI - prepend http://<host> |
cmp byte[proxyAddr], 0 |
jz .noproxy |
mov dword[edi], 'http' |
mov byte[edi+4], ':' |
mov word[edi+5], '//' |
add edi, 7 |
mov esi, webAddr |
.copy_host_loop: |
lodsb |
cmp al, ' ' |
jz .noproxy |
stosb |
jmp .copy_host_loop |
.noproxy: |
xor edx, edx ; 0 |
.next_edx: |
; Determine the length of the url to send in the GET request |
mov al, [edx+document] |
cmp al, ' ' |
je .document_done |
mov [edi], al |
inc edi |
inc edx |
jmp .next_edx |
.document_done: |
mov esi, stringh |
mov ecx, stringh_end-stringh |
rep movsb |
xor edx, edx ; 0 |
.webaddr_next: |
mov al, [webAddr + edx] |
cmp al, ' ' |
je .webaddr_done |
mov [edi], al |
inc edi |
inc edx |
jmp .webaddr_next |
.webaddr_done: |
cmp byte[proxyUser], 0 |
jz @f |
call append_proxy_auth_header |
@@: |
mov esi, connclose |
mov ecx, connclose_end-connclose |
rep movsb |
pusha |
mov eax, 63 |
mov ebx, 1 |
mov edx, request |
@@: |
mov cl, [edx] |
cmp edx, edi |
jz @f |
mcall |
inc edx |
jmp @b |
@@: |
popa |
mov esi, edi |
sub esi, request ; length |
xor edi, edi ; flags |
;;;;now write \r\nConnection: Close \r\n\r\n |
mcall send, [socketnum], request ;' HTTP/1.1 .. ' |
popa |
ret |
;**************************************************************************** |
; Function |
; print_status |
; |
; Description |
; displays the socket/data received status information |
; |
;**************************************************************************** |
print_status: |
pusha |
mcall 26, 9 |
cmp eax, [nextupdate] |
jb status_return |
add eax, 25 |
mov [nextupdate], eax |
mov ecx, [winys] |
shl ecx, 16 |
add ecx, -18 shl 16 + 10 |
mcall 13, <5, 100>, , 0xffffff |
mov edx, 12 shl 16 - 18 |
add edx, [winys] |
xor esi, esi |
mcall 47, <3, 0>, [status], , |
mov edx, 40 shl 16 - 18 |
add edx, [winys] |
mcall , <6, 0>, [pos] |
status_return: |
popa |
ret |
;**************************************************************************** |
; Function |
; read_incoming_data |
; |
; Description |
; receive the web page from the server, storing it without processing |
; |
;**************************************************************************** |
read_incoming_data: |
cmp [onoff], 1 |
je .rid |
ret |
.rid: |
push esi |
push edi |
DEBUGF 1, "rid\n" |
.read: |
mcall recv, [socketnum], primary_buf, primary_buffer_size, 0 |
inc eax ; -1 = error (socket closed?) |
jz .no_more_data |
dec eax ; 0 bytes... |
jz .read |
mov edi, [pos] |
add [pos], eax |
push eax |
mcall 68, 20, [pos], [buf_ptr] |
mov [buf_ptr], eax |
add edi, eax |
mov esi, primary_buf |
pop ecx ; number of recently read bytes |
lea edx, [ecx - 3] |
rep movsb |
.no_more_data: |
; mov [status], 4 ; connection closed by server |
call parse_result |
mov ecx, [shared_name] |
test ecx, ecx |
jz @f |
cmp [ecx], byte 0 |
jnz save_in_shared |
@@: |
mcall 70, fileinfo |
mov ecx, [sc.work_text] |
or ecx, 0x80000000 |
mcall 4, <10, 93>, , download_complete |
DEBUGF 1, "file saved\n" |
pop edi |
pop esi |
; if called from command line, then exit |
cmp byte[params], 0 |
jne exit |
ret |
save_in_shared: |
mov esi, 1 ; SHM_OPEN+SHM_WRITE |
mcall 68, 22 |
test eax, eax |
jz save_in_shared_done |
sub edx, 4 |
jbe save_in_shared_done |
mov ecx, [final_size] |
cmp ecx, edx |
jb @f |
mov ecx, edx |
@@: |
mov [eax], ecx |
lea edi, [eax+4] |
mov esi, [final_buffer] |
mov edx, ecx |
shr ecx, 2 |
rep movsd |
mov ecx, edx |
and ecx, 3 |
rep movsb |
save_in_shared_done: |
pop edi |
pop esi |
jmp exit |
; this function cuts header, and removes chunk sizes if doc is chunked |
; in: buf_ptr, pos; out: buf_ptr, pos. |
parse_result: |
; close socket |
mcall close, [socketnum] |
DEBUGF 1, "close socketnum: 0x%x\n", eax |
mov edi, [buf_ptr] |
mov edx, [pos] |
mov [buf_size], edx |
; mcall 70, fileinfo_tmp |
DEBUGF 1, "pos = 0x%x\n", edx |
; first, find end of headers |
.next_byte: |
cmp dword[edi], 0x0d0a0d0a ; ìíå ëåíü ÷èòàòü ñòàíäàðò, ïóñòü áóäóò îáà âàðèàíòà |
je .end_of_headers |
cmp dword[edi], 0x0a0d0a0d |
je .end_of_headers |
inc edi |
dec edx |
jne .next_byte |
; no end of headers. it's an error. let client see all those headers. |
ret |
.end_of_headers: |
; here we look at headers and search content-length or transfer-encoding headers |
; DEBUGF 1, "eoh\n" |
sub edi, [buf_ptr] |
add edi, 4 |
mov [body_pos], edi ; store position where document body starts |
mov [is_chunked], 0 |
; find content-length in headers |
; not good method, but should work for 'Content-Length:' |
mov esi, [buf_ptr] |
mov edi, s_contentlength |
mov ebx, [body_pos] |
xor edx, edx ; 0 |
.cl_next: |
mov al, [esi] |
cmp al, [edi + edx] |
jne .cl_fail |
inc edx |
cmp edx, len_contentlength |
je .cl_found |
jmp .cl_incr |
.cl_fail: |
xor edx, edx ; 0 |
.cl_incr: |
inc esi |
dec ebx |
je .cl_error |
jmp .cl_next |
.cl_error: |
; DEBUGF 1, "content-length not found\n" |
; find 'chunked' |
; äà, ÿ êîïèðóþ êîä, ýòî óæàñíî, íî ìíå õî÷åòñÿ, ÷òîáû ïîñêîðåå çàðàáîòàëî |
; à òàì óæ îòðåôàêòîðþ |
mov esi, [buf_ptr] |
mov edi, s_chunked |
mov ebx, [body_pos] |
xor edx, edx ; 0 |
.ch_next: |
mov al, [esi] |
cmp al, [edi + edx] |
jne .ch_fail |
inc edx |
cmp edx, len_chunked |
je .ch_found |
jmp .ch_incr |
.ch_fail: |
xor edx, edx ; 0 |
.ch_incr: |
inc esi |
dec ebx |
je .ch_error |
jmp .ch_next |
.ch_error: |
; if neither of the 2 headers is found, it's an error |
; DEBUGF 1, "transfer-encoding: chunked not found\n" |
mov eax, [pos] |
sub eax, [body_pos] |
jmp .write_final_size |
.ch_found: |
mov [is_chunked], 1 |
mov eax, [body_pos] |
add eax, [buf_ptr] |
sub eax, 2 |
mov [prev_chunk_end], eax |
jmp parse_chunks |
.cl_found: |
call read_number ; eax = number from *esi |
.write_final_size: |
mov [final_size], eax ; if this works, i will b very happy... |
mov ebx, [pos] ; we well check if it is right |
sub ebx, [body_pos] |
; everything is ok, so we return |
mov eax, [body_pos] |
mov ebx, [buf_ptr] |
add ebx, eax |
mov [final_buffer], ebx |
; mov ebx, [pos] |
; sub ebx, eax |
; mov [final_size], ebx |
ret |
parse_chunks: |
; DEBUGF 1, "parse chunks\n" |
; we have to look through the data and remove sizes of chunks we see |
; 1. read size of next chunk |
; 2. if 0, it's end. if not, continue. |
; 3. make a good buffer and copy a chunk there |
xor eax, eax |
mov [final_buffer], eax ; 0 |
mov [final_size], eax ; 0 |
.read_size: |
mov eax, [prev_chunk_end] |
mov ebx, eax |
sub ebx, [buf_ptr] |
mov edx, eax |
; DEBUGF 1, "rs " |
cmp ebx, [pos] |
jae chunks_end ; not good |
call read_hex ; in: eax=pointer to text. out:eax=hex number, ebx=end of text. |
cmp eax, 0 |
jz chunks_end |
add ebx, 1 |
mov edx, ebx ; edx = size of size of chunk |
add ebx, eax |
mov [prev_chunk_end], ebx |
; DEBUGF 1, "sz " |
; do copying: from buf_ptr+edx to final_buffer+prev_final_size count eax |
; realloc final buffer |
push eax |
push edx |
push dword [final_size] |
add [final_size], eax |
mcall 68, 20, [final_size], [final_buffer] |
mov [final_buffer], eax |
; DEBUGF 1, "re " |
pop edi |
pop esi |
pop ecx |
; add [pos], ecx |
add edi, [final_buffer] |
; DEBUGF 1, "cp " |
rep movsb |
jmp .read_size |
chunks_end: |
; free old buffer |
DEBUGF 1, "chunks end\n" |
mcall 68, 13, [buf_ptr] |
; done! |
ret |
; reads content-length from [edi+ecx], result in eax |
read_number: |
push ebx |
xor eax, eax |
xor ebx, ebx |
.next: |
mov bl, [esi] |
cmp bl, '0' |
jb .not_number |
cmp bl, '9' |
ja .not_number |
sub bl, '0' |
shl eax, 1 |
lea eax, [eax + eax * 4] ; eax *= 10 |
add eax, ebx |
.not_number: |
cmp bl, 13 |
je .done |
inc esi |
jmp .next |
.done: |
pop ebx |
ret |
; reads hex from eax, result in eax, end of text in ebx |
read_hex: |
add eax, 2 |
mov ebx, eax |
mov eax, [ebx] |
mov [deba], eax |
xor eax, eax |
xor ecx, ecx |
.next: |
mov cl, [ebx] |
inc ebx |
cmp cl, 0x0d |
jz .done |
or cl, 0x20 |
sub cl, '0' |
jb .bad |
cmp cl, 0x9 |
jbe .adding |
sub cl, 'a'-'0'-10 |
cmp cl, 0x0a |
jb .bad |
cmp cl, 0x0f |
ja .bad |
.adding: |
shl eax, 4 |
or eax, ecx |
; jmp .not_number |
;.bad: |
.bad: |
jmp .next |
.done: |
ret |
;**************************************************************************** |
; Function |
; socket_commands |
; |
; Description |
; opens or closes the socket |
; |
;**************************************************************************** |
socket_commands: |
cmp ah, 22 ; open socket |
jne tst3 |
DEBUGF 1, "opening socket\n" |
; Clear all page memory |
xor eax, eax |
mov [prev_chunk_end], eax ; 0 |
cmp [buf_ptr], eax ; 0 |
jz no_free |
mcall 68, 13, [buf_ptr] ; free buffer |
no_free: |
xor eax, eax |
mov [buf_size], eax ; 0 |
; Parse the entered url |
call parse_url |
mov edx, 80 |
cmp byte [proxyAddr], 0 |
jz @f |
mov eax, [proxyPort] |
xchg al, ah |
mov [server_port], ax |
@@: |
mcall socket, AF_INET4, SOCK_STREAM, 0 |
mov [socketnum], eax |
mcall connect, [socketnum], sockaddr1, 18 |
mov [pagexs], 80 |
push eax |
xor eax, eax ; 0 |
mov [pos], eax |
mov [pagex], eax |
mov [pagey], eax |
mov [command_on_off], eax |
mov [is_body], eax |
pop eax |
ret |
tst3: |
cmp ah, 24 ; close socket |
jne no_24 |
mcall close, [socketnum] |
no_24: |
ret |
;**************************************************************************** |
; Function |
; parse_url |
; |
; Description |
; parses the full url typed in by the user into a web address ( that |
; can be turned into an IP address by DNS ) and the page to display |
; DNS will be used to translate the web address into an IP address, if |
; needed. |
; url is at document_user and will be space terminated. |
; web address goes to webAddr and is space terminated. |
; ip address goes to server_ip |
; page goes to document and is space terminated. |
; |
; Supported formats: |
; <protocol://>address<page> |
; <protocol> is optional, removed and ignored - only http supported |
; <address> is required. It can be an ip address or web address |
; <page> is optional and must start with a leading / character |
; |
;**************************************************************************** |
parse_url: |
; First, reset destination variables |
mov al, ' ' |
mov edi, document |
mov ecx, URLMAXLEN |
rep stosb |
mov edi, webAddr |
mov ecx, URLMAXLEN |
rep stosb |
mov al, '/' |
mov [document], al |
mov esi, document_user |
; remove any leading protocol text |
mov ecx, URLMAXLEN |
mov ax, '//' |
pu_000: |
cmp [esi], byte ' ' ; end of text? |
je pu_002 ; yep, so not found |
cmp [esi], ax |
je pu_001 ; Found it, so esi+2 is start |
inc esi |
loop pu_000 |
pu_002: |
; not found, so reset esi to start |
mov esi, document_user-2 |
pu_001: |
add esi, 2 |
mov ebx, esi ; save address of start of web address |
mov edi, document_user + URLMAXLEN ; end of string |
; look for page delimiter - it's a '/' character |
pu_003: |
cmp [esi], byte ' ' ; end of text? |
je pu_004 ; yep, so none found |
cmp esi, edi ; end of string? |
je pu_004 ; yep, so none found |
cmp [esi], byte '/' ; delimiter? |
je pu_005 ; yep - process it |
inc esi |
jmp pu_003 |
pu_005: |
; copy page to document address |
; esi = delimiter |
push esi |
mov ecx, edi ; end of document_user |
mov edi, document |
pu_006: |
movsb |
cmp esi, ecx |
je pu_007 ; end of string? |
cmp [esi], byte ' ' ; end of text |
; je pu_007 ; äçåí-àññåìáëåð |
; jmp pu_006 ; íå íàäî ïëîäèòü ñóùíîñòè ïî íàïðàñíó |
jne pu_006 |
pu_007: |
pop esi ; point esi to '/' delimiter |
pu_004: |
; copy web address to webAddr |
; start in ebx, end in esi-1 |
mov ecx, esi |
mov esi, ebx |
mov edi, webAddr |
pu_008: |
movsb |
cmp esi, ecx |
; je pu_009 ; äçåí-àññåìáëåð |
; jmp pu_008 ; íå íàäî ïëîäèòü ñóùíîñòè ïî íàïðàñíó |
jne pu_008 |
pu_009: |
; For debugging, display resulting strings |
DEBUGF 1, "%s", document_user |
DEBUGF 1, "%s", webAddr |
DEBUGF 1, "%s", document |
; Look up the ip address, or was it specified? |
mov al, [proxyAddr] |
cmp al, 0 |
jnz pu_015 |
mov al, [webAddr] |
pu_015: |
cmp al, '0' |
jb pu_010 ; Resolve address |
cmp al, '9' |
ja pu_010 ; Resolve address |
DEBUGF 1, "GotIP\n" |
; Convert address |
; If proxy is given, get proxy address instead of server |
mov esi, proxyAddr-1 |
cmp byte[esi+1], 0 |
jne pu_020 |
mov esi, webAddr-1 |
pu_020: |
mov edi, server_ip |
xor eax, eax |
ip1: |
inc esi |
cmp [esi], byte '0' |
jb ip2 |
cmp [esi], byte '9' |
ja ip2 |
imul eax, 10 |
movzx ebx, byte [esi] |
sub ebx, 48 |
add eax, ebx |
jmp ip1 |
ip2: |
mov [edi], al |
xor eax, eax |
inc edi |
cmp edi, server_ip+3 |
jbe ip1 |
jmp pu_011 |
pu_010: |
DEBUGF 1, "Resolving...\n" |
; resolve name |
push esp ; reserve stack place |
push esp ; fourth parameter |
push 0 ; third parameter |
push 0 ; second parameter |
push webAddr |
call [getaddrinfo] |
pop esi |
; test for error |
test eax, eax |
; jnz .fail_dns |
; fill in ip in sockstruct |
mov eax, [esi + addrinfo.ai_addr] |
mov eax, [eax + sockaddr_in.sin_addr] |
mov [server_ip], eax |
DEBUGF 1, "Resolved\n" |
pu_011: |
ret |
;*************************************************************************** |
; Function |
; load_settings |
; |
; Description |
; Load settings from configuration file network.ini |
; |
;*************************************************************************** |
load_settings: |
invoke ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr |
invoke ini.get_int, inifile, sec_proxy, key_proxyport, 80 |
mov [proxyPort], eax |
invoke ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser |
invoke ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword |
ret |
;*************************************************************************** |
; Function |
; append_proxy_auth_header |
; |
; Description |
; Append header to HTTP request for proxy authentification |
; |
;*************************************************************************** |
append_proxy_auth_header: |
mov esi, proxy_auth_basic |
mov ecx, proxy_auth_basic_end - proxy_auth_basic |
rep movsb |
; base64-encode string <user>:<password> |
mov esi, proxyUser |
apah000: |
lodsb |
test al, al |
jz apah001 |
call encode_base64_byte |
jmp apah000 |
apah001: |
mov al, ':' |
call encode_base64_byte |
mov esi, proxyPassword |
apah002: |
lodsb |
test al, al |
jz apah003 |
call encode_base64_byte |
jmp apah002 |
apah003: |
call encode_base64_final |
ret |
encode_base64_byte: |
inc ecx |
shl edx, 8 |
mov dl, al |
cmp ecx, 3 |
je ebb001 |
ret |
ebb001: |
shl edx, 8 |
inc ecx |
ebb002: |
rol edx, 6 |
xor eax, eax |
xchg al, dl |
mov al, [base64_table+eax] |
stosb |
loop ebb002 |
ret |
encode_base64_final: |
mov al, 0 |
test ecx, ecx |
jz ebf000 |
call encode_base64_byte |
test ecx, ecx |
jz ebf001 |
call encode_base64_byte |
mov byte [edi-2], '=' |
ebf001: |
mov byte [edi-1], '=' |
ebf000: |
ret |
; ********************************************* |
; ******* WINDOW DEFINITIONS AND DRAW ******** |
; ********************************************* |
draw_window: |
mcall 12, 1 |
mcall 48, 3, sc, 40 ;get system colors |
mov edx, [sc.work] |
or edx, 0x34000000 |
mcall 0, <50, 370>, <350, 140>, , 0, title ;draw window |
mov ecx, [sc.work_text] |
or ecx, 80000000h |
mcall 4, <14, 14>, , type_pls ;"URL:" |
edit_boxes_set_sys_color edit1, editboxes_end, sc |
stdcall [edit_box_draw], edit1 |
; RELOAD |
mcall 8, <90, 68>, <54, 16>, 22, [sc.work_button] |
; STOP |
mcall , <166, 50>, <54, 16>, 24 |
; SAVE |
mcall , <224, 54>, , 26 |
; BUTTON TEXT |
mov ecx, [sc.work_button_text] |
or ecx, 80000000h |
mcall 4, <102, 59>, , button_text |
mcall 12, 2 ; end window redraw |
ret |
;----------------------------------------------------------------------------- |
; Data area |
;----------------------------------------------------------------------------- |
align 4 |
@IMPORT: |
library libini, 'libini.obj', \ |
box_lib, 'box_lib.obj', \ |
network, 'network.obj' |
import libini, \ |
ini.get_str, 'ini_get_str', \ |
ini.get_int, 'ini_get_int' |
import box_lib, \ |
edit_box_draw, 'edit_box', \ |
edit_box_key, 'edit_box_key', \ |
edit_box_mouse, 'edit_box_mouse' |
import network,\ |
getaddrinfo, 'getaddrinfo',\ |
freeaddrinfo, 'freeaddrinfo',\ |
inet_ntoa, 'inet_ntoa' |
;--------------------------------------------------------------------- |
fileinfo dd 2, 0, 0 |
final_size dd 0 |
final_buffer dd 0 |
db '/rd/1/.download', 0 |
body_pos dd 0 |
buf_size dd 0 |
buf_ptr dd 0 |
deba dd 0 |
db 0 |
;--------------------------------------------------------------------- |
mouse_dd dd 0 |
edit1 edit_box 295, 48, 10, 0xffffff, 0xff, 0x80ff, 0, 0x8000, URLMAXLEN, document_user, mouse_dd, ed_focus+ed_always_focus, 7, 7 |
editboxes_end: |
;--------------------------------------------------------------------- |
include_debug_strings |
;--------------------------------------------------------------------- |
type_pls db 'URL:', 0 |
button_text db 'DOWNLOAD STOP RESAVE', 0 |
download_complete db 'File saved as /rd/1/.download', 0 |
display_from dd 20 |
pos dd 0 |
pagex dd 0 |
pagey dd 0 |
pagexs dd 80 |
command_on_off dd 0 |
text_type db 1 |
com2 dd 0 |
script dd 0 |
socketnum dd 0 |
addr dd 0 |
ya dd 0 |
len dd 0 |
title db 'HTTP Downloader', 0 |
;--------------------------------------------------------------------- |
s_contentlength db 'Content-Length:' |
len_contentlength = 15 |
s_chunked db 'Transfer-Encoding: chunked' |
len_chunked = $ - s_chunked |
is_body dd 0 ; 0 if headers, 1 if content |
is_chunked dd 0 |
prev_chunk_end dd 0 |
cur_chunk_size dd 0 |
string0: db 'GET ' |
stringh db ' HTTP/1.1', 13, 10, 'Host: ' |
stringh_end: |
proxy_auth_basic db 13, 10, 'Proxy-Authorization: Basic ' |
proxy_auth_basic_end: |
connclose db 13, 10, 'User-Agent: Kolibrios Downloader', 13, 10, 'Connection: Close', 13, 10, 13, 10 |
connclose_end: |
base64_table db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' |
db '0123456789+/' |
inifile db '/sys/network.ini', 0 |
sec_proxy: |
key_proxy db 'proxy', 0 |
key_proxyport db 'port', 0 |
key_user db 'user', 0 |
key_password db 'password', 0 |
sockaddr1: |
dw AF_INET4 |
server_port dw 0x5000 ; 80 |
server_ip dd 0 |
rb 10 |
proxyPort dd 80 |
shared_name dd 0 |
status dd 0x0 |
prev_status dd 0x0 |
onoff dd 0x0 |
nextupdate dd 0 |
winys dd 400 |
;--------------------------------------------------------------------- |
document_user db 'http://', 0 |
;--------------------------------------------------------------------- |
IM_END: |
;--------------------------------------------------------------------- |
rb URLMAXLEN-(IM_END - document_user) |
;--------------------------------------------------------------------- |
sc system_colors |
;--------------------------------------------------------------------- |
align 4 |
document rb URLMAXLEN |
;--------------------------------------------------------------------- |
align 4 |
webAddr rb URLMAXLEN+1 |
;--------------------------------------------------------------------- |
align 4 |
primary_buf rb primary_buffer_size |
params rb 1024 |
request rb 256 |
proxyAddr rb 256 |
proxyUser rb 256 |
proxyPassword rb 256 |
rb 4096 ; stack |
I_END: |
/kernel/branches/net/applications/ftpc/ftpc.asm |
---|
0,0 → 1,390 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; ftpc.asm - FTP client for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
format binary as "" |
__DEBUG__ = 0 |
__DEBUG_LEVEL__ = 1 |
BUFFERSIZE = 1024 |
STATUS_CONNECTING = 0 |
STATUS_CONNECTED = 1 |
STATUS_NEEDPASSWORD = 1 |
STATUS_LOGGED_IN = 3 |
use32 |
; standard header |
db 'MENUET01' ; signature |
dd 1 ; header version |
dd start ; entry point |
dd i_end ; initialized size |
dd mem+0x1000 ; required memory |
dd mem+0x1000 ; stack pointer |
dd s ; parameters |
dd 0 ; path |
include '../macros.inc' |
purge mov,add,sub |
include '../proc32.inc' |
include '../dll.inc' |
include '../debug-fdo.inc' |
include '../network.inc' |
include 'usercommands.inc' |
include 'servercommands.inc' |
; entry point |
start: |
DEBUGF 1, "hello" |
; load libraries |
stdcall dll.Load, @IMPORT |
test eax, eax |
jnz exit |
; initialize console |
push 1 |
call [con_start] |
push title |
push 25 |
push 80 |
push 25 |
push 80 |
call [con_init] |
; Check for parameters |
cmp byte [s], 0 |
jne resolve |
main: |
call [con_cls] |
; Welcome user |
push str1 |
call [con_write_asciiz] |
; write prompt |
push str2 |
call [con_write_asciiz] |
; read string |
mov esi, s |
push 256 |
push esi |
call [con_gets] |
; check for exit |
test eax, eax |
jz done |
cmp byte [esi], 10 |
jz done |
resolve: |
; delete terminating '\n' |
mov esi, s |
@@: |
lodsb |
cmp al, 0x20 |
ja @r |
mov byte [esi-1], 0 |
; call [con_cls] |
push str3 |
call [con_write_asciiz] |
push s |
call [con_write_asciiz] |
; resolve name |
push esp ; reserve stack place |
push esp ; fourth parameter |
push 0 ; third parameter |
push 0 ; second parameter |
push s ; first parameter |
call [getaddrinfo] |
pop esi |
; test for error |
test eax, eax |
jnz fail |
; write results |
push str8 |
call [con_write_asciiz] |
; mov edi, esi |
; convert IP address to decimal notation |
mov eax, [esi+addrinfo.ai_addr] |
mov eax, [eax+sockaddr_in.sin_addr] |
mov [sockaddr1.ip], eax |
push eax |
call [inet_ntoa] |
; write result |
push eax |
call [con_write_asciiz] |
; free allocated memory |
push esi |
call [freeaddrinfo] |
push str9 |
call [con_write_asciiz] |
mcall socket, AF_INET4, SOCK_STREAM, 0 |
cmp eax, -1 |
jz fail2 |
mov [socketnum], eax |
mcall connect, [socketnum], sockaddr1, 18 |
mcall 40, 1 shl 7 |
mov [status], STATUS_CONNECTING |
mov [offset], buffer_ptr |
wait_for_serverdata: |
mcall 10 |
call [con_get_flags] |
test eax, 0x200 ; con window closed? |
jnz exit |
; receive socket data |
mcall recv, [socketnum], [offset], BUFFERSIZE, 0 |
inc eax |
jz wait_for_serverdata |
dec eax |
jz wait_for_serverdata |
; extract commands, copy them to "s" buffer |
add eax, buffer_ptr ; eax = end pointer |
mov esi, buffer_ptr ; esi = current pointer |
.nextcommand: |
mov edi, s |
.byteloop: |
cmp esi, eax |
jae wait_for_serverdata |
lodsb |
cmp al, 10 ; excellent, we might have a command |
je .got_command |
cmp al, 13 ; just ignore this crap |
je .byteloop |
stosb |
jmp .byteloop |
; we have a newline check if its a command |
.got_command: |
xor al, al |
stosb |
; push esi eax |
; print it to the screen |
pushd s |
call [con_write_asciiz] |
pushd str4 ; newline |
call [con_write_asciiz] |
; cmp byte[s+2], " " |
; jne .not_command |
lea ecx, [edi - s] |
call server_parser |
; .not_command: |
; pop eax esi |
; jmp .nextcommand |
wait_for_usercommand: |
cmp [status], STATUS_CONNECTED |
je .connected |
cmp [status], STATUS_NEEDPASSWORD |
je .needpass |
; write prompt |
push str2 |
call [con_write_asciiz] |
; read string |
mov esi, s |
push 256 |
push esi |
call [con_gets] |
call [con_get_flags] |
test eax, 0x200 ; con window closed? |
jnz exit |
cmp dword[s], "list" |
je cmd_list |
cmp dword[s], "help" |
je cmd_help |
push str_unkown |
call [con_write_asciiz] |
jmp wait_for_usercommand |
.connected: |
push str_user |
call [con_write_asciiz] |
mov dword[s], "USER" |
mov byte[s+4], " " |
jmp .send |
.needpass: |
push str_pass |
call [con_write_asciiz] |
mov dword[s], "PASS" |
mov byte[s+4], " " |
.send: |
; read string |
mov esi, s+5 |
push 256 |
push esi |
call [con_gets] |
mov edi, s+5 |
mov ecx, 256 |
xor al, al |
repne scasb |
lea esi, [edi-s-1] |
mcall send, [socketnum], s |
jmp wait_for_usercommand |
open_dataconnection: |
cmp [status], STATUS_LOGGED_IN |
jne .fail |
mov dword[s], "PASV" |
mov byte[s+4], 10 |
mcall send, [socketnum], s, 5 |
ret |
.fail: |
push str6 |
call [con_write_asciiz] |
ret |
fail2: |
push str6 |
call [con_write_asciiz] |
jmp fail.wait |
fail: |
push str5 |
call [con_write_asciiz] |
.wait: |
push str10 |
call [con_write_asciiz] |
call [con_getch2] |
jmp main |
done: |
push 1 |
call [con_exit] |
exit: |
mcall close, [socketnum] |
mcall -1 |
; data |
title db 'FTP client',0 |
str1 db 'FTP client for KolibriOS v0.01',10,10,'Please enter ftp server address.',10,0 |
str2 db '> ',0 |
str3 db 'Connecting to ',0 |
str4 db 10,0 |
str5 db 10,'Name resolution failed.',10,0 |
str6 db 10,'Socket error.',10,0 |
str8 db ' (',0 |
str9 db ')',10,0 |
str10 db 'Push any key to continue.',0 |
str_user db "username: ",0 |
str_pass db "password: ",0 |
str_unkown db "unkown command",10,0 |
str_help db "available commands:",10,10 |
db "help list",10,0 |
str_open db "opening data socket",10,0 |
sockaddr1: |
dw AF_INET4 |
.port dw 0x1500 ; 21 |
.ip dd 0 |
rb 10 |
sockaddr2: |
dw AF_INET4 |
.port dw 0 |
.ip dd 0 |
rb 10 |
include_debug_strings ; ALWAYS present in data section |
; import |
align 4 |
@IMPORT: |
library network, 'network.obj', console, 'console.obj' |
import network, \ |
getaddrinfo, 'getaddrinfo', \ |
freeaddrinfo, 'freeaddrinfo', \ |
inet_ntoa, 'inet_ntoa' |
import console, \ |
con_start, 'START', \ |
con_init, 'con_init', \ |
con_write_asciiz,'con_write_asciiz', \ |
con_exit, 'con_exit', \ |
con_gets, 'con_gets',\ |
con_cls, 'con_cls',\ |
con_getch2, 'con_getch2',\ |
con_set_cursor_pos, 'con_set_cursor_pos',\ |
con_write_string, 'con_write_string',\ |
con_get_flags, 'con_get_flags' |
i_end: |
active_passive db ? |
socketnum dd ? |
datasocket dd ? |
buffer_ptr rb 2*BUFFERSIZE |
status db ? |
offset dd ? |
s rb 1024 |
mem: |
/kernel/branches/net/applications/ftpc/servercommands.inc |
---|
0,0 → 1,114 |
server_parser: |
; Commands are always 3 numbers and followed by a space |
; If a server decides it needs multiline output, |
; first lines will have a dash instead of space after numbers, |
; thus they are simply ignored. |
cmp dword[s], "150 " |
je data_ok |
cmp dword[s], "220 " |
je welcome |
cmp dword[s], "227 " |
je pasv_ok |
cmp dword[s], "230 " |
je login_ok |
cmp dword[s], "331 " |
je pass |
ret |
welcome: |
mov [status], STATUS_CONNECTED |
ret |
pass: |
mov [status], STATUS_NEEDPASSWORD |
ret |
login_ok: |
mov [status], STATUS_LOGGED_IN |
ret |
pasv_ok: |
sub ecx, 5 |
jb .fail |
mov al, "(" |
mov edi, s + 5 |
repne scasb |
mcall socket, AF_INET4, SOCK_STREAM, 0 |
cmp eax, -1 |
je fail |
mov [datasocket], eax |
mov esi, edi |
call ascii_dec |
mov byte[sockaddr2.ip+0], bl |
call ascii_dec |
mov byte[sockaddr2.ip+1], bl |
call ascii_dec |
mov byte[sockaddr2.ip+2], bl |
call ascii_dec |
mov byte[sockaddr2.ip+3], bl |
call ascii_dec |
mov byte[sockaddr2.port+1], bl |
call ascii_dec |
mov byte[sockaddr2.port+0], bl |
push str_open |
call [con_write_asciiz] |
mcall connect, [datasocket], sockaddr2, 18 |
.fail: |
ret |
data_ok: |
mcall recv, [datasocket], buffer_ptr, BUFFERSIZE, 0 ; fixme: use other buffer |
inc eax |
jz .fail |
dec eax |
jz .fail |
mov byte[buffer_ptr + eax], 0 |
pushd buffer_ptr |
call [con_write_asciiz] |
.fail: |
ret |
ascii_dec: |
xor ebx, ebx |
mov cl, 3 |
.loop: |
lodsb |
sub al, '0' |
jb .done |
cmp al, 9 |
ja .done |
lea ebx, [ebx*4+ebx] |
shl ebx, 1 |
add bl, al |
dec cl |
jnz .loop |
.done: |
ret |
/kernel/branches/net/applications/ftpc/usercommands.inc |
---|
0,0 → 1,17 |
cmd_list: |
call open_dataconnection |
mov dword[s], "LIST" |
mov word[s+4], 0x0d0a |
mcall send, [socketnum], s, 6 |
jmp wait_for_serverdata |
cmd_help: |
push str_help |
call [con_write_asciiz] |
jmp wait_for_usercommand |
/kernel/branches/net/applications/ircc/window.inc |
---|
78,6 → 78,21 |
ret |
window_updated: |
mov edi, [window_print] |
test [edi + window.flags], FLAG_UPDATED |
jnz .skip |
or [edi + window.flags], FLAG_UPDATED |
; now play a sound :) |
.skip: |
ret |
print_text: ; eax = start ptr |
; dl = end char |
pusha |
/kernel/branches/net/applications/libio.inc |
---|
78,7 → 78,7 |
FileSizeHigh dd ? |
ends |
ends |
FileName rb 252 |
FileName rb 264 |
ends |
struct FileInfoW |
94,7 → 94,7 |
FileSizeHigh dd ? |
ends |
ends |
FileName rw 260 |
FileName rw 264 |
ends |
virtual at 0 |
109,4 → 109,5 |
FA_LABEL = 00001000b |
FA_FOLDER = 00010000b |
FA_ARCHIVED = 00100000b |
FA_ANY = 00111111b |
FA_NORMAL = 01000000b |
FA_ANY = 01111111b |
/kernel/branches/net/data32.inc |
---|
500,4 → 500,5 |
BiosDiskCaches rb 80h*(cache_ide1-cache_ide0) |
BiosDiskPartitions rd 80h |
tetten: |
IncludeUGlobals |
/kernel/branches/net/gui/event.inc |
---|
527,7 → 527,7 |
cmp eax, 5 |
je .mouse_check ; eax=5, retvals=eax+1 (event 6) |
ja .FlagAutoReset ; eax=[6..8], retvals=eax+1 (event 7...10) |
ja .FlagAutoReset ; eax=[6..9], retvals=eax+1 (event 7...10) |
cmp eax, 1 |
jae .BtKy ; eax=[1,2], retvals=eax+1 (event 2,3) |
/kernel/branches/net/kernel.asm |
---|
77,11 → 77,12 |
USE_COM_IRQ equ 1 ; make irq 3 and irq 4 available for PCI devices |
; Enabling the next line will enable serial output console |
;debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
include "proc32.inc" |
include "kglobals.inc" |
include "lang.inc" |
;include "lang.inc" |
lang fix en |
include "const.inc" |
max_processes equ 255 |
136,7 → 137,7 |
if lang eq sp |
include "kernelsp.inc" ; spanish kernel messages |
else |
version db 'Kolibri OS version 0.7.7.0+ ',13,10,13,10,0 |
version db 'Kolibri OS network development ',13,10,13,10,0 |
end if |
include "boot/bootstr.inc" ; language-independent boot messages |
/kernel/trunk/const.inc |
---|
399,7 → 399,8 |
EVENT_IPC equ 0x00000040 |
EVENT_NETWORK equ 0x00000080 |
EVENT_DEBUG equ 0x00000100 |
EVENT_EXTENDED equ 0x00000200 |
EVENT_NETWORK2 equ 0x00000200 |
EVENT_EXTENDED equ 0x00000400 |
EV_INTR equ 1 |
/kernel/trunk/core/conf_lib.inc |
---|
72,54 → 72,7 |
udev_midibase db 'midibase',0 |
udev_midibase_def db '0x320',0 |
endg |
;set up netvork configuration |
proc set_network_conf |
locals |
par db 30 dup(?) |
endl |
pushad |
;[net] |
;active |
lea eax, [par] |
invoke ini.get_int, conf_fname, unet, unet_active, 0 |
or eax, eax |
jz .do_not_set_net |
mov eax, [stack_config] |
and eax, 0xFFFFFF80 |
add eax, 3 |
mov [stack_config], eax |
call ash_eth_enable |
;addr |
lea eax, [par] |
push eax |
invoke ini.get_str, conf_fname, unet, unet_addr, eax, 30, unet_def |
pop eax |
stdcall do_inet_adr, eax |
mov [stack_ip], eax |
;mask |
lea eax, [par] |
push eax |
invoke ini.get_str, conf_fname, unet, unet_mask, eax, 30, unet_def |
pop eax |
stdcall do_inet_adr, eax |
mov [subnet_mask], eax |
;gate |
lea eax, [par] |
push eax |
invoke ini.get_str, conf_fname, unet, unet_gate, eax, 30, unet_def |
pop eax |
stdcall do_inet_adr, eax |
mov [gateway_ip], eax |
.do_not_set_net: |
popad |
ret |
endp |
iglobal |
if lang eq sp |
include 'core/conf_lib-sp.inc' |
/kernel/trunk/core/exports.inc |
---|
101,6 → 101,12 |
szUSBNormalTransferAsync db 'USBNormalTransferAsync',0 |
szUSBControlTransferAsync db 'USBControlTransferAsync',0 |
szNetRegDev db 'NetRegDev',0 |
szNetUnRegDev db 'NetUnRegDev',0 |
szNetPtrToNum db 'NetPtrToNum',0 |
szNetLinkChanged db 'NetLinkChanged',0 |
szEth_input db 'Eth_input',0 |
align 16 |
kernel_export: |
dd szRegService , reg_service |
191,6 → 197,12 |
dd szUSBNormalTransferAsync, usb_normal_transfer_async |
dd szUSBControlTransferAsync, usb_control_async |
dd szNetRegDev , NET_add_device |
dd szNetUnRegDev , NET_remove_device |
dd szNetPtrToNum , NET_ptr_to_num |
dd szNetLinkChanged , NET_link_changed |
dd szEth_input , ETH_input |
exp_lfb: |
dd szLFBAddress , 0 |
dd 0 ;terminator, must be zero |
/kernel/trunk/core/syscall.inc |
---|
112,11 → 112,11 |
align 4 |
servetable: |
dd socket ; 53-Socket interface |
dd 0 |
dd 0 |
dd 0 |
dd 0 |
dd 0 |
dd file_system ; 58-Common file system interface |
dd 0 |
dd 0 |
182,8 → 182,8 |
dd sys_apm ; 49-Advanced Power Management (APM) |
dd syscall_set_window_shape ; 50-Window shape & scale |
dd syscall_threads ; 51-Threads |
dd stack_driver_stat ; 52-Stack driver status |
dd cross_order ; 53-Socket interface |
dd undefined_syscall ; 52-Stack driver status |
dd undefined_syscall ; 53-Socket interface |
dd undefined_syscall ; 54-reserved |
dd sound_interface ; 55-Sound interface |
dd undefined_syscall ; 56-reserved |
204,9 → 204,9 |
dd syscall_window_settings ; 71-Window settings |
dd sys_sendwindowmsg ; 72-Send window message |
dd blit_32 ; 73-blitter; |
dd undefined_syscall ; 74-reserved for new stack |
dd undefined_syscall ; 75-reserved for new stack |
dd undefined_syscall ; 76-reserved for new stack |
dd sys_network ; 74-reserved for new stack |
dd sys_socket ; 75-reserved for new stack |
dd sys_protocols ; 76-reserved for new stack |
times 255 - ( ($-servetable2) /4 ) dd undefined_syscall |
dd sys_end ; -1-end application |
/kernel/trunk/gui/event.inc |
---|
515,8 → 515,8 |
jz .no_events ; исчерпали все биты маски, но ничего не нашли ??? |
btr ecx, eax ; сбрасываем проверяемый бит маски |
; переходим на обработчик этого (eax) бита |
cmp eax, 9 |
jae .loop ; eax=[9..31], ignored (event 10...32) |
cmp eax, 10 |
jae .loop ; eax=[10..31], ignored (event 10...32) |
cmp eax, 3 |
je .loop ; eax=3, ignored (event 4) |
527,7 → 527,7 |
cmp eax, 5 |
je .mouse_check ; eax=5, retvals=eax+1 (event 6) |
ja .FlagAutoReset ; eax=[6..8], retvals=eax+1 (event 7...9) |
ja .FlagAutoReset ; eax=[6..9], retvals=eax+1 (event 7...10) |
cmp eax, 1 |
jae .BtKy ; eax=[1,2], retvals=eax+1 (event 2,3) |
/kernel/trunk/kernel.asm |
---|
77,7 → 77,7 |
USE_COM_IRQ equ 1 ; make irq 3 and irq 4 available for PCI devices |
; Enabling the next line will enable serial output console |
;debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
; The following constant, if nonzero, duplicates debug output to the screen. |
debug_direct_print equ 0 |
638,12 → 638,6 |
mov dword [current_slot], SLOT_BASE + 256*2 |
mov dword [TASK_BASE], CURRENT_TASK + 32*2 |
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 |
mov esi, boot_initirq |
883,12 → 877,6 |
stdcall map_page, tss._io_map_1, \ |
[SLOT_BASE+256+APPDATA.io_map+4], PG_MAP |
mov ax, [OS_BASE+0x10000+bx_from_load] |
cmp ax, 'r1'; if not rused ram disk - load network configuration from files {SPraid.simba} |
je no_st_network |
call set_network_conf |
no_st_network: |
; LOAD FIRST APPLICATION |
cli |
2029,6 → 2017,13 |
popa |
@@: |
;-------------------------------------- |
; kill all sockets this process owns |
pusha |
mov edx, [TASK_BASE] |
mov edx, [edx+TASKDATA.pid] |
call SOCKET_process_end |
popa |
;-------------------------------------- |
mov ecx, [current_slot] |
mov eax, [ecx+APPDATA.tls_base] |
test eax, eax |
2155,6 → 2150,12 |
test eax, eax |
jz noprocessterminate |
;-------------------------------------- |
; terminate all network sockets it used |
pusha |
mov eax, edx |
call SOCKET_process_end |
popa |
;-------------------------------------- |
cmp [_display.select_cursor], 0 |
je .restore_end |
; restore default cursor before killing |
5366,28 → 5367,6 |
align 4 |
stack_driver_stat: |
call app_stack_handler ; Stack status |
; mov [check_idle_semaphore],5 ; enable these for zero delay |
; call change_task ; between sent packet |
mov [esp+32], eax |
ret |
align 4 |
socket: ; Socket interface |
call app_socket_handler |
; mov [check_idle_semaphore],5 ; enable these for zero delay |
; call change_task ; between sent packet |
mov [esp+36], eax |
mov [esp+24], ebx |
ret |
paleholder: |
ret |
;------------------------------------------------------------------------------ |
/kernel/trunk/network/ip.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/ethernet.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/arp.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/forcedeth.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/rtl8029.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/i8255x.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/rtl8139.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/sis900.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/rtl8169.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/r6040.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/3c59x.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers/pcnet32.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv/drivers |
---|
Property changes: |
Deleted: svn:ignore |
-*.mnt |
-lang.inc |
-*.bat |
-out.txt |
-scin* |
-*.obj |
/kernel/trunk/network/eth_drv/pci.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/eth_drv |
---|
Property changes: |
Deleted: svn:ignore |
-*.mnt |
-lang.inc |
-*.bat |
-out.txt |
-scin* |
-*.obj |
/kernel/trunk/network/ARP.inc |
---|
0,0 → 1,645 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; ARP.INC ;; |
;; ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
;; Based on the work of [Johnny_B] and [smb] ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June- 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3386 $ |
ARP_NO_ENTRY = 0 |
ARP_VALID_MAPPING = 1 |
ARP_AWAITING_RESPONSE = 2 |
ARP_RESPONSE_TIMEOUT = 3 |
ARP_REQUEST_TTL = 31 ; 20 s |
ARP_ENTRY_TTL = 937 ; 600 s |
ARP_STATIC_ENTRY = -1 |
ARP_REQ_OPCODE = 0x0100 ; request |
ARP_REP_OPCODE = 0x0200 ; reply |
ARP_TABLE_SIZE = 20 ; Size of table |
struct ARP_entry |
IP dd ? |
MAC dp ? |
Status dw ? |
TTL dw ? |
ends |
struct ARP_header |
HardwareType dw ? |
ProtocolType dw ? |
HardwareSize db ? |
ProtocolSize db ? |
Opcode dw ? |
SenderMAC dp ? |
SenderIP dd ? |
TargetMAC dp ? |
TargetIP dd ? |
ends |
align 4 |
uglobal |
NumARP dd ? |
ARP_table rb ARP_TABLE_SIZE * sizeof.ARP_entry ; TODO: separate ARP table and stats per interface |
ARP_PACKETS_TX rd MAX_NET_DEVICES |
ARP_PACKETS_RX rd MAX_NET_DEVICES |
ARP_CONFLICTS rd MAX_NET_DEVICES |
endg |
;----------------------------------------------------------------- |
; |
; ARP_init |
; |
; This function resets all ARP variables |
; |
;----------------------------------------------------------------- |
macro ARP_init { |
xor eax, eax |
mov [NumARP], eax |
mov edi, ARP_PACKETS_TX |
mov ecx, 3*MAX_NET_DEVICES |
rep stosd |
} |
;--------------------------------------------------------------------------- |
; |
; ARP_decrease_entry_ttls |
; |
;--------------------------------------------------------------------------- |
macro ARP_decrease_entry_ttls { |
local .loop |
local .exit |
; The TTL field is decremented every second, and is deleted when it reaches 0. |
; It is refreshed every time a packet is received. |
; If the TTL field is 0xFFFF it is a static entry and is never deleted. |
; The status field can be the following values: |
; 0x0000 entry not used |
; 0x0001 entry holds a valid mapping |
; 0x0002 entry contains an IP address, awaiting ARP response |
; 0x0003 No response received to ARP request. |
; The last status value is provided to allow the network layer to delete |
; a packet that is queued awaiting an ARP response |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
mov esi, ARP_table |
.loop: |
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY |
je .next |
dec [esi + ARP_entry.TTL] |
jz .time_out |
.next: |
add esi, sizeof.ARP_entry |
dec ecx |
jnz .loop |
jmp .exit |
.time_out: |
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE |
je .response_timeout |
push esi ecx |
call ARP_del_entry |
pop ecx esi |
jmp .next |
.response_timeout: |
mov [esi + ARP_entry.Status], ARP_RESPONSE_TIMEOUT |
mov [esi + ARP_entry.TTL], 10 |
jmp .next |
.exit: |
} |
;----------------------------------------------------------------- |
; |
; ARP_input |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; packet size (without ethernet header) in ecx |
; packet ptr in edx |
; device ptr in ebx |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_input: |
;----------------------------------------- |
; Check validity and print some debug info |
cmp ecx, sizeof.ARP_header |
jb .exit |
call NET_ptr_to_num |
cmp edi, -1 |
jz .exit |
inc [ARP_PACKETS_RX + 4*edi] ; update stats |
DEBUGF 1,"ARP_input: got packet from %u.%u.%u.%u through device %u\n",\ |
[edx + ARP_header.SenderIP]:1, [edx + ARP_header.SenderIP + 1]:1,\ |
[edx + ARP_header.SenderIP + 2]:1, [edx + ARP_header.SenderIP + 3]:1, edi |
;------------------------------ |
; First, check for IP collision |
mov eax, [edx + ARP_header.SenderIP] |
cmp eax, [IP_LIST + 4*edi] |
je .collision |
;--------------------- |
; Handle reply packets |
cmp [edx + ARP_header.Opcode], ARP_REP_OPCODE |
jne .maybe_request |
DEBUGF 1,"ARP_input: It's a reply\n" |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
mov esi, ARP_table |
.loop: |
cmp [esi + ARP_entry.IP], eax |
je .gotit |
add esi, sizeof.ARP_entry |
dec ecx |
jnz .loop |
DEBUGF 1,"ARP_input: no matching entry found\n" |
jmp .exit |
.gotit: |
DEBUGF 1,"ARP_input: found matching entry\n" |
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY ; if it is a static entry, dont touch it |
je .exit |
DEBUGF 1,"ARP_input: updating entry\n" |
mov [esi + ARP_entry.Status], ARP_VALID_MAPPING |
mov [esi + ARP_entry.TTL], ARP_ENTRY_TTL |
mov eax, dword [edx + ARP_header.SenderMAC] |
mov dword [esi + ARP_entry.MAC], eax |
mov cx, word [edx + ARP_header.SenderMAC + 4] |
mov word [esi + ARP_entry.MAC + 4], cx |
jmp .exit |
;----------------------- |
; Handle request packets |
.maybe_request: |
cmp [edx + ARP_header.Opcode], ARP_REQ_OPCODE |
jne .exit |
DEBUGF 1,"ARP_input: its a request\n" |
mov eax, [IP_LIST + 4*edi] |
cmp eax, [edx + ARP_header.TargetIP] ; Is it looking for my IP address? |
jne .exit |
push eax |
push edi |
; OK, it is a request for one of our MAC addresses. |
; Build the frame and send it. We can reuse the buffer. (faster then using ARP_create_packet) |
lea esi, [edx + ARP_header.SenderMAC] |
lea edi, [edx + ARP_header.TargetMAC] |
movsd ; Move Sender Mac to Dest MAC |
movsw ; |
movsd ; Move sender IP to Dest IP |
pop esi |
mov esi, [NET_DRV_LIST + 4*esi] |
lea esi, [esi + ETH_DEVICE.mac] |
lea edi, [edx + ARP_header.SenderMAC] |
movsd ; Copy MAC address from in MAC_LIST |
movsw ; |
pop eax |
stosd ; Write our IP |
mov [edx + ARP_header.Opcode], ARP_REP_OPCODE |
; Now, Fill in ETHERNET header |
mov edi, [esp] |
lea esi, [edx + ARP_header.TargetMAC] |
movsd |
movsw |
lea esi, [edx + ARP_header.SenderMAC] |
movsd |
movsw |
; mov ax , ETHER_ARP ; It's already there, I'm sure of it! |
; stosw |
DEBUGF 1,"ARP_input: Sending reply\n" |
call [ebx + NET_DEVICE.transmit] |
ret |
.collision: |
inc [ARP_CONFLICTS + 4*edi] |
DEBUGF 1,"ARP_input: IP address conflict detected!\n" |
.exit: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
DEBUGF 1,"ARP_input: exiting\n" |
ret |
;--------------------------------------------------------------------------- |
; |
; ARP_output_request |
; |
; IN: ip in eax |
; device in edi |
; OUT: / |
; |
;--------------------------------------------------------------------------- |
align 4 |
ARP_output_request: |
push eax ; DestIP |
pushd [IP_LIST + edi] ; SenderIP |
inc [ARP_PACKETS_TX + edi] ; assume we will succeed |
DEBUGF 1,"ARP_output_request: ip=%u.%u.%u.%u\n",\ |
[esp + 4]:1, [esp + 5]:1, [esp + 6]:1, [esp + 7]:1 |
mov ebx, [NET_DRV_LIST + edi] ; device ptr |
lea eax, [ebx + ETH_DEVICE.mac] ; local device mac |
mov edx, ETH_BROADCAST ; broadcast mac |
mov ecx, sizeof.ARP_header |
mov di, ETHER_ARP |
call ETH_output |
jz .exit |
mov ecx, eax |
mov [edi + ARP_header.HardwareType], 0x0100 ; Ethernet |
mov [edi + ARP_header.ProtocolType], 0x0008 ; IP |
mov [edi + ARP_header.HardwareSize], 6 ; MAC-addr length |
mov [edi + ARP_header.ProtocolSize], 4 ; IP-addr length |
mov [edi + ARP_header.Opcode], ARP_REQ_OPCODE ; Request |
add edi, ARP_header.SenderMAC |
lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac |
movsw ; |
movsd ; |
pop eax ; SenderIP |
stosd ; |
mov eax, -1 ; DestMac |
stosd ; |
stosw ; |
pop eax ; DestIP |
stosd ; |
DEBUGF 1,"ARP_output_request: device=%x\n", ebx |
push edx ecx |
call [ebx + NET_DEVICE.transmit] |
ret |
.exit: |
add esp, 4 + 4 |
DEBUGF 1,"ARP_output_request: failed\n" |
sub eax, eax |
ret |
;----------------------------------------------------------------- |
; |
; ARP_add_entry (or update) |
; |
; IN: esi = ptr to entry (can easily be made on the stack) |
; OUT: eax = entry #, -1 on error |
; edi = ptr to newly created entry |
; |
;----------------------------------------------------------------- ; TODO: use a mutex |
align 4 |
ARP_add_entry: |
DEBUGF 1,"ARP_add_entry: " |
mov ecx, [NumARP] |
cmp ecx, ARP_TABLE_SIZE ; list full ? |
jae .error |
xor eax, eax |
mov edi, ARP_table |
mov ecx, [esi + ARP_entry.IP] |
.loop: |
cmp [edi + ARP_entry.Status], ARP_NO_ENTRY ; is this slot empty? |
je .add |
cmp [edi + ARP_entry.IP], ecx ; if not, check if it doesnt collide |
jne .maybe_next |
cmp [edi + ARP_entry.TTL], ARP_STATIC_ENTRY ; ok, its the same IP, update it if not static |
jne .add |
.maybe_next: ; try the next slot |
add edi, sizeof.ARP_entry |
inc eax |
cmp eax, ARP_TABLE_SIZE |
jae .error |
jmp .loop |
.add: |
mov ecx, sizeof.ARP_entry/2 |
rep movsw |
inc [NumARP] |
sub edi, sizeof.ARP_entry |
DEBUGF 1,"entry=%u\n", eax |
ret |
.error: |
DEBUGF 1,"failed\n" |
mov eax, -1 |
ret |
;----------------------------------------------------------------- |
; |
; ARP_del_entry |
; |
; IN: esi = ptr to arp entry |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_del_entry: |
DEBUGF 1,"ARP_del_entry: entry=%x entrys=%u\n", esi, [NumARP] |
DEBUGF 1,"ARP_del_entry: IP=%u.%u.%u.%u\n", \ |
[esi + ARP_entry.IP]:1, [esi + ARP_entry.IP + 1]:1, [esi + ARP_entry.IP + 2]:1, [esi + ARP_entry.IP + 3]:1 |
mov ecx, ARP_table + (ARP_TABLE_SIZE - 1) * sizeof.ARP_entry |
sub ecx, esi |
shr ecx, 1 |
mov edi, esi |
add esi, sizeof.ARP_entry |
rep movsw |
xor eax, eax |
mov ecx, sizeof.ARP_entry/2 |
rep stosw |
dec [NumARP] |
DEBUGF 1,"ARP_del_entry: success\n" |
ret |
;----------------------------------------------------------------- |
; |
; ARP_IP_to_MAC |
; |
; This function translates an IP address to a MAC address |
; |
; IN: eax = IPv4 address |
; edi = device number |
; OUT: eax = -1 on error, -2 means request send |
; else, ax = first two bytes of mac (high 16 bits of eax will be 0) |
; ebx = last four bytes of mac |
; edi = unchanged |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_IP_to_MAC: |
DEBUGF 1,"ARP_IP_to_MAC: %u.%u", al, ah |
rol eax, 16 |
DEBUGF 1,".%u.%u\n", al, ah |
rol eax, 16 |
cmp eax, 0xffffffff |
je .broadcast |
;-------------------------------- |
; Try to find the IP in ARP_table |
mov ecx, [NumARP] |
test ecx, ecx |
jz .not_in_list |
mov esi, ARP_table + ARP_entry.IP |
.scan_loop: |
cmp [esi], eax |
je .found_it |
add esi, sizeof.ARP_entry |
loop .scan_loop |
.not_in_list: |
DEBUGF 1,"ARP_IP_to_MAC: preparing for ARP request\n" |
;-------------------- |
; Send an ARP request |
push eax edi ; save IP for ARP_output_request |
; Now create the ARP entry |
pushw ARP_REQUEST_TTL ; TTL |
pushw ARP_AWAITING_RESPONSE ; status |
pushd 0 ; mac |
pushw 0 |
pushd eax ; ip |
mov esi, esp |
call ARP_add_entry |
add esp, sizeof.ARP_entry ; clear the entry from stack |
cmp eax, -1 ; did ARP_add_entry fail? |
je .full |
mov esi, edi |
pop edi eax ; IP in eax, device number in edi, for ARP_output_request |
push esi edi |
call ARP_output_request ; And send a request |
pop edi esi |
;----------------------------------------------- |
; At this point, we got an ARP entry in the list |
.found_it: |
cmp [esi + ARP_entry.Status], ARP_VALID_MAPPING ; Does it have a MAC assigned? |
je .valid |
if ARP_BLOCK |
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE ; Are we waiting for reply from remote end? |
jne .give_up |
push esi |
mov esi, 10 ; wait 10 ms |
call delay_ms |
pop esi |
jmp .found_it ; now check again |
else |
jmp .give_up |
end if |
.valid: |
DEBUGF 1,"ARP_IP_to_MAC: found MAC\n" |
movzx eax, word[esi + ARP_entry.MAC] |
mov ebx, dword[esi + ARP_entry.MAC + 2] |
ret |
.full: |
DEBUGF 1,"ARP_IP_to_MAC: table is full!\n" |
add esp, 8 |
.give_up: |
DEBUGF 1,"ARP_IP_to_MAC: entry has no valid mapping!\n" |
mov eax, -1 |
ret |
.broadcast: |
mov eax, 0x0000ffff |
mov ebx, 0xffffffff |
ret |
;----------------------------------------------------------------- |
; |
; ARP_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: ? |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_api: |
movzx eax, bh |
shl eax, 2 |
and ebx, 0xff |
cmp ebx, .number |
ja .error |
jmp dword [.table + 4*ebx] |
.table: |
dd .packets_tx ; 0 |
dd .packets_rx ; 1 |
dd .entries ; 2 |
dd .read ; 3 |
dd .write ; 4 |
dd .remove ; 5 |
dd .send_announce ; 6 |
dd .conflicts ; 7 |
.number = ($ - .table) / 4 - 1 |
.error: |
mov eax, -1 |
ret |
.packets_tx: |
mov eax, [ARP_PACKETS_TX + eax] |
ret |
.packets_rx: |
mov eax, [ARP_PACKETS_RX + eax] |
ret |
.conflicts: |
mov eax, [ARP_CONFLICTS + eax] |
ret |
.entries: |
mov eax, [NumARP] |
ret |
.read: |
cmp ecx, [NumARP] |
jae .error |
; edi = pointer to buffer |
; ecx = # entry |
imul ecx, sizeof.ARP_entry |
add ecx, ARP_table |
mov esi, ecx |
mov ecx, sizeof.ARP_entry/2 |
rep movsw |
xor eax, eax |
ret |
.write: |
; esi = pointer to buffer |
call ARP_add_entry ; out: eax = entry number, -1 on error |
ret |
.remove: |
; ecx = # entry |
cmp ecx, [NumARP] |
jae .error |
imul ecx, sizeof.ARP_entry |
lea esi, [ARP_table + ecx] |
call ARP_del_entry |
ret |
.send_announce: |
mov edi, eax |
mov eax, [IP_LIST + eax] |
call ARP_output_request ; now send a gratuitous ARP |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/IPv4.inc |
---|
0,0 → 1,1019 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; IPv4.INC ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Based on the work of [Johnny_B] and [smb] ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3515 $ |
MAX_FRAGMENTS = 64 |
struct IPv4_header |
VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits] |
TypeOfService db ? ; precedence [7-5] minimize delay [4], maximize throughput [3], maximize riliability [2] minimize momentary cost [1] and zero [0] |
TotalLength dw ? |
Identification dw ? |
FlagsAndFragmentOffset dw ? ; Flags[0-2] and FragmentOffset[3-15] |
TimeToLive db ? ; |
Protocol db ? |
HeaderChecksum dw ? |
SourceAddress dd ? |
DestinationAddress dd ? |
ends |
struct FRAGMENT_slot |
ttl dw ? ; Time to live for this entry, 0 for empty slot's |
id dw ? ; Identification field from IP header |
SrcIP dd ? ; .. from IP header |
DstIP dd ? ; .. from IP header |
ptr dd ? ; Pointer to first packet |
ends |
struct FRAGMENT_entry ; This structure will replace the ethernet header in fragmented ip packets |
PrevPtr dd ? ; Pointer to previous fragment entry (-1 for first packet) |
NextPtr dd ? ; Pointer to next fragment entry (-1 for last packet) |
Owner dd ? ; Pointer to structure of driver |
rb 2 ; to match ethernet header size ;;; FIXME |
; Ip header begins here (we will need the IP header to re-construct the complete packet) |
ends |
align 4 |
uglobal |
IP_LIST rd MAX_NET_DEVICES |
SUBNET_LIST rd MAX_NET_DEVICES |
DNS_LIST rd MAX_NET_DEVICES |
GATEWAY_LIST rd MAX_NET_DEVICES |
BROADCAST_LIST rd MAX_NET_DEVICES |
IP_packets_tx rd MAX_NET_DEVICES |
IP_packets_rx rd MAX_NET_DEVICES |
IP_packets_dumped rd MAX_NET_DEVICES |
FRAGMENT_LIST rb MAX_FRAGMENTS * sizeof.FRAGMENT_slot |
endg |
;----------------------------------------------------------------- |
; |
; IPv4_init |
; |
; This function resets all IP variables |
; |
;----------------------------------------------------------------- |
macro IPv4_init { |
xor eax, eax |
mov edi, IP_LIST |
mov ecx, 7*MAX_NET_DEVICES + (sizeof.FRAGMENT_slot*MAX_FRAGMENTS)/4 |
rep stosd |
} |
;----------------------------------------------------------------- |
; |
; Decrease TimeToLive of all fragment slots |
; |
;----------------------------------------------------------------- |
macro IPv4_decrease_fragment_ttls { |
local .loop, .next |
mov esi, FRAGMENT_LIST |
mov ecx, MAX_FRAGMENTS |
.loop: |
cmp [esi + FRAGMENT_slot.ttl], 0 |
je .next |
dec [esi + FRAGMENT_slot.ttl] |
jz .died |
.next: |
add esi, sizeof.FRAGMENT_slot |
dec ecx |
jnz .loop |
jmp .done |
.died: |
DEBUGF 2,"IPv4 Fragment slot timed-out!\n" |
;;; TODO: clear all entry's of timed-out slot |
jmp .next |
.done: |
} |
macro IPv4_checksum ptr { |
; This is the fast procedure to create or check an IP header without options |
; To create a new checksum, the checksum field must be set to 0 before computation |
; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct |
push ebx |
xor ebx, ebx |
add bl, [ptr+1] |
adc bh, [ptr+0] |
adc bl, [ptr+3] |
adc bh, [ptr+2] |
adc bl, [ptr+5] |
adc bh, [ptr+4] |
adc bl, [ptr+7] |
adc bh, [ptr+6] |
adc bl, [ptr+9] |
adc bh, [ptr+8] |
; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation |
adc bl, [ptr+13] |
adc bh, [ptr+12] |
adc bl, [ptr+15] |
adc bh, [ptr+14] |
adc bl, [ptr+17] |
adc bh, [ptr+16] |
adc bl, [ptr+19] |
adc bh, [ptr+18] |
adc ebx, 0 |
push ecx |
mov ecx, ebx |
shr ecx, 16 |
and ebx, 0xffff |
add ebx, ecx |
mov ecx, ebx |
shr ecx, 16 |
add ebx, ecx |
not bx |
jnz .not_zero |
dec bx |
.not_zero: |
xchg bl, bh |
pop ecx |
neg word [ptr+10] ; zero will stay zero so we just get the checksum |
add word [ptr+10], bx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) |
pop ebx |
} |
;----------------------------------------------------------------- |
; |
; IPv4_input: |
; |
; Will check if IPv4 Packet isnt damaged |
; and call appropriate handler. (TCP/UDP/ICMP/..) |
; |
; It will also re-construct fragmented packets |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; pointer to device struct in ebx |
; pointer to IPv4 header in edx |
; size of IPv4 packet in ecx |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
IPv4_input: ; TODO: add IPv4 raw sockets support |
DEBUGF 2,"IPv4_input, packet from: %u.%u.%u.%u ",\ |
[edx + IPv4_header.SourceAddress + 0]:1,[edx + IPv4_header.SourceAddress + 1]:1,\ |
[edx + IPv4_header.SourceAddress + 2]:1,[edx + IPv4_header.SourceAddress + 3]:1 |
DEBUGF 2,"to: %u.%u.%u.%u\n",\ |
[edx + IPv4_header.DestinationAddress + 0]:1,[edx + IPv4_header.DestinationAddress + 1]:1,\ |
[edx + IPv4_header.DestinationAddress + 2]:1,[edx + IPv4_header.DestinationAddress + 3]:1 |
;------------------------------- |
; re-calculate the checksum |
IPv4_checksum edx |
jnz .dump ; if checksum isn't valid then dump packet |
DEBUGF 1,"IPv4_input: Checksum ok\n" |
;----------------------------------- |
; Check if destination IP is correct |
call NET_ptr_to_num |
shl edi, 2 |
; check if it matches local ip (Using RFC1122 strong end system model) |
mov eax, [edx + IPv4_header.DestinationAddress] |
cmp eax, [IP_LIST + edi] |
je .ip_ok |
; check for broadcast (IP or (not SUBNET)) |
cmp eax, [BROADCAST_LIST + edi] |
je .ip_ok |
; or a special broadcast (255.255.255.255) |
cmp eax, 0xffffffff |
je .ip_ok |
; maybe it's a multicast (224.0.0.0/4) |
and eax, 0x0fffffff |
cmp eax, 224 |
je .ip_ok |
; or a loopback address (127.0.0.0/8) |
and eax, 0x00ffffff |
cmp eax, 127 |
je .ip_ok |
; or it's just not meant for us.. :( |
DEBUGF 2,"IPv4_input: Destination address does not match!\n" |
jmp .dump |
;------------------------ |
; Now we can update stats |
.ip_ok: |
inc [IP_packets_rx + edi] |
;---------------------------------- |
; Check if the packet is fragmented |
test [edx + IPv4_header.FlagsAndFragmentOffset], 1 shl 5 ; Is 'more fragments' flag set ? |
jnz .has_fragments ; If so, we definately have a fragmented packet |
test [edx + IPv4_header.FlagsAndFragmentOffset], 0xff1f ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets |
jnz .is_last_fragment |
;------------------------------------------------------------------- |
; No, it's just a regular IP packet, pass it to the higher protocols |
.handle_it: ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed |
movzx esi, [edx + IPv4_header.VersionAndIHL] ; Calculate Header length by using IHL field |
and esi, 0x0000000f ; |
shl esi, 2 ; |
movzx ecx, [edx + IPv4_header.TotalLength] ; Calculate length of encapsulated Packet |
xchg cl, ch ; |
sub ecx, esi ; |
lea edi, [edx + IPv4_header.SourceAddress] ; make edi ptr to source and dest IPv4 address |
mov al, [edx + IPv4_header.Protocol] |
add esi, edx ; make esi ptr to data |
cmp al, IP_PROTO_TCP |
je TCP_input |
cmp al, IP_PROTO_UDP |
je UDP_input |
cmp al, IP_PROTO_ICMP |
je ICMP_input |
DEBUGF 2,"IPv4_input: unknown protocol %u\n", al |
.dump: |
DEBUGF 1,"IPv4_input: dumping\n" |
inc [IP_packets_dumped] ; FIXME: use correct interface |
call kernel_free |
add esp, 4 ; pop (balance stack) |
ret |
;--------------------------- |
; Fragmented packet handler |
.has_fragments: |
movzx eax, [edx + IPv4_header.FlagsAndFragmentOffset] |
xchg al, ah |
shl ax, 3 |
DEBUGF 1,"IPv4_input: fragmented packet offset=%u id=%x\n", ax, [edx + IPv4_header.Identification]:4 |
test ax, ax ; Is this the first packet of the fragment? |
jz .is_first_fragment |
;------------------------------------------------------- |
; We have a fragmented IP packet, but it's not the first |
DEBUGF 1,"IPv4_input: Middle fragment packet received!\n" |
call IPv4_find_fragment_slot |
cmp esi, -1 |
je .dump |
mov [esi + FRAGMENT_slot.ttl], 15 ; Reset the ttl |
mov esi, [esi + FRAGMENT_slot.ptr] |
or edi, -1 |
.find_last_entry: ; The following routine will try to find the last entry |
cmp edi, [esi + FRAGMENT_entry.PrevPtr] |
jne .destroy_slot ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!) |
mov edi, esi |
mov esi, [esi + FRAGMENT_entry.NextPtr] |
cmp esi, -1 |
jne .find_last_entry |
; We found the last entry (pointer is now in edi) |
; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure |
pop eax ; pointer to packet |
mov [edi + FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry |
mov [eax + FRAGMENT_entry.NextPtr], -1 |
mov [eax + FRAGMENT_entry.PrevPtr], edi |
mov [eax + FRAGMENT_entry.Owner], ebx |
add esp, 4 |
ret |
;------------------------------------ |
; We have received the first fragment |
.is_first_fragment: |
DEBUGF 1,"IPv4_input: First fragment packet received!\n" |
; try to locate a free slot.. |
mov ecx, MAX_FRAGMENTS |
mov esi, FRAGMENT_LIST |
.find_free_slot: |
cmp word [esi + FRAGMENT_slot.ttl], 0 |
je .found_free_slot |
add esi, sizeof.FRAGMENT_slot |
loop .find_free_slot |
jmp .dump ; If no free slot was found, dump the packet |
.found_free_slot: ; We found a free slot, let's fill in the FRAGMENT_slot structure |
mov [esi + FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl |
mov ax, [edx + IPv4_header.Identification] |
mov [esi + FRAGMENT_slot.id], ax |
mov eax, [edx + IPv4_header.SourceAddress] |
mov [esi + FRAGMENT_slot.SrcIP], eax |
mov eax, [edx + IPv4_header.DestinationAddress] |
mov [esi + FRAGMENT_slot.DstIP], eax |
pop eax |
mov [esi + FRAGMENT_slot.ptr], eax |
; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure |
mov [eax + FRAGMENT_entry.NextPtr], -1 |
mov [eax + FRAGMENT_entry.PrevPtr], -1 |
mov [eax + FRAGMENT_entry.Owner], ebx |
add esp, 4 ; balance stack and exit |
ret |
;----------------------------------- |
; We have received the last fragment |
.is_last_fragment: |
DEBUGF 1,"IPv4_input: Last fragment packet received!\n" |
call IPv4_find_fragment_slot |
cmp esi, -1 |
je .dump |
mov esi, [esi + FRAGMENT_slot.ptr] ; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer |
push esi |
xor eax, eax |
or edi, -1 |
.count_bytes: |
cmp [esi + FRAGMENT_entry.PrevPtr], edi |
jne .destroy_slot_pop ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!) |
mov cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.TotalLength] ; Add total length |
xchg cl, ch |
DEBUGF 1,"IPv4_input: Packet size=%u\n", cx |
add ax, cx |
movzx cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Sub Header length |
and cx, 0x000F |
shl cx, 2 |
DEBUGF 1,"IPv4_input: Header size=%u\n", cx |
sub ax, cx |
mov edi, esi |
mov esi, [esi + FRAGMENT_entry.NextPtr] |
cmp esi, -1 |
jne .count_bytes |
mov esi, [esp+4] |
mov [edi + FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code |
mov [esi + FRAGMENT_entry.NextPtr], -1 |
mov [esi + FRAGMENT_entry.PrevPtr], edi |
mov [esi + FRAGMENT_entry.Owner], ebx |
mov cx, [edx + IPv4_header.TotalLength] ; Note: This time we dont substract Header length |
xchg cl, ch |
DEBUGF 1,"IPv4_input: Packet size=%u\n", cx |
add ax, cx |
DEBUGF 1,"IPv4_input: Total Received data size=%u\n", eax |
push eax |
mov ax, [edx + IPv4_header.FlagsAndFragmentOffset] |
xchg al, ah |
shl ax, 3 |
add cx, ax |
pop eax |
DEBUGF 1,"IPv4_input: Total Fragment size=%u\n", ecx |
cmp ax, cx |
jne .destroy_slot_pop |
push eax |
push eax |
call kernel_alloc |
test eax, eax |
je .destroy_slot_pop ; If we dont have enough space to allocate the buffer, discard all packets in slot |
mov edx, [esp+4] ; Get pointer to first fragment entry back in edx |
.rebuild_packet_loop: |
movzx ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.FlagsAndFragmentOffset] ; Calculate the fragment offset |
xchg cl, ch ; intel byte order |
shl cx, 3 ; multiply by 8 and clear first 3 bits |
DEBUGF 1,"IPv4_input: Fragment offset=%u\n", cx |
lea edi, [eax + ecx] ; Notice that edi will be equal to eax for first fragment |
movzx ebx, [edx + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Find header size (in ebx) of fragment |
and bx, 0x000F ; |
shl bx, 2 ; |
lea esi, [edx + sizeof.FRAGMENT_entry] ; Set esi to the correct begin of fragment |
movzx ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.TotalLength] ; Calculate total length of fragment |
xchg cl, ch ; intel byte order |
cmp edi, eax ; Is this packet the first fragment ? |
je .first_fragment |
sub cx, bx ; If not, dont copy the header |
add esi, ebx ; |
.first_fragment: |
push cx ; First copy dword-wise, then byte-wise |
shr cx, 2 ; |
rep movsd ; |
pop cx ; |
and cx, 3 ; |
rep movsb ; |
push eax |
push edx ; Push pointer to fragment onto stack |
mov ebx, [edx + FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet |
mov edx, [edx + FRAGMENT_entry.NextPtr] ; Set edx to the next pointer |
call kernel_free ; free the previous fragment buffer (this uses the value from stack) |
pop eax |
cmp edx, -1 ; Check if it is last fragment in chain |
jne .rebuild_packet_loop |
pop ecx |
xchg cl, ch |
mov edx, eax |
mov [edx + IPv4_header.TotalLength], cx |
add esp, 8 |
xchg cl, ch |
push ecx |
push eax |
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr |
.destroy_slot_pop: |
add esp, 4 |
.destroy_slot: |
DEBUGF 1,"IPv4_input: Destroy fragment slot!\n" |
; TODO! |
jmp .dump |
;----------------------------------------------------------------- |
; |
; find fragment slot |
; |
; IN: pointer to fragmented packet in edx |
; OUT: pointer to slot in esi, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
IPv4_find_fragment_slot: |
;;; TODO: the RFC says we should check protocol number too |
push eax ebx ecx edx |
mov ax, [edx + IPv4_header.Identification] |
mov ecx, MAX_FRAGMENTS |
mov esi, FRAGMENT_LIST |
mov ebx, [edx + IPv4_header.SourceAddress] |
mov edx, [edx + IPv4_header.DestinationAddress] |
.find_slot: |
cmp [esi + FRAGMENT_slot.id], ax |
jne .try_next |
cmp [esi + FRAGMENT_slot.SrcIP], ebx |
jne .try_next |
cmp [esi + FRAGMENT_slot.DstIP], edx |
je .found_slot |
.try_next: |
add esi, sizeof.FRAGMENT_slot |
loop .find_slot |
or esi, -1 |
.found_slot: |
pop edx ecx ebx eax |
ret |
;------------------------------------------------------------------ |
; |
; IPv4_output |
; |
; IN: eax = dest ip |
; ebx = output device ptr/0 for automatic choice |
; ecx = data length |
; edx = source ip |
; di = TTL shl 8 + protocol |
; |
; OUT: eax = pointer to buffer start |
; ebx = pointer to device struct (needed for sending procedure) |
; ecx = unchanged (packet size of embedded data) |
; edx = size of complete buffer |
; edi = pointer to start of data (0 on error) |
; |
;------------------------------------------------------------------ |
align 4 |
IPv4_output: |
DEBUGF 1,"IPv4_output: size=%u\n", ecx |
cmp ecx, 65500 ; Max IPv4 packet size |
ja .too_large |
push ecx eax edx di |
cmp eax, 1 shl 24 + 127 |
je .loopback |
call IPv4_route ; outputs device number in edi, dest ip in eax |
call ARP_IP_to_MAC |
test eax, 0xffff0000 ; error bits |
jnz .arp_error |
push ebx ; push the mac onto the stack |
push ax |
inc [IP_packets_tx + edi] ; update stats |
mov ebx, [NET_DRV_LIST + edi] |
lea eax, [ebx + ETH_DEVICE.mac] |
mov edx, esp |
mov ecx, [esp + 10 + 6] |
add ecx, sizeof.IPv4_header |
mov di, ETHER_IPv4 |
call ETH_output |
jz .eth_error |
add esp, 6 ; pop the mac out of the stack |
.continue: |
xchg cl, ch ; internet byte order |
mov [edi + IPv4_header.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header) |
mov [edi + IPv4_header.TypeOfService], 0 ; nothing special, just plain ip packet |
mov [edi + IPv4_header.TotalLength], cx |
mov [edi + IPv4_header.Identification], 0 ; fragment id: FIXME |
mov [edi + IPv4_header.FlagsAndFragmentOffset], 0 |
pop word [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol |
; [edi + IPv4_header.Protocol] |
mov [edi + IPv4_header.HeaderChecksum], 0 |
popd [edi + IPv4_header.SourceAddress] |
popd [edi + IPv4_header.DestinationAddress] |
pop ecx |
IPv4_checksum edi |
add edi, sizeof.IPv4_header |
DEBUGF 2,"IPv4_output: success!\n" |
ret |
.eth_error: |
DEBUGF 2,"IPv4_output: ethernet error\n" |
add esp, 3*4+2+6 |
xor edi, edi |
ret |
.arp_error: |
DEBUGF 2,"IPv4_output: ARP error=%x\n", eax |
add esp, 3*4+2 |
xor edi, edi |
ret |
.too_large: |
DEBUGF 2,"IPv4_output: Packet too large!\n" |
xor edi, edi |
ret |
.loopback: |
mov dword [esp + 2], eax |
add ecx, sizeof.IPv4_header |
mov di, ETHER_IPv4 |
call LOOP_output |
jmp .continue |
;------------------------------------------------------------------ |
; |
; IPv4_output_raw |
; |
; IN: eax = socket ptr |
; ecx = data length |
; esi = data ptr |
; |
; OUT: / |
; |
;------------------------------------------------------------------ |
align 4 |
IPv4_output_raw: |
DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax |
cmp ecx, 1480 ;;;;; FIXME |
ja .too_large |
sub esp, 8 |
push esi eax |
call IPv4_route |
call ARP_IP_to_MAC |
test eax, 0xffff0000 ; error bits |
jnz .arp_error |
push ebx ; push the mac |
push ax |
inc [IP_packets_tx + edi] |
mov ebx, [NET_DRV_LIST + edi] |
lea eax, [ebx + ETH_DEVICE.mac] |
mov edx, esp |
mov ecx, [esp + 6 + 4] |
add ecx, sizeof.IPv4_header |
mov di, ETHER_IPv4 |
call ETH_output |
jz .error |
add esp, 6 ; pop the mac |
mov dword[esp+4+4], edx |
mov dword[esp+4+4+4], eax |
pop eax esi |
;; todo: check socket options if we should add header, or just compute checksum |
push edi ecx |
rep movsb |
pop ecx edi |
; [edi + IPv4_header.VersionAndIHL] ; IPv4, normal length (no Optional header) |
; [edi + IPv4_header.TypeOfService] ; nothing special, just plain ip packet |
; [edi + IPv4_header.TotalLength] |
; [edi + IPv4_header.TotalLength] ; internet byte order |
; [edi + IPv4_header.FlagsAndFragmentOffset] |
mov [edi + IPv4_header.HeaderChecksum], 0 |
; [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol |
; [edi + IPv4_header.Protocol] |
; [edi + IPv4_header.Identification] ; fragment id |
; [edi + IPv4_header.SourceAddress] |
; [edi + IPv4_header.DestinationAddress] |
IPv4_checksum edi ;;;; todo: checksum for IP packet with options! |
add edi, sizeof.IPv4_header |
DEBUGF 2,"IPv4_output_raw: device=%x\n", ebx |
call [ebx + NET_DEVICE.transmit] |
ret |
.error: |
add esp, 6 |
.arp_error: |
add esp, 8+4+4 |
.too_large: |
DEBUGF 2,"IPv4_output_raw: Failed\n" |
sub edi, edi |
ret |
;-------------------------------------------------------- |
; |
; |
; IN: dword [esp] = pointer to buffer containing ipv4 packet to be fragmented |
; dword [esp+4] = buffer size |
; esi = pointer to ip header in that buffer |
; ecx = max size of fragments |
; |
; OUT: / |
; |
;-------------------------------------------------------- |
align 4 |
IPv4_fragment: |
DEBUGF 1,"IPv4_fragment\n" |
and ecx, not 111b ; align 4 |
cmp ecx, sizeof.IPv4_header + 8 ; must be able to put at least 8 bytes |
jb .err2 |
push esi ecx |
mov eax, [esi + IPv4_header.DestinationAddress] |
call ARP_IP_to_MAC |
pop ecx esi |
cmp eax, -1 |
jz .err2 |
push ebx |
push ax |
mov ebx, [NET_DRV_LIST] |
lea eax, [ebx + ETH_DEVICE.mac] |
push eax |
push esi ; ptr to ip header |
sub ecx, sizeof.IPv4_header ; substract header size |
push ecx ; max data size |
push dword 0 ; offset |
.new_fragment: |
DEBUGF 1,"Ipv4_fragment: new fragment" |
mov eax, [esp + 3*4] |
lea ebx, [esp + 4*4] |
mov di , ETHER_IPv4 |
call ETH_output |
cmp edi, -1 |
jz .err |
; copy header |
mov esi, [esp + 2*4] |
mov ecx, 5 ; 5 dwords: TODO: use IHL field of the header! |
rep movsd |
; copy data |
mov esi, [esp + 2*4] |
add esi, sizeof.IPv4_header |
add esi, [esp] ; offset |
mov ecx, [esp + 1*4] |
DEBUGF 1,"IPv4_fragment: copying %u bytes\n", ecx |
rep movsb |
; now, correct header |
mov ecx, [esp + 1*4] |
add ecx, sizeof.IPv4_header |
xchg cl, ch |
mov [edi + IPv4_header.TotalLength], cx |
mov ecx, [esp] ; offset |
xchg cl, ch |
; cmp dword[esp + 4*4], 0 ; last fragment?;<<<<<< |
; je .last_fragment |
or cx, 1 shl 2 ; more fragments |
; .last_fragment: |
mov [edi + IPv4_header.FlagsAndFragmentOffset], cx |
mov [edi + IPv4_header.HeaderChecksum], 0 |
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet |
mov ecx, [esp + 1*4] |
push edx eax |
IPv4_checksum edi |
call [ebx + NET_DEVICE.transmit] |
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
mov ecx, [esp+4] |
add [esp], ecx |
mov ecx, [esp+3*4+6+4] ; ptr to begin of buff |
add ecx, [esp+3*4+6+4+4] ; buff size |
sub ecx, [esp+2*4] ; ptr to ip header |
add ecx, [esp] ; offset |
DEBUGF 1,"Ipv4_fragment: %u bytes remaining\n", ecx |
cmp ecx, [esp+1*4] |
jae .new_fragment |
mov [esp+4], ecx ; set fragment size to remaining packet size |
jmp .new_fragment |
.err: |
DEBUGF 1,"Ipv4_fragment: failed\n" |
.done: |
add esp, 12 + 4 + 6 |
.err2: |
DEBUGF 1,"Ipv4_fragment: dumping\n" |
call kernel_free |
add esp, 4 |
ret |
;--------------------------------------------------------------------------- |
; |
; IPv4_route |
; |
; IN: eax = Destination IP |
; OUT: edi = device id * 4 |
; eax = ip of gateway if nescessary, unchanged otherwise |
; |
;--------------------------------------------------------------------------- |
align 4 |
IPv4_route: |
cmp eax, 0xffffffff |
je .broadcast |
xor edi, edi |
mov ecx, MAX_NET_DEVICES |
.loop: |
mov ebx, [IP_LIST+edi] |
and ebx, [SUBNET_LIST+edi] |
jz .next |
mov edx, eax |
and edx, [SUBNET_LIST+edi] |
cmp ebx, edx |
je .found_it |
.next: |
add edi, 4 |
dec ecx |
jnz .loop |
.invalid: |
xor edi, edi ; if none found, use device 0 as default |
mov eax, [GATEWAY_LIST] |
.found_it: |
DEBUGF 1,"IPv4_dest_to_dev: %u\n", edi |
ret |
.broadcast: |
xor edi, edi |
ret |
;--------------------------------------------------------------------------- |
; |
; IPv4_get_frgmnt_num |
; |
; IN: / |
; OUT: fragment number in ax |
; |
;--------------------------------------------------------------------------- |
align 4 |
IPv4_get_frgmnt_num: |
xor ax, ax ;;; TODO: replace this with real code |
ret |
;--------------------------------------------------------------------------- |
; |
; IPv4_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;--------------------------------------------------------------------------- |
align 4 |
IPv4_api: |
movzx eax, bh |
shl eax, 2 |
and ebx, 0x000000ff |
cmp ebx, .number |
ja .error |
jmp dword [.table + 4*ebx] |
.table: |
dd .packets_tx ; 0 |
dd .packets_rx ; 1 |
dd .read_ip ; 2 |
dd .write_ip ; 3 |
dd .read_dns ; 4 |
dd .write_dns ; 5 |
dd .read_subnet ; 6 |
dd .write_subnet ; 7 |
dd .read_gateway ; 8 |
dd .write_gateway ; 9 |
.number = ($ - .table) / 4 - 1 |
.error: |
mov eax, -1 |
ret |
.packets_tx: |
mov eax, [IP_packets_tx + eax] |
ret |
.packets_rx: |
mov eax, [IP_packets_rx + eax] |
ret |
.read_ip: |
mov eax, [IP_LIST + eax] |
ret |
.write_ip: |
mov [IP_LIST + eax], ecx |
mov edi, eax ; device number, we'll need it for ARP |
; pre-calculate the local broadcast address |
mov ebx, [SUBNET_LIST + eax] |
not ebx |
or ebx, ecx |
mov [BROADCAST_LIST + eax], ebx |
mov eax, ecx |
call ARP_output_request ; now send a gratuitous ARP |
call NET_send_event |
xor eax, eax |
ret |
.read_dns: |
mov eax, [DNS_LIST + eax] |
ret |
.write_dns: |
mov [DNS_LIST + eax], ecx |
call NET_send_event |
xor eax, eax |
ret |
.read_subnet: |
mov eax, [SUBNET_LIST + eax] |
ret |
.write_subnet: |
mov [SUBNET_LIST + eax], ecx |
; pre-calculate the local broadcast address |
mov ebx, [IP_LIST + eax] |
not ecx |
or ecx, ebx |
mov [BROADCAST_LIST + eax], ecx |
call NET_send_event |
xor eax, eax |
ret |
.read_gateway: |
mov eax, [GATEWAY_LIST + eax] |
ret |
.write_gateway: |
mov [GATEWAY_LIST + eax], ecx |
call NET_send_event |
xor eax, eax |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/IPv6.inc |
---|
0,0 → 1,298 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2012-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; IPv6.INC ;; |
;; ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3251 $ |
struct IPv6_header |
VersionTrafficFlow dd ? ; Version[0-3], Traffic class[4-11], Flow Label [12-31] |
PayloadLength dw ? ; 16 bits, unsigned length of payload (extension headers are part of this) |
NextHeader db ? ; Values are same as in IPv4 'Protocol' field |
HopLimit db ? ; Decremented by every node, packet is discarded when it reaches 0 |
SourceAddress rd 4 ; 128-bit addresses |
DestinationAddress rd 4 ; |
Payload rb 0 |
ends |
align 4 |
uglobal |
IPv6: |
.addresses rd 4*MAX_NET_DEVICES |
.subnet rd 4*MAX_NET_DEVICES |
.dns rd 4*MAX_NET_DEVICES |
.gateway rd 4*MAX_NET_DEVICES |
.packets_tx rd MAX_NET_DEVICES |
.packets_rx rd MAX_NET_DEVICES |
endg |
;----------------------------------------------------------------- |
; |
; IPv6_init |
; |
; This function resets all IP variables |
; |
;----------------------------------------------------------------- |
macro IPv6_init { |
xor eax, eax |
mov edi, IPv6 |
mov ecx, (4*4*4+2*4)MAX_IP |
rep stosd |
} |
;----------------------------------------------------------------- |
; |
; IPv6_input: |
; |
; Will check if IPv6 Packet isnt damaged |
; and call appropriate handler. (TCP/UDP/ICMP/..) |
; |
; It will also re-construct fragmented packets |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; pointer to device struct in ebx |
; pointer to IPv6 header in edx |
; size of IPv6 packet in ecx |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
IPv6_input: |
DEBUGF 2,"IPv6_input from: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\ |
[edx + IPv6_header.SourceAddress + 0]:2,[edx + IPv6_header.SourceAddress + 1]:2,\ |
[edx + IPv6_header.SourceAddress + 2]:2,[edx + IPv6_header.SourceAddress + 3]:2,\ |
[edx + IPv6_header.SourceAddress + 4]:2,[edx + IPv6_header.SourceAddress + 5]:2,\ |
[edx + IPv6_header.SourceAddress + 6]:2,[edx + IPv6_header.SourceAddress + 7]:2,\ |
[edx + IPv6_header.SourceAddress + 8]:2,[edx + IPv6_header.SourceAddress + 9]:2,\ |
[edx + IPv6_header.SourceAddress + 10]:2,[edx + IPv6_header.SourceAddress + 11]:2,\ |
[edx + IPv6_header.SourceAddress + 12]:2,[edx + IPv6_header.SourceAddress + 13]:2,\ |
[edx + IPv6_header.SourceAddress + 14]:2,[edx + IPv6_header.SourceAddress + 15]:2 |
DEBUGF 1,"IPv6_input to: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\ |
[edx + IPv6_header.DestinationAddress + 0]:2,[edx + IPv6_header.DestinationAddress + 1]:2,\ |
[edx + IPv6_header.DestinationAddress + 2]:2,[edx + IPv6_header.DestinationAddress + 3]:2,\ |
[edx + IPv6_header.DestinationAddress + 4]:2,[edx + IPv6_header.DestinationAddress + 5]:2,\ |
[edx + IPv6_header.DestinationAddress + 6]:2,[edx + IPv6_header.DestinationAddress + 7]:2,\ |
[edx + IPv6_header.DestinationAddress + 8]:2,[edx + IPv6_header.DestinationAddress + 9]:2,\ |
[edx + IPv6_header.DestinationAddress + 10]:2,[edx + IPv6_header.DestinationAddress + 11]:2,\ |
[edx + IPv6_header.DestinationAddress + 12]:2,[edx + IPv6_header.DestinationAddress + 13]:2,\ |
[edx + IPv6_header.DestinationAddress + 14]:2,[edx + IPv6_header.DestinationAddress + 15]:2 |
sub ecx, sizeof.IPv6_header |
jb .dump |
cmp cx, [edx + IPv6_header.PayloadLength] |
jb .dump |
;------------------------------------------------------------------- |
; No, it's just a regular IP packet, pass it to the higher protocols |
.handle_it: |
movzx ecx, [edx + IPv6_header.PayloadLength] |
lea edi, [edx + IPv6_header.SourceAddress] ; make edi ptr to source and dest IPv6 address |
lea esi, [edx + IPv6_header.Payload] ; make esi ptr to data |
mov al, [edx + IPv6_header.NextHeader] |
.scan: |
cmp al, 59 ; no next |
je .dump |
cmp al, 0 |
je .hop_by_hop |
cmp al, 43 |
je .routing |
cmp al, 44 |
je .fragment |
cmp al, 60 |
je .dest_opts |
; cmp al, IP_PROTO_TCP |
; je TCP_input |
; cmp al, IP_PROTO_UDP |
; je UDP_input |
; cmp al, 58 |
; je ICMP6_input |
DEBUGF 2,"IPv6_input - unknown protocol: %u\n", al |
.dump: |
DEBUGF 1,"IPv6_input - dumping\n" |
call kernel_free |
add esp, 4 |
ret |
.dump_options: |
add esp, 2+4+4 |
jmp .dump |
.nextheader: |
pop esi |
pop ecx |
pop ax |
jmp .scan |
;------------------------- |
; Hop-by-Hop |
.hop_by_hop: |
DEBUGF 1,"IPv6_input - hop by hop\n" |
pushw [esi] ; 8 bit identifier for option type |
movzx eax, byte[esi + 1] ; Hdr Ext Len |
inc eax ; first 8 octets not counted |
shl eax, 3 ; * 8 |
sub ecx, eax |
push ecx |
add eax, esi |
push eax |
inc esi |
inc esi |
mov al, [esi] |
cmp al, 0 |
je .pad_1 |
cmp al, 1 |
je .pad_n |
; TODO: check with other known options |
; unknown option.. discard packet or not? |
; check highest two bits |
test al, 0xc0 ; discard packet |
jnz .dump_options |
.pad_n: |
movzx eax, byte[esi + 1] |
DEBUGF 1,"IPv6_input - pad %u\n", eax |
inc esi |
inc esi |
add esi, eax |
sub ecx, eax |
jmp .hop_by_hop |
.pad_1: |
DEBUGF 1,"IPv6_input - pad 1\n" |
inc esi |
dec ecx |
jmp .hop_by_hop |
.dest_opts: |
DEBUGF 1,"IPv6_input - dest opts\n" |
jmp .nextheader |
.routing: |
DEBUGF 1,"IPv6_input - routing\n" |
pushw [esi] ; 8 bit identifier for option type |
movzx eax, byte[esi + 1] ; Hdr Ext Len |
inc eax ; first 8 octets not counted |
shl eax, 3 ; * 8 |
sub ecx, eax |
push ecx |
add eax, esi |
push eax |
inc esi |
inc esi |
cmp al, 0 |
je .pad_1 |
cmp al, 1 |
je .pad_n |
mov al, [esi] ; routing type |
jmp .nextheader |
.fragment: |
DEBUGF 1,"IPv6_input - fragment\n" |
jmp .nextheader |
;--------------------------------------------------------------------------- |
; |
; IPv6_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;--------------------------------------------------------------------------- |
align 4 |
IPv6_api: |
movzx eax, bh |
shl eax, 2 |
and ebx, 0x000000ff |
cmp ebx, .number |
ja .error |
jmp dword [.table + 4*ebx] |
.table: |
dd .packets_tx ; 0 |
dd .packets_rx ; 1 |
; dd .read_ip ; 2 |
; dd .write_ip ; 3 |
; dd .read_dns ; 4 |
; dd .write_dns ; 5 |
; dd .read_subnet ; 6 |
; dd .write_subnet ; 7 |
; dd .read_gateway ; 8 |
; dd .write_gateway ; 9 |
.number = ($ - .table) / 4 - 1 |
.error: |
mov eax, -1 |
ret |
.packets_tx: |
mov eax, [IPv6.packets_tx + eax] |
ret |
.packets_rx: |
mov eax, [IPv6.packets_rx + eax] |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/PPPoE.inc |
---|
0,0 → 1,340 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; PPPoE.INC ;; |
;; ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
struct PPPoE_frame |
VersionAndType db ? |
Code db ? |
SessionID dw ? |
Length dw ? ; Length of payload, does NOT include the length PPPoE header. |
Payload rb 0 |
ends |
uglobal |
PPPoE_SID dw ? |
PPPoE_MAC dp ? |
endg |
;----------------------------------------------------------------- |
; |
; PPPoE_init |
; |
; This function resets all IP variables |
; |
;----------------------------------------------------------------- |
macro PPPoE_init { |
call PPPoE_stop_connection |
} |
;----------------------------------------------------------------- |
; |
; PPPoE discovery input |
; |
; Handler of received Ethernet packet with type = Discovery |
; |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; pointer to device struct in ebx |
; pointer to PPP header in edx |
; size of PPP packet in ecx |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
PPPoE_discovery_input: |
DEBUGF 2,"PPPoE_discovery_input\n" |
; First, find open PPPoE socket |
mov eax, net_sockets |
.next_socket: |
mov eax, [eax + SOCKET.NextPtr] |
or eax, eax |
jz .dump |
cmp [eax + SOCKET.Domain], AF_PPP |
jne .next_socket |
cmp [eax + SOCKET.Protocol], PPP_PROTO_ETHERNET |
jne .next_socket |
; Now, send it to the this socket |
mov ecx, [esp + 4] |
mov esi, [esp] |
jmp SOCKET_input |
.dump: |
DEBUGF 1,'PPPoE_discovery_input: dumping\n' |
call kernel_free |
add esp, 4 |
ret |
;-------------------------------------- |
; |
; Send discovery packet |
; |
; IN: eax = socket pointer |
; ecx = number of bytes to send |
; esi = pointer to data |
; |
;-------------------------------------- |
align 4 |
PPPoE_discovery_output: |
DEBUGF 2,"PPPoE_discovery_output: socket=%x buffer=%x size=%d\n", eax, esi, ecx |
; RFC2516: An entire PADI packet (including the PPPoE header) MUST NOT |
; exceed 1484 octets. |
cmp ecx, 1484 + 14 |
ja .bad |
; Check that device exists and is ethernet device |
mov ebx, [eax + SOCKET.device] |
cmp ebx, MAX_NET_DEVICES |
ja .bad |
mov ebx, [NET_DRV_LIST + 4*ebx] |
test ebx, ebx |
jz .bad |
cmp [ebx + NET_DEVICE.type], NET_TYPE_ETH |
jne .bad |
DEBUGF 2,"PPPoE_discovery_output: device=%x\n", ebx |
; Create packet. |
push ecx esi |
stdcall kernel_alloc, 1500 |
pop esi ecx |
test eax, eax |
jz .bad |
mov edx, ecx |
mov edi, eax |
rep movsb |
cmp edx, 60 ; Min ETH size |
ja @f |
mov edx, 60 |
@@: |
push edx eax ; size and packet ptr for driver send proc |
; Overwrite source MAC and protocol type |
lea edi, [eax + ETH_header.SrcMAC] |
lea esi, [ebx + ETH_DEVICE.mac] |
movsd |
movsw |
cmp word[edi], ETHER_PPP_SESSION ; Allow only PPP_discovery, or LCP |
je @f |
mov ax, ETHER_PPP_DISCOVERY |
stosw |
@@: |
; And send the packet |
call [ebx + NET_DEVICE.transmit] |
xor eax, eax |
ret |
.bad: |
or eax, -1 |
ret |
;----------------------------------------------------------------- |
; |
; PPPoE session input |
; |
; Handler of received Ethernet packet with type = Session |
; |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; pointer to device struct in ebx |
; pointer to PPP header in edx |
; size of PPP packet in ecx |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
PPPoE_session_input: |
cmp [edx + PPPoE_frame.VersionAndType], 0x11 |
jne .dump |
cmp [edx + PPPoE_frame.Code], 0x00 |
jne .dump |
movzx ecx, [edx + PPPoE_frame.Length] |
xchg cl, ch |
mov ax, [edx + PPPoE_frame.SessionID] |
DEBUGF 2,"PPPoE_input: session ID=%x, length=%u\n", ax, cx |
cmp ax, [PPPoE_SID] |
jne .dump |
mov ax, word [edx + PPPoE_frame.Payload] |
add edx, PPPoE_frame.Payload + 2 |
cmp ax, PPP_IPv4 |
je IPv4_input |
; cmp ax, PPP_IPv6 |
; je IPv6_input |
jmp PPPoE_discovery_input ; Send LCP,CHAP,CBCP,... packets to the PPP dialer |
DEBUGF 2,"PPPoE_input: Unknown protocol=%x\n", ax |
.dump: |
DEBUGF 2,"PPPoE_input: dumping\n" |
call kernel_free |
add esp, 4 |
ret |
;----------------------------------------------------------------- |
; |
; PPPoE_output |
; |
; IN: |
; ebx = device ptr |
; ecx = packet size |
; |
; di = protocol |
; |
; OUT: edi = 0 on error, pointer to buffer otherwise |
; eax = buffer start |
; ebx = to device structure |
; ecx = unchanged (packet size of embedded data) |
; edx = size of complete buffer |
; |
;----------------------------------------------------------------- |
align 4 |
PPPoE_output: |
DEBUGF 1,"PPPoE_output: size=%u device=%x\n", ecx, ebx |
pushw di |
pushw [PPPoE_SID] |
lea eax, [ebx + ETH_DEVICE.mac] |
lea edx, [PPPoE_MAC] |
add ecx, PPPoE_frame.Payload + 2 |
mov di, ETHER_PPP_SESSION |
call ETH_output |
jz .eth_error |
sub ecx, PPPoE_frame.Payload |
mov [edi + PPPoE_frame.VersionAndType], 0x11 |
mov [edi + PPPoE_frame.Code], 0 |
popw [edi + PPPoE_frame.SessionID] |
xchg cl, ch |
mov [edi + PPPoE_frame.Length], cx |
xchg cl, ch |
pop word [edi + PPPoE_frame.Payload] |
sub ecx, 2 |
add edi, PPPoE_frame.Payload + 2 |
DEBUGF 1,"PPPoE_output: success!\n" |
ret |
.eth_error: |
add esp, 4 |
xor edi, edi |
ret |
PPPoE_start_connection: |
DEBUGF 2,"PPPoE_start_connection: %x\n", cx |
cmp [PPPoE_SID], 0 |
jne .fail |
mov [PPPoE_SID], cx |
mov dword [PPPoE_MAC], edx |
mov word [PPPoE_MAC + 4], si |
xor eax, eax |
ret |
.fail: |
or eax, -1 |
ret |
align 4 |
PPPoE_stop_connection: |
DEBUGF 2,"PPPoE_stop_connection\n" |
xor eax, eax |
mov [PPPoE_SID], ax |
mov dword [PPPoE_MAC], eax |
mov word [PPPoE_MAC + 4], ax |
ret |
;--------------------------------------------------------------------------- |
; |
; PPPoE API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;--------------------------------------------------------------------------- |
align 4 |
PPPoE_api: |
movzx eax, bh |
shl eax, 2 |
and ebx, 0xff |
cmp ebx, .number |
ja .error |
jmp dword [.table + 4*ebx] |
.table: |
dd PPPoE_start_connection ; 0 |
dd PPPoE_stop_connection ; 1 |
.number = ($ - .table) / 4 - 1 |
.error: |
mov eax, -1 |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/ethernet.inc |
---|
0,0 → 1,233 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; ETHERNET.INC ;; |
;; ;; |
;; Ethernet network layer for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3346 $ |
ETH_FRAME_MINIMUM = 60 |
struct ETH_header |
DstMAC dp ? ; destination MAC-address |
SrcMAC dp ? ; source MAC-address |
Type dw ? ; type of the upper-layer protocol |
ends |
struct ETH_DEVICE NET_DEVICE |
mac dp ? |
ends |
align 4 |
iglobal |
ETH_BROADCAST dp 0xffffffffffff |
endg |
;----------------------------------------------------------------- |
; |
; ETH_input |
; |
; This function is called by ethernet drivers, |
; It pushes the received ethernet packets onto the eth_in_queue |
; |
; IN: [esp] = Pointer to buffer |
; [esp+4] = size of buffer |
; ebx = pointer to eth_device |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_input: |
mov eax, [esp] |
mov ecx, [esp+4] |
DEBUGF 1,"ETH_input: size=%u\n", ecx |
cmp ecx, ETH_FRAME_MINIMUM |
jb .dump |
sub ecx, sizeof.ETH_header |
lea edx, [eax + sizeof.ETH_header] |
mov ax, [eax + ETH_header.Type] |
cmp ax, ETHER_IPv4 |
je IPv4_input |
cmp ax, ETHER_ARP |
je ARP_input |
cmp ax, ETHER_IPv6 |
je IPv6_input |
cmp ax, ETHER_PPP_DISCOVERY |
je PPPoE_discovery_input |
cmp ax, ETHER_PPP_SESSION |
je PPPoE_session_input |
DEBUGF 2,"ETH_input: Unknown packet type=%x\n", ax |
.dump: |
DEBUGF 2,"ETH_input: dumping\n" |
call kernel_free |
add esp, 4 |
ret |
;----------------------------------------------------------------- |
; |
; ETH_output |
; |
; IN: eax = pointer to source mac |
; ebx = device ptr |
; ecx = packet size |
; edx = pointer to destination mac |
; di = protocol |
; |
; OUT: edi = 0 on error, pointer to buffer otherwise |
; eax = buffer start |
; ebx = to device structure |
; ecx = unchanged (packet size of embedded data) |
; edx = size of complete buffer |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_output: |
DEBUGF 1,"ETH_output: size=%u device=%x\n", ecx, ebx |
cmp ecx, [ebx + NET_DEVICE.mtu] |
ja .exit |
push ecx |
push di eax edx |
add ecx, sizeof.ETH_header |
stdcall kernel_alloc, ecx |
test eax, eax |
jz .out_of_ram |
mov edi, eax |
pop esi |
movsd |
movsw |
pop esi |
movsd |
movsw |
pop ax |
stosw |
lea eax, [edi - sizeof.ETH_header] ; Set eax to buffer start |
pop ecx |
lea edx, [ecx + sizeof.ETH_header] ; Set edx to complete buffer size |
cmp edx, ETH_FRAME_MINIMUM |
jbe .adjust_size |
.done: |
DEBUGF 1,"ETH_output: ptr=%x size=%u\n", eax, edx |
ret |
.adjust_size: |
mov edx, ETH_FRAME_MINIMUM |
test edx, edx ; clear zero flag |
jmp .done |
.out_of_ram: |
DEBUGF 2,"ETH_output: Out of ram!\n" |
add esp, 4+4+2+4 |
sub edi, edi |
ret |
.exit: |
DEBUGF 2,"ETH_output: Packet too large!\n" |
sub edi, edi |
ret |
;----------------------------------------------------------------- |
; |
; ETH_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_api: |
cmp bh, MAX_NET_DEVICES |
ja .error |
movzx eax, bh |
mov eax, dword [NET_DRV_LIST + 4*eax] |
cmp [eax + NET_DEVICE.type], NET_TYPE_ETH |
jne .error |
and ebx, 0xff |
cmp ebx, .number |
ja .error |
jmp dword [.table + 4*ebx] |
.table: |
dd .packets_tx ; 0 |
dd .packets_rx ; 1 |
dd .bytes_tx ; 2 |
dd .bytes_rx ; 3 |
dd .read_mac ; 4 |
dd .state ; 5 |
.number = ($ - .table) / 4 - 1 |
.error: |
or eax, -1 |
ret |
.packets_tx: |
mov eax, [eax + NET_DEVICE.packets_tx] |
ret |
.packets_rx: |
mov eax, [eax + NET_DEVICE.packets_rx] |
ret |
.bytes_tx: |
mov ebx, dword [eax + NET_DEVICE.bytes_tx + 4] |
mov eax, dword [eax + NET_DEVICE.bytes_tx] |
mov [esp+20+4], ebx ; TODO: fix this ugly code |
ret |
.bytes_rx: |
mov ebx, dword [eax + NET_DEVICE.bytes_rx + 4] |
mov eax, dword [eax + NET_DEVICE.bytes_rx] |
mov [esp+20+4], ebx ; TODO: fix this ugly code |
ret |
.read_mac: |
movzx ebx, word [eax + ETH_DEVICE.mac] |
mov eax, dword [eax + ETH_DEVICE.mac + 2] |
mov [esp+20+4], ebx ; TODO: fix this ugly code |
ret |
.state: |
mov eax, [eax + NET_DEVICE.state] |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/icmp.inc |
---|
1,193 → 1,431 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; ICMP.INC ;; |
;; ;; |
;; Internet Control Message Protocol ( RFC 792 ) ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
;; Last revision: 11.11.2006 ;; |
;; Based on the work of [Johnny_B] and [smb] ;; |
;; ;; |
;; This file contains the following: ;; |
;; icmp_rx - processes ICMP-packets received by the IP layer ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Changes history: ;; |
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;; |
;; 11.11.2006 - [Johnny_B] and [smb] ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;; Current status: ;; |
;; This implemetation of ICMP proto supports message of ECHO type. |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 2924 $ |
struc ICMP_PACKET |
{ .Type db ? ;+00 |
.Code db ? ;+01 |
.Checksum dw ? ;+02 |
.Identifier dw ? ;+04 |
.SequenceNumber dw ? ;+06 |
.Data db ? ;+08 |
} |
; ICMP types & codes |
virtual at 0 |
ICMP_PACKET ICMP_PACKET |
end virtual |
ICMP_ECHOREPLY = 0 ; echo reply message |
ICMP_UNREACH = 3 |
ICMP_UNREACH_NET = 0 ; bad net |
ICMP_UNREACH_HOST = 1 ; bad host |
ICMP_UNREACH_PROTOCOL = 2 ; bad protocol |
ICMP_UNREACH_PORT = 3 ; bad port |
ICMP_UNREACH_NEEDFRAG = 4 ; IP_DF caused drop |
ICMP_UNREACH_SRCFAIL = 5 ; src route failed |
ICMP_UNREACH_NET_UNKNOWN = 6 ; unknown net |
ICMP_UNREACH_HOST_UNKNOWN = 7 ; unknown host |
ICMP_UNREACH_ISOLATED = 8 ; src host isolated |
ICMP_UNREACH_NET_PROHIB = 9 ; prohibited access |
ICMP_UNREACH_HOST_PROHIB = 10 ; ditto |
ICMP_UNREACH_TOSNET = 11 ; bad tos for net |
ICMP_UNREACH_TOSHOST = 12 ; bad tos for host |
ICMP_UNREACH_FILTER_PROHIB = 13 ; admin prohib |
ICMP_UNREACH_HOST_PRECEDENCE = 14 ; host prec vio. |
ICMP_UNREACH_PRECEDENCE_CUTOFF = 15 ; prec cutoff |
; Example: |
; ECHO message format |
ICMP_SOURCEQUENCH = 4 ; Packet lost, slow down |
ICMP_REDIRECT = 5 ; shorter route, codes: |
ICMP_REDIRECT_NET = 0 ; for network |
ICMP_REDIRECT_HOST = 1 ; for host |
ICMP_REDIRECT_TOSNET = 2 ; for tos and net |
ICMP_REDIRECT_TOSHOST = 3 ; for tos and host |
ICMP_ALTHOSTADDR = 6 ; alternate host address |
ICMP_ECHO = 8 ; echo service |
ICMP_ROUTERADVERT = 9 ; router advertisement |
ICMP_ROUTERADVERT_NORMAL = 0 ; normal advertisement |
ICMP_ROUTERADVERT_NOROUTE_COMMON= 16 ; selective routing |
ICMP_ROUTERSOLICIT = 10 ; router solicitation |
ICMP_TIMXCEED = 11 ; time exceeded, code: |
ICMP_TIMXCEED_INTRANS = 0 ; ttl==0 in transit |
ICMP_TIMXCEED_REASS = 1 ; ttl==0 in reass |
ICMP_PARAMPROB = 12 ; ip header bad |
ICMP_PARAMPROB_ERRATPTR = 0 ; error at param ptr |
ICMP_PARAMPROB_OPTABSENT = 1 ; req. opt. absent |
ICMP_PARAMPROB_LENGTH = 2 ; bad length |
ICMP_TSTAMP = 13 ; timestamp request |
ICMP_TSTAMPREPLY = 14 ; timestamp reply |
ICMP_IREQ = 15 ; information request |
ICMP_IREQREPLY = 16 ; information reply |
ICMP_MASKREQ = 17 ; address mask request |
ICMP_MASKREPLY = 18 ; address mask reply |
ICMP_TRACEROUTE = 30 ; traceroute |
ICMP_DATACONVERR = 31 ; data conversion error |
ICMP_MOBILE_REDIRECT = 32 ; mobile host redirect |
ICMP_IPV6_WHEREAREYOU = 33 ; IPv6 where-are-you |
ICMP_IPV6_IAMHERE = 34 ; IPv6 i-am-here |
ICMP_MOBILE_REGREQUEST = 35 ; mobile registration req |
ICMP_MOBILE_REGREPLY = 36 ; mobile registreation reply |
ICMP_SKIP = 39 ; SKIP |
ICMP_PHOTURIS = 40 ; Photuris |
ICMP_PHOTURIS_UNKNOWN_INDEX = 1 ; unknown sec index |
ICMP_PHOTURIS_AUTH_FAILED = 2 ; auth failed |
ICMP_PHOTURIS_DECRYPT_FAILED = 3 ; decrypt failed |
struct ICMP_header |
Type db ? |
Code db ? |
Checksum dw ? |
Identifier dw ? |
SequenceNumber dw ? |
ends |
align 4 |
uglobal |
ICMP_PACKETS_TX rd MAX_NET_DEVICES |
ICMP_PACKETS_RX rd MAX_NET_DEVICES |
endg |
;----------------------------------------------------------------- |
; |
; ICMP_init |
; |
; 0 1 2 3 |
; 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | Type | Code | Checksum | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | Identifier | Sequence Number | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | Data ... |
; +-+-+-+-+- |
; |
;----------------------------------------------------------------- |
macro ICMP_init { |
xor eax, eax |
mov edi, ICMP_PACKETS_TX |
mov ecx, 2*MAX_NET_DEVICES |
rep stosd |
} |
;----------------------------------------------------------------- |
; |
; ICMP types & codes, RFC 792 and FreeBSD's ICMP sources |
; ICMP_input: |
; |
; This procedure will send reply's to ICMP echo's |
; and insert packets into sockets when needed |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; ebx = pointer to device struct |
; ecx = ICMP Packet size |
; esi = ptr to ICMP Packet data |
; edi = ptr to ipv4 source and dest address |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ICMP_input: |
ICMP_ECHOREPLY equ 0 ; echo reply message |
DEBUGF 1,"ICMP_input:\n" |
ICMP_UNREACH equ 3 |
ICMP_UNREACH_NET equ 0 ; bad net |
ICMP_UNREACH_HOST equ 1 ; bad host |
ICMP_UNREACH_PROTOCOL equ 2 ; bad protocol |
ICMP_UNREACH_PORT equ 3 ; bad port |
ICMP_UNREACH_NEEDFRAG equ 4 ; IP_DF caused drop |
ICMP_UNREACH_SRCFAIL equ 5 ; src route failed |
ICMP_UNREACH_NET_UNKNOWN equ 6 ; unknown net |
ICMP_UNREACH_HOST_UNKNOWN equ 7 ; unknown host |
ICMP_UNREACH_ISOLATED equ 8 ; src host isolated |
ICMP_UNREACH_NET_PROHIB equ 9 ; prohibited access |
ICMP_UNREACH_HOST_PROHIB equ 10 ; ditto |
ICMP_UNREACH_TOSNET equ 11 ; bad tos for net |
ICMP_UNREACH_TOSHOST equ 12 ; bad tos for host |
ICMP_UNREACH_FILTER_PROHIB equ 13 ; admin prohib |
ICMP_UNREACH_HOST_PRECEDENCE equ 14 ; host prec vio. |
ICMP_UNREACH_PRECEDENCE_CUTOFF equ 15 ; prec cutoff |
; First, check the checksum (altough some implementations ignore it) |
ICMP_SOURCEQUENCH equ 4 ; packet lost, slow down |
push esi ecx |
push [esi + ICMP_header.Checksum] |
mov [esi + ICMP_header.Checksum], 0 |
xor edx, edx |
call checksum_1 |
call checksum_2 |
pop si |
cmp dx, si |
pop ecx edx |
jne .checksum_mismatch |
ICMP_REDIRECT equ 5 ; shorter route, codes: |
ICMP_REDIRECT_NET equ 0 ; for network |
ICMP_REDIRECT_HOST equ 1 ; for host |
ICMP_REDIRECT_TOSNET equ 2 ; for tos and net |
ICMP_REDIRECT_TOSHOST equ 3 ; for tos and host |
cmp [edx + ICMP_header.Type], ICMP_ECHO ; Is this an echo request? |
jne .check_sockets |
ICMP_ALTHOSTADDR equ 6 ; alternate host address |
ICMP_ECHO equ 8 ; echo service |
ICMP_ROUTERADVERT equ 9 ; router advertisement |
ICMP_ROUTERADVERT_NORMAL equ 0 ; normal advertisement |
ICMP_ROUTERADVERT_NOROUTE_COMMON equ 16 ; selective routing |
; We well re-use the packet so we can create the response as fast as possible |
; Notice: this only works on pure ethernet |
ICMP_ROUTERSOLICIT equ 10 ; router solicitation |
ICMP_TIMXCEED equ 11 ; time exceeded, code: |
ICMP_TIMXCEED_INTRANS equ 0 ; ttl==0 in transit |
ICMP_TIMXCEED_REASS equ 1 ; ttl==0 in reass |
DEBUGF 1,"got echo request\n" |
mov [edx + ICMP_header.Type], ICMP_ECHOREPLY ; Change Packet type to reply |
ICMP_PARAMPROB equ 12 ; ip header bad |
ICMP_PARAMPROB_ERRATPTR equ 0 ; error at param ptr |
ICMP_PARAMPROB_OPTABSENT equ 1 ; req. opt. absent |
ICMP_PARAMPROB_LENGTH equ 2 ; bad length |
mov esi, [esp] ; Start of buffer |
ICMP_TSTAMP equ 13 ; timestamp request |
ICMP_TSTAMPREPLY equ 14 ; timestamp reply |
ICMP_IREQ equ 15 ; information request |
ICMP_IREQREPLY equ 16 ; information reply |
ICMP_MASKREQ equ 17 ; address mask request |
ICMP_MASKREPLY equ 18 ; address mask reply |
ICMP_TRACEROUTE equ 30 ; traceroute |
ICMP_DATACONVERR equ 31 ; data conversion error |
ICMP_MOBILE_REDIRECT equ 32 ; mobile host redirect |
ICMP_IPV6_WHEREAREYOU equ 33 ; IPv6 where-are-you |
ICMP_IPV6_IAMHERE equ 34 ; IPv6 i-am-here |
ICMP_MOBILE_REGREQUEST equ 35 ; mobile registration req |
ICMP_MOBILE_REGREPLY equ 36 ; mobile registreation reply |
ICMP_SKIP equ 39 ; SKIP |
cmp dword[edi + 4], 1 shl 24 + 127 |
je .loopback |
ICMP_PHOTURIS equ 40 ; Photuris |
ICMP_PHOTURIS_UNKNOWN_INDEX equ 1 ; unknown sec index |
ICMP_PHOTURIS_AUTH_FAILED equ 2 ; auth failed |
ICMP_PHOTURIS_DECRYPT_FAILED equ 3 ; decrypt failed |
; Update stats (and validate device ptr) |
call NET_ptr_to_num |
cmp edi,-1 |
je .dump |
inc [ICMP_PACKETS_RX + 4*edi] |
inc [ICMP_PACKETS_TX + 4*edi] |
; exchange dest and source address in IP header |
; exchange dest and source MAC in ETH header |
push dword [esi + ETH_header.DstMAC] |
push dword [esi + ETH_header.SrcMAC] |
pop dword [esi + ETH_header.DstMAC] |
pop dword [esi + ETH_header.SrcMAC] |
push word [esi + ETH_header.DstMAC + 4] |
push word [esi + ETH_header.SrcMAC + 4] |
pop word [esi + ETH_header.DstMAC + 4] |
pop word [esi + ETH_header.SrcMAC + 4] |
add esi, sizeof.ETH_header-2 |
;*************************************************************************** |
; Function |
; icmp_rx [by Johnny_B] |
.loopback: |
add esi, 2 |
push [esi + IPv4_header.SourceAddress] |
push [esi + IPv4_header.DestinationAddress] |
pop [esi + IPv4_header.SourceAddress] |
pop [esi + IPv4_header.DestinationAddress] |
; Recalculate ip header checksum |
movzx ecx, [esi + IPv4_header.VersionAndIHL] ; Calculate IP Header length by using IHL field |
and ecx, 0x0f |
shl cx, 2 |
mov edi, ecx ; IP header length |
mov eax, edx ; ICMP packet start addr |
push esi ; Calculate the IP checksum |
xor edx, edx ; |
call checksum_1 ; |
call checksum_2 ; |
pop esi ; |
mov [esi + IPv4_header.HeaderChecksum], dx ; |
; Recalculate ICMP CheckSum |
movzx ecx, [esi + IPv4_header.TotalLength] ; Find length of IP Packet |
xchg ch, cl ; |
sub ecx, edi ; IP packet length - IP header length = ICMP packet length |
mov esi, eax ; Calculate ICMP checksum |
xor edx, edx ; |
call checksum_1 ; |
call checksum_2 ; |
mov [eax + ICMP_header.Checksum], dx ; |
; Transmit the packet (notice that packet ptr and packet size have been on stack since start of the procedure!) |
call [ebx + NET_DEVICE.transmit] |
ret |
.check_sockets: |
; Look for an open ICMP socket |
mov esi, [edi] ; ipv4 source address |
mov eax, net_sockets |
.try_more: |
; mov , [edx + ICMP_header.Identifier] |
.next_socket: |
mov eax, [eax + SOCKET.NextPtr] |
or eax, eax |
jz .dump |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne .next_socket |
cmp [eax + SOCKET.Protocol], IP_PROTO_ICMP |
jne .next_socket |
cmp [eax + IP_SOCKET.RemoteIP], esi |
jne .next_socket |
; cmp [eax + ICMP_SOCKET.Identifier], |
; jne .next_socket |
; call IPv4_dest_to_dev |
; cmp edi,-1 |
; je .dump |
; inc [ICMP_PACKETS_RX+edi] |
DEBUGF 1,"socket=%x\n", eax |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
mov esi, edx |
jmp SOCKET_input |
.checksum_mismatch: |
DEBUGF 1,"checksum mismatch\n" |
.dump: |
DEBUGF 1,"ICMP_input: dumping\n" |
call kernel_free |
add esp, 4 ; pop (balance stack) |
ret |
;----------------------------------------------------------------- |
; |
; Description |
; ICMP protocol handler |
; This is a kernel function, called by ip_rx |
; ICMP_output |
; |
; IN: |
; buffer_number - # of IP-buffer. This buffer must be reused or marked as empty afterwards |
; IPPacketBase - IP_PACKET base address |
; IPHeaderLength - Header length of IP_PACKET |
; IN: eax = dest ip |
; ebx = source ip |
; ecx = data length |
; dh = type |
; dl = code |
; esi = data offset |
; edi = identifier shl 16 + sequence number |
; |
; OUT: |
; EAX=not defined |
;----------------------------------------------------------------- |
align 4 |
ICMP_output: |
DEBUGF 1,"Creating ICMP Packet\n" |
push esi edi dx |
mov edx, [eax + IP_SOCKET.LocalIP] |
mov eax, [eax + IP_SOCKET.RemoteIP] |
add ecx, sizeof.ICMP_header |
mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL |
call IPv4_output |
jz .exit |
DEBUGF 1,"full icmp packet size: %u\n", edx |
pop word [edi + ICMP_header.Type] ; Write both type and code bytes at once |
pop dword [edi + ICMP_header.Identifier] ; identifier and sequence number |
mov [edi + ICMP_header.Checksum], 0 |
push ebx ecx edx |
mov esi, edi |
xor edx, edx |
call checksum_1 |
call checksum_2 |
mov [edi + ICMP_header.Checksum], dx |
pop edx ecx ebx esi |
sub ecx, sizeof.ICMP_header |
add edi, sizeof.ICMP_header |
push cx |
shr cx, 2 |
rep movsd |
pop cx |
and cx, 3 |
rep movsb |
sub edi, edx ;;; TODO: find a better way to remember start of packet |
push edx edi |
DEBUGF 1,"Sending ICMP Packet\n" |
call [ebx + NET_DEVICE.transmit] |
ret |
.exit: |
DEBUGF 1,"Creating ICMP Packet failed\n" |
add esp, 2*4 + 2 |
ret |
;----------------------------------------------------------------- |
; |
; All used registers will be saved |
; ICMP_output |
; |
;*************************************************************************** |
proc icmp_rx stdcall uses ebx esi edi,\ |
buffer_number:DWORD,IPPacketBase:DWORD,IPHeaderLength:DWORD |
; IN: eax = socket ptr |
; ecx = data length |
; esi = data offset |
; |
;----------------------------------------------------------------- |
align 4 |
ICMP_output_raw: |
mov esi, [IPPacketBase];esi=IP_PACKET base address |
mov edi, esi |
add edi, [IPHeaderLength];edi=ICMP_PACKET base address |
DEBUGF 1,"Creating ICMP Packet for socket %x, data ptr=%x\n", eax, edx |
cmp byte[edi + ICMP_PACKET.Type], ICMP_ECHO; Is this an echo request? discard if not |
jz .icmp_echo |
push edx |
mov eax, [buffer_number] |
call freeBuff |
jmp .exit |
mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL |
mov edx, [eax + IP_SOCKET.LocalIP] |
mov eax, [eax + IP_SOCKET.RemoteIP] |
call IPv4_output |
jz .exit |
.icmp_echo: |
pop esi |
push edx |
push eax |
; swap the source and destination addresses |
mov ecx, [esi + IP_PACKET.DestinationAddress] |
mov ebx, [esi + IP_PACKET.SourceAddress] |
mov [esi + IP_PACKET.DestinationAddress], ebx |
mov [esi + IP_PACKET.SourceAddress], ecx |
push edi ecx |
DEBUGF 1,"copying %u bytes from %x to %x\n", ecx, esi, edi |
rep movsb |
pop ecx edi |
; recalculate the IP header checksum |
mov eax, [IPHeaderLength] |
stdcall checksum_jb, esi, eax;buf_ptr,buf_size |
mov [edi + ICMP_header.Checksum], 0 |
mov byte[esi + IP_PACKET.HeaderChecksum], ah |
mov byte[esi + IP_PACKET.HeaderChecksum + 1], al ; ?? correct byte order? |
mov esi, edi |
xor edx, edx |
call checksum_1 |
call checksum_2 |
mov [edi + ICMP_header.Checksum], dx |
mov byte[edi + ICMP_PACKET.Type], ICMP_ECHOREPLY; change the request to a response |
mov word[edi + ICMP_PACKET.Checksum], 0; clear ICMP checksum prior to re-calc |
DEBUGF 1,"Sending ICMP Packet\n" |
call [ebx + NET_DEVICE.transmit] |
ret |
.exit: |
DEBUGF 1,"Creating ICMP Packet failed\n" |
add esp, 4 |
ret |
; Calculate the length of the ICMP data ( IP payload) |
xor eax, eax |
mov ah, byte[esi + IP_PACKET.TotalLength] |
mov al, byte[esi + IP_PACKET.TotalLength + 1] |
sub ax, word[IPHeaderLength];ax=ICMP-packet length |
stdcall checksum_jb, edi, eax;buf_ptr,buf_size |
mov byte[edi + ICMP_PACKET.Checksum], ah |
mov byte[edi + ICMP_PACKET.Checksum + 1], al |
; Queue packet for transmission |
mov ebx, [buffer_number] |
mov eax, NET1OUT_QUEUE |
call queue |
;----------------------------------------------------------------- |
; |
; ICMP_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;----------------------------------------------------------------- |
align 4 |
ICMP_api: |
.exit: |
movzx eax, bh |
shl eax, 2 |
test bl, bl |
jz .packets_tx ; 0 |
dec bl |
jz .packets_rx ; 1 |
.error: |
mov eax, -1 |
ret |
endp |
.packets_tx: |
mov eax, [ICMP_PACKETS_TX + eax] |
ret |
.packets_rx: |
mov eax, [ICMP_PACKETS_RX + eax] |
ret |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/loopback.inc |
---|
0,0 → 1,126 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; loopback.inc ;; |
;; ;; |
;; LoopBack device for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 2891 $ |
iglobal |
LOOPBACK_DEVICE: |
.type dd NET_TYPE_LOOPBACK |
.mtu dd 4096 |
.name dd .namestr |
.unload dd .dummy_fn |
.reset dd .dummy_fn |
.transmit dd LOOP_input |
.bytes_tx dq 0 |
.bytes_rx dq 0 |
.packets_tx dd 0 |
.packets_rx dd 0 |
.namestr db 'loopback', 0 |
.dummy_fn: |
ret |
endg |
;----------------------------------------------------------------- |
; |
; LOOP_input |
; |
; IN: [esp+4] = Pointer to buffer |
; [esp+8] = size of buffer |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
LOOP_input: |
pop ebx |
pop eax |
pop ecx |
push ebx |
push ecx |
push eax |
DEBUGF 1,"LOOP_input: size=%u\n", ecx |
lea edx, [eax + 2] |
mov ax, word[eax] |
mov ebx, LOOPBACK_DEVICE |
cmp ax, ETHER_IPv4 |
je IPv4_input |
DEBUGF 2,"LOOP_input: Unknown packet type=%x\n", ax |
.dump: |
DEBUGF 2,"LOOP_input: dumping\n" |
call kernel_free |
add esp, 4 |
ret |
;----------------------------------------------------------------- |
; |
; LOOP_output |
; |
; IN: |
; ecx = packet size |
; di = protocol |
; |
; OUT: edi = 0 on error, pointer to buffer otherwise |
; eax = buffer start |
; ebx = to device structure |
; ecx = unchanged (packet size of embedded data) |
; edx = size of complete buffer |
; |
;----------------------------------------------------------------- |
align 4 |
LOOP_output: |
DEBUGF 1,"LOOP_output\n" |
push ecx |
push di |
add ecx, 2 |
cmp ecx, [LOOPBACK_DEVICE.mtu] |
ja .out_of_ram |
stdcall kernel_alloc, ecx |
test eax, eax |
jz .out_of_ram |
mov edi, eax |
pop ax |
stosw |
lea eax, [edi - 2] ; Set eax to buffer start |
pop ecx |
lea edx, [ecx + 2] ; Set edx to complete buffer size |
mov ebx, LOOPBACK_DEVICE |
.done: |
DEBUGF 2,"LOOP_output: ptr=%x size=%u\n", eax, edx |
ret |
.out_of_ram: |
DEBUGF 2,"LOOP_output: failed\n" |
add esp, 2+4 |
sub edi, edi |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/queue.inc |
---|
1,229 → 1,100 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; queue.inc ;; |
;; ;; |
;; QUEUE.INC ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Buffer queue management for Menuet OS TCP/IP Stack ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; |
;; ;; |
;; See file COPYING for details ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 2305 $ |
;******************************************************************* |
; Interface |
; The Queues implemented by these macros form a ring-buffer. |
; The data to these queue's always looks like this: |
; |
; queueInit Configures the queues to empty |
; dequeue Removes a buffer pointer from a queue |
; queue Inserts a buffer pointer into a queue |
; freeBuff Adds the buffer pointer to the list of free buffers |
; queueSize Returns the number of entries in a queue |
; |
; The various defines for queue names can be found in stack.inc |
; |
;******************************************************************* |
; At top, you have the queue struct, wich has the size (number of currently queued packets, read and write pointers. |
; This struct is followed by a number of slots wich you can read and write to using the macros. |
; How these slots look like is up to you to chose, normally they should have at least a pointer to where the real data is. |
; (you can see some examples below) |
;*************************************************************************** |
; Function |
; freeBuff |
; |
; Description |
; Adds a buffer number to the beginning of the free list. |
; buffer number in eax ( ms word zeroed ) |
; all other registers preserved |
; This always works, so no error returned |
;*************************************************************************** |
;uglobal |
; freeBuff_cnt dd ? |
;endg |
freeBuff: |
; inc [freeBuff_cnt] |
; DEBUGF 1, "K : freeBuff (%u)\n", [freeBuff_cnt] |
push ebx |
push ecx |
mov ebx, queues + EMPTY_QUEUE * 2 |
cli ; Ensure that another process does not interfer |
mov cx, [ebx] |
mov [ebx], ax |
mov [queueList + eax * 2], cx |
sti |
pop ecx |
pop ebx |
struct queue |
ret |
size dd ? ; number of queued packets in this queue |
w_ptr dd ? ; current writing pointer in queue |
r_ptr dd ? ; current reading pointer |
ends |
;*************************************************************************** |
; Function |
; queueSize |
; |
; Description |
; Counts the number of entries in a queue |
; queue number in ebx ( ms word zeroed ) |
; Queue size returned in eax |
; This always works, so no error returned |
;*************************************************************************** |
queueSize: |
xor eax, eax |
shl ebx, 1 |
add ebx, queues |
movzx ecx, word [ebx] |
cmp cx, NO_BUFFER |
je qs_exit |
; The following macros share these inputs: |
qs_001: |
inc eax |
shl ecx, 1 |
add ecx, queueList |
movzx ecx, word [ecx] |
cmp cx, NO_BUFFER |
je qs_exit |
jmp qs_001 |
; ptr = pointer to where the queue data is located |
; size = number of slots/entrys in the queue |
; entry_size = size of one slot, in bytes |
; failaddr = the address where macro will jump to when there is no data in the queue |
qs_exit: |
ret |
; additionally, add_to_queue requires you to set esi to the data wich you want to queue |
; get_from_queue on the other hand will return a pointer in esi, to the entry you're interessed in |
; PS: macros WILL destroy ecx and edi |
macro add_to_queue ptr, size, entry_size, failaddr { |
;*************************************************************************** |
; Function |
; queue |
; |
; Description |
; Adds a buffer number to the *end* of a queue |
; This is quite quick because these queues will be short |
; queue number in eax ( ms word zeroed ) |
; buffer number in ebx ( ms word zeroed ) |
; all other registers preserved |
; This always works, so no error returned |
;*************************************************************************** |
;uglobal |
; queue_cnt dd ? |
;endg |
queue: |
; inc [queue_cnt] |
; DEBUGF 1, "K : queue (%u)\n", [queue_cnt] |
push ebx |
shl ebx, 1 |
add ebx, queueList ; eax now holds address of queue entry |
mov [ebx], word NO_BUFFER; This buffer will be the last |
cmp [ptr + queue.size], size ; Check if queue isnt full |
jae failaddr |
cli |
shl eax, 1 |
add eax, queues ; eax now holds address of queue |
movzx ebx, word [eax] |
inc [ptr + queue.size] ; if not full, queue one more |
cmp bx, NO_BUFFER |
jne qu_001 |
mov edi, [ptr + queue.w_ptr] ; Current write pointer (FIFO!) |
mov ecx, entry_size/4 ; Write the queue entry |
rep movsd ; |
pop ebx |
; The list is empty, so add this to the head |
mov [eax], bx |
jmp qu_exit |
lea ecx, [size*entry_size+ptr+sizeof.queue] |
cmp edi, ecx ; entry size |
jb .no_wrap |
qu_001: |
; Find the last entry |
shl ebx, 1 |
add ebx, queueList |
mov eax, ebx |
movzx ebx, word [ebx] |
cmp bx, NO_BUFFER |
jne qu_001 |
sub edi, size*entry_size |
mov ebx, eax |
pop eax |
mov [ebx], ax |
.no_wrap: |
mov [ptr + queue.w_ptr], edi |
qu_exit: |
sti |
ret |
} |
;*************************************************************************** |
; Function |
; dequeue |
; |
; Description |
; removes a buffer number from the head of a queue |
; This is fast, as it unlinks the first entry in the list |
; queue number in eax ( ms word zeroed ) |
; buffer number returned in eax ( ms word zeroed ) |
; all other registers preserved |
; |
;*************************************************************************** |
;uglobal |
; dequeue_cnt dd ? |
;endg |
dequeue: |
push ebx |
shl eax, 1 |
add eax, queues ; eax now holds address of queue |
mov ebx, eax |
cli |
movzx eax, word [eax] |
cmp ax, NO_BUFFER |
je dq_exit |
; inc [dequeue_cnt] |
; DEBUGF 1, "K : dequeue (%u)\n", [dequeue_cnt] |
push eax |
shl eax, 1 |
add eax, queueList ; eax now holds address of queue entry |
mov ax, [eax] |
mov [ebx], ax |
pop eax |
macro get_from_queue ptr, size, entry_size, failaddr { |
dq_exit: |
sti |
pop ebx |
ret |
cmp [ptr + queue.size], 0 ; any packets queued? |
je failaddr |
dec [ptr + queue.size] ; if so, dequeue one |
;*************************************************************************** |
; Function |
; queueInit |
; |
; Description |
; Initialises the queues to empty, and creates the free queue |
; list. |
; |
;*************************************************************************** |
queueInit: |
mov esi, queues |
mov ecx, NUMQUEUES |
mov ax, NO_BUFFER |
mov esi, [ptr + queue.r_ptr] |
push esi |
qi001: |
mov [esi], ax |
inc esi |
inc esi |
loop qi001 |
add esi, entry_size |
mov esi, queues + ( 2 * EMPTY_QUEUE ) |
lea ecx, [size*entry_size+ptr+sizeof.queue] |
cmp esi, ecx ; entry size |
jb .no_wrap |
; Initialise empty queue list |
sub esi, size*entry_size |
xor ax, ax |
mov [esi], ax |
.no_wrap: |
mov dword [ptr + queue.r_ptr], esi |
mov ecx, NUMQUEUEENTRIES - 1 |
mov esi, queueList |
pop esi |
qi002: |
inc ax |
mov [esi], ax |
inc esi |
inc esi |
loop qi002 |
} |
mov ax, NO_BUFFER |
mov [esi], ax |
macro init_queue ptr { |
ret |
mov [ptr + queue.size] , 0 |
lea edi, [ptr + sizeof.queue] |
mov [ptr + queue.w_ptr], edi |
mov [ptr + queue.r_ptr], edi |
} |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/socket.inc |
---|
1,1129 → 1,2284 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; SOCKET.INC ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Sockets constants, structures and functions ;; |
;; Written by hidnplayr@kolibrios.org, ;; |
;; and Clevermouse. ;; |
;; ;; |
;; This file contains the following: ;; |
;; is_localport_unused ;; |
;; get_free_socket ;; |
;; socket_open ;; |
;; socket_open_tcp ;; |
;; socket_close ;; |
;; socket_close_tcp ;; |
;; socket_poll ;; |
;; socket_status ;; |
;; socket_read ;; |
;; socket_write ;; |
;; socket_write_tcp ;; |
;; Based on code by mike.dld ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;; Changes history: ;; |
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;; |
;; 11.11.2006 - [Johnny_B] and [smb] ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 3514 $ |
; socket data structure |
struct SOCKET |
NextPtr dd ? ; pointer to next socket in list |
PrevPtr dd ? ; pointer to previous socket in list |
NextPtr dd ? ; pointer to next socket in list |
Number dd ? ; socket number (unique within single process) |
PID dd ? ; application process id |
LocalIP dd ? ; local IP address |
LocalPort dw ? ; local port |
RemoteIP dd ? ; remote IP address |
RemotePort dw ? ; remote port |
OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state) |
OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state) |
rxDataCount dd ? ; rx data count |
TCBState dd ? ; TCB state |
TCBTimer dd ? ; TCB timer (seconds) |
ISS dd ? ; initial send sequence |
IRS dd ? ; initial receive sequence |
SND_UNA dd ? ; sequence number of unack'ed sent packets |
SND_NXT dd ? ; bext send sequence number to use |
Number dd ? ; socket number |
mutex MUTEX |
PID dd ? ; process ID |
TID dd ? ; thread ID |
Domain dd ? ; INET/LOCAL/.. |
Type dd ? ; RAW/STREAM/DGRAP |
Protocol dd ? ; ICMP/IPv4/ARP/TCP/UDP |
errorcode dd ? |
device dd ? ; driver pointer, socket pointer if it's an LOCAL socket |
options dd ? |
state dd ? |
backlog dw ? ; how many incoming connections that can be queued |
snd_proc dd ? |
rcv_proc dd ? |
ends |
struct IP_SOCKET SOCKET |
LocalIP rd 4 ; network byte order |
RemoteIP rd 4 ; network byte order |
ends |
struct TCP_SOCKET IP_SOCKET |
LocalPort dw ? ; network byte order |
RemotePort dw ? ; network byte order |
t_state dd ? ; TCB state |
t_rxtshift db ? |
rb 3 ; align |
t_rxtcur dd ? |
t_dupacks dd ? |
t_maxseg dd ? |
t_force dd ? |
t_flags dd ? |
;--------------- |
; RFC783 page 21 |
; send sequence |
SND_UNA dd ? ; sequence number of unack'ed sent Packets |
SND_NXT dd ? ; next send sequence number to use |
SND_UP dd ? ; urgent pointer |
SND_WL1 dd ? ; window minus one |
SND_WL2 dd ? ; |
ISS dd ? ; initial send sequence number |
SND_WND dd ? ; send window |
; receive sequence |
RCV_WND dd ? ; receive window |
RCV_NXT dd ? ; next receive sequence number to use |
RCV_WND dd ? ; receive window |
SEG_LEN dd ? ; segment length |
SEG_WND dd ? ; segment window |
wndsizeTimer dd ? ; window size timer |
mutex MUTEX ; lock mutex |
rxData dd ? ; receive data buffer here |
RCV_UP dd ? ; urgent pointer |
IRS dd ? ; initial receive sequence number |
;--------------------- |
; Additional variables |
; receive variables |
RCV_ADV dd ? |
; retransmit variables |
SND_MAX dd ? |
; congestion control |
SND_CWND dd ? |
SND_SSTHRESH dd ? |
;---------------------- |
; Transmit timing stuff |
t_idle dd ? |
t_rtt dd ? |
t_rtseq dd ? |
t_srtt dd ? |
t_rttvar dd ? |
t_rttmin dd ? |
max_sndwnd dd ? |
;----------------- |
; Out-of-band data |
t_oobflags dd ? |
t_iobc dd ? |
t_softerror dd ? |
;--------- |
; RFC 1323 ; the order of next 4 elements may not change |
SND_SCALE db ? |
RCV_SCALE db ? |
requested_s_scale db ? |
request_r_scale db ? |
ts_recent dd ? ; a copy of the most-recent valid timestamp from the other end |
ts_recent_age dd ? |
last_ack_sent dd ? |
;------- |
; Timers |
timer_retransmission dd ? ; rexmt |
timer_persist dd ? |
timer_keepalive dd ? ; keepalive/syn timeout |
timer_timed_wait dd ? ; also used as 2msl timer |
; extra |
ts_ecr dd ? ; timestamp echo reply |
ts_val dd ? |
seg_next dd ? ; re-assembly queue |
temp_bits db ? |
rb 3 ; align |
ends |
; TCP opening modes |
SOCKET_PASSIVE = 0 |
SOCKET_ACTIVE = 1 |
struct UDP_SOCKET IP_SOCKET |
; socket types |
SOCK_STREAM = 1 |
SOCK_DGRAM = 2 |
LocalPort dw ? ; network byte order |
RemotePort dw ? ; network byte order |
firstpacket db ? |
; pointer to bitmap of free ports (1=free, 0=used) |
ends |
struct ICMP_SOCKET IP_SOCKET |
Identifier dw ? |
ends |
struct RING_BUFFER |
mutex MUTEX |
start_ptr dd ? ; Pointer to start of buffer |
end_ptr dd ? ; pointer to end of buffer |
read_ptr dd ? ; Read pointer |
write_ptr dd ? ; Write pointer |
size dd ? ; Number of bytes buffered |
ends |
struct STREAM_SOCKET TCP_SOCKET |
rcv RING_BUFFER |
snd RING_BUFFER |
ends |
struct socket_queue_entry |
data_ptr dd ? |
buf_ptr dd ? |
data_size dd ? |
ends |
SOCKETBUFFSIZE = 4096 ; in bytes |
SOCKET_QUEUE_SIZE = 10 ; maximum number of incoming packets queued for 1 socket |
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start |
SOCKET_QUEUE_LOCATION = (SOCKETBUFFSIZE - SOCKET_QUEUE_SIZE*sizeof.socket_queue_entry - sizeof.queue) |
uglobal |
align 4 |
network_free_ports dd ? |
net_sockets rd 4 |
last_socket_num dd ? |
last_UDP_port dw ? ; These values give the number of the last used ephemeral port |
last_TCP_port dw ? ; |
endg |
iglobal |
;----------------------------------------------------------------- |
; |
; SOCKET_init |
; |
;----------------------------------------------------------------- |
macro SOCKET_init { |
xor eax, eax |
mov edi, net_sockets |
mov ecx, 5 |
rep stosd |
@@: |
pseudo_random eax |
cmp ax, MIN_EPHEMERAL_PORT |
jb @r |
cmp ax, MAX_EPHEMERAL_PORT |
ja @r |
xchg al, ah |
mov [last_UDP_port], ax |
@@: |
pseudo_random eax |
cmp ax, MIN_EPHEMERAL_PORT |
jb @r |
cmp ax, MAX_EPHEMERAL_PORT |
ja @r |
xchg al, ah |
mov [last_TCP_port], ax |
} |
;----------------------------------------------------------------- |
; |
; Socket API (function 74) |
; |
;----------------------------------------------------------------- |
align 4 |
network_free_hint dd 1024/8 |
endg |
sys_socket: |
;; 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). |
cmp ebx, 255 |
jz SOCKET_debug |
cmp ebx, .number |
ja s_error |
jmp dword [.table + 4*ebx] |
.table: |
dd SOCKET_open ; 0 |
dd SOCKET_close ; 1 |
dd SOCKET_bind ; 2 |
dd SOCKET_listen ; 3 |
dd SOCKET_connect ; 4 |
dd SOCKET_accept ; 5 |
dd SOCKET_send ; 6 |
dd SOCKET_receive ; 7 |
dd SOCKET_set_opt ; 8 |
dd SOCKET_get_opt ; 9 |
dd SOCKET_pair ; 10 |
.number = ($ - .table) / 4 - 1 |
s_error: |
DEBUGF 2,"SOCKET: error\n" |
mov dword [esp+32], -1 |
ret |
;----------------------------------------------------------------- |
; |
; @return socket structure address in EAX |
;; |
proc net_socket_alloc stdcall uses ebx ecx edx edi |
stdcall kernel_alloc, SOCKETBUFFSIZE |
DEBUGF 1, "K : net_socket_alloc (0x%x)\n", eax |
; check if we can allocate needed amount of memory |
or eax, eax |
jz .exit |
; SOCKET_open |
; |
; IN: domain in ecx |
; type in edx |
; protocol in esi |
; OUT: eax is socket num, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_open: |
; zero-initialize allocated memory |
DEBUGF 2,"SOCKET_open: domain=%u type=%u protocol=%x ", ecx, edx, esi |
push ecx edx esi |
call SOCKET_alloc |
pop esi edx ecx |
jz s_error |
mov [esp+32], edi ; return socketnumber |
DEBUGF 2,"socknum=%u\n", edi |
; push edx |
; and edx, SO_NONBLOCK |
or [eax + SOCKET.options], SO_NONBLOCK ;edx |
; pop edx |
; and edx, not SO_NONBLOCK |
mov [eax + SOCKET.Domain], ecx |
mov [eax + SOCKET.Type], edx |
mov [eax + SOCKET.Protocol], esi |
cmp ecx, AF_INET4 |
jne .no_inet4 |
cmp edx, SOCK_DGRAM |
je .udp |
cmp edx, SOCK_STREAM |
je .tcp |
cmp edx, SOCK_RAW |
je .raw |
.no_inet4: |
cmp ecx, AF_PPP |
jne .no_ppp |
cmp esi, PPP_PROTO_ETHERNET |
je .pppoe |
.no_ppp: |
DEBUGF 2,"Unknown socket family/protocol\n" |
ret |
align 4 |
.raw: |
test esi, esi ; IP_PROTO_IP |
jz .ip |
cmp esi, IP_PROTO_ICMP |
je .icmp |
cmp esi, IP_PROTO_UDP |
je .udp |
cmp esi, IP_PROTO_TCP |
je .tcp |
ret |
align 4 |
.udp: |
mov [eax + SOCKET.Protocol], IP_PROTO_UDP |
mov [eax + SOCKET.snd_proc], SOCKET_send_udp |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram |
ret |
align 4 |
.tcp: |
mov [eax + SOCKET.Protocol], IP_PROTO_TCP |
mov [eax + SOCKET.snd_proc], SOCKET_send_tcp |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_stream |
TCP_init_socket eax |
ret |
align 4 |
.ip: |
mov [eax + SOCKET.snd_proc], SOCKET_send_ip |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram |
ret |
align 4 |
.icmp: |
mov [eax + SOCKET.snd_proc], SOCKET_send_icmp |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram |
ret |
align 4 |
.pppoe: |
push eax |
mov edi, eax |
mov ecx, SOCKETBUFFSIZE / 4 |
cld |
xor eax, eax |
rep stosd |
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue |
pop eax |
mov [eax + SOCKET.snd_proc], SOCKET_send_pppoe |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_bind |
; |
; IN: socket number in ecx |
; pointer to sockaddr struct in edx |
; length of that struct in esi |
; OUT: 0 on success |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_bind: |
DEBUGF 2,"SOCKET_bind: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi |
call SOCKET_num_to_ptr |
jz s_error |
cmp esi, 2 |
jb s_error |
cmp word [edx], AF_INET4 |
je .af_inet4 |
cmp word [edx], AF_LOCAL |
je .af_local |
jmp s_error |
.af_local: |
; TODO: write code here |
mov dword [esp+32], 0 |
ret |
.af_inet4: |
cmp esi, 6 |
jb s_error |
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP |
je .udp |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
je .tcp |
jmp s_error |
.tcp: |
.udp: |
mov ebx, [edx + 4] ; First, fill in the IP |
test ebx, ebx ; If IP is 0, use default |
jnz @f |
mov ebx, [NET_DEFAULT] |
mov ebx, [IP_LIST + 4*ebx] |
@@: |
mov [eax + IP_SOCKET.LocalIP], ebx |
mov bx, [edx + 2] ; Now fill in the local port if it's still available |
call SOCKET_check_port |
jz s_error ; ZF is set by socket_check_port, on error |
DEBUGF 1,"SOCKET_bind: local ip=%u.%u.%u.%u\n",\ |
[eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\ |
[eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1 |
mov dword [esp+32], 0 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_connect |
; |
; IN: socket number in ecx |
; pointer to sockaddr struct in edx |
; length of that struct in esi |
; OUT: 0 on success |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_connect: |
DEBUGF 2,"SOCKET_connect: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi |
call SOCKET_num_to_ptr |
jz s_error |
cmp esi, 8 |
jb s_error |
cmp word [edx], AF_INET4 |
je .af_inet4 |
jmp s_error |
.af_inet4: |
cmp [eax + IP_SOCKET.LocalIP], 0 |
jne @f |
push [IP_LIST] ; FIXME |
pop [eax + IP_SOCKET.LocalIP] |
@@: |
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP |
je .udp |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
je .tcp |
cmp [eax + SOCKET.Protocol], IP_PROTO_IP |
je .ip |
cmp [eax + SOCKET.Protocol], IP_PROTO_ICMP |
je .ip |
jmp s_error |
align 4 |
.udp: |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
pushw [edx + 2] |
pop [eax + UDP_SOCKET.RemotePort] |
pushd [edx + 4] |
pop [eax + IP_SOCKET.RemoteIP] |
cmp [eax + UDP_SOCKET.LocalPort], 0 |
jne @f |
call SOCKET_find_port |
@@: |
mov [eax + UDP_SOCKET.firstpacket], 0 |
push eax |
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue |
pop eax |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
mov dword [esp+32], 0 |
ret |
align 4 |
.tcp: |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
pushw [edx + 2] |
pop [eax + TCP_SOCKET.RemotePort] |
pushd [edx + 4] |
pop [eax + IP_SOCKET.RemoteIP] |
cmp [eax + TCP_SOCKET.LocalPort], 0 |
jne @f |
call SOCKET_find_port |
@@: |
mov [eax + TCP_SOCKET.timer_persist], 0 |
mov [eax + TCP_SOCKET.t_state], TCPS_SYN_SENT |
push [TCP_sequence_num] |
add [TCP_sequence_num], 6400 |
pop [eax + TCP_SOCKET.ISS] |
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_init |
TCP_sendseqinit eax |
; mov [ebx + TCP_SOCKET.timer_retransmission], ;; todo: create macro to set retransmission timer |
mov ebx, eax |
lea ecx, [eax+SOCKET.mutex] |
call mutex_init |
lea eax, [ebx + STREAM_SOCKET.snd] |
call SOCKET_ring_create |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_create |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
popa |
mov eax, ebx |
call TCP_output |
; add socket to the list by changing pointers |
mov ebx, net_sockets |
push [ebx + SOCKET.NextPtr] |
mov [ebx + SOCKET.NextPtr], eax |
mov [eax + SOCKET.PrevPtr], ebx |
pop ebx |
mov [eax + SOCKET.NextPtr], ebx |
or ebx, ebx |
;;; TODO: wait for successfull connection if blocking socket |
mov dword [esp+32], 0 |
ret |
align 4 |
.ip: |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
pushd [edx + 4] |
pop [eax + IP_SOCKET.RemoteIP] |
push eax |
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue |
pop eax |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
mov dword [esp+32], 0 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_listen |
; |
; IN: socket number in ecx |
; backlog in edx |
; OUT: eax is socket num, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_listen: |
DEBUGF 2,"SOCKET_listen: socknum=%u backlog=%u\n", ecx, edx |
call SOCKET_num_to_ptr |
jz s_error |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne s_error |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
jne s_error |
cmp [eax + TCP_SOCKET.LocalPort], 0 |
je s_error |
cmp [eax + IP_SOCKET.LocalIP], 0 |
jne @f |
push [IP_LIST] |
pop [eax + IP_SOCKET.LocalIP] |
@@: |
cmp edx, MAX_backlog |
jbe @f |
mov edx, MAX_backlog |
@@: |
mov [eax + SOCKET.backlog], dx |
or [eax + SOCKET.options], SO_ACCEPTCON |
mov [eax + TCP_SOCKET.t_state], TCPS_LISTEN |
mov [eax + TCP_SOCKET.timer_keepalive], 0 ; disable keepalive timer |
push eax |
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up sockets queue |
pop eax |
mov dword [esp+32], 0 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_accept |
; |
; IN: socket number in ecx |
; addr in edx |
; addrlen in esi |
; OUT: eax is socket num, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_accept: |
DEBUGF 2,"SOCKET_accept: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi |
call SOCKET_num_to_ptr |
jz s_error |
test [eax + SOCKET.options], SO_ACCEPTCON |
jz s_error |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne s_error |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
jne s_error |
.loop: |
get_from_queue (eax + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .block |
; Ok, we got a socket ptr |
mov eax, [esi] |
; Change thread ID to that of the current thread |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.TID], ebx |
; Convert it to a socket number |
call SOCKET_ptr_to_num |
jz s_error |
; and return it to caller |
mov [esp+32], eax |
ret |
.block: |
test [eax + SOCKET.options], SO_NONBLOCK |
jnz s_error |
call SOCKET_block |
jmp .loop |
;----------------------------------------------------------------- |
; |
; SOCKET_close |
; |
; IN: socket number in ecx |
; OUT: eax is socket num, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_close: |
DEBUGF 2,"SOCKET_close: socknum=%u\n", ecx |
call SOCKET_num_to_ptr |
jz s_error |
mov dword [esp+32], 0 ; The socket exists, so we will succeed in closing it. |
.socket: |
or [eax + SOCKET.options], SO_NONBLOCK ; Mark the socket as non blocking, we dont want it to block any longer! |
test [eax + SOCKET.state], SS_BLOCKED ; Is the socket still in blocked state? |
jz @f |
mov [ebx + SOCKET.PrevPtr], eax |
call SOCKET_notify.unblock ; Unblock it. |
@@: |
@@: ; set socket owner PID to the one of calling process |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne .free |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
je .tcp |
.free: |
call SOCKET_free |
ret |
.tcp: |
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED ; state must be LISTEN, SYN_SENT or CLOSED |
jb .free |
call TCP_usrclosed |
call TCP_output ;;;; Fixme: is this nescessary?? |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_receive |
; |
; IN: socket number in ecx |
; addr to buffer in edx |
; length of buffer in esi |
; flags in edi |
; OUT: eax is number of bytes copied, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_receive: |
DEBUGF 2,"SOCKET_receive: socknum=%u bufaddr=%x buflength=%u flags=%x\n", ecx, edx, esi, edi |
call SOCKET_num_to_ptr |
jz s_error |
jmp [eax + SOCKET.rcv_proc] |
align 4 |
SOCKET_receive_dgram: |
DEBUGF 1,"SOCKET_receive: DGRAM\n" |
mov ebx, esi |
mov edi, edx ; addr to buffer |
.loop: |
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, .block ; destroys esi and ecx |
mov ecx, [esi + socket_queue_entry.data_size] |
DEBUGF 1,"SOCKET_receive: %u bytes data\n", ecx |
cmp ecx, ebx |
ja .too_small |
push [esi + socket_queue_entry.buf_ptr] ; save the buffer addr so we can clear it later |
mov esi, [esi + socket_queue_entry.data_ptr] |
DEBUGF 1,"SOCKET_receive: Source buffer=%x real addr=%x\n", [esp], esi |
mov [esp+32+4], ecx ; return number of bytes copied |
; copy the data |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: |
shr ecx, 1 |
jnc .nw |
movsw |
.nw: |
test ecx, ecx |
jz .nd |
rep movsd |
.nd: |
call kernel_free ; remove the packet |
ret |
.too_small: |
DEBUGF 2,"SOCKET_receive: Buffer too small\n" |
jmp s_error |
.block: |
test [eax + SOCKET.options], SO_NONBLOCK |
jnz s_error |
call SOCKET_block |
jmp .loop |
align 4 |
SOCKET_receive_local: |
; does this socket have a PID yet? |
cmp [eax + SOCKET.PID], 0 |
jne @f |
; Change PID to that of current process |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.PID], ebx |
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :( |
@@: |
; find first free socket number and use it |
;mov edx, ebx |
mov ebx, net_sockets |
xor ecx, ecx |
.next_socket_number: |
inc ecx |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .last_socket_number |
cmp [ebx + SOCKET.Number], ecx |
jne .next_socket |
;cmp [ebx + SOCKET.PID], edx |
;jne .next_socket |
mov ebx, net_sockets |
jmp .next_socket_number |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_stream |
.last_socket_number: |
mov [eax + SOCKET.Number], ecx |
align 4 |
SOCKET_receive_stream: |
.exit: |
DEBUGF 1,"SOCKET_receive: STREAM\n" |
mov ebx, edi |
mov ecx, esi |
mov edi, edx |
xor edx, edx |
test ebx, MSG_DONTWAIT |
jnz .dontwait |
.loop: |
cmp [eax + STREAM_SOCKET.rcv + RING_BUFFER.size], 0 |
je .block |
.dontwait: |
test ebx, MSG_PEEK |
jnz .peek |
add eax, STREAM_SOCKET.rcv |
call SOCKET_ring_read |
call SOCKET_ring_free |
mov [esp+32], ecx ; return number of bytes copied |
ret |
endp |
;; Free socket data memory and pop socket off the list |
.peek: |
mov ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size] |
mov [esp+32], ecx ; return number of bytes available |
ret |
.block: |
test [eax + SOCKET.options], SO_NONBLOCK |
jnz .return0 |
call SOCKET_block |
jmp .loop |
.return0: |
xor ecx, ecx |
mov [esp+32], ecx |
ret |
;----------------------------------------------------------------- |
; |
; @param sockAddr is a socket structure address |
;; |
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD |
mov eax, [sockAddr] |
DEBUGF 1, "K : net_socket_free (0x%x)\n", eax |
; check if we got something similar to socket structure address |
or eax, eax |
jz .error |
; SOCKET_send |
; |
; |
; IN: socket number in ecx |
; pointer to data in edx |
; datalength in esi |
; flags in edi |
; OUT: -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_send: |
; make sure sockAddr is one of the socket addresses in the list |
mov ebx, net_sockets |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .error |
cmp ebx, eax |
jne .next_socket |
;cmp [ebx + SOCKET.PID], ecx |
;jne .next_socket |
DEBUGF 2,"SOCKET_send: socknum=%u data ptr=%x length=%u flags=%x\n", ecx, edx, esi, edi |
; okay, we found the correct one |
; mark local port as unused |
movzx ebx, [eax + SOCKET.LocalPort] |
call SOCKET_num_to_ptr |
jz s_error |
mov ecx, esi |
mov esi, edx |
jmp [eax + SOCKET.snd_proc] |
align 4 |
SOCKET_send_udp: |
DEBUGF 1,"SOCKET_send: UDP\n" |
mov [esp+32], ecx |
call UDP_output |
cmp eax, -1 |
je s_error |
ret |
align 4 |
SOCKET_send_tcp: |
DEBUGF 1,"SOCKET_send: TCP\n" |
push eax |
mov eax, [network_free_ports] |
xchg bl, bh |
lock bts [eax], ebx |
add eax, STREAM_SOCKET.snd |
call SOCKET_ring_write |
pop eax |
; remove it from the list first, changing pointers |
mov ebx, [eax + SOCKET.NextPtr] |
mov eax, [eax + SOCKET.PrevPtr] |
mov [eax + SOCKET.NextPtr], ebx |
or ebx, ebx |
jz @f |
mov [ebx + SOCKET.PrevPtr], eax |
@@: ; and finally free the memory structure used |
stdcall kernel_free, [sockAddr] |
mov [esp+32], ecx |
call TCP_output |
ret |
.error: |
DEBUGF 1, "K : failed\n" |
align 4 |
SOCKET_send_ip: |
DEBUGF 1,"SOCKET_send: IPv4\n" |
mov [esp+32], ecx |
call IPv4_output_raw |
cmp eax, -1 |
je s_error |
ret |
endp |
;; Get socket structure address by its number |
; Scan through sockets list to find the socket with specified number. |
; This proc uses SOCKET.PID indirectly to check if socket is owned by |
; calling process. |
align 4 |
SOCKET_send_icmp: |
DEBUGF 1,"SOCKET_send: ICMP\n" |
mov [esp+32], ecx |
call ICMP_output_raw |
cmp eax, -1 |
je s_error |
ret |
align 4 |
SOCKET_send_pppoe: |
DEBUGF 1,"SOCKET_send: PPPoE\n" |
mov [esp+32], ecx |
mov ebx, [eax + SOCKET.device] |
call PPPoE_discovery_output |
cmp eax, -1 |
je s_error |
ret |
align 4 |
SOCKET_send_local: |
; does this socket have a PID yet? |
cmp [eax + SOCKET.PID], 0 |
jne @f |
; Change PID to that of current process |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.PID], ebx |
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :( |
@@: |
mov [eax + SOCKET.snd_proc], SOCKET_send_local_ |
align 4 |
SOCKET_send_local_: |
DEBUGF 1,"SOCKET_send: LOCAL\n" |
; get the other side's socket and check if it still exists |
mov eax, [eax + SOCKET.device] |
call SOCKET_check |
jz s_error |
; allright, shove in the data! |
push eax |
add eax, STREAM_SOCKET.rcv |
call SOCKET_ring_write |
pop eax |
; return the number of written bytes (or errorcode) to application |
mov [esp+32], ecx |
; and notify the other end |
call SOCKET_notify |
ret |
;----------------------------------------------------------------- |
; |
; @param sockNum is a socket number |
; @return socket structure address or 0 (not found) in EAX |
;; |
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD |
mov eax, [sockNum] |
; check if we got something similar to socket number |
or eax, eax |
jz .error |
; SOCKET_get_options |
; |
; IN: ecx = socket number |
; edx = pointer to the options: |
; dd level, optname, optval, optlen |
; OUT: -1 on error |
; |
; At moment, uses only pseudo-optname -2 for get last_ack_number for TCP. |
; TODO: find best way to notify that send()'ed data were acknowledged |
; Also pseudo-optname -3 is valid and returns socket state, one of TCPS_*. |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_get_opt: |
; scan through sockets list |
mov ebx, net_sockets |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .error |
cmp [ebx + SOCKET.Number], eax |
jne .next_socket |
;cmp [ebx + SOCKET.PID], ecx |
;jne .next_socket |
DEBUGF 2,"SOCKET_get_opt\n" |
; okay, we found the correct one |
mov eax, ebx |
call SOCKET_num_to_ptr |
jz s_error |
cmp dword [edx], IP_PROTO_TCP |
jne s_error |
cmp dword [edx+4], -2 |
je @f |
cmp dword [edx+4], -3 |
jne s_error |
@@: |
; mov eax, [edx+12] |
; test eax, eax |
; jz .fail |
; cmp dword [eax], 4 |
; mov dword [eax], 4 |
; jb .fail |
; stdcall net_socket_num_to_addr, ecx |
; test eax, eax |
; jz .fail |
; ; todo: check that eax is really TCP socket |
; mov ecx, [eax + TCP_SOCKET.last_ack_number] |
; cmp dword [edx+4], -2 |
; jz @f |
; mov ecx, [eax + TCP_SOCKET.state] |
@@: |
mov eax, [edx+8] |
test eax, eax |
jz @f |
mov [eax], ecx |
@@: |
mov dword [esp+32], 0 |
ret |
.error: |
xor eax, eax |
;----------------------------------------------------------------- |
; |
; SOCKET_set_options |
; |
; IN: ecx = socket number |
; edx = pointer to the options: |
; dd level, optname, optlen, optval |
; OUT: -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_set_opt: |
DEBUGF 2,"SOCKET_set_opt\n" |
call SOCKET_num_to_ptr |
jz s_error |
cmp dword [edx], SOL_SOCKET |
jne s_error |
cmp dword [edx+4], SO_BINDTODEVICE |
je .bind |
cmp dword [edx+4], SO_BLOCK |
je .block |
jmp s_error |
.bind: |
cmp dword [edx+8], 0 |
je .unbind |
movzx edx, byte [edx + 9] |
cmp edx, MAX_NET_DEVICES |
ja s_error |
mov edx, [NET_DRV_LIST + 4*edx] |
test edx, edx |
jz s_error |
mov [eax + SOCKET.device], edx |
DEBUGF 1,"SOCKET_set_opt: Bound socket %x to device %x\n",eax, edx |
mov dword [esp+32], 0 ; success! |
ret |
endp |
;; Get socket number by its structure address |
; Scan through sockets list to find the socket with specified address. |
; This proc uses SOCKET.PID indirectly to check if socket is owned by |
; calling process. |
.unbind: |
mov [eax + SOCKET.device], 0 |
mov dword [esp+32], 0 ; success! |
ret |
.block: |
cmp dword [edx+8], 0 |
je .unblock |
and [eax + SOCKET.options], not SO_NONBLOCK |
mov dword [esp+32], 0 ; success! |
ret |
.unblock: |
or [eax + SOCKET.options], SO_NONBLOCK |
mov dword [esp+32], 0 ; success! |
ret |
;----------------------------------------------------------------- |
; |
; @param sockAddr is a socket structure address |
; @return socket number (SOCKET.Number) or 0 (not found) in EAX |
;; |
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD |
mov eax, [sockAddr] |
; check if we got something similar to socket structure address |
or eax, eax |
jz .error |
; SOCKET_pair |
; |
; Allocates a pair of linked LOCAL domain sockets |
; |
; IN: / |
; OUT: eax is socket1 num, -1 on error |
; ebx is socket2 num |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_pair: |
; scan through sockets list |
mov ebx, net_sockets |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
DEBUGF 2,"SOCKET_pair\n" |
call SOCKET_alloc |
jz s_error |
mov [esp+32], edi ; application's eax |
mov [eax + SOCKET.Domain], AF_LOCAL |
mov [eax + SOCKET.Type], SOCK_STREAM |
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME |
mov [eax + SOCKET.snd_proc], SOCKET_send_local |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_local |
mov [eax + SOCKET.PID], 0 |
mov ebx, eax |
call SOCKET_alloc |
jz .error |
cmp ebx, eax |
jne .next_socket |
;cmp [ebx + SOCKET.PID], ecx |
;jne .next_socket |
mov [esp+24], edi ; application's ebx |
; okay, we found the correct one |
mov eax, [ebx + SOCKET.Number] |
mov [eax + SOCKET.Domain], AF_LOCAL |
mov [eax + SOCKET.Type], SOCK_STREAM |
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME |
mov [eax + SOCKET.snd_proc], SOCKET_send_local |
mov [eax + SOCKET.rcv_proc], SOCKET_receive_local |
mov [eax + SOCKET.PID], 0 |
; Link the two sockets to eachother |
mov [eax + SOCKET.device], ebx |
mov [ebx + SOCKET.device], eax |
lea eax, [eax + STREAM_SOCKET.rcv] |
call SOCKET_ring_create |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_create |
pop eax |
ret |
.error: |
xor eax, eax |
ret |
endp |
mov eax, ebx |
call SOCKET_free |
jmp s_error |
;; [53.9] Check if local port is used by any socket in the system. |
; Scan through sockets list, checking SOCKET.LocalPort. |
; Useful when you want a to generate a unique local port number. |
; This proc doesn't guarantee that after calling it and trying to use |
; the port reported being free in calls to socket_open/socket_open_tcp it'll |
; still be free or otherwise it'll still be used if reported being in use. |
;----------------------------------------------------------------- |
; |
; @param BX is a port number |
; @return 1 (port is free) or 0 (port is in use) in EAX |
;; |
proc is_localport_unused stdcall |
movzx ebx, bx |
mov eax, [network_free_ports] |
bt [eax], ebx |
setc al |
movzx eax, al |
; SOCKET_debug |
; |
; Copies socket variables to application buffer |
; |
; IN: ecx = socket number |
; edx = pointer to buffer |
; |
; OUT: -1 on error |
;----------------------------------------------------------------- |
align 4 |
SOCKET_debug: |
DEBUGF 1,"SOCKET_debug\n" |
mov edi, edx |
test ecx, ecx |
jz .returnall |
call SOCKET_num_to_ptr |
jz s_error |
mov esi, eax |
mov ecx, SOCKETBUFFSIZE/4 |
rep movsd |
mov dword [esp+32], 0 |
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. |
.returnall: |
mov ebx, net_sockets |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
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 |
jz .done |
mov eax, [ebx + SOCKET.Number] |
stosd |
jmp .next_socket |
.done: |
xor eax, eax |
stosd |
mov dword [esp+32], 0 |
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 |
; @param CX is remote port number |
; @param EDX is remote IP address |
; @return socket number or -1 (error) in EAX |
;; |
proc socket_open stdcall |
call net_socket_alloc |
or eax, eax |
jz .error |
; SOCKET_find_port |
; |
; Fills in the local port number for TCP and UDP sockets |
; This procedure always works because the number of sockets is |
; limited to a smaller number then the number of possible ports |
; |
; IN: eax = socket pointer |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_find_port: |
DEBUGF 1, "K : socket_open (0x%x)\n", eax |
DEBUGF 2,"SOCKET_find_port\n" |
push eax |
push ebx esi ecx |
call set_local_port |
jc .error.free |
xchg ch, cl |
mov [eax + SOCKET.RemotePort], cx |
mov ebx, [stack_ip] |
mov [eax + SOCKET.LocalIP], ebx |
mov [eax + SOCKET.RemoteIP], edx |
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP |
je .udp |
;pop eax ; Get the socket number back, so we can return it |
stdcall net_socket_addr_to_num |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
je .tcp |
pop ecx esi ebx |
ret |
.error.free: |
stdcall net_socket_free;, eax |
.udp: |
mov bx, [last_UDP_port] |
call .findit |
mov [last_UDP_port], bx |
.error: |
DEBUGF 1, "K : socket_open (fail)\n" |
or eax, -1 |
pop ecx esi ebx |
ret |
endp |
;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way) |
.tcp: |
mov bx, [last_TCP_port] |
call .findit |
mov [last_TCP_port], bx |
pop ecx esi ebx |
ret |
.restart: |
mov bx, MIN_EPHEMERAL_PORT_N |
.findit: |
cmp bx, MAX_EPHEMERAL_PORT_N |
je .restart |
add bh, 1 |
adc bl, 0 |
call SOCKET_check_port |
jz .findit |
ret |
;----------------------------------------------------------------- |
; |
; @param BX is local port number |
; @param CX is remote port number |
; @param EDX is remote IP address |
; @param ESI is open mode (SOCKET_ACTIVE, SOCKET_PASSIVE) |
; @return socket number or -1 (error) in EAX |
;; |
proc socket_open_tcp stdcall |
local sockAddr dd ? |
; SOCKET_check_port (to be used with AF_INET only!) |
; |
; Checks if a local port number is unused |
; If the proposed port number is unused, it is filled in in the socket structure |
; |
; IN: eax = socket ptr (to find out if its a TCP/UDP socket) |
; bx = proposed socket number (network byte order) |
; |
; OUT: ZF = set on error |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_check_port: |
cmp esi, SOCKET_PASSIVE |
jne .skip_port_check |
DEBUGF 2,"SOCKET_check_port: " |
push ebx |
mov eax, ebx |
xchg al, ah |
mov ebx, net_sockets |
mov ecx, [eax + SOCKET.Protocol] |
mov edx, [eax + IP_SOCKET.LocalIP] |
mov esi, net_sockets |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .last_socket |
cmp [ebx + SOCKET.TCBState], TCB_LISTEN |
mov esi, [esi + SOCKET.NextPtr] |
or esi, esi |
jz .port_ok |
cmp [esi + SOCKET.Protocol], ecx |
jne .next_socket |
cmp [ebx + SOCKET.LocalPort], ax |
cmp [esi + IP_SOCKET.LocalIP], edx |
jne .next_socket |
xchg al, ah |
DEBUGF 1, "K : error: port %u is listened by 0x%x\n", ax, ebx |
pop ebx |
jmp .error |
cmp [esi + UDP_SOCKET.LocalPort], bx |
jne .next_socket |
.last_socket: |
pop ebx |
DEBUGF 2,"local port %x already in use\n", bx ; FIXME: find a way to print big endian values with debugf |
ret |
.skip_port_check: |
call net_socket_alloc |
or eax, eax |
jz .error |
.port_ok: |
DEBUGF 2,"local port %x is free\n", bx ; FIXME: find a way to print big endian values with debugf |
mov [eax + UDP_SOCKET.LocalPort], bx |
or bx, bx ; clear the zero-flag |
ret |
DEBUGF 1, "K : socket_open_tcp (0x%x)\n", eax |
mov [sockAddr], eax |
; TODO - check this works! |
;mov [eax + SOCKET.wndsizeTimer], 0 ; Reset the window timer. |
;----------------------------------------------------------------- |
; |
; SOCKET_input |
; |
; Updates a (stateless) socket with received data |
; |
; Note: the mutex should already be set ! |
; |
; IN: eax = socket ptr |
; ecx = data size |
; esi = ptr to data |
; [esp] = ptr to buf |
; [esp + 4] = buf size |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_input: |
call set_local_port |
jc .error.free |
xchg ch, cl |
mov [eax + SOCKET.RemotePort], cx |
mov [eax + SOCKET.OrigRemotePort], cx |
mov ebx, [stack_ip] |
mov [eax + SOCKET.LocalIP], ebx |
mov [eax + SOCKET.RemoteIP], edx |
mov [eax + SOCKET.OrigRemoteIP], edx |
DEBUGF 2,"SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx |
mov ebx, TCB_LISTEN |
cmp esi, SOCKET_PASSIVE |
je @f |
mov ebx, TCB_SYN_SENT |
@@: |
mov [eax + SOCKET.TCBState], ebx ; Indicate the state of the TCB |
mov [esp+4], ecx |
push esi |
mov esi, esp |
cmp ebx, TCB_LISTEN |
je .exit |
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, SOCKET_input.full |
; Now, if we are in active mode, then we have to send a SYN to the specified remote port |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
DEBUGF 1,"SOCKET_input: success\n" |
add esp, sizeof.socket_queue_entry |
push eax |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
popa |
mov bl, TH_SYN |
xor ecx, ecx |
stdcall build_tcp_packet, [sockAddr] |
jmp SOCKET_notify |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.full: |
DEBUGF 2,"SOCKET_input: socket %x is full!\n", eax |
.not_local: |
; Send it. |
pop ebx |
call queue |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
popa |
mov esi, [sockAddr] |
call kernel_free |
add esp, 8 |
; increment SND.NXT in socket |
add esi, SOCKET.SND_NXT |
call inc_inet_esi |
.exit: |
; Get the socket number back, so we can return it |
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 |
;-------------------------- |
; |
; eax = ptr to ring struct (just a buffer of the right size) |
; |
align 4 |
SOCKET_ring_create: |
push esi |
mov esi, eax |
push edx |
stdcall create_ring_buffer, SOCKET_MAXDATA, PG_SW |
pop edx |
DEBUGF 1,"SOCKET_ring_created: %x\n", eax |
pusha |
lea ecx, [esi + RING_BUFFER.mutex] |
call mutex_init |
popa |
mov [esi + RING_BUFFER.start_ptr], eax |
mov [esi + RING_BUFFER.write_ptr], eax |
mov [esi + RING_BUFFER.read_ptr], eax |
mov [esi + RING_BUFFER.size], 0 |
add eax, SOCKET_MAXDATA |
mov [esi + RING_BUFFER.end_ptr], eax |
mov eax, esi |
pop esi |
ret |
endp |
;; [53.1] Close DGRAM socket |
;----------------------------------------------------------------- |
; |
; @param EBX is socket number |
; @return 0 (closed successfully) or -1 (error) in EAX |
;; |
proc socket_close stdcall |
DEBUGF 1, "K : socket_close (0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
jz .error |
; SOCKET_ring_write |
; |
; Adds data to a stream socket, and updates write pointer and size |
; |
; IN: eax = ptr to ring struct |
; ecx = data size |
; esi = ptr to data |
; |
; OUT: ecx = number of bytes stored |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_write: |
stdcall net_socket_free, eax |
DEBUGF 1,"SOCKET_ring_write: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx |
xor eax, eax |
ret |
; lock mutex |
pusha |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_lock ; TODO: check what registers this function actually destroys |
popa |
.error: |
DEBUGF 1, "K : socket_close (fail)\n" |
or eax, -1 |
; calculate available size |
mov edi, SOCKET_MAXDATA |
sub edi, [eax + RING_BUFFER.size] ; available buffer size in edi |
cmp ecx, edi |
jbe .copy |
mov ecx, edi |
.copy: |
mov edi, [eax + RING_BUFFER.write_ptr] |
DEBUGF 2,"SOCKET_ring_write: %u bytes from %x to %x\n", ecx, esi, edi |
; update write ptr |
push edi |
add edi, ecx |
cmp edi, [eax + RING_BUFFER.end_ptr] |
jb @f |
sub edi, SOCKET_MAXDATA ; WRAP |
@@: |
mov [eax + RING_BUFFER.write_ptr], edi |
pop edi |
; update size |
add [eax + RING_BUFFER.size], ecx |
; copy the data |
push ecx |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: |
shr ecx, 1 |
jnc .nw |
movsw |
.nw: |
test ecx, ecx |
jz .nd |
rep movsd |
.nd: |
pop ecx |
; unlock mutex |
push eax ecx |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_unlock ; TODO: check what registers this function actually destroys |
pop ecx eax |
ret |
endp |
;; [53.8] Close STREAM socket |
; Closing TCP sockets takes time, so when you get successful return code |
; from this function doesn't always mean that socket is actually closed. |
;----------------------------------------------------------------- |
; |
; @param EBX is socket number |
; @return 0 (closed successfully) or -1 (error) in EAX |
;; |
proc socket_close_tcp stdcall |
local sockAddr dd ? |
; SOCKET_ring_read |
; |
; IN: eax = ring struct ptr |
; ecx = bytes to read |
; edx = offset |
; edi = ptr to buffer start |
; |
; OUT: eax = unchanged |
; ecx = number of bytes read (0 on error) |
; edx = destroyed |
; esi = destroyed |
; edi = ptr to buffer end |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_read: |
DEBUGF 1, "K : socket_close_tcp (0x%x)\n", ebx |
; first, remove any resend entries |
DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u offset=%x\n", eax, edi, ecx, edx |
pusha |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_lock ; TODO: check what registers this function actually destroys |
popa |
mov esi, resendQ |
mov ecx, 0 |
mov esi, [eax + RING_BUFFER.read_ptr] |
add esi, edx ; esi = start_ptr + offset |
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .last_resendq ; None left |
cmp [esi + 4], ebx |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
neg edx |
add edx, [eax + RING_BUFFER.size] ; edx = snd.size - offset |
jle .no_data_at_all |
@@: |
mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
pusha |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_unlock ; TODO: check what registers this function actually destroys |
popa |
.last_resendq: |
cmp ecx, edx |
ja .less_data |
.copy: |
DEBUGF 2,"SOCKET_ring_read: %u bytes from %x to %x\n", ecx, esi, edi |
push ecx |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: |
shr ecx, 1 |
jnc .nw |
movsw |
.nw: |
test ecx, ecx |
jz .nd |
rep movsd |
.nd: |
pop ecx |
ret |
.no_data_at_all: |
pusha |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_unlock ; TODO: check what registers this function actually destroys |
popa |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
jz .error |
DEBUGF 1,"SOCKET_ring_read: no data at all!\n" |
xor ecx, ecx |
ret |
mov ebx, eax |
mov [sockAddr], eax |
.less_data: |
mov ecx, edx |
jmp .copy |
cmp [ebx + SOCKET.TCBState], TCB_LISTEN |
je .destroy_tcb |
cmp [ebx + SOCKET.TCBState], TCB_SYN_SENT |
je .destroy_tcb |
cmp [ebx + SOCKET.TCBState], TCB_CLOSED |
je .destroy_tcb |
; Now construct the response, and queue for sending by IP |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .error |
;----------------------------------------------------------------- |
; |
; SOCKET_ring_free |
; |
; Free's some bytes from the ringbuffer |
; |
; IN: eax = ptr to ring struct |
; ecx = data size |
; |
; OUT: ecx = number of bytes free-ed |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_free: |
DEBUGF 1,"SOCKET_ring_free: %u bytes from ring %x\n", ecx, eax |
push eax ecx |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_lock ; TODO: check what registers this function actually destroys |
pop ecx eax |
sub [eax + RING_BUFFER.size], ecx |
jb .error |
add [eax + RING_BUFFER.read_ptr], ecx |
mov edx, [eax + RING_BUFFER.end_ptr] |
cmp [eax + RING_BUFFER.read_ptr], edx |
jb @f |
sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA |
@@: |
push eax ecx |
lea ecx, [eax + RING_BUFFER.mutex] ; TODO: check what registers this function actually destroys |
call mutex_unlock |
pop ecx eax |
ret |
.error: ; we could free all available bytes, but that would be stupid, i guess.. |
DEBUGF 1,"SOCKET_ring_free: buffer=%x error!\n", eax |
add [eax + RING_BUFFER.size], ecx |
push eax |
lea ecx, [eax + RING_BUFFER.mutex] |
call mutex_unlock ; TODO: check what registers this function actually destroys |
pop eax |
mov bl, TH_FIN+TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
ret |
mov ebx, [sockAddr] |
; increament SND.NXT in socket |
lea esi, [ebx + SOCKET.SND_NXT] |
call inc_inet_esi |
; Get the socket state |
mov eax, [ebx + SOCKET.TCBState] |
cmp eax, TCB_SYN_RECEIVED |
je .fin_wait_1 |
cmp eax, TCB_ESTABLISHED |
je .fin_wait_1 |
;----------------------------------------------------------------- |
; |
; SOCKET_block |
; |
; Suspends the thread attached to a socket |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_block: |
; assume CLOSE WAIT |
; Send a fin, then enter last-ack state |
mov [ebx + SOCKET.TCBState], TCB_LAST_ACK |
jmp .send |
DEBUGF 1,"SOCKET_block: %x\n", eax |
.fin_wait_1: |
; Send a fin, then enter finwait2 state |
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1 |
pushf |
cli |
.send: |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
; Set the 'socket is blocked' flag |
or [eax + SOCKET.state], SS_BLOCKED |
.not_local: |
; Send it. |
pop ebx |
call queue |
jmp .exit |
; Suspend the thread |
push edx |
mov edx, [TASK_BASE] |
mov [edx + TASKDATA.state], 1 ; Suspended |
.destroy_tcb: |
; Remember the thread ID so we can wake it up again |
mov edx, [edx + TASKDATA.pid] |
DEBUGF 1,"SOCKET_block: suspending thread: %u\n", edx |
mov [eax + SOCKET.TID], edx |
pop edx |
; Clear the socket variables |
stdcall net_socket_free, ebx |
call change_task |
popf |
.exit: |
xor eax, eax |
ret |
DEBUGF 1,"SOCKET_block: continueing\n" |
.error: |
DEBUGF 1, "K : socket_close_tcp (fail)\n" |
or eax, -1 |
ret |
endp |
;; [53.2] Poll socket |
;----------------------------------------------------------------- |
; |
; @param EBX is socket number |
; @return count or bytes in rx buffer or 0 (error) in EAX |
;; |
proc socket_poll stdcall |
; DEBUGF 1, "socket_poll(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
; SOCKET_notify |
; |
; notify's the owner of a socket that something happened |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_notify: |
DEBUGF 1,"SOCKET_notify: %x\n", eax |
call SOCKET_check |
jz .error |
mov eax, [eax + SOCKET.rxDataCount] |
ret |
test [eax + SOCKET.state], SS_BLOCKED |
jnz .unblock |
.error: |
xor eax, eax |
ret |
endp |
test [eax + SOCKET.options], SO_NONBLOCK |
jz .error |
;; [53.6] Get socket TCB state |
; |
; @param EBX is socket number |
; @return socket TCB state or 0 (error) in EAX |
;; |
proc socket_status stdcall |
;; DEBUGF 1, "socket_status(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
push eax ecx esi |
; socket exists and is of non blocking type. |
; We'll try to flag an event to the thread |
mov eax, [eax + SOCKET.TID] |
test eax, eax |
jz .done |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
; PID not found, TODO: close socket! |
jmp .done |
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK |
DEBUGF 1,"SOCKET_notify: Raised a network event!\n" |
jmp .done |
.unblock: |
push eax ecx esi |
; Clear the 'socket is blocked' flag |
and [eax + SOCKET.state], not SS_BLOCKED |
; Find the thread's TASK_DATA |
mov eax, [eax + SOCKET.TID] |
test eax, eax |
jz .error |
xor ecx, ecx |
inc ecx |
mov esi, TASK_DATA |
.next: |
cmp [esi + TASKDATA.pid], eax |
je .found |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next |
jmp .error |
.found: |
mov eax, [eax + SOCKET.TCBState] |
ret |
; Run the thread |
mov [esi + TASKDATA.state], 0 ; Running |
DEBUGF 1,"SOCKET_notify: Unblocked socket!\n" |
.done: |
pop esi ecx eax |
.error: |
xor eax, eax |
ret |
endp |
;; [53.3] Get one byte from rx buffer |
; This function can return 0 in two cases: if there's one byte read and |
; non left, and if an error occured. Behavior should be changed and function |
; shouldn't be used for now. Consider using [53.11] instead. |
;-------------------------------------------------------------------- |
; |
; @param EBX is socket number |
; @return number of bytes left in rx buffer or 0 (error) in EAX |
; @return byte read in BL |
;; |
proc socket_read stdcall |
; DEBUGF 1, "socket_read(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
; SOCKET_alloc |
; |
; 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). |
; |
; IN: / |
; OUT: eax = 0 on error, socket ptr otherwise |
; edi = socket number |
; ZF = cleared on error |
; |
;-------------------------------------------------------------------- |
align 4 |
SOCKET_alloc: |
push ebx |
stdcall kernel_alloc, SOCKETBUFFSIZE |
DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax |
or eax, eax |
jz .error |
jz .exit |
mov ebx, eax |
; zero-initialize allocated memory |
push eax |
mov edi, eax |
mov ecx, SOCKETBUFFSIZE / 4 |
xor eax, eax |
rep stosd |
pop eax |
; set send-and receive procedures to return -1 |
mov [eax + SOCKET.snd_proc], s_error |
mov [eax + SOCKET.rcv_proc], s_error |
; find first free socket number and use it |
mov edi, [last_socket_num] |
.next_socket_number: |
inc edi |
jz .next_socket_number ; avoid socket nr 0 |
cmp edi, -1 |
je .next_socket_number ; avoid socket nr -1 |
mov ebx, net_sockets |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
test ebx, ebx |
jz .last_socket |
cmp [ebx + SOCKET.Number], edi |
jne .next_socket |
jmp .next_socket_number |
.last_socket: |
mov [last_socket_num], edi |
mov [eax + SOCKET.Number], edi |
DEBUGF 1, "SOCKET_alloc: number=%u\n", edi |
; Fill in PID |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.PID], ebx |
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :( |
; init mutex |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
call mutex_init |
popa |
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes |
test eax, eax |
jz .error_release |
; add socket to the list by re-arranging some pointers |
mov ebx, [net_sockets + SOCKET.NextPtr] |
dec eax |
mov esi, ebx ; esi is address of socket |
mov [ebx + SOCKET.rxDataCount], eax ; store new count |
movzx eax, byte[ebx + SOCKET.rxData] ; get the byte |
mov [eax + SOCKET.PrevPtr], net_sockets |
mov [eax + SOCKET.NextPtr], ebx |
mov ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1 |
lea edi, [esi + SOCKET.rxData] |
lea esi, [edi + 1] |
cld |
push ecx |
shr ecx, 2 |
rep movsd |
pop ecx |
and ecx, 3 |
rep movsb |
test ebx, ebx |
jz @f |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
mov ebx, eax |
call mutex_unlock |
mov eax, ebx |
ret |
call mutex_lock |
popa |
.error_release: |
mov [ebx + SOCKET.PrevPtr], eax |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
.error: |
xor ebx, ebx |
xor eax, eax |
popa |
@@: |
mov [net_sockets + SOCKET.NextPtr], eax |
or eax, eax ; used to clear zero flag |
.exit: |
pop ebx |
ret |
endp |
;; [53.11] Get specified number of bytes from rx buffer |
; Number of bytes in rx buffer can be less than requested size. In this case, |
; only available number of bytes is read. |
; This function can return 0 in two cases: if there's no data to read, and if |
; an error occured. Behavior should be changed. |
;---------------------------------------------------- |
; |
; @param EBX is socket number |
; @param ECX is pointer to application buffer |
; @param EDX is application buffer size (number of bytes to read) |
; @return number of bytes read or 0 (error) in EAX |
;; |
proc socket_read_packet stdcall |
; DEBUGF 1, "socket_read_packet(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx ; get real socket address |
or eax, eax |
; SOCKET_free |
; |
; Free socket data memory and remove socket from the list |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;---------------------------------------------------- |
align 4 |
SOCKET_free: |
DEBUGF 1, "SOCKET_free: %x\n", eax |
call SOCKET_check |
jz .error |
mov ebx, eax |
push ebx |
push ecx edx |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
pop edx ecx |
popa |
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes |
test eax, eax ; if count of bytes is zero.. |
jz .exit ; exit function (eax will be zero) |
cmp [eax + SOCKET.Domain], AF_INET4 |
jnz .no_tcp |
test edx, edx ; if buffer size is zero, copy all data |
jz .copy_all_bytes |
cmp edx, eax ; if buffer size is larger then the bytes of data, copy all data |
jge .copy_all_bytes |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
jnz .no_tcp |
sub eax, edx ; store new count (data bytes in buffer - bytes we're about to copy) |
mov [ebx + SOCKET.rxDataCount], eax ; |
push eax |
mov eax, edx ; number of bytes we want to copy must be in eax |
call .start_copy ; copy to the application |
mov esi, ebx ; now we're going to copy the remaining bytes to the beginning |
add esi, SOCKET.rxData ; we dont need to copy the header |
mov edi, esi ; edi is where we're going to copy to |
add esi, edx ; esi is from where we copy |
pop ecx ; count of bytes we have left |
push ecx ; push it again so we can re-use it later |
shr ecx, 2 ; divide eax by 4 |
cld |
rep movsd ; copy all full dwords |
pop ecx |
and ecx, 3 |
rep movsb ; copy remaining bytes |
.exit: |
lea ecx, [ebx + SOCKET.mutex] |
mov ebx, eax |
call mutex_unlock |
stdcall kernel_free, [ebx + STREAM_SOCKET.rcv.start_ptr] |
stdcall kernel_free, [ebx + STREAM_SOCKET.snd.start_ptr] |
mov eax, ebx |
ret ; at last, exit |
.no_tcp: |
push eax ; this will be passed to kernel_free |
mov ebx, [eax + SOCKET.NextPtr] |
mov eax, [eax + SOCKET.PrevPtr] |
DEBUGF 1, "SOCKET_free: linking socket %x to socket %x\n", eax, ebx |
test eax, eax |
jz @f |
mov [eax + SOCKET.NextPtr], ebx |
@@: |
test ebx, ebx |
jz @f |
mov [ebx + SOCKET.PrevPtr], eax |
@@: |
call kernel_free |
pop ebx |
DEBUGF 1, "SOCKET_free: success!\n" |
.error: |
xor eax, eax |
ret |
.copy_all_bytes: |
xor esi, esi |
mov [ebx + SOCKET.rxDataCount], esi ; store new count (zero) |
call .start_copy |
lea ecx, [ebx + SOCKET.mutex] |
mov ebx, eax |
call mutex_unlock |
mov eax, ebx |
ret |
;------------------------------------ |
; |
; SOCKET_fork |
; |
; Create a child socket |
; |
; IN: socket nr in ebx |
; OUT: child socket nr in eax |
; |
;----------------------------------- |
align 4 |
SOCKET_fork: |
.start_copy: |
mov edi, ecx |
mov esi, ebx |
add esi, SOCKET.rxData ; we dont need to copy the header |
mov ecx, eax ; eax is count of bytes |
push ecx |
shr ecx, 2 ; divide eax by 4 |
cld ; copy all full dwords |
DEBUGF 1,"SOCKET_fork: %x\n", ebx |
; Exit if backlog queue is full |
mov eax, [ebx + SOCKET_QUEUE_LOCATION + queue.size] |
cmp ax, [ebx + SOCKET.backlog] |
jae .fail |
; Allocate new socket |
push ebx |
call SOCKET_alloc |
pop ebx |
jz .fail |
push eax |
mov esi, esp |
add_to_queue (ebx + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .fail2 |
pop eax |
; Copy structure from current socket to new |
; We start at PID to preserve the socket num, and the 2 pointers at beginning of socket |
lea esi, [ebx + SOCKET.PID] |
lea edi, [eax + SOCKET.PID] |
mov ecx, (SOCKET_QUEUE_LOCATION - SOCKET.PID + 3)/4 |
rep movsd |
pop ecx |
and ecx, 3 |
rep movsb ; copy the rest bytes |
retn ; exit, or go back to shift remaining bytes if any |
endp |
;; [53.4] Send data through DGRAM socket |
and [eax + SOCKET.options], not SO_ACCEPTCON |
ret |
.fail2: |
add esp, 4+4+4 |
.fail: |
DEBUGF 1,"SOCKET_fork: failed\n" |
xor eax, eax |
ret |
;--------------------------------------------------- |
; |
; @param EBX is socket number |
; @param ECX is application data size (number of bytes to send) |
; @param EDX is pointer to application data buffer |
; @return 0 (sent successfully) or -1 (error) in EAX |
;; |
proc socket_write stdcall |
; DEBUGF 1, "socket_write(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx ; get real socket address |
; SOCKET_num_to_ptr |
; |
; Get socket structure address by its number |
; |
; IN: ecx = socket number |
; OUT: eax = 0 on error, socket ptr otherwise |
; ZF = set on error |
; |
;--------------------------------------------------- |
align 4 |
SOCKET_num_to_ptr: |
DEBUGF 1,"SOCKET_num_to_ptr: num=%u ", ecx |
mov eax, net_sockets |
.next_socket: |
mov eax, [eax + SOCKET.NextPtr] |
or eax, eax |
jz .error |
cmp [eax + SOCKET.Number], ecx |
jne .next_socket |
mov ebx, eax |
test eax, eax |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .error |
DEBUGF 1,"ptr=%x\n", eax |
ret |
; Save the queue entry number |
push eax |
.error: |
DEBUGF 1,"not found\n", eax |
ret |
; save the pointers to the data buffer & size |
push edx |
push ecx |
; convert buffer pointer eax to the absolute address |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
;--------------------------------------------------- |
; |
; SOCKET_ptr_to_num |
; |
; Get socket number by its address |
; |
; IN: eax = socket ptr |
; OUT: eax = 0 on error, socket num otherwise |
; ZF = set on error |
; |
;--------------------------------------------------- |
align 4 |
SOCKET_ptr_to_num: |
mov edx, eax |
DEBUGF 1,"SOCKET_ptr_to_num: ptr=%x ", eax |
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr |
call SOCKET_check |
jz .error |
; Fill in the IP header (some data is in the socket descriptor) |
mov eax, [ebx + SOCKET.LocalIP] |
mov [edx + IP_PACKET.SourceAddress], eax |
mov eax, [ebx + SOCKET.RemoteIP] |
mov [edx + IP_PACKET.DestinationAddress], eax |
mov eax, [eax + SOCKET.Number] |
mov [edx + IP_PACKET.VersionAndIHL], 0x45 |
mov [edx + IP_PACKET.TypeOfService], 0 |
DEBUGF 1,"num=%u\n", eax |
ret |
pop eax ; Get the UDP data length |
push eax |
.error: |
DEBUGF 1,"not found\n", eax |
ret |
add eax, 20 + 8 ; add IP header and UDP header lengths |
xchg al, ah |
mov [edx + IP_PACKET.TotalLength], ax |
xor eax, eax |
mov [edx + IP_PACKET.Identification], ax |
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040 |
mov [edx + IP_PACKET.TimeToLive], 0x20 |
mov [edx + IP_PACKET.Protocol], PROTOCOL_UDP |
; Checksum left unfilled |
mov [edx + IP_PACKET.HeaderChecksum], ax |
;--------------------------------------------------- |
; |
; SOCKET_check |
; |
; checks if the given value is really a socket ptr |
; |
; IN: eax = socket ptr |
; OUT: eax = 0 on error, unchanged otherwise |
; ZF = set on error |
; |
;--------------------------------------------------- |
align 4 |
SOCKET_check: |
; Fill in the UDP header (some data is in the socket descriptor) |
mov ax, [ebx + SOCKET.LocalPort] |
mov [edx + 20 + UDP_PACKET.SourcePort], ax |
DEBUGF 1,"SOCKET_check: %x\n", eax |
mov ax, [ebx + SOCKET.RemotePort] |
mov [edx + 20 + UDP_PACKET.DestinationPort], ax |
push ebx |
mov ebx, net_sockets |
pop eax |
push eax |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .done |
cmp ebx, eax |
jnz .next_socket |
add eax, 8 |
xchg al, ah |
mov [edx + 20 + UDP_PACKET.Length], ax |
.done: |
mov eax, ebx |
test eax, eax |
pop ebx |
; Checksum left unfilled |
xor eax, eax |
mov [edx + 20 + UDP_PACKET.Checksum], ax |
ret |
pop ecx ; count of bytes to send |
mov ebx, ecx ; need the length later |
pop eax ; get callers ptr to data to send |
; Get the address of the callers data |
mov edi, [TASK_BASE] |
add edi, TASKDATA.mem_start |
add eax, [edi] |
mov esi, eax |
mov edi, edx |
add edi, 28 |
cld |
rep movsb ; copy the data across |
;--------------------------------------------------- |
; |
; SOCKET_check_owner |
; |
; checks if the caller application owns the socket |
; |
; IN: eax = socket ptr |
; OUT: ZF = true/false |
; |
;--------------------------------------------------- |
align 4 |
SOCKET_check_owner: |
; we have edx as IPbuffer ptr. |
; Fill in the UDP checksum |
; First, fill in pseudoheader |
mov eax, [edx + IP_PACKET.SourceAddress] |
mov [pseudoHeader], eax |
mov eax, [edx + IP_PACKET.DestinationAddress] |
mov [pseudoHeader + 4], eax |
mov word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0 ; 0 + protocol |
add ebx, 8 |
mov eax, ebx |
xchg al, ah |
mov [pseudoHeader + 10], ax |
DEBUGF 1,"SOCKET_check_owner: %x\n", eax |
mov eax, pseudoHeader |
mov [checkAdd1], eax |
mov [checkSize1], word 12 |
mov eax, edx |
add eax, 20 |
mov [checkAdd2], eax |
mov eax, ebx |
mov [checkSize2], ax ; was eax!! mjh 8/7/02 |
push ebx |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
cmp [eax + SOCKET.PID], ebx |
pop ebx |
call checksum |
ret |
; store it in the UDP checksum ( in the correct order! ) |
mov ax, [checkResult] |
; If the UDP checksum computes to 0, we must make it 0xffff |
; (0 is reserved for 'not used') |
test ax, ax |
jnz @f |
mov ax, 0xffff |
@@: |
xchg al, ah |
mov [edx + 20 + UDP_PACKET.Checksum], ax |
; Fill in the IP header checksum |
GET_IHL ecx,edx ; get IP-Header length |
stdcall checksum_jb, edx, ecx; buf_ptr, buf_size |
xchg al, ah |
mov [edx + IP_PACKET.HeaderChecksum], ax |
;------------------------------------------------------ |
; |
; SOCKET_process_end |
; |
; Kernel calls this function when a certain process ends |
; This function will check if the process had any open sockets |
; And update them accordingly |
; |
; IN: edx = pid |
; OUT: / |
; |
;------------------------------------------------------ |
align 4 |
SOCKET_process_end: |
; Check destination IP address. |
; If it is the local host IP, route it back to IP_RX |
DEBUGF 1, "SOCKET_process_end: %x\n", edx |
pop ebx |
push ebx |
mov ebx, net_sockets |
mov eax, NET1OUT_QUEUE |
mov ecx, [edx + SOCKET.RemoteIP] |
mov edx, [stack_ip] |
cmp edx, ecx |
jne .not_local |
mov eax, IPIN_QUEUE |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
.next_socket_test: |
test ebx, ebx |
jz .done |
.not_local: |
; Send it. |
call queue |
cmp [ebx + SOCKET.PID], edx |
jne .next_socket |
xor eax, eax |
ret |
DEBUGF 1, "SOCKET_process_end: killing socket %x\n", ebx |
.error: |
or eax, -1 |
mov [ebx + SOCKET.PID], 0 |
mov eax, ebx |
mov ebx, [ebx + SOCKET.NextPtr] |
pusha |
call SOCKET_close.socket |
popa |
jmp .next_socket_test |
.done: |
pop ebx |
ret |
endp |
;; [53.7] Send data through STREAM socket |
;----------------------------------------------------------------- |
; |
; @param EBX is socket number |
; @param ECX is application data size (number of bytes to send) |
; @param EDX is pointer to application data buffer |
; @return 0 (sent successfully) or -1 (error) in EAX |
;; |
proc socket_write_tcp stdcall |
local sockAddr dd ? |
; SOCKET_is_connecting |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
jz .error |
align 4 |
SOCKET_is_connecting: |
mov ebx, eax |
mov [sockAddr], ebx |
DEBUGF 1,"SOCKET_is_connecting: %x\n", eax |
; If the sockets window timer is nonzero, do not queue packet |
cmp [ebx + SOCKET.wndsizeTimer], 0 |
jne .error |
and [eax + SOCKET.options], not (SS_ISCONNECTED + SS_ISDISCONNECTING + SS_ISCONFIRMING) |
or [eax + SOCKET.options], SS_ISCONNECTING |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .error |
jmp SOCKET_notify |
push eax |
; Get the address of the callers data |
mov edi, [TASK_BASE] |
add edi, TASKDATA.mem_start |
add edx, [edi] |
mov esi, edx |
pop eax |
push eax |
;----------------------------------------------------------------- |
; |
; SOCKET_is_connected |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
push ecx |
mov bl, TH_ACK |
stdcall build_tcp_packet, [sockAddr] |
pop ecx |
align 4 |
SOCKET_is_connected: |
; Check destination IP address. |
; If it is the local host IP, route it back to IP_RX |
DEBUGF 1,"SOCKET_is_connected: %x\n", eax |
pop ebx |
push ecx |
and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISDISCONNECTING + SS_ISCONFIRMING) |
or [eax + SOCKET.options], SS_ISCONNECTED |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
jmp SOCKET_notify |
.not_local: |
pop ecx |
push ebx ; save ipbuffer number |
call queue |
mov esi, [sockAddr] |
; increament SND.NXT in socket |
; Amount to increment by is in ecx |
add esi, SOCKET.SND_NXT |
call add_inet_esi |
;----------------------------------------------------------------- |
; |
; SOCKET_is_disconnecting |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
pop ebx |
align 4 |
SOCKET_is_disconnecting: |
; Copy the IP buffer to a resend queue |
; If there isn't one, dont worry about it for now |
mov esi, resendQ |
mov ecx, 0 |
DEBUGF 1,"SOCKET_is_disconnecting: %x\n", eax |
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .exit ; None found |
cmp dword[esi + 4], 0 |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
and [eax + SOCKET.options], not (SS_ISCONNECTING) |
or [eax + SOCKET.options], SS_ISDISCONNECTING + SS_CANTRCVMORE + SS_CANTSENDMORE |
@@: |
push ebx |
jmp SOCKET_notify |
; OK, we have a buffer descriptor ptr in esi. |
; resend entry # in ecx |
; Populate it |
; socket # |
; retries count |
; retry time |
; fill IP buffer associated with this descriptor |
stdcall net_socket_addr_to_num, [sockAddr] |
mov [esi + 4], eax |
mov byte[esi + 1], TCP_RETRIES |
mov word[esi + 2], TCP_TIMEOUT |
inc ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
mov edi, resendBuffer - IPBUFFSIZE |
;----------------------------------------------------------------- |
; |
; SOCKET_is_disconnected |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
@@: |
add edi, IPBUFFSIZE |
loop @b |
align 4 |
SOCKET_is_disconnected: |
; we have dest buffer location in edi |
pop eax |
; convert source buffer pointer eax to the absolute address |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
mov esi, eax |
DEBUGF 1,"SOCKET_is_disconnected: %x\n", eax |
; do copy |
mov ecx, IPBUFFSIZE |
cld |
rep movsb |
and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING) |
or [eax + SOCKET.options], SS_CANTRCVMORE + SS_CANTSENDMORE |
.exit: |
xor eax, eax |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
je .tcp |
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP |
je .udp |
jmp SOCKET_notify |
.tcp: |
.udp: |
mov [eax + UDP_SOCKET.LocalPort], 0 ; UDP and TCP structs store localport at the same offset |
mov [eax + UDP_SOCKET.RemotePort], 0 |
jmp SOCKET_notify |
;----------------------------------------------------------------- |
; |
; SOCKET_cant_recv_more |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_cant_recv_more: |
DEBUGF 1,"SOCKET_cant_recv_more: %x\n", eax |
or [eax + SOCKET.options], SS_CANTRCVMORE |
ret |
.error: |
or eax, -1 |
ret |
endp |
;----------------------------------------------------------------- |
; |
; SOCKET_cant_send_more |
; |
; IN: eax = socket ptr |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_cant_send_more: |
DEBUGF 1,"SOCKET_cant_send_more: %x\n", eax |
or [eax + SOCKET.options], SS_CANTSENDMORE |
ret |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/stack.inc |
---|
1,918 → 1,789 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; STACK.INC ;; |
;; ;; |
;; TCP/IP stack for Menuet OS ;; |
;; TCP/IP stack for KolibriOS ;; |
;; ;; |
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; See file COPYING for details ;; |
;; Some parts of code are based on the work of: ;; |
;; Mike Hibbett (menuetos network stack) ;; |
;; Eugen Brasoveanu (solar os network stack and drivers) ;; |
;; mike.dld (kolibrios socket code) ;; |
;; ;; |
;; Version 0.7 ;; |
;; Added a timer per socket to allow delays when rx window ;; |
;; gets below 1KB ;; |
;; TCP part is based on 4.4BSD ;; |
;; ;; |
;;10.01.2007 Bugfix for checksum function from Paolo Franchetti ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 3523 $ |
;******************************************************************* |
; Interface |
; The interfaces defined in ETHERNET.INC plus: |
; stack_init |
; stack_handler |
; app_stack_handler |
; app_socket_handler |
; checksum |
; |
;******************************************************************* |
uglobal |
StackCounters: |
dumped_rx_count dd 0 |
arp_tx_count: |
dd 0 |
arp_rx_count: |
dd 0 |
ip_rx_count: |
dd 0 |
ip_tx_count: |
dd 0 |
net_10ms dd ? |
net_tmr_count dw ? |
endg |
; socket buffers |
SOCKETBUFFSIZE equ 4096 ; state + config + buffer. |
SOCKETHEADERSIZE equ SOCKET.rxData ; thus 4096 - SOCKETHEADERSIZE bytes data |
MAX_NET_DEVICES = 16 |
ARP_BLOCK = 1 ; true or false |
;NUM_SOCKETS equ 16 ; Number of open sockets supported. Was 20 |
MIN_EPHEMERAL_PORT = 49152 |
MIN_EPHEMERAL_PORT_N = 0x00C0 ; same in Network byte order (FIXME) |
MAX_EPHEMERAL_PORT = 61000 |
MAX_EPHEMERAL_PORT_N = 0x48EE ; same in Network byte order (FIXME) |
; IPBUFF status values |
BUFF_EMPTY equ 0 |
BUFF_RX_FULL equ 1 |
BUFF_ALLOCATED equ 2 |
BUFF_TX_FULL equ 3 |
; Ethernet protocol numbers |
ETHER_ARP = 0x0608 |
ETHER_IPv4 = 0x0008 |
ETHER_IPv6 = 0xDD86 |
ETHER_PPP_DISCOVERY = 0x6388 |
ETHER_PPP_SESSION = 0x6488 |
NUM_IPBUFFERS equ 20 ; buffers allocated for TX/RX |
; PPP protocol numbers |
PPP_IPv4 = 0x2100 |
PPP_IPV6 = 0x5780 |
NUMQUEUES equ 4 |
;Protocol family |
AF_UNSPEC = 0 |
AF_LOCAL = 1 |
AF_INET4 = 2 |
AF_INET6 = 10 |
AF_PPP = 777 |
EMPTY_QUEUE equ 0 |
IPIN_QUEUE equ 1 |
IPOUT_QUEUE equ 2 |
NET1OUT_QUEUE equ 3 |
; Internet protocol numbers |
IP_PROTO_IP = 0 |
IP_PROTO_ICMP = 1 |
IP_PROTO_TCP = 6 |
IP_PROTO_UDP = 17 |
NO_BUFFER equ 0xFFFF |
IPBUFFSIZE equ 1500 ; MTU of an ethernet packet |
NUMQUEUEENTRIES equ NUM_IPBUFFERS |
NUMRESENDENTRIES equ 18 ; Buffers for TCP resend packets |
; PPP protocol number |
PPP_PROTO_ETHERNET = 666 |
; These are the 0x40 function codes for application access to the stack |
STACK_DRIVER_STATUS equ 52 |
SOCKET_INTERFACE equ 53 |
; Socket types |
SOCK_STREAM = 1 |
SOCK_DGRAM = 2 |
SOCK_RAW = 3 |
; Socket options |
SO_ACCEPTCON = 1 shl 0 |
SO_BROADCAST = 1 shl 1 |
SO_DEBUG = 1 shl 2 |
SO_DONTROUTE = 1 shl 3 |
SO_KEEPALIVE = 1 shl 4 |
SO_OOBINLINE = 1 shl 5 |
SO_REUSEADDR = 1 shl 6 |
SO_REUSEPORT = 1 shl 7 |
SO_USELOOPBACK = 1 shl 8 |
SO_BINDTODEVICE = 1 shl 9 |
; 128KB allocated for the stack and network driver buffers and other |
; data requirements |
;stack_data_start equ 0x700000 |
;eth_data_start equ 0x700000 |
;stack_data equ 0x704000 |
;stack_data_end equ 0x71ffff |
SO_BLOCK = 1 shl 10 ; TO BE REMOVED |
SO_NONBLOCK = 1 shl 31 |
; 32 bit word |
stack_config equ stack_data |
; Socket flags for user calls |
MSG_PEEK = 0x02 |
MSG_DONTWAIT = 0x40 |
; 32 bit word - IP Address in network format |
stack_ip equ stack_data + 4 |
; Socket level |
SOL_SOCKET = 0 |
; 1 byte. 0 == inactive, 1 = active |
ethernet_active equ stack_data + 9 |
; Socket States |
SS_NOFDREF = 0x0001 ; no file table ref any more |
SS_ISCONNECTED = 0x0002 ; socket connected to a peer |
SS_ISCONNECTING = 0x0004 ; in process of connecting to peer |
SS_ISDISCONNECTING = 0x0008 ; in process of disconnecting |
SS_CANTSENDMORE = 0x0010 ; can't send more data to peer |
SS_CANTRCVMORE = 0x0020 ; can't receive more data from peer |
SS_RCVATMARK = 0x0040 ; at mark on input |
SS_ISABORTING = 0x0080 ; aborting fd references - close() |
SS_RESTARTSYS = 0x0100 ; restart blocked system calls |
SS_ISDISCONNECTED = 0x0800 ; socket disconnected from peer |
; TODO :: empty memory area |
SS_ASYNC = 0x0100 ; async i/o notify |
SS_ISCONFIRMING = 0x0200 ; deciding to accept connection req |
SS_MORETOCOME = 0x0400 |
; Address of selected socket |
;sktAddr equ stack_data + 32 |
; Parameter to checksum routine - data ptr |
checkAdd1 equ stack_data + 36 |
; Parameter to checksum routine - 2nd data ptr |
checkAdd2 equ stack_data + 40 |
; Parameter to checksum routine - data size |
checkSize1 equ stack_data + 44 |
; Parameter to checksum routine - 2nd data size |
checkSize2 equ stack_data + 46 |
; result of checksum routine |
checkResult equ stack_data + 48 |
SS_BLOCKED = 0x8000 |
; holds the TCP/UDP pseudo header. SA|DA|0|prot|UDP len| |
pseudoHeader equ stack_data + 50 |
; receive and transmit IP buffer allocation |
;sockets equ stack_data + 62 |
Next_free2 equ stack_data + 62;Next_free2 equ sockets + (SOCKETBUFFSIZE * NUM_SOCKETS) |
; 1560 byte buffer for rx / tx ethernet packets |
Ether_buffer equ Next_free2 |
Next_free3 equ Ether_buffer + 1518 |
last_1sTick equ Next_free3 |
IPbuffs equ Next_free3 + 1 |
queues equ IPbuffs + ( NUM_IPBUFFERS * IPBUFFSIZE ) |
queueList equ queues + (2 * NUMQUEUES) |
last_1hsTick equ queueList + ( 2 * NUMQUEUEENTRIES ) |
SOCKET_MAXDATA = 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8 |
;resendQ equ queueList + ( 2 * NUMQUEUEENTRIES ) |
;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP |
; equ resendBuffer + ( IPBUFFSIZE * NUMRESENDENTRIES ) |
; Network driver types |
NET_TYPE_LOOPBACK = 0 |
NET_TYPE_ETH = 1 |
NET_TYPE_SLIP = 2 |
MAX_backlog = 20 ; maximum backlog for stream sockets |
; Error Codes |
ENOBUFS = 55 |
ECONNREFUSED = 61 |
ECONNRESET = 52 |
ETIMEDOUT = 60 |
ECONNABORTED = 53 |
;resendQ equ 0x770000 |
;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP ; XTODO: validate size |
resendBuffer equ resendQ + ( 8 * NUMRESENDENTRIES ) ; for TCP |
; Api protocol numbers |
API_ETH = 0 |
API_IPv4 = 1 |
API_ICMP = 2 |
API_UDP = 3 |
API_TCP = 4 |
API_ARP = 5 |
API_PPPOE = 6 |
API_IPv6 = 7 |
HWACC_TCP_IPv4 = 1 shl 0 |
uglobal |
net_sockets rd 2 |
endg |
struct NET_DEVICE |
; simple macro for memory set operation |
macro _memset_dw adr,value,amount |
{ |
mov edi, adr |
mov ecx, amount |
if value = 0 |
xor eax, eax |
else |
mov eax, value |
end if |
cld |
rep stosd |
type dd ? ; Type field |
mtu dd ? ; Maximal Transmission Unit |
name dd ? ; Ptr to 0 terminated string |
unload dd ? ; Ptrs to driver functions |
reset dd ? ; |
transmit dd ? ; |
bytes_tx dq ? ; Statistics, updated by the driver |
bytes_rx dq ? ; |
packets_tx dd ? ; |
packets_rx dd ? ; |
state dd ? ; link state (0 = no link) |
hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines) |
ends |
; Exactly as it says.. |
macro pseudo_random reg { |
add reg, [esp] |
rol reg, 5 |
xor reg, [timer_ticks] |
; add reg, [CPU_FREQ] |
imul reg, 214013 |
xor reg, 0xdeadbeef |
rol reg, 9 |
} |
; Network to Hardware byte order (dword) |
macro ntohd reg { |
; Below, the main network layer source code is included |
; |
rol word reg, 8 |
rol dword reg, 16 |
rol word reg , 8 |
} |
; Network to Hardware byte order (word) |
macro ntohw reg { |
rol word reg, 8 |
} |
include "queue.inc" |
include "eth_drv/ethernet.inc" |
include "ip.inc" |
include "loopback.inc" |
include "ethernet.inc" |
include "PPPoE.inc" |
include "ARP.inc" |
include "IPv4.inc" |
include "IPv6.inc" |
include "icmp.inc" |
include "udp.inc" |
include "tcp.inc" |
include "socket.inc" |
;*************************************************************************** |
; Function |
align 4 |
uglobal |
NET_RUNNING dd ? |
NET_DEFAULT dd ? |
NET_DRV_LIST rd MAX_NET_DEVICES |
endg |
;----------------------------------------------------------------- |
; |
; stack_init |
; |
; Description |
; Clear all allocated memory to zero. This ensures that |
; on startup, the stack is inactive, and consumes no resources |
; This is a kernel function, called prior to the OS main loop |
; in set_variables |
; This function calls all network init procedures |
; |
;*************************************************************************** |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
stack_init: |
; Init two address spaces with default values |
_memset_dw stack_data_start, 0, 0x20000/4 |
_memset_dw resendQ, 0, NUMRESENDENTRIES * 2 |
mov [net_sockets], 0 |
mov [net_sockets + 4], 0 |
; Init the network drivers list |
xor eax, eax |
mov edi, NET_RUNNING |
mov ecx, (MAX_NET_DEVICES + 2) |
rep stosd |
; Queries initialization |
call queueInit |
PPPoE_init |
; The following block sets up the 1s timer |
mov al, 0x0 |
out 0x70, al |
in al, 0x71 |
mov [last_1sTick], al |
IPv4_init |
; IPv6_init |
ICMP_init |
ARP_init |
UDP_init |
TCP_init |
SOCKET_init |
mov [net_tmr_count], 0 |
ret |
; Wakeup every tick. |
proc stack_handler_has_work? |
mov eax, [timer_ticks] |
cmp eax, [last_1hsTick] |
cmp eax, [net_10ms] |
ret |
endp |
;*************************************************************************** |
; Function |
;----------------------------------------------------------------- |
; |
; stack_handler |
; |
; Description |
; The kernel loop routine for the stack |
; This is a kernel function, called in the main loop |
; This function is called in kernel loop |
; |
;*************************************************************************** |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
stack_handler: |
call ethernet_driver |
call ip_rx |
; Test for 10ms tick |
mov eax, [timer_ticks] |
cmp eax, [net_10ms] |
je .exit |
mov [net_10ms], eax |
cmp [NET_RUNNING], 0 |
je .exit |
; Test for 10ms tick, call tcp timer |
mov eax, [timer_ticks];[0xfdf0] |
cmp eax, [last_1hsTick] |
je sh_001 |
test [net_10ms], 0x0f ; 160ms |
jnz .exit |
mov [last_1hsTick], eax |
call tcp_tx_handler |
TCP_timer_160ms |
sh_001: |
test [net_10ms], 0x3f ; 640ms |
jnz .exit |
; Test for 1 second event, call 1s timer functions |
mov al, 0x0;second |
out 0x70, al |
in al, 0x71 |
cmp al, [last_1sTick] |
je sh_exit |
TCP_timer_640ms |
ARP_decrease_entry_ttls |
IPv4_decrease_fragment_ttls |
mov [last_1sTick], al |
.exit: |
ret |
stdcall arp_table_manager, ARP_TABLE_TIMER, 0, 0 |
call tcp_tcb_handler |
sh_exit: |
ret |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; Checksum [by Johnny_B] |
;; IN: |
;; buf_ptr=POINTER to buffer |
;; buf_size=SIZE of buffer |
;; OUT: |
;; AX=16-bit checksum |
;; Saves all used registers |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
proc checksum_jb stdcall uses ebx esi ecx,\ |
buf_ptr:DWORD, buf_size:DWORD |
align 4 |
NET_link_changed: |
xor eax, eax |
xor ebx, ebx;accumulator |
mov esi, dword[buf_ptr] |
mov ecx, dword[buf_size] |
shr ecx, 1; ecx=ecx/2 |
jnc @f ; if CF==0 then size is even number |
mov bh, byte[esi + ecx*2] |
@@: |
cld |
DEBUGF 1,"NET_link_changed device=0x%x status=0x%x\n", ebx, [ebx + NET_DEVICE.state] |
align 4 |
NET_send_event: |
DEBUGF 1,"NET_send_event\n" |
; Send event to all applications |
push edi ecx |
mov edi, SLOT_BASE |
mov ecx, [TASK_COUNT] |
.loop: |
lodsw ;eax=word[esi],esi=esi+2 |
xchg ah, al;cause must be a net byte-order |
add ebx, eax |
add edi, 256 |
or [edi + APPDATA.event_mask], EVENT_NETWORK2 |
loop .loop |
pop ecx edi |
mov eax, ebx |
shr eax, 16 |
add ax, bx |
not ax |
ret |
endp |
;*************************************************************************** |
; Function |
; checksum |
;----------------------------------------------------------------- |
; |
; Description |
; checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult |
; Dont break anything; Most registers are used by the caller |
; This code is derived from the 'C' source, cksum.c, in the book |
; Internetworking with TCP/IP Volume II by D.E. Comer |
; NET_add_device: |
; |
;*************************************************************************** |
; This function is called by the network drivers, |
; to register each running NIC to the kernel |
; |
; IN: Pointer to device structure in ebx |
; OUT: Device num in eax, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
NET_add_device: |
DEBUGF 1,"NET_Add_Device: %x\n", ebx ;;; TODO: use mutex to lock net device list |
checksum: |
pusha |
mov eax, [checkAdd1] |
xor edx, edx ; edx is the accumulative checksum |
xor ebx, ebx |
mov cx, [checkSize1] |
shr cx, 1 |
jz cs1_1 |
cmp [NET_RUNNING], MAX_NET_DEVICES |
jae .error |
cs1: |
mov bh, [eax] |
mov bl, [eax + 1] |
;---------------------------------- |
; Check if device is already listed |
mov eax, ebx |
mov ecx, MAX_NET_DEVICES ; We need to check whole list because a device may be removed without re-organizing list |
mov edi, NET_DRV_LIST |
add eax, 2 |
add edx, ebx |
repne scasd ; See if device is already in the list |
jz .error |
loopw cs1 |
;---------------------------- |
; Find empty slot in the list |
xor eax, eax |
mov ecx, MAX_NET_DEVICES |
mov edi, NET_DRV_LIST |
cs1_1: |
and word [checkSize1], 0x01 |
jz cs_test2 |
repne scasd |
jnz .error |
mov bh, [eax] |
xor bl, bl |
sub edi, 4 |
add edx, ebx |
;----------------------------- |
; Add device to the found slot |
mov [edi], ebx ; add device to list |
cs_test2: |
mov cx, [checkSize2] |
cmp cx, 0 |
jz cs_exit ; Finished if no 2nd buffer |
mov eax, edi ; Calculate device number in eax |
sub eax, NET_DRV_LIST |
shr eax, 2 |
mov eax, [checkAdd2] |
inc [NET_RUNNING] ; Indicate that one more network device is up and running |
shr cx, 1 |
jz cs2_1 |
cmp eax, 1 ; If it's the first network device, try to set it as default |
jne @f |
push eax |
call NET_set_default |
pop eax |
@@: |
cs2: |
mov bh, [eax] |
mov bl, [eax + 1] |
call NET_send_event |
add eax, 2 |
add edx, ebx |
DEBUGF 1,"Device number: %u\n", eax |
ret |
loopw cs2 |
cs2_1: |
and word [checkSize2], 0x01 |
jz cs_exit |
mov bh, [eax] |
xor bl, bl |
add edx, ebx |
cs_exit: |
mov ebx, edx |
shr ebx, 16 |
and edx, 0xffff |
add edx, ebx |
mov eax, edx |
shr eax, 16 |
add edx, eax |
not dx |
mov [checkResult], dx |
popa |
.error: |
or eax, -1 |
DEBUGF 2,"Adding network device failed\n" |
ret |
;*************************************************************************** |
; Function |
; app_stack_handler |
;----------------------------------------------------------------- |
; |
; Description |
; This is an application service, called by int 0x40, function 52 |
; It provides application access to the network interface layer |
; NET_set_default |
; |
;*************************************************************************** |
iglobal |
; API to set the default interface |
; |
; IN: Device num in eax |
; OUT: Device num in eax, -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
f52call: |
dd app_stack_handler.00 |
dd app_stack_handler.01 |
dd app_stack_handler.02 |
dd app_stack_handler.03 |
dd app_stack_handler.fail ;04 |
dd app_stack_handler.fail ;05 |
dd stack_insert_packet ;app_stack_handler.06 |
dd app_stack_handler.fail ;07 |
dd stack_get_packet ;app_stack_handler.08 |
dd app_stack_handler.09 |
dd app_stack_handler.10 |
dd app_stack_handler.11 |
dd app_stack_handler.12 |
dd app_stack_handler.13 |
dd app_stack_handler.14 |
dd app_stack_handler.15 |
endg |
app_stack_handler: |
;in ebx,ecx |
;out eax |
cmp ebx, 15 |
ja .fail ;if more than 15 then exit |
NET_set_default: |
jmp dword [f52call+ebx*4] |
DEBUGF 1,"NET_set_default: device=%x\n", eax |
cmp eax, MAX_NET_DEVICES |
jae .error |
.00: |
; Read the configuration word |
mov eax, [stack_config] |
cmp [NET_DRV_LIST+eax*4], 0 |
je .error |
mov [NET_DEFAULT], eax |
DEBUGF 1,"NET_set_default: succes\n" |
ret |
.01: |
; read the IP address |
mov eax, [stack_ip] |
.error: |
or eax, -1 |
DEBUGF 1,"NET_set_default: failed\n" |
ret |
.02: |
; write the configuration word |
mov [stack_config], ecx |
; <Slip shouldn't be active anyway - thats an operational issue.> |
; If ethernet now enabled, probe for the card, reset it and empty |
; the packet buffer |
; If all successfull, enable the card. |
; If ethernet now disabled, set it as disabled. Should really |
; empty the tcpip data area too. |
;----------------------------------------------------------------- |
; |
; NET_Remove_Device: |
; |
; This function is called by etwork drivers, |
; to unregister network devices from the kernel |
; |
; IN: Pointer to device structure in ebx |
; OUT: eax: -1 on error |
; |
;----------------------------------------------------------------- |
align 4 |
NET_remove_device: |
; ethernet interface is '3' in ls 7 bits |
and cl, 0x7f |
cmp cl, 3 |
je ash_eth_enable |
; Ethernet isn't enabled, so make sure that the card is disabled |
mov [ethernet_active], byte 0 |
ret |
cmp [NET_RUNNING], 0 |
je .error |
.03: |
; write the IP Address |
mov [stack_ip], ecx |
ret |
;old functions was deleted |
;.06: |
; Insert an IP packet into the stacks received packet queue |
; call stack_insert_packet |
; ret |
cmp [NET_DRV_LIST], ebx |
jne @f |
mov [NET_DRV_LIST], 0 |
cmp [NET_RUNNING], 1 |
je @f |
; there are still active devices, find one and make it default |
xor eax, eax |
mov ecx, MAX_NET_DEVICES |
mov edi, NET_DRV_LIST |
repe scasd |
je @f |
shr edi, 2 |
dec edi |
mov [NET_DEFAULT], edi |
@@: |
; Test for any packets queued for transmission over the network |
;---------------------------- |
; Find the driver in the list |
;.08: |
; call stack_get_packet |
; Extract a packet queued for transmission by the network |
; ret |
mov eax, ebx |
mov ecx, MAX_NET_DEVICES |
mov edi, NET_DRV_LIST+4 |
.09: |
; read the gateway IP address |
mov eax, [gateway_ip] |
ret |
repne scasd |
jnz .error |
.10: |
; read the subnet mask |
mov eax, [subnet_mask] |
ret |
.11: |
; write the gateway IP Address |
mov [gateway_ip], ecx |
ret |
;------------------------ |
; Remove it from the list |
.12: |
; write the subnet mask |
mov [subnet_mask], ecx |
ret |
xor eax, eax |
mov dword [edi-4], eax |
.13: |
; read the dns |
mov eax, [dns_ip] |
ret |
call NET_send_event |
.14: |
; write the dns IP Address |
mov [dns_ip], ecx |
dec [NET_RUNNING] |
ret |
.15: |
;<added by Frank Sommer> |
; in ecx we need 4 to read the last 2 bytes |
; or we need 0 to read the first 4 bytes |
cmp ecx, 4 |
ja .param_error |
; read MAC, returned (in mirrored byte order) in eax |
mov eax, [node_addr + ecx] |
.error: |
or eax, -1 |
ret |
.param_error: |
or eax, -1 ; params not accepted |
ret |
.16: |
; 0 -> arp_probe |
; 1 -> arp_announce |
; 2 -> arp_responce (not supported yet) |
test ecx, ecx |
je a_probe |
dec ebx |
jz a_ann ; arp announce |
.fail: |
or eax, -1 |
ret |
;----------------------------------------------------------------- |
; |
; NET_ptr_to_num |
; |
; IN: ebx = ptr to device struct |
; OUT: edi = -1 on error, device number otherwise |
; |
;----------------------------------------------------------------- |
align 4 |
NET_ptr_to_num: |
push ecx |
; cmp ebx,2 |
; jne a_resp ; arp response |
mov ecx, MAX_NET_DEVICES |
mov edi, NET_DRV_LIST |
; arp probe, sender IP must be set to 0.0.0.0, target IP is set to address being probed |
; ecx: pointer to target MAC, MAC should set to 0 by application |
; edx: target IP |
a_probe: |
push dword [stack_ip] |
.loop: |
cmp ebx, [edi] |
jz .found |
add edi, 4 |
dec ecx |
jnz .loop |
mov edx, [stack_ip] |
and [stack_ip], dword 0 |
mov esi, ecx ; pointer to target MAC address |
call arp_request |
; repnz scasd could work too if eax is used instead of ebx! |
pop dword [stack_ip] |
ret |
or edi, -1 |
; arp announce, sender IP must be set to target IP |
; ecx: pointer to target MAC |
a_ann: |
mov edx, [stack_ip] |
mov esi, ecx ; pointer to target MAC address |
call arp_request |
pop ecx |
ret |
.17: |
;</added by Frank Sommer> |
; modified by [smb] |
.found: |
sub edi, NET_DRV_LIST |
shr edi, 2 |
;<added by Johnny_B> |
; ARPTable manager interface |
;see "proc arp_table_manager" for more details |
stdcall arp_table_manager, ecx, edx, esi;Opcode,Index,Extra |
pop ecx |
ret |
;</added by Johnny_B> |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
ash_eth_enable: |
; Probe for the card. This will reset it and enable the interface |
; if found |
call eth_probe |
test eax, eax |
jz ash_eth_done ; Abort if no hardware found |
mov [ethernet_active], byte 1 |
ash_eth_done: |
ret |
;*************************************************************************** |
; Function |
; app_socket_handler |
;----------------------------------------------------------------- |
; |
; Description |
; This is an application service, called by int 0x40, function 53 |
; It provides application access to stack socket services |
; such as opening sockets |
; checksum_1 |
; |
;*************************************************************************** |
iglobal |
; This is the first of two functions needed to calculate a checksum. |
; |
; IN: edx = start offset for semi-checksum |
; esi = pointer to data |
; ecx = data size |
; OUT: edx = semi-checksum |
; |
; |
; Code was optimized by diamond |
; |
;----------------------------------------------------------------- |
align 4 |
f53call: |
dd socket_open ;00 |
dd socket_close ;01 |
dd socket_poll ;02 |
dd socket_read ;03 |
dd socket_write ;04 |
dd socket_open_tcp ;05 |
dd socket_status ;06 |
dd socket_write_tcp ;07 |
dd socket_close_tcp ;08 |
dd is_localport_unused ;09 |
dd app_socket_handler.10 |
dd socket_read_packet ;11 |
endg |
checksum_1: |
app_socket_handler: |
;in ebx,ecx,edx,wsi |
;out eax |
cmp eax, 255 |
je stack_internal_status |
shr ecx, 1 |
pushf |
jz .no_2 |
cmp eax, 11 |
ja .fail ;if more than 15 then exit |
shr ecx, 1 |
pushf |
jz .no_4 |
jmp dword [f53call+eax*4] |
shr ecx, 1 |
pushf |
jz .no_8 |
.10: |
mov eax, dword[drvr_cable] |
test eax, eax |
jnz @f ; if function is not implented, return -1 |
or al, -1 |
ret |
@@: |
jmp dword[drvr_cable] |
.loop: |
add dl, [esi+1] |
adc dh, [esi+0] |
.fail: |
or eax, -1 |
ret |
uglobal |
ARPTmp: |
times 14 db 0 |
endg |
adc dl, [esi+3] |
adc dh, [esi+2] |
;*************************************************************************** |
; Function |
; stack_internal_status |
; |
; Description |
; Returns information about the internal status of the stack |
; This is only useful for debugging |
; It works with the ethernet driver |
; sub function in ebx |
; return requested data in eax |
; |
;*************************************************************************** |
; This sub function allows access to debugging information on the stack |
; ecx holds the request: |
; 100 : return length of empty queue |
; 101 : return length of IPOUT QUEUE |
; 102 : return length of IPIN QUEUE |
; 103 : return length of NET1OUT QUEUE |
; 200 : return # of ARP entries |
; 201 : return size of ARP table ( max # entries ) |
; 202 : select ARP table entry # |
; 203 : return IP of selected table entry |
; 204 : return High 4 bytes of MAC address of selected table entry |
; 205 : return low 2 bytes of MAC address of selected table entry |
; 206 : return status word of selected table entry |
; 207 : return Time to live of selected table entry |
adc dl, [esi+5] |
adc dh, [esi+4] |
adc dl, [esi+7] |
adc dh, [esi+6] |
; 2 : return number of IP packets received |
; 3 : return number of packets transmitted |
; 4 : return number of received packets dumped |
; 5 : return number of arp packets received |
; 6 : return status of packet driver |
; ( 0 == not active, FFFFFFFF = successful ) |
adc edx, 0 |
add esi, 8 |
dec ecx |
jnz .loop |
stack_internal_status: |
cmp ebx, 100 |
jnz notsis100 |
adc edx, 0 |
; 100 : return length of EMPTY QUEUE |
mov ebx, EMPTY_QUEUE |
call queueSize |
ret |
.no_8: |
popf |
jnc .no_4 |
notsis100: |
cmp ebx, 101 |
jnz notsis101 |
add dl, [esi+1] |
adc dh, [esi+0] |
; 101 : return length of IPOUT QUEUE |
mov ebx, IPOUT_QUEUE |
call queueSize |
ret |
adc dl, [esi+3] |
adc dh, [esi+2] |
notsis101: |
cmp ebx, 102 |
jnz notsis102 |
adc edx, 0 |
add esi, 4 |
; 102 : return length of IPIN QUEUE |
mov ebx, IPIN_QUEUE |
call queueSize |
ret |
.no_4: |
popf |
jnc .no_2 |
notsis102: |
cmp ebx, 103 |
jnz notsis103 |
add dl, [esi+1] |
adc dh, [esi+0] |
; 103 : return length of NET1OUT QUEUE |
mov ebx, NET1OUT_QUEUE |
call queueSize |
ret |
adc edx, 0 |
inc esi |
inc esi |
notsis103: |
cmp ebx, 200 |
jnz notsis200 |
.no_2: |
popf |
jnc .end |
; 200 : return num entries in arp table |
movzx eax, byte [NumARP] |
add dh, [esi+0] |
adc edx, 0 |
.end: |
ret |
notsis200: |
cmp ebx, 201 |
jnz notsis201 |
;----------------------------------------------------------------- |
; |
; checksum_2 |
; |
; This function calculates the final ip/tcp/udp checksum for you |
; |
; IN: edx = semi-checksum |
; OUT: dx = checksum (in INET byte order) |
; |
;----------------------------------------------------------------- |
align 4 |
checksum_2: |
; 201 : return arp table size |
mov eax, 20; ARP_TABLE_SIZE |
ret |
mov ecx, edx |
shr ecx, 16 |
and edx, 0xffff |
add edx, ecx |
notsis201: |
cmp ebx, 202 |
jnz notsis202 |
mov ecx, edx |
shr ecx, 16 |
add dx, cx |
test dx, dx ; it seems that ZF is not set when CF is set :( |
not dx |
jnz .not_zero |
dec dx |
.not_zero: |
xchg dl, dh |
; 202 - read the requested table entry |
; into a temporary buffer |
; ecx holds the entry number |
DEBUGF 1,"Checksum: %x\n", dx |
mov eax, ecx |
mov ecx, 14; ARP_ENTRY_SIZE |
mul ecx |
mov ecx, [eax + ARPTable] |
mov [ARPTmp], ecx |
mov ecx, [eax + ARPTable+4] |
mov [ARPTmp+4], ecx |
mov ecx, [eax + ARPTable+8] |
mov [ARPTmp+8], ecx |
mov cx, [eax + ARPTable+12] |
mov [ARPTmp+12], cx |
ret |
notsis202: |
cmp ebx, 203 |
jnz notsis203 |
; 203 - return IP address |
mov eax, [ARPTmp] |
ret |
notsis203: |
cmp ebx, 204 |
jnz notsis204 |
;---------------------------------------------------------------- |
; |
; System function to work with network devices (75) |
; |
;---------------------------------------------------------------- |
align 4 |
sys_network: ; FIXME: make default device easily accessible |
; 204 - return MAC high dword |
mov eax, [ARPTmp+4] |
ret |
cmp ebx, -1 |
jne @f |
notsis204: |
cmp ebx, 205 |
jnz notsis205 |
mov eax, [NET_RUNNING] |
jmp .return |
; 205 - return MAC ls word |
movzx eax, word [ARPTmp+8] |
ret |
@@: |
cmp bh, MAX_NET_DEVICES ; Check if device number exists |
jae .doesnt_exist |
notsis205: |
cmp ebx, 206 |
jnz notsis206 |
mov esi, ebx |
and esi, 0x0000ff00 |
shr esi, 6 |
; 206 - return status word |
movzx eax, word [ARPTmp+10] |
ret |
cmp dword [esi + NET_DRV_LIST], 0 ; check if driver is running |
je .doesnt_exist |
notsis206: |
cmp ebx, 207 |
jnz notsis207 |
mov eax, [esi + NET_DRV_LIST] |
; 207 - return ttl word |
movzx eax, word [ARPTmp+12] |
ret |
and ebx, 0x000000ff |
cmp ebx, .number |
ja .doesnt_exist |
jmp dword [.table + 4*ebx] |
notsis207: |
cmp ebx, 2 |
jnz notsis2 |
.table: |
dd .get_type ; 0 |
dd .get_dev_name ; 1 |
dd .reset ; 2 |
dd .stop ; 3 |
dd .get_ptr ; 4 |
dd .get_drv_name ; 5 |
dd .set_default ; 6 |
.number = ($ - .table) / 4 - 1 |
; 2 : return number of IP packets received |
mov eax, [ip_rx_count] |
ret |
.get_type: ; 0 = Get device type (ethernet/token ring/...) |
notsis2: |
cmp ebx, 3 |
jnz notsis3 |
mov eax, [eax + NET_DEVICE.type] |
jmp .return |
; 3 : return number of packets transmitted |
mov eax, [ip_tx_count] |
ret |
notsis3: |
cmp ebx, 4 |
jnz notsis4 |
.get_dev_name: ; 1 = Get device name |
; 4 : return number of received packets dumped |
mov eax, [dumped_rx_count] |
ret |
mov esi, [eax + NET_DEVICE.name] |
mov edi, ecx |
notsis4: |
cmp ebx, 5 |
jnz notsis5 |
mov ecx, 64/4 ; max length |
rep movsd |
; 5 : return number of arp packets received |
mov eax, [arp_rx_count] |
ret |
xor eax, eax |
jmp .return |
notsis5: |
cmp ebx, 6 |
jnz notsis6 |
.reset: ; 2 = Reset the device |
; 6 : return status of packet driver |
; ( 0 == not active, FFFFFFFF = successful ) |
mov eax, [eth_status] |
ret |
call [eax + NET_DEVICE.reset] |
jmp .return |
notsis6: |
xor eax, eax |
ret |
.stop: ; 3 = Stop driver for this device |
call [eax + NET_DEVICE.unload] |
jmp .return |
;*************************************************************************** |
; Function |
; stack_get_packet |
; |
; Description |
; extracts an IP packet from the NET1 output queue |
; and sends the data to the calling process |
; pointer to data in edx |
; returns number of bytes read in eax |
; |
;*************************************************************************** |
stack_get_packet: |
; Look for a buffer to tx |
mov eax, NET1OUT_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je sgp_non_exit ; Exit if no buffer available |
.get_ptr: ; 4 = Get driver pointer |
push eax ; Save buffer number for freeing at end |
jmp .return |
push edx |
; convert buffer pointer eax to the absolute address |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
pop edx |
push eax ; save address of IP data |
; Get the address of the callers data |
mov edi, [TASK_BASE] |
add edi, TASKDATA.mem_start |
add edx, [edi] |
mov edi, edx |
pop eax |
.get_drv_name: ; 5 = Get driver name |
mov ecx, 1500 ; should get the actual number of bytes to write |
mov esi, eax |
cld |
rep movsb ; copy the data across |
xor eax, eax |
jmp .return |
; And finally, return the buffer to the free queue |
pop eax |
call freeBuff |
mov eax, 1500 |
ret |
.set_default: ; 6 = Set default device |
sgp_non_exit: |
xor eax, eax |
call NET_set_default |
jmp .return |
.doesnt_exist: |
mov eax, -1 |
.return: |
mov [esp+32], eax |
ret |
;*************************************************************************** |
; Function |
; stack_insert_packet |
;---------------------------------------------------------------- |
; |
; Description |
; writes an IP packet into the stacks receive queue |
; # of bytes to write in ecx |
; pointer to data in edx |
; returns 0 in eax ok, -1 == failed |
; System function to work with protocols (76) |
; |
;*************************************************************************** |
stack_insert_packet: |
;---------------------------------------------------------------- |
align 4 |
sys_protocols: |
cmp bh, MAX_NET_DEVICES ; Check if device number exists |
jae .doesnt_exist |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je sip_err_exit |
mov esi, ebx |
and esi, 0x0000ff00 |
shr esi, 6 ; now we have the device num * 4 in esi |
cmp [esi + NET_DRV_LIST], 0 ; check if driver is running |
je .doesnt_exist |
push eax |
push .return ; return address (we will be using jumps instead of calls) |
; save the pointers to the data buffer & size |
push edx |
push ecx |
mov eax, ebx ; set ax to protocol number |
shr eax, 16 ; |
; convert buffer pointer eax to the absolute address |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
cmp ax, API_ETH |
je ETH_api |
mov edx, eax |
cmp ax, API_IPv4 |
je IPv4_api |
; So, edx holds the IPbuffer ptr |
cmp ax, API_ICMP |
je ICMP_api |
pop ecx ; count of bytes to send |
mov ebx, ecx ; need the length later |
pop eax ; get callers ptr to data to send |
cmp ax, API_UDP |
je UDP_api |
; Get the address of the callers data |
mov edi, [TASK_BASE] |
add edi, TASKDATA.mem_start |
add eax, [edi] |
mov esi, eax |
cmp ax, API_TCP |
je TCP_api |
mov edi, edx |
cld |
rep movsb ; copy the data across |
cmp ax, API_ARP |
je ARP_api |
pop ebx |
cmp ax, API_PPPOE |
je PPPoE_api |
mov eax, IPIN_QUEUE |
call queue |
cmp ax, API_IPv6 |
je IPv6_api |
inc dword [ip_rx_count] |
add esp, 4 ; if we reached here, no function was called, so we need to balance stack |
mov eax, 0 |
ret |
.doesnt_exist: |
mov eax, -1 |
sip_err_exit: |
mov eax, 0xFFFFFFFF |
.return: |
mov [esp+28+4], eax ; return eax value to the program |
ret |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/tcp.inc |
---|
1,1176 → 1,224 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; TCP.INC ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; TCP Processes for Menuet OS TCP/IP stack ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; See file COPYING for details ;; |
;; v0.6 : Added reset handling in the established state ;; |
;; Added a timer per socket to allow delays when ;; |
;; rx window gets below 1KB ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 3406 $ |
; Socket states |
TCPS_CLOSED = 0 |
TCPS_LISTEN = 1 |
TCPS_SYN_SENT = 2 |
TCPS_SYN_RECEIVED = 3 |
TCPS_ESTABLISHED = 4 |
TCPS_CLOSE_WAIT = 5 |
TCPS_FIN_WAIT_1 = 6 |
TCPS_CLOSING = 7 |
TCPS_LAST_ACK = 8 |
TCPS_FIN_WAIT_2 = 9 |
TCPS_TIMED_WAIT = 10 |
; TCP TCB states |
TCB_LISTEN equ 1 |
TCB_SYN_SENT equ 2 |
TCB_SYN_RECEIVED equ 3 |
TCB_ESTABLISHED equ 4 |
TCB_FIN_WAIT_1 equ 5 |
TCB_FIN_WAIT_2 equ 6 |
TCB_CLOSE_WAIT equ 7 |
TCB_CLOSING equ 8 |
TCB_LAST_ACK equ 9 |
TCB_TIMED_WAIT equ 10 |
TCB_CLOSED equ 11 |
; Socket Flags |
TF_ACKNOW = 1 shl 0 ; ack peer immediately |
TF_DELACK = 1 shl 1 ; ack, but try to delay it |
TF_NODELAY = 1 shl 2 ; don't delay packets to coalesce |
TF_NOOPT = 1 shl 3 ; don't use tcp options |
TF_SENTFIN = 1 shl 4 ; have sent FIN |
TF_REQ_SCALE = 1 shl 5 ; have/will request window scaling |
TF_RCVD_SCALE = 1 shl 6 ; other side has requested scaling |
TF_REQ_TSTMP = 1 shl 7 ; have/will request timestamps |
TF_RCVD_TSTMP = 1 shl 8 ; a timestamp was received in SYN |
TF_SACK_PERMIT = 1 shl 9 ; other side said I could SACK |
TH_FIN = 0x01 |
TH_SYN = 0x02 |
TH_RST = 0x04 |
TH_PUSH = 0x08 |
TH_ACK = 0x10 |
TH_URG = 0x20 |
; Segment flags |
TH_FIN = 1 shl 0 |
TH_SYN = 1 shl 1 |
TH_RST = 1 shl 2 |
TH_PUSH = 1 shl 3 |
TH_ACK = 1 shl 4 |
TH_URG = 1 shl 5 |
TWOMSL equ 10 ; # of secs to wait before closing socket |
; Segment header options |
TCP_OPT_EOL = 0 ; End of option list. |
TCP_OPT_NOP = 1 ; No-Operation. |
TCP_OPT_MAXSEG = 2 ; Maximum Segment Size. |
TCP_OPT_WINDOW = 3 ; window scale |
TCP_OPT_SACK_PERMIT = 4 ; Selective Acknowledgement |
TCP_OPT_SACK = 5 |
TCP_OPT_TIMESTAMP = 8 |
TCP_RETRIES equ 5 ; Number of times to resend a packet |
TCP_TIMEOUT equ 20 ; resend if not replied to in x hs |
; Fundamental timer values |
TCP_time_MSL = 47 ; max segment lifetime (30s) |
TCP_time_re_min = 2 ; min retransmission (1,28s) |
TCP_time_re_max = 100 ; max retransmission (64s) |
TCP_time_pers_min = 8 ; min persist (5,12s) |
TCP_time_pers_max = 94 ; max persist (60,16s) |
TCP_time_keep_init = 118 ; connection establishment (75,52s) |
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h) |
TCP_time_keep_interval = 118 ; between probes when no response (75,52s) |
TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s) |
TCP_time_srtt_default = 0 ; |
TCP_time_max_idle = 8*TCP_time_keep_interval ; FIXME |
;******************************************************************* |
; Interface |
; |
; tcp_tx_handler Handles the TCP transmit queue |
; tcp_rx The protocol handler for received data |
; buildTCPPacket fills in the packet headers and data |
; tcpStateMachine Main state machine for received TCP packets |
; tcp_tcb_handler 1s timer, to erase tcb's in TIME_WAIT state |
; |
;******************************************************************* |
; timer constants |
TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK |
TCP_max_keepcnt = 8 ; max keepalive probes |
; TCP Payload ( Data field in IP datagram ) |
; |
; 0 1 2 3 |
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;20 | Source Port | Destination Port | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;24 | Sequence Number | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;28 | Acknowledgment Number | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;32 | Data | |U|A|P|R|S|F| | |
; | Offset| Reserved |R|C|S|S|Y|I| Window | |
; | | |G|K|H|T|N|N| | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;36 | Checksum | Urgent Pointer | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;40 | Options | Padding | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | data |
TCP_max_winshift = 14 |
TCP_max_win = 65535 |
TCP_re_xmit_thresh = 3 |
struc TCP_PACKET |
{ .SourcePort dw ? ;+00 |
.DestinationPort dw ? ;+02 |
.SequenceNumber dd ? ;+04 |
.AckNumber dd ? ;+08 |
.DataOffset db ? ;+12 - DataOffset[0-3 bits] and Reserved[4-7] |
.Flags db ? ;+13 - Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN |
.Window dw ? ;+14 |
.Checksum dw ? ;+16 |
.UrgentPointer dw ? ;+18 |
.Options rb 3 ;+20 |
.Padding db ? ;+23 |
.Data db ? ;+24 |
} |
TCP_mss_default = 1480 ; default max segment size |
virtual at 0 |
TCP_PACKET TCP_PACKET |
end virtual |
; smoothed round trip time and estimated variance are stored as fixed point numbers, |
; shifted by the value below. |
; With these scales, srtt has 3 bits to the right of the binary point, and thus an "alpha" |
; of .875. rttvar has 2 bits to the right and thus "alpha" of 0.75 |
TCP_RTT_SHIFT = 3 |
TCP_RTTVAR_SHIFT = 2 |
; bits used by tcp_input and tcp_output |
TCP_BIT_NEEDOUTPUT = 1 shl 0 |
TCP_BIT_TIMESTAMP = 1 shl 1 |
TCP_BIT_DROPSOCKET = 1 shl 2 |
TCP_BIT_SENDALOT = 1 shl 0 |
;*************************************************************************** |
; Function |
; tcp_tcb_handler |
; |
; Description |
; Handles sockets in the timewait state, closing them |
; when the TCB timer expires |
; |
;*************************************************************************** |
TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds |
proc tcp_tcb_handler stdcall uses ebx |
; scan through all the sockets, decrementing active timers |
TCP_QUEUE_SIZE = 50 |
mov ebx, net_sockets |
struct TCP_header |
cmp [ebx + SOCKET.NextPtr], 0 |
je .exit |
;DEBUGF 1, "K : sockets:\n" |
SourcePort dw ? |
DestinationPort dw ? |
SequenceNumber dd ? |
AckNumber dd ? |
DataOffset db ? ; DataOffset[0-3 bits] and Reserved[4-7] |
Flags db ? ; Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN |
Window dw ? |
Checksum dw ? |
UrgentPointer dw ? |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .exit |
ends |
;DEBUGF 1, "K : %x-%x: %x-%x-%x-%u\n", [ebx + SOCKET.PID]:2, [ebx + SOCKET.Number]:2, [ebx + SOCKET.LocalPort]:4, [ebx + SOCKET.RemoteIP], [ebx + SOCKET.RemotePort]:4, [ebx + SOCKET.TCBState] |
struct TCP_queue_entry |
cmp [ebx + SOCKET.TCBTimer], 0 |
jne .decrement_tcb |
cmp [ebx + SOCKET.wndsizeTimer], 0 |
jne .decrement_wnd |
jmp .next_socket |
ip_ptr dd ? |
segment_ptr dd ? |
segment_size dd ? |
device_ptr dd ? |
.decrement_tcb: |
; decrement it, delete socket if TCB timer = 0 & socket in timewait state |
dec [ebx + SOCKET.TCBTimer] |
jnz .next_socket |
buffer_ptr dd ? |
timestamp dd ? |
cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
jne .next_socket |
ends |
push [ebx + SOCKET.PrevPtr] |
stdcall net_socket_free, ebx |
pop ebx |
jmp .next_socket |
align 4 |
uglobal |
TCP_segments_tx rd MAX_NET_DEVICES |
TCP_segments_rx rd MAX_NET_DEVICES |
TCP_segments_missed rd MAX_NET_DEVICES |
TCP_segments_dumped rd MAX_NET_DEVICES |
; TCP_bytes_rx rq MAX_NET_DEVICES |
; TCP_bytes_tx rq MAX_NET_DEVICES |
TCP_sequence_num dd ? |
TCP_queue rd TCP_QUEUE_SIZE*sizeof.TCP_queue_entry/4 |
TCP_input_event dd ? |
endg |
.decrement_wnd: |
; TODO - prove it works! |
dec [ebx + SOCKET.wndsizeTimer] |
jmp .next_socket |
.exit: |
ret |
endp |
;*************************************************************************** |
; Function |
; tcp_tx_handler |
;----------------------------------------------------------------- |
; |
; Description |
; Handles queued TCP data |
; This is a kernel function, called by stack_handler |
; TCP_init |
; |
;*************************************************************************** |
; This function resets all TCP variables |
; |
;----------------------------------------------------------------- |
macro TCP_init { |
proc tcp_tx_handler stdcall |
; decrement all resend buffers timers. If they |
; expire, queue them for sending, and restart the timer. |
; If the retries counter reach 0, delete the entry |
xor eax, eax |
mov edi, TCP_segments_tx |
mov ecx, (6*MAX_NET_DEVICES) |
rep stosd |
mov esi, resendQ |
mov ecx, 0 |
pseudo_random eax |
mov [TCP_sequence_num], eax |
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .exit ; None left |
cmp dword[esi + 4], 0 |
jne @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
init_queue TCP_queue |
@@: ; we have one. decrement it's timer by 1 |
dec word[esi + 2] |
jz @f |
inc ecx |
add esi, 8 |
jmp .next_resendq ; Timer not zero, so move on |
@@: |
xor ebx, ebx |
; restart timer, and decrement retries |
; After the first resend, back of on next, by a factor of 5 |
mov [esi + 2], word TCP_TIMEOUT * 5 |
dec byte[esi + 1] |
jnz @f |
; retries now 0, so delete from queue |
xchg [esi + 4], ebx |
@@: ; resend packet |
pushad |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
jne .tth004z |
; TODO - try again in 10ms. |
test ebx, ebx |
jnz @f |
mov [esi + 4], ebx |
@@: ; Mark it to expire in 10ms - 1 tick |
mov byte[esi + 1], 1 |
mov word[esi + 2], 1 |
jmp .tth005 |
.tth004z: |
; we have a buffer # in ax |
push eax ecx |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
; we have the buffer address in eax |
mov edi, eax |
pop ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
imul esi, ecx, IPBUFFSIZE |
add esi, resendBuffer |
; we have resend buffer location in esi |
mov ecx, IPBUFFSIZE |
; copy data across |
push edi |
cld |
rep movsb |
pop edi |
; queue packet |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
cmp edx, [edi + IP_PACKET.DestinationAddress] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
push 1 |
pop ebx |
call queue |
mov ecx, TCP_process_input |
call new_sys_threads |
.tth005: |
popad |
} |
inc ecx |
add esi, 8 |
jmp .next_resendq |
.exit: |
ret |
endp |
include 'tcp_timer.inc' |
include 'tcp_subr.inc' |
include 'tcp_usreq.inc' |
include 'tcp_input.inc' |
include 'tcp_output.inc' |
;*************************************************************************** |
; Function |
; tcp_rx |
;--------------------------------------------------------------------------- |
; |
; Description |
; TCP protocol handler |
; This is a kernel function, called by ip_rx |
; IP buffer address given in edx |
; IP buffer number in eax |
; Free up (or re-use) IP buffer when finished |
; TCP_API |
; |
;*************************************************************************** |
proc tcp_rx stdcall uses ebx |
; The process is as follows. |
; Look for a socket with matching remote IP, remote port, local port |
; if not found, then |
; look for remote IP + local port match ( where sockets remote port = 0) |
; if not found, then |
; look for a socket where local socket port == IP packets remote port |
; where sockets remote port, remote IP = 0 |
; discard if not found |
; Call sockets tcbStateMachine, with pointer to packet. |
; the state machine will not delete the packet, so do that here. |
push eax |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; IP Packet SA = Remote IP |
; IP Packet TCP Source Port = remote Port |
mov ebx, net_sockets |
.next_socket.1: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.1.exit |
; DEBUGF 1, "K : tcp_rx - 1.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; get the dest. port from the TCP hdr |
jne .next_socket.1 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 1.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP] |
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP |
jne .next_socket.1 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 1.sport: %x - %x\n", [edx + 20 + TCP_PACKET.SourcePort]:4, [ebx + SOCKET.RemotePort]:4 |
mov ax, [edx + 20 + TCP_PACKET.SourcePort] ; get the source port from the TCP hdr |
cmp [ebx + SOCKET.RemotePort], ax ; compare with socket's remote port |
jne .next_socket.1 ; different - try next socket |
; We have a complete match - use this socket |
jmp .change_state |
.next_socket.1.exit: |
; If we got here, there was no match |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; IP Packet SA = Remote IP |
; socket remote Port = 0 |
mov ebx, net_sockets |
.next_socket.2: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.2.exit |
; DEBUGF 1, "K : tcp_rx - 2.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.2 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 2.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP] |
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP |
jne .next_socket.2 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 2.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.2 ; different - try next socket |
; We have a complete match - use this socket |
jmp .change_state |
.next_socket.2.exit: |
; If we got here, there was no match |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; socket Remote IP = 0 |
; socket remote Port = 0 |
mov ebx, net_sockets |
.next_socket.3: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.3.exit |
; DEBUGF 1, "K : tcp_rx - 3.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get destination port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.3 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 3.addr: 00000000 - %x\n", [ebx + SOCKET.RemoteIP] |
cmp [ebx + SOCKET.RemoteIP], 0 ; only match a socket remote IP of 0 |
jne .next_socket.3 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 3.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.3 ; different - try next socket |
; We have a complete match - use this socket |
jmp .change_state |
.next_socket.3.exit: |
; If we got here, we need to reject the packet |
DEBUGF 1, "K : tcp_rx - dumped\n" |
DEBUGF 1, "K : --------: %x-%x-%x (flags: %x)\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [edx + IP_PACKET.SourceAddress], [edx + 20 + TCP_PACKET.SourcePort]:4, [edx + 20 + TCP_PACKET.Flags]:2 |
inc [dumped_rx_count] |
jmp .exit |
.change_state: |
; We have a valid socket/TCB, so call the TCB State Machine for that skt. |
; socket is pointed to by ebx |
; IP packet is pointed to by edx |
; IP buffer number is on stack ( it will be popped at the end) |
stdcall tcpStateMachine, ebx |
.exit: |
pop eax |
call freeBuff |
ret |
endp |
;*************************************************************************** |
; Function |
; buildTCPPacket |
; This function is called by system function 76 |
; |
; Description |
; builds an IP Packet with TCP data fully populated for transmission |
; You may destroy any and all registers |
; TCP control flags specified in bl |
; This TCB is in [sktAddr] |
; User data pointed to by esi |
; Data length in ecx |
; Transmit buffer number in eax |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
;*************************************************************************** |
proc build_tcp_packet stdcall, sockAddr:DWORD |
push ecx ; Save data length |
; convert buffer pointer eax to the absolute address |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
mov edx, eax |
mov [edx + 20 + TCP_PACKET.Flags], bl ; TCP flags |
mov ebx, [sockAddr] |
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr |
; Fill in the IP header ( some data is in the socket descriptor) |
mov eax, [ebx + SOCKET.LocalIP] |
mov [edx + IP_PACKET.SourceAddress], eax |
mov eax, [ebx + SOCKET.RemoteIP] |
mov [edx + IP_PACKET.DestinationAddress], eax |
mov [edx + IP_PACKET.VersionAndIHL], 0x45 |
mov [edx + IP_PACKET.TypeOfService], 0 |
pop eax ; Get the TCP data length |
push eax |
add eax, 20 + 20 ; add IP header and TCP header lengths |
rol ax, 8 |
mov [edx + IP_PACKET.TotalLength], ax |
mov [edx + IP_PACKET.Identification], 0 |
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040 |
mov [edx + IP_PACKET.TimeToLive], 0x20 |
mov [edx + IP_PACKET.Protocol], PROTOCOL_TCP |
; Checksum left unfilled |
mov [edx + IP_PACKET.HeaderChecksum], 0 |
; Fill in the TCP header (some data is in the socket descriptor) |
mov ax, [ebx + SOCKET.LocalPort] |
mov [edx + 20 + TCP_PACKET.SourcePort], ax ; Local Port |
mov ax, [ebx + SOCKET.RemotePort] |
mov [edx + 20 + TCP_PACKET.DestinationPort], ax ; desitination Port |
; Checksum left unfilled |
mov [edx + 20 + TCP_PACKET.Checksum], 0 |
; sequence number |
mov eax, [ebx + SOCKET.SND_NXT] |
mov [edx + 20 + TCP_PACKET.SequenceNumber], eax |
; ack number |
mov eax, [ebx + SOCKET.RCV_NXT] |
mov [edx + 20 + TCP_PACKET.AckNumber], eax |
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size) |
; 768 bytes seems better |
mov [edx + 20 + TCP_PACKET.Window], 0x0003 |
; Urgent pointer (0) |
mov [edx + 20 + TCP_PACKET.UrgentPointer], 0 |
; data offset ( 0x50 ) |
mov [edx + 20 + TCP_PACKET.DataOffset], 0x50 |
pop ecx ; count of bytes to send |
mov ebx, ecx ; need the length later |
cmp ebx, 0 |
jz @f |
mov edi, edx |
add edi, 40 |
cld |
rep movsb ; copy the data across |
@@: ; we have edx as IPbuffer ptr. |
; Fill in the TCP checksum |
; First, fill in pseudoheader |
mov eax, [edx + IP_PACKET.SourceAddress] |
mov [pseudoHeader], eax |
mov eax, [edx + IP_PACKET.DestinationAddress] |
mov [pseudoHeader + 4], eax |
mov word[pseudoHeader + 8], PROTOCOL_TCP shl 8 + 0 |
add ebx, 20 |
mov [pseudoHeader + 10], bh |
mov [pseudoHeader + 11], bl |
mov eax, pseudoHeader |
mov [checkAdd1], eax |
mov word[checkSize1], 12 |
mov eax, edx |
add eax, 20 |
mov [checkAdd2], eax |
mov eax, ebx |
mov [checkSize2], ax |
call checksum |
; store it in the TCP checksum ( in the correct order! ) |
mov ax, [checkResult] |
rol ax, 8 |
mov [edx + 20 + TCP_PACKET.Checksum], ax |
; Fill in the IP header checksum |
GET_IHL eax, edx ; get IP-Header length |
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size |
rol ax, 8 |
mov [edx + IP_PACKET.HeaderChecksum], ax |
ret |
endp |
; Increments the 32 bit value pointed to by esi in internet order |
proc inc_inet_esi stdcall |
push eax |
mov eax, [esi] |
bswap eax |
inc eax |
bswap eax |
mov [esi], eax |
pop eax |
ret |
endp |
; Increments the 32 bit value pointed to by esi in internet order |
; by the value in ecx |
proc add_inet_esi stdcall |
push eax |
mov eax, [esi] |
bswap eax |
add eax, ecx |
bswap eax |
mov [esi], eax |
pop eax |
ret |
endp |
iglobal |
TCBStateHandler dd \ |
stateTCB_LISTEN, \ |
stateTCB_SYN_SENT, \ |
stateTCB_SYN_RECEIVED, \ |
stateTCB_ESTABLISHED, \ |
stateTCB_FIN_WAIT_1, \ |
stateTCB_FIN_WAIT_2, \ |
stateTCB_CLOSE_WAIT, \ |
stateTCB_CLOSING, \ |
stateTCB_LAST_ACK, \ |
stateTCB_TIME_WAIT, \ |
stateTCB_CLOSED |
endg |
;*************************************************************************** |
; Function |
; tcpStateMachine |
; OUT: |
; |
; Description |
; TCP state machine |
; This is a kernel function, called by tcp_rx |
; |
; IP buffer address given in edx |
; Socket/TCB address in ebx |
; |
; The IP buffer will be released by the caller |
;*************************************************************************** |
;--------------------------------------------------------------------------- |
align 4 |
TCP_api: |
proc tcpStateMachine stdcall, sockAddr:DWORD |
; as a packet has been received, update the TCB timer |
mov [ebx + SOCKET.TCBTimer], TWOMSL |
movzx eax, bh |
shl eax, 2 |
; If the received packet has an ACK bit set, |
; remove any packets in the resend queue that this |
; received packet acknowledges |
pushad |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .call_handler ; No ACK, so no data yet |
test bl, bl |
jz .packets_tx ; 0 |
dec bl |
jz .packets_rx ; 1 |
dec bl |
jz .packets_missed ; 2 |
dec bl |
jz .packets_dumped ; 3 |
; get skt number in eax |
stdcall net_socket_addr_to_num, ebx |
; The ack number is in [edx + 28], inet format |
; skt in eax |
mov esi, resendQ |
xor ecx, ecx |
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .call_handler ; None left |
cmp [esi + 4], eax |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
@@: ; Can we delete this buffer? |
; If yes, goto @@. No, goto .next_resendq |
; Get packet data address |
push ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
imul edi, ecx, IPBUFFSIZE |
add edi, resendBuffer |
; we have dest buffer location in edi. incoming packet in edx. |
; Get this packets sequence number |
; preserve al, ecx, esi, edx |
mov ecx, [edi + 20 + TCP_PACKET.SequenceNumber] |
bswap ecx |
movzx ebx, word[edi + 2] |
xchg bl, bh |
sub ebx, 40 |
add ecx, ebx ; ecx is now seq# of last byte +1, intel format |
; get recievd ack #, in intel format |
mov ebx, [edx + 20 + TCP_PACKET.AckNumber] |
bswap ebx |
cmp ebx, ecx ; Finally. ecx = rx'ed ack. ebx = last byte in que |
; DANGER! need to handle case that we have just |
; passed the 2**32, and wrapped round! |
pop ecx |
jae @f ; if rx > old, delete old |
inc ecx |
add esi, 8 |
jmp .next_resendq |
@@: |
mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
.call_handler: |
popad |
; Call handler for given TCB state |
mov eax, [ebx + SOCKET.TCBState] |
cmp eax, TCB_LISTEN |
jb .exit |
cmp eax, TCB_CLOSED |
ja .exit |
stdcall [TCBStateHandler + (eax - 1) * 4], [sockAddr] |
.exit: |
.error: |
mov eax, -1 |
ret |
endp |
;*************************************************************************** |
; Function |
; signal_network_event |
; |
; Description |
; Signals about network event to socket owner |
; This is a kernel function, called from TCP handler |
; |
; Socket/TCB address in ebx |
;*************************************************************************** |
proc signal_network_event |
push ecx esi eax |
mov eax, [ebx + SOCKET.PID] |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event |
pop eax esi ecx |
.packets_tx: |
mov eax, [TCP_segments_tx + eax] |
ret |
endp |
proc stateTCB_LISTEN stdcall, sockAddr:DWORD |
; In this case, we are expecting a SYN packet |
; For now, if the packet is a SYN, process it, and send a response |
; If not, ignore it |
; Look at control flags |
test [edx + 20 + TCP_PACKET.Flags], TH_SYN |
jz .exit |
; We have a SYN. update the socket with this IP packets details, |
; And send a response |
mov eax, [edx + IP_PACKET.SourceAddress] |
mov [ebx + SOCKET.RemoteIP], eax |
mov ax, [edx + 20 + TCP_PACKET.SourcePort] |
mov [ebx + SOCKET.RemotePort], ax |
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
mov [ebx + SOCKET.IRS], eax |
mov [ebx + SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi ; RCV.NXT |
mov eax, [ebx + SOCKET.ISS] |
mov [ebx + SOCKET.SND_NXT], eax |
; Now construct the response, and queue for sending by IP |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
push ebx |
push eax |
mov bl, TH_SYN + TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
call queue |
pop ebx |
mov esi, [sockAddr] |
mov [esi + SOCKET.TCBState], TCB_SYN_RECEIVED |
call signal_network_event |
; increment SND.NXT in socket |
add esi, SOCKET.SND_NXT |
call inc_inet_esi |
.exit: |
.packets_rx: |
mov eax, [TCP_segments_rx + eax] |
ret |
endp |
proc stateTCB_SYN_SENT stdcall, sockAddr:DWORD |
; We are awaiting an ACK to our SYN, with a SYM |
; Look at control flags - expecting an ACK |
mov al, [edx + 20 + TCP_PACKET.Flags] |
and al, TH_SYN + TH_ACK |
cmp al, TH_SYN + TH_ACK |
je .syn_ack |
test al, TH_SYN |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_SYN_RECEIVED |
push TH_SYN + TH_ACK |
jmp .send |
.syn_ack: |
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
push TH_ACK |
.send: |
call signal_network_event |
; Store the recv.nxt field |
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
; Update our recv.nxt field |
mov [ebx + SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
; Send an ACK |
; Now construct the response, and queue for sending by IP |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
pop ebx |
je .exit |
push eax |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
call queue |
.exit: |
.packets_missed: |
mov eax, [TCP_segments_missed + eax] |
ret |
endp |
proc stateTCB_SYN_RECEIVED stdcall, sockAddr:DWORD |
; In this case, we are expecting an ACK packet |
; For now, if the packet is an ACK, process it, |
; If not, ignore it |
test [edx + 20 + TCP_PACKET.Flags], TH_RST |
jz .check_ack |
push [ebx + SOCKET.OrigRemotePort] [ebx + SOCKET.OrigRemoteIP] |
pop [ebx + SOCKET.RemoteIP] [ebx + SOCKET.RemotePort] |
mov [ebx + SOCKET.TCBState], TCB_LISTEN |
jmp .signal |
.check_ack: |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
.signal: |
call signal_network_event |
.exit: |
.packets_dumped: |
mov eax, [TCP_segments_dumped + eax] |
ret |
endp |
proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD |
; Here we are expecting data, or a request to close |
; OR both... |
; Ignore all packets with sequnce number other than next expected |
; recv.nxt is in dword [edx+24], in inet format |
; recv seq is in [sktAddr]+56, in inet format |
; just do a comparision |
mov eax, [ebx + SOCKET.RCV_NXT] |
cmp eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
jne .exit |
; Did we receive a FIN or RST? |
test [edx + 20 + TCP_PACKET.Flags], TH_FIN+TH_RST |
jz .check_ack |
; It was a fin or reset. |
; Remove resend entries from the queue - I dont want to send any more data |
pushad |
; get skt # |
stdcall net_socket_addr_to_num, ebx |
mov esi, resendQ |
mov ecx, 0 |
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .last_resendq ; None left |
cmp [esi + 4], eax |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
@@: |
mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
.last_resendq: |
popad |
@@: ; Send an ACK to that fin, and enter closewait state |
mov [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
test [edx + 20 + TCP_PACKET.Flags], TH_RST |
je @f |
mov [ebx + SOCKET.TCBState], TCB_CLOSED |
@@: |
call signal_network_event |
lea esi, [ebx + SOCKET.RCV_NXT] |
mov eax, [esi] ; save original |
call inc_inet_esi |
;; jmp ste_ack - NO, there may be data |
.check_ack: |
; Check that we received an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
; TODO - done, I think! |
; First, look at the incoming window. If this is less than or equal to 1024, |
; Set the socket window timer to 1. This will stop an additional packets being queued. |
; ** I may need to tweak this value, since I do not know how many packets are already queued |
mov cx, [edx + 20 + TCP_PACKET.Window] |
xchg cl, ch |
cmp cx, 1024 |
ja @f |
mov [ebx + SOCKET.wndsizeTimer], 1 |
@@: ; OK, here is the deal |
; Read the data bytes, store in socket buffer |
movzx ecx, [edx + IP_PACKET.TotalLength] |
xchg cl, ch |
sub ecx, 40 ; Discard 40 bytes of header |
ja .data ; Read data, if any |
; If we had received a fin, we need to ACK it. |
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
je .ack |
jmp .exit |
.data: |
push ecx |
push ecx edx |
lea ecx, [ebx+SOCKET.mutex] |
call mutex_lock |
pop edx ecx |
push ebx |
mov eax, [ebx + SOCKET.rxDataCount] |
add eax, ecx |
cmp eax, SOCKETBUFFSIZE - SOCKETHEADERSIZE |
ja .overflow |
mov [ebx + SOCKET.rxDataCount], eax ; increment the count of bytes in buffer |
; point to the location to store the data |
lea edi, [ebx + eax + SOCKETHEADERSIZE] |
sub edi, ecx |
add edx, 40 ; edx now points to the data |
mov esi, edx |
cld |
rep movsb ; copy the data across |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
; flag an event to the application |
pop ebx |
call signal_network_event |
pop ecx |
; Update our recv.nxt field |
lea esi, [ebx + SOCKET.RCV_NXT] |
call add_inet_esi |
.ack: |
; Send an ACK |
; Now construct the response, and queue for sending by IP |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
call queue |
.exit: |
ret |
.overflow: |
; no place in buffer |
; so simply restore stack and exit |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax ecx |
ret |
endp |
proc stateTCB_FIN_WAIT_1 stdcall, sockAddr:DWORD |
; We can either receive an ACK of a fin, or a fin |
mov al, [edx + 20 + TCP_PACKET.Flags] |
and al, TH_FIN + TH_ACK |
cmp al, TH_ACK |
jne @f |
; It was an ACK |
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_2 |
jmp .exit |
@@: |
mov [ebx + SOCKET.TCBState], TCB_CLOSING |
cmp al, TH_FIN |
je @f |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
@@: |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
; Send an ACK |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
call queue |
.exit: |
ret |
endp |
proc stateTCB_FIN_WAIT_2 stdcall, sockAddr:DWORD |
test [edx + 20 + TCP_PACKET.Flags], TH_FIN |
jz .exit |
; Change state, as we have a fin |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
; Send an ACK |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
call queue |
.exit: |
ret |
endp |
proc stateTCB_CLOSE_WAIT stdcall, sockAddr:DWORD |
; Intentionally left empty |
; socket_close_tcp handles this |
ret |
endp |
proc stateTCB_CLOSING stdcall, sockAddr:DWORD |
; We can either receive an ACK of a fin, or a fin |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
.exit: |
ret |
endp |
proc stateTCB_LAST_ACK stdcall, sockAddr:DWORD |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
; delete the socket |
stdcall net_socket_free, ebx |
.exit: |
ret |
endp |
proc stateTCB_TIME_WAIT stdcall, sockAddr:DWORD |
ret |
endp |
proc stateTCB_CLOSED stdcall, sockAddr:DWORD |
ret |
endp |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/network/tcp_input.inc |
---|
0,0 → 1,1663 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3407 $ |
;----------------------------------------------------------------- |
; |
; TCP_input: |
; |
; Add a segment to the incoming TCP queue |
; |
; IN: [esp] = ptr to buffer |
; [esp+4] = buffer size (dont care) |
; ebx = ptr to device struct |
; ecx = segment size |
; esi = ptr to TCP segment |
; edi = ptr to ipv4 source address, followed by ipv4 dest address |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
TCP_input: |
; record the current time |
mov eax, [timer_ticks] ; in 1/100 seconds |
mov [esp + 4], eax |
push ebx ecx esi edi ; mind the order |
mov esi, esp |
pushf |
cli |
add_to_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .fail |
popf |
add esp, sizeof.TCP_queue_entry |
xor edx, edx |
mov eax, [TCP_input_event] |
mov ebx, [eax + EVENT.id] |
xor esi, esi |
call raise_event |
ret |
.fail: |
popf |
DEBUGF 2, "TCP incoming queue is full, discarding packet!\n" |
inc [TCP_segments_missed] ; FIXME: use correct interface |
add esp, sizeof.TCP_queue_entry - 8 |
call kernel_free |
add esp, 4 |
ret |
align 4 |
TCP_process_input: |
xor esi, esi |
mov ecx, MANUAL_DESTROY |
call create_event |
mov [TCP_input_event], eax |
.wait: |
mov eax, [TCP_input_event] |
mov ebx, [eax + EVENT.id] |
call wait_event |
.loop: |
get_from_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .wait |
push [esi + TCP_queue_entry.timestamp] |
push [esi + TCP_queue_entry.buffer_ptr] |
mov ebx, [esi + TCP_queue_entry.device_ptr] |
mov ecx, [esi + TCP_queue_entry.segment_size] |
mov edi, [esi + TCP_queue_entry.ip_ptr] ; ptr to ipv4 source address, followed by ipv4 destination address |
mov esi, [esi + TCP_queue_entry.segment_ptr] ; change esi last |
DEBUGF 1,"TCP_input: size=%u time=%d\n", ecx, [timer_ticks] |
mov edx, esi |
cmp ebx, LOOPBACK_DEVICE |
je .checksum_ok |
; re-calculate the checksum (if not already done by hw) |
; test [ebx + NET_DEVICE.hwacc], HWACC_TCP_IPv4_IN |
; jnz .checksum_ok |
push ecx esi |
pushw [esi + TCP_header.Checksum] |
mov [esi + TCP_header.Checksum], 0 |
TCP_checksum (edi), (edi+4) |
pop cx ; previous checksum |
cmp cx, dx |
pop edx ecx |
jne .drop_no_socket |
.checksum_ok: |
; Verify the data offset |
and [edx + TCP_header.DataOffset], 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) |
shr [edx + TCP_header.DataOffset], 2 |
cmp [edx + TCP_header.DataOffset], sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header |
jb .drop_no_socket ; If not, drop the packet |
movzx eax, [edx + TCP_header.DataOffset] |
sub ecx, eax ; substract TCP header size from total segment size |
jb .drop_no_socket ; If total segment size is less then the advertised header size, drop packet |
DEBUGF 1,"TCP_input: %u bytes of data\n", ecx |
;------------------------------------------- |
; Convert Big-endian values to little endian |
ntohd [edx + TCP_header.SequenceNumber] |
ntohd [edx + TCP_header.AckNumber] |
ntohw [edx + TCP_header.Window] |
ntohw [edx + TCP_header.UrgentPointer] |
;------------------------ |
; Find the socket pointer |
; IP Packet TCP Destination Port = local Port |
; (IP Packet SenderAddress = Remote IP) OR (Remote IP = 0) |
; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0) |
.findpcb: |
mov ebx, net_sockets |
mov si, [edx + TCP_header.DestinationPort] |
.socket_loop: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .respond_seg_reset |
cmp [ebx + SOCKET.Domain], AF_INET4 |
jne .socket_loop |
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP |
jne .socket_loop |
cmp [ebx + TCP_SOCKET.LocalPort], si |
jne .socket_loop |
mov eax, [ebx + IP_SOCKET.RemoteIP] |
cmp eax, [edi] ; Ipv4 source address |
je @f |
test eax, eax |
jnz .socket_loop |
@@: |
mov ax, [ebx + TCP_SOCKET.RemotePort] |
cmp [edx + TCP_header.SourcePort], ax |
je .found_socket |
test ax, ax |
jnz .socket_loop |
.found_socket: ; ebx now contains the socketpointer |
DEBUGF 1,"TCP_input: socket ptr=%x state=%u flags=%x\n", ebx, [ebx + TCP_SOCKET.t_state], [edx + TCP_header.Flags]:2 |
;------------- |
; update stats |
inc [TCP_segments_rx] ; FIXME: correct interface? |
;---------------------------- |
; Check if socket isnt closed |
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSED |
je .drop_no_socket |
;---------------- |
; Lock the socket |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_lock |
popa |
DEBUGF 1,"TCP_input: socket locked\n" |
;--------------------------- |
; disable all temporary bits |
mov [ebx + TCP_SOCKET.temp_bits], 0 |
;--------------------------------------- |
; unscale the window into a 32 bit value |
movzx eax, [edx + TCP_header.Window] |
push ecx |
mov cl, [ebx + TCP_SOCKET.SND_SCALE] |
shl eax, cl |
mov dword [edx + TCP_header.Window], eax ; word after window is checksum, we dont need checksum anymore |
pop ecx |
;--------------------------------------- |
; Are we accepting incoming connections? |
test [ebx + SOCKET.options], SO_ACCEPTCON |
jz .no_accept |
DEBUGF 1,"TCP_input: Accepting new connection\n" |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
popa |
push ecx edx esi edi ;;; |
call SOCKET_fork |
pop edi esi edx ecx |
test eax, eax |
jz .drop_no_socket |
mov ebx, eax |
mov [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET ;;; FIXME: should we take over bits from previous socket? |
push dword [edi + 4] ; Ipv4 destination addres |
pop [ebx + IP_SOCKET.LocalIP] |
push [edx + TCP_header.DestinationPort] |
pop [ebx + TCP_SOCKET.LocalPort] |
mov [ebx + TCP_SOCKET.t_state], TCPS_LISTEN |
.no_accept: |
;------------------------------------- |
; Reset idle timer and keepalive timer |
mov [ebx + TCP_SOCKET.t_idle], 0 |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle |
;-------------------- |
; Process TCP options |
push ecx |
movzx ecx, [edx + TCP_header.DataOffset] |
cmp ecx, sizeof.TCP_header ; Does header contain any options? |
je .no_options |
DEBUGF 1,"TCP_input: Segment has options\n" |
;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS |
;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state |
;;; jz .not_uni_xfer ; also no header prediction |
add ecx, edx |
lea esi, [edx + sizeof.TCP_header] |
.opt_loop: |
cmp esi, ecx ; are we scanning outside of header? |
jae .no_options |
lodsb |
cmp al, TCP_OPT_EOL ; end of option list? |
je .no_options |
cmp al, TCP_OPT_NOP |
je .opt_loop |
cmp al, TCP_OPT_MAXSEG |
je .opt_maxseg |
cmp al, TCP_OPT_WINDOW |
je .opt_window |
cmp al, TCP_OPT_SACK_PERMIT |
je .opt_sack_permit |
; cmp al, TCP_OPT_SACK |
; je .opt_sack |
cmp al, TCP_OPT_TIMESTAMP |
je .opt_timestamp |
DEBUGF 1,"TCP_input: unknown option:%u\n", al |
jmp .no_options ; If we reach here, some unknown options were received, skip them all! |
.opt_maxseg: |
lodsb |
cmp al, 4 |
jne .no_options ; error occured, ignore all options! |
test [edx + TCP_header.Flags], TH_SYN |
jz @f |
lodsw |
rol ax, 8 |
DEBUGF 1,"TCP_input: Maxseg=%u\n", ax |
call TCP_mss |
@@: |
jmp .opt_loop |
.opt_window: |
lodsb |
cmp al, 3 |
jne .no_options |
test [edx + TCP_header.Flags], TH_SYN |
jz @f |
DEBUGF 1,"TCP_input: Got window scale option\n" |
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
lodsb |
mov [ebx + TCP_SOCKET.SND_SCALE], al |
;;;;; TODO |
@@: |
jmp .opt_loop |
.opt_sack_permit: |
lodsb |
cmp al, 2 |
jne .no_options |
test [edx + TCP_header.Flags], TH_SYN |
jz @f |
DEBUGF 1,"TCP_input: Selective Acknowledgement permitted\n" |
or [ebx + TCP_SOCKET.t_flags], TF_SACK_PERMIT |
@@: |
jmp .opt_loop |
.opt_timestamp: |
lodsb |
cmp al, 10 ; length must be 10 |
jne .no_options |
DEBUGF 1,"TCP_input: Got timestamp option\n" |
test [edx + TCP_header.Flags], TH_SYN |
jz @f |
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
@@: |
lodsd |
mov [ebx + TCP_SOCKET.ts_val], eax |
lodsd ; timestamp echo reply |
mov [ebx + TCP_SOCKET.ts_ecr], eax |
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
; Since we have a timestamp, lets do the paws test right away! |
test [edx + TCP_header.Flags], TH_RST |
jnz .no_paws |
mov eax, [ebx + TCP_SOCKET.ts_recent] |
test eax, eax |
jz .no_paws |
cmp eax, [ebx + TCP_SOCKET.ts_val] |
jge .no_paws |
DEBUGF 1,"TCP_input: PAWS: detected an old segment\n" |
mov eax, [esp+4+4] ; tcp_now |
sub eax, [ebx + TCP_SOCKET.ts_recent_age] |
pop ecx |
cmp eax, TCP_PAWS_IDLE |
jle .drop_after_ack ; TODO: update stats |
push ecx |
mov [ebx + TCP_SOCKET.ts_recent], 0 ; timestamp was invalid, fix it. |
.no_paws: |
jmp .opt_loop |
.no_options: |
pop ecx |
;----------------------------------------------------------------------- |
; Time to do some header prediction (Original Principle by Van Jacobson) |
; There are two common cases for an uni-directional data transfer. |
; |
; General rule: the packets has no control flags, is in-sequence, |
; window width didnt change and we're not retransmitting. |
; |
; Second rules: |
; - If the length is 0 and the ACK moved forward, we're the sender side of the transfer. |
; In this case we'll free the ACK'ed data and notify higher levels that we have free space in buffer |
; |
; - If the length is not 0 and the ACK didn't move, we're the receiver side of the transfer. |
; If the packets are in order (data queue is empty), add the data to the socket buffer and request a delayed ACK |
cmp [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED |
jnz .not_uni_xfer |
test [edx + TCP_header.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG |
jnz .not_uni_xfer |
test [edx + TCP_header.Flags], TH_ACK |
jz .not_uni_xfer |
mov eax, [edx + TCP_header.SequenceNumber] |
cmp eax, [ebx + TCP_SOCKET.RCV_NXT] |
jne .not_uni_xfer |
mov eax, dword [edx + TCP_header.Window] |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jne .not_uni_xfer |
mov eax, [ebx + TCP_SOCKET.SND_NXT] |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
jne .not_uni_xfer |
;--------------------------------------- |
; check if we are sender in the uni-xfer |
; If the following 4 conditions are all true, this segment is a pure ACK. |
; |
; - The segment contains no data. |
test ecx, ecx |
jnz .not_sender |
; - The congestion window is greater than or equal to the current send window. |
; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance. |
mov eax, [ebx + TCP_SOCKET.SND_CWND] |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jb .not_uni_xfer |
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent. |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
ja .not_uni_xfer |
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number. |
sub eax, [ebx + TCP_SOCKET.SND_UNA] |
jbe .not_uni_xfer |
DEBUGF 1,"TCP_input: Header prediction: we are sender\n" |
;--------------------------------- |
; Packet is a pure ACK, process it |
; Delete acknowledged bytes from send buffer |
pusha |
mov ecx, eax |
lea eax, [ebx + STREAM_SOCKET.snd] |
call SOCKET_ring_free |
popa |
; Update RTT estimators |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
jz .no_timestamp_rtt |
mov eax, [esp + 4] ; timestamp when this segment was received |
sub eax, [ebx + TCP_SOCKET.ts_ecr] |
inc eax |
call TCP_xmit_timer |
jmp .rtt_done |
.no_timestamp_rtt: |
cmp [ebx + TCP_SOCKET.t_rtt], 0 |
je .rtt_done |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.t_rtseq] |
jbe .rtt_done |
mov eax, [ebx + TCP_SOCKET.t_rtt] |
call TCP_xmit_timer |
.rtt_done: |
; update window pointers |
mov eax, [edx + TCP_header.AckNumber] |
mov [ebx + TCP_SOCKET.SND_UNA], eax |
; Stop retransmit timer |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 |
; Unlock the socket |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
popa |
; Awaken waiting processes |
mov eax, ebx |
call SOCKET_notify |
; Generate more output |
call TCP_output |
jmp .drop_no_socket |
;------------------------------------------------- |
; maybe we are the receiver in the uni-xfer then.. |
.not_sender: |
; - The amount of data in the segment is greater than 0 (data count is in ecx) |
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment. |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_UNA] |
jne .not_uni_xfer |
; - The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp). |
;;; TODO |
; jnz .not_uni_xfer |
; Complete processing of received data |
DEBUGF 1,"TCP_input: Header prediction: we are receiving %u bytes\n", ecx |
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied |
movzx esi, [edx + TCP_header.DataOffset] |
add esi, edx |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_write ; Add the data to the socket buffer |
mov eax, ebx |
call SOCKET_notify |
or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag |
jmp .drop |
;-------------------------------------------------- |
; Header prediction failed, do it the slow way |
.not_uni_xfer: |
DEBUGF 1,"TCP_input: Header prediction failed\n" |
; Calculate receive window size |
push edx |
mov eax, SOCKETBUFFSIZE |
sub eax, [ebx + STREAM_SOCKET.rcv.size] |
mov edx, [ebx + TCP_SOCKET.RCV_ADV] |
sub edx, [ebx + TCP_SOCKET.RCV_NXT] |
cmp eax, edx |
jg @f |
mov eax, edx |
@@: |
DEBUGF 1,"Receive window size=%d\n", eax |
mov [ebx + TCP_SOCKET.RCV_WND], eax |
pop edx |
; If we are in listen or syn_sent state, go to that specific code right away |
cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN |
je .LISTEN |
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_SENT |
je .SYN_SENT |
;---------------------------- |
; trim any data not in window |
; check for duplicate data at beginning of segment (635) |
mov eax, [ebx + TCP_SOCKET.RCV_NXT] |
sub eax, [edx + TCP_header.SequenceNumber] |
jle .no_duplicate |
DEBUGF 1,"TCP_input: %u bytes duplicate data!\n", eax |
test [edx + TCP_header.Flags], TH_SYN |
jz .no_dup_syn |
DEBUGF 1,"TCP_input: got duplicate syn\n" |
and [edx + TCP_header.Flags], not (TH_SYN) |
inc [edx + TCP_header.SequenceNumber] |
cmp [edx + TCP_header.UrgentPointer], 1 |
jbe @f |
dec [edx + TCP_header.UrgentPointer] |
jmp .dup_syn |
@@: |
and [edx + TCP_header.Flags], not (TH_URG) |
.dup_syn: |
dec eax |
.no_dup_syn: |
; Check for entire duplicate segment (646) |
cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size |
jb .duplicate |
jnz @f |
test [edx + TCP_header.Flags], TH_FIN |
jnz .duplicate |
@@: |
; Any valid FIN must be to the left of the window. |
; At this point the FIN must be out of sequence or a duplicate, drop it |
and [edx + TCP_header.Flags], not TH_FIN |
; send an ACK and resynchronize and drop any data. |
; But keep on processing for RST or ACK |
DEBUGF 1, "616\n" |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov eax, ecx |
;TODO: update stats |
;----------------------------------------------- |
; Remove duplicate data and update urgent offset |
.duplicate: |
;;; TODO: 677 |
add [edx + TCP_header.SequenceNumber], eax |
sub ecx, eax |
sub [edx + TCP_header.UrgentPointer], ax |
jg @f |
and [edx + TCP_header.Flags], not (TH_URG) |
mov [edx + TCP_header.UrgentPointer], 0 |
@@: |
;-------------------------------------------------- |
; Handle data that arrives after process terminates (687) |
.no_duplicate: |
cmp [ebx + SOCKET.PID], 0 |
jne .not_terminated |
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT |
jbe .not_terminated |
test ecx, ecx |
jz .not_terminated |
mov eax, ebx |
call TCP_close |
;;;TODO: update stats |
jmp .respond_seg_reset |
;---------------------------------------- |
; Remove data beyond right edge of window (700-736) |
.not_terminated: |
mov eax, [edx + TCP_header.SequenceNumber] |
add eax, ecx |
sub eax, [ebx + TCP_SOCKET.RCV_NXT] |
sub eax, [ebx + TCP_SOCKET.RCV_WND] ; eax now holds the number of bytes to drop |
jle .no_excess_data |
DEBUGF 1,"%d bytes beyond right edge of window\n", eax |
;;; TODO: update stats |
cmp eax, ecx |
jl .dont_drop_all |
; If a new connection request is received while in TIME_WAIT, drop the old connection and start over, |
; if the sequence numbers are above the previous ones |
test [edx + TCP_header.Flags], TH_SYN |
jz .no_new_request |
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
jne .no_new_request |
; mov edx, [ebx + TCP_SOCKET.RCV_NXT] |
; cmp edx, [edx + TCP_header.SequenceNumber] |
; add edx, 64000 ; TCP_ISSINCR FIXME |
mov eax, ebx |
call TCP_close |
jmp .findpcb ; FIXME: skip code for unscaling window, ... |
.no_new_request: |
; If window is closed can only take segments at window edge, and have to drop data and PUSH from |
; incoming segments. Continue processing, but remember to ACK. Otherwise drop segment and ACK |
cmp [ebx + TCP_SOCKET.RCV_WND], 0 |
jne .drop_after_ack |
mov eax, [edx + TCP_header.SequenceNumber] |
cmp eax, [ebx + TCP_SOCKET.RCV_NXT] |
jne .drop_after_ack |
DEBUGF 1, "690\n" |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
;;; TODO: update stats |
jmp .no_excess_data |
.dont_drop_all: |
;;; TODO: update stats |
;;; TODO: 733 |
sub ecx, eax |
and [ebx + TCP_SOCKET.t_flags], not (TH_PUSH or TH_FIN) |
.no_excess_data: |
;----------------- |
; Record timestamp (737-746) |
; If last ACK falls within this segments sequence numbers, record its timestamp |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
jz .no_timestamp |
mov eax, [ebx + TCP_SOCKET.last_ack_sent] |
sub eax, [edx + TCP_header.SequenceNumber] |
jb .no_timestamp |
test [ebx + TCP_header.Flags], TH_SYN or TH_FIN ; syn and fin occupy one byte |
jz @f |
dec eax |
@@: |
sub eax, ecx |
jae .no_timestamp |
DEBUGF 1,"Recording timestamp\n" |
mov eax, [esp + 4] ; tcp_now |
mov [ebx + TCP_SOCKET.ts_recent_age], eax |
mov eax, [ebx + TCP_SOCKET.ts_val] |
mov [ebx + TCP_SOCKET.ts_recent], eax |
.no_timestamp: |
;------------------ |
; Process RST flags |
test [edx + TCP_header.Flags], TH_RST |
jz .no_rst |
DEBUGF 1,"TCP_input: Got an RST flag\n" |
mov eax, [ebx + TCP_SOCKET.t_state] |
shl eax, 2 |
jmp dword [eax + .rst_sw_list] |
.rst_sw_list: |
dd .no_rst ; TCPS_CLOSED |
dd .no_rst ; TCPS_LISTEN |
dd .no_rst ; TCPS_SYN_SENT |
dd .econnrefused ; TCPS_SYN_RECEIVED |
dd .econnreset ; TCPS_ESTABLISHED |
dd .econnreset ; TCPS_CLOSE_WAIT |
dd .econnreset ; TCPS_FIN_WAIT_1 |
dd .rst_close ; TCPS_CLOSING |
dd .rst_close ; TCPS_LAST_ACK |
dd .econnreset ; TCPS_FIN_WAIT_2 |
dd .rst_close ; TCPS_TIMED_WAIT |
.econnrefused: |
DEBUGF 1,"TCP_input: Connection refused\n" |
mov [ebx + SOCKET.errorcode], ECONNREFUSED |
jmp .close |
.econnreset: |
DEBUGF 1,"TCP_input: Connection reset\n" |
mov [ebx + SOCKET.errorcode], ECONNRESET |
.close: |
DEBUGF 1,"TCP_input: Closing connection\n" |
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED |
;;; TODO: update stats (tcp drops) |
mov eax, ebx |
call TCP_close |
jmp .drop_no_socket |
.rst_close: |
DEBUGF 1,"TCP_input: Closing with reset\n" |
mov eax, ebx |
call TCP_close |
jmp .drop_no_socket |
.no_rst: |
;-------------------------------------- |
; handle SYN-full and ACK-less segments |
test [edx + TCP_header.Flags], TH_SYN |
jz .not_syn_full |
mov eax, ebx |
mov ebx, ECONNRESET |
call TCP_drop |
jmp .drop_with_reset |
.not_syn_full: |
;--------------- |
; ACK processing |
test [edx + TCP_header.Flags], TH_ACK |
jz .drop |
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED |
jb .ack_processed ; states: closed, listen, syn_sent |
ja .no_syn_rcv ; established, fin_wait_1, fin_wait_2, close_wait, closing, last_ack, time_wait |
DEBUGF 1,"TCP_input: state=syn_received\n" |
mov eax, [edx + TCP_header.AckNumber] |
cmp [ebx + TCP_SOCKET.SND_UNA], eax |
ja .drop_with_reset |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
ja .drop_with_reset |
;;; TODO: update stats |
mov eax, ebx |
call SOCKET_is_connected |
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED |
; Do window scaling? |
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
jz @f |
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE |
jz @f |
push word [ebx + TCP_SOCKET.requested_s_scale] ; Set send and receive scale factors to the received values |
pop word [ebx + TCP_SOCKET.SND_SCALE] |
@@: |
;;; TODO: call TCP_reassemble |
mov eax, [edx + TCP_header.SequenceNumber] |
dec eax |
mov [ebx + TCP_SOCKET.SND_WL1], eax |
.no_syn_rcv: |
;------------------------- |
; check for duplicate ACKs |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_UNA] |
ja .not_dup_ack |
test ecx, ecx |
jnz .reset_dupacks |
mov eax, dword [edx + TCP_header.Window] |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jne .reset_dupacks |
DEBUGF 1,"TCP_input: Processing duplicate ACK\n" |
; If we have outstanding data, other than a window probe, this is a completely duplicate ACK |
; (window info didnt change) The ACK is the biggest we've seen and we've seen exactly our rexmt threshold of them, |
; assume a packet has been dropped and retransmit it. Kludge snd_nxt & the congestion window so we send only this one packet. |
cmp [ebx + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME |
jg @f |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_UNA] |
je .dup_ack |
@@: |
mov [ebx + TCP_SOCKET.t_dupacks], 0 |
jmp .not_dup_ack |
.dup_ack: |
inc [ebx + TCP_SOCKET.t_dupacks] |
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh |
jne .no_re_xmit |
push [ebx + TCP_SOCKET.SND_NXT] ; >>>> |
mov eax, [ebx + TCP_SOCKET.SND_WND] |
cmp eax, [ebx + TCP_SOCKET.SND_CWND] |
cmova eax, [ebx + TCP_SOCKET.SND_CWND] |
shr eax, 1 |
push edx |
xor edx, edx |
div [ebx + TCP_SOCKET.t_maxseg] |
cmp eax, 2 |
ja @f |
xor eax, eax |
mov al, 2 |
@@: |
mul [ebx + TCP_SOCKET.t_maxseg] |
pop edx |
mov [ebx + TCP_SOCKET.SND_SSTHRESH], eax |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; turn off retransmission timer |
mov [ebx + TCP_SOCKET.t_rtt], 0 |
mov eax, [edx + TCP_header.AckNumber] |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
mov eax, [ebx + TCP_SOCKET.t_maxseg] |
mov [ebx + TCP_SOCKET.SND_CWND], eax |
; Unlock the socket |
push ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
; retransmit missing segment |
mov eax, [esp] |
call TCP_output |
; Lock the socket again |
mov ecx, [esp] |
add ecx, SOCKET.mutex |
call mutex_lock |
pop ebx |
; Continue processing |
xor edx, edx |
mov eax, [ebx + TCP_SOCKET.t_maxseg] |
mul [ebx + TCP_SOCKET.t_dupacks] |
add eax, [ebx + TCP_SOCKET.SND_SSTHRESH] |
mov [ebx + TCP_SOCKET.SND_CWND], eax |
pop eax ; <<<< |
cmp eax, [ebx + TCP_SOCKET.SND_NXT] |
jb @f |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
@@: |
jmp .drop |
.no_re_xmit: |
jbe .not_dup_ack |
DEBUGF 1,"TCP_input: Increasing congestion window\n" |
mov eax, [ebx + TCP_SOCKET.t_maxseg] |
add [ebx + TCP_SOCKET.SND_CWND], eax |
; Unlock the socket |
push ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
; retransmit missing segment |
mov eax, [esp] |
call TCP_output |
; Lock the socket again |
mov ecx, [esp] |
add ecx, SOCKET.mutex |
call mutex_lock |
pop ebx |
jmp .drop |
.not_dup_ack: |
;------------------------------------------------- |
; If the congestion window was inflated to account |
; for the other side's cached packets, retract it |
mov eax, [ebx + TCP_SOCKET.SND_SSTHRESH] |
cmp eax, [ebx + TCP_SOCKET.SND_CWND] |
ja @f |
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh |
jbe @f |
mov [ebx + TCP_SOCKET.SND_CWND], eax |
@@: |
mov [ebx + TCP_SOCKET.t_dupacks], 0 |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
jbe @f |
;;; TODO: update stats |
jmp .drop_after_ack |
@@: |
mov edi, [edx + TCP_header.AckNumber] |
sub edi, [ebx + TCP_SOCKET.SND_UNA] ; now we got the number of acked bytes in edi |
;;; TODO: update stats |
DEBUGF 1,"TCP_input: acceptable ACK for %u bytes\n", edi |
;------------------------------------------ |
; RTT measurements and retransmission timer (912-926) |
; If we have a timestamp, update smoothed RTT |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
jz .timestamp_not_present |
mov eax, [esp+4] |
sub eax, [ebx + TCP_SOCKET.ts_ecr] |
inc eax |
call TCP_xmit_timer |
jmp .rtt_done_ |
; If no timestamp but transmit timer is running and timed sequence number was acked, |
; update smoothed RTT. Since we now have an RTT measurement, cancel the timer backoff |
; (Phil Karn's retransmit algo) |
; Recompute the initial retransmit timer |
.timestamp_not_present: |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.t_rtseq] |
jbe .rtt_done_ |
mov eax, [ebx + TCP_SOCKET.t_rtt] |
test eax, eax |
jz .rtt_done_ |
call TCP_xmit_timer |
.rtt_done_: |
; If all outstanding data is acked, stop retransmit timer and remember to restart (more output or persist) |
; If there is more data to be acked, restart retransmit timer, using current (possible backed-off) value. |
mov eax, [ebx + TCP_SOCKET.SND_MAX] |
cmp eax, [edx + TCP_header.AckNumber] |
jne .more_data |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 |
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
jmp .no_restart |
.more_data: |
cmp [ebx + TCP_SOCKET.timer_persist], 0 |
jne .no_restart |
mov eax, [ebx + TCP_SOCKET.t_rxtcur] |
mov [ebx + TCP_SOCKET.timer_retransmission], eax |
.no_restart: |
;------------------------------------------- |
; Open congestion window in response to ACKs |
mov esi, [ebx + TCP_SOCKET.SND_CWND] |
mov eax, [ebx + TCP_SOCKET.t_maxseg] |
cmp esi, [ebx + TCP_SOCKET.SND_SSTHRESH] |
jbe @f |
push edx |
push eax |
mul eax |
div esi |
pop edx |
shr edx, 3 |
add eax, edx |
pop edx |
@@: |
add esi, eax |
push ecx |
mov cl, [ebx + TCP_SOCKET.SND_SCALE] |
mov eax, TCP_max_win |
shl eax, cl |
pop ecx |
cmp esi, eax |
cmova esi, eax |
mov [ebx + TCP_SOCKET.SND_CWND], esi |
;------------------------------------------ |
; Remove acknowledged data from send buffer |
cmp edi, [ebx + STREAM_SOCKET.snd.size] |
jbe .finiacked |
push ecx edx ebx |
mov ecx, [ebx + STREAM_SOCKET.snd.size] |
lea eax, [ebx + STREAM_SOCKET.snd] |
sub [ebx + TCP_SOCKET.SND_WND], ecx |
call SOCKET_ring_free |
pop ebx edx ecx |
DEBUGF 1,"TCP_input: our FIN is acked\n" |
stc |
jmp .wakeup |
.finiacked: |
push ecx edx ebx |
mov ecx, edi |
lea eax, [ebx + STREAM_SOCKET.snd] |
call SOCKET_ring_free |
pop ebx |
sub [ebx + TCP_SOCKET.SND_WND], ecx |
pop edx ecx |
DEBUGF 1,"TCP_input: our FIN is not acked\n" |
clc |
;---------------------------------------- |
; Wake up process waiting on send buffer |
.wakeup: |
pushf ; Keep the flags (Carry flag) |
mov eax, ebx |
call SOCKET_notify |
; Update TCPS |
mov eax, [edx + TCP_header.AckNumber] |
mov [ebx + TCP_SOCKET.SND_UNA], eax |
cmp eax, [ebx + TCP_SOCKET.SND_NXT] |
jb @f |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
@@: |
popf |
; General ACK handling complete |
; Now do the state-specific ones |
; Carry flag is set when our FIN is acked |
mov eax, [ebx + TCP_SOCKET.t_state] |
jmp dword [eax*4 + .ACK_sw_list] |
.ACK_sw_list: |
dd .ack_processed ; TCPS_CLOSED |
dd .ack_processed ; TCPS_LISTEN |
dd .ack_processed ; TCPS_SYN_SENT |
dd .ack_processed ; TCPS_SYN_RECEIVED |
dd .ack_processed ; TCPS_ESTABLISHED |
dd .ack_processed ; TCPS_CLOSE_WAIT |
dd .ack_fw1 ; TCPS_FIN_WAIT_1 |
dd .ack_c ; TCPS_CLOSING |
dd .ack_la ; TCPS_LAST_ACK |
dd .ack_processed ; TCPS_FIN_WAIT_2 |
dd .ack_tw ; TCPS_TIMED_WAIT |
.ack_fw1: |
jnc .ack_processed |
test [ebx + SOCKET.state], SS_CANTRCVMORE |
jnz @f |
mov eax, ebx |
call SOCKET_is_disconnected |
mov [ebx + TCP_SOCKET.timer_timed_wait], TCP_time_max_idle |
@@: |
mov [ebx + TCP_SOCKET.t_state], TCPS_FIN_WAIT_2 |
jmp .ack_processed |
.ack_c: |
jnc .ack_processed |
mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
mov eax, ebx |
call TCP_cancel_timers |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
mov eax, ebx |
call SOCKET_is_disconnected |
jmp .ack_processed |
.ack_la: |
jnc .ack_processed |
mov eax, ebx |
call TCP_disconnect |
jmp .drop |
.ack_tw: |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
jmp .drop_after_ack |
.reset_dupacks: ; We got a new ACK, reset duplicate ACK counter |
mov [ebx + TCP_SOCKET.t_dupacks], 0 |
jmp .ack_processed |
;------- |
; LISTEN |
align 4 |
.LISTEN: |
DEBUGF 1,"TCP_input: state=listen\n" |
test [edx + TCP_header.Flags], TH_RST |
jnz .drop |
test [edx + TCP_header.Flags], TH_ACK |
jnz .drop_with_reset |
test [edx + TCP_header.Flags], TH_SYN |
jz .drop |
;;; TODO: check if it's a broadcast or multicast, and drop if so |
push dword [edi] ; Ipv4 source addres |
pop [ebx + IP_SOCKET.RemoteIP] |
push [edx + TCP_header.SourcePort] |
pop [ebx + TCP_SOCKET.RemotePort] |
push [edx + TCP_header.SequenceNumber] |
pop [ebx + TCP_SOCKET.IRS] |
mov eax, [TCP_sequence_num] |
add [TCP_sequence_num], 64000 / 2 |
mov [ebx + TCP_SOCKET.ISS], eax |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
TCP_sendseqinit ebx |
TCP_rcvseqinit ebx |
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED |
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval ;;;; macro |
lea eax, [ebx + STREAM_SOCKET.snd] |
call SOCKET_ring_create |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_create |
and [ebx + TCP_SOCKET.temp_bits], not TCP_BIT_DROPSOCKET |
;;; call SOCKET_notify_owner |
jmp .trim_then_step6 |
;------------ |
; Active Open |
align 4 |
.SYN_SENT: |
DEBUGF 1,"TCP_input: state=syn_sent\n" |
test [edx + TCP_header.Flags], TH_ACK |
jz @f |
mov eax, [edx + TCP_header.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.ISS] |
jbe .drop_with_reset |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
ja .drop_with_reset |
@@: |
test [edx + TCP_header.Flags], TH_RST |
jz @f |
test [edx + TCP_header.Flags], TH_ACK |
jz .drop |
mov eax, ebx |
mov ebx, ECONNREFUSED |
call TCP_drop |
jmp .drop |
@@: |
test [edx + TCP_header.Flags], TH_SYN |
jz .drop |
; at this point, segment seems to be valid |
test [edx + TCP_header.Flags], TH_ACK |
jz .no_syn_ack |
; now, process received SYN in response to an active open |
mov eax, [edx + TCP_header.AckNumber] |
mov [ebx + TCP_SOCKET.SND_UNA], eax |
cmp eax, [ebx + TCP_SOCKET.SND_NXT] |
jbe @f |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
@@: |
.no_syn_ack: |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission |
push [edx + TCP_header.SequenceNumber] |
pop [ebx + TCP_SOCKET.IRS] |
TCP_rcvseqinit ebx |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov eax, [ebx + TCP_SOCKET.SND_UNA] |
cmp eax, [ebx + TCP_SOCKET.ISS] |
jbe .simultaneous_open |
test [edx + TCP_header.Flags], TH_ACK |
jz .simultaneous_open |
DEBUGF 1,"TCP_input: active open\n" |
;;; TODO: update stats |
; set socket state to connected |
mov [ebx + SOCKET.state], SS_ISCONNECTED |
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED |
; Do window scaling on this connection ? |
mov eax, [ebx + TCP_SOCKET.t_flags] |
and eax, TF_REQ_SCALE or TF_RCVD_SCALE |
cmp eax, TF_REQ_SCALE or TF_RCVD_SCALE |
jne .no_scaling |
mov ax, word [ebx + TCP_SOCKET.requested_s_scale] |
mov word [ebx + TCP_SOCKET.SND_SCALE], ax |
.no_scaling: |
;;; TODO: reassemble packets queue |
mov eax, [ebx + TCP_SOCKET.t_rtt] |
test eax, eax |
je .trim_then_step6 |
call TCP_xmit_timer |
jmp .trim_then_step6 |
.simultaneous_open: |
DEBUGF 1,"TCP_input: simultaneous open\n" |
; We have received a syn but no ACK, so we are having a simultaneous open.. |
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED |
;------------------------------------- |
; Common processing for receipt of SYN |
.trim_then_step6: |
inc [edx + TCP_header.SequenceNumber] |
;;; TODO: Drop any received data that follows receive window (590) |
mov eax, [edx + TCP_header.SequenceNumber] |
mov [ebx + TCP_SOCKET.RCV_UP], eax |
dec eax |
mov [ebx + TCP_SOCKET.SND_WL1], eax |
;------- |
; step 6 |
.ack_processed: |
DEBUGF 1,"TCP_input: ACK processed\n" |
;---------------------------------------------- |
; check if we need to update window information |
test [edx + TCP_header.Flags], TH_ACK |
jz .no_window_update |
mov eax, [ebx + TCP_SOCKET.SND_WL1] |
cmp eax, [edx + TCP_header.SequenceNumber] |
jb .update_window |
ja @f |
mov eax, [ebx + TCP_SOCKET.SND_WL2] |
cmp eax, [edx + TCP_header.AckNumber] |
jb .update_window |
ja .no_window_update |
@@: |
mov eax, dword [edx + TCP_header.Window] |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jbe .no_window_update |
.update_window: |
;;; TODO: update stats (Keep track of pure window updates) |
mov eax, dword [edx + TCP_header.Window] |
cmp eax, [ebx + TCP_SOCKET.max_sndwnd] |
jbe @f |
mov [ebx + TCP_SOCKET.max_sndwnd], eax |
@@: |
mov [ebx + TCP_SOCKET.SND_WND], eax |
DEBUGF 1,"TCP_input: Updating window to %u\n", eax |
push [edx + TCP_header.SequenceNumber] |
pop [ebx + TCP_SOCKET.SND_WL1] |
push [edx + TCP_header.AckNumber] |
pop [ebx + TCP_SOCKET.SND_WL2] |
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
.no_window_update: |
;----------------- |
; process URG flag |
test [edx + TCP_header.Flags], TH_URG |
jz .not_urgent |
cmp [edx + TCP_header.UrgentPointer], 0 |
jz .not_urgent |
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
je .not_urgent |
; Ignore bogus urgent offsets |
movzx eax, [edx + TCP_header.UrgentPointer] |
add eax, [ebx + STREAM_SOCKET.rcv.size] |
cmp eax, SOCKET_MAXDATA |
jbe .not_urgent |
mov [edx + TCP_header.UrgentPointer], 0 |
and [edx + TCP_header.Flags], not (TH_URG) |
jmp .do_data |
.not_urgent: |
; processing of received urgent pointer |
;;; TODO (1051-1093) |
;--------------------------------------- |
; process the data in the segment (1094) |
.do_data: |
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
jae .final_processing |
test [edx + TCP_header.Flags], TH_FIN |
jnz @f |
test ecx, ecx |
jnz .final_processing |
@@: |
; The segment is in order? |
mov eax, [edx + TCP_header.SequenceNumber] |
cmp eax, [ebx + TCP_SOCKET.RCV_NXT] |
jne .out_of_order |
; The reassembly queue is empty? |
cmp [ebx + TCP_SOCKET.seg_next], 0 |
jne .out_of_order |
; The connection is established? |
cmp [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED |
jne .out_of_order |
; Ok, lets do this.. Set delayed ACK flag and copy data into socket buffer |
or [ebx + TCP_SOCKET.t_flags], TF_DELACK |
pusha |
movzx esi, [edx + TCP_header.DataOffset] |
add esi, edx |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_write ; Add the data to the socket buffer |
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied |
popa |
; Wake up the sleeping process |
mov eax, ebx |
call SOCKET_notify |
jmp .data_done |
.out_of_order: |
; Uh-oh, some data is out of order, lets call TCP reassemble for help |
call TCP_reassemble |
DEBUGF 1, "1470\n" |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
.data_done: |
;--------------- |
; FIN processing |
test [edx + TCP_header.Flags], TH_FIN |
jz .final_processing |
DEBUGF 1,"TCP_input: Processing FIN\n" |
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
jae .not_first_fin |
DEBUGF 1,"TCP_input: First FIN for this connection\n" |
mov eax, ebx |
call SOCKET_cant_recv_more |
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
inc [ebx + TCP_SOCKET.RCV_NXT] |
.not_first_fin: |
mov eax, [ebx + TCP_SOCKET.t_state] |
shl eax, 2 |
jmp dword [eax + .FIN_sw_list] |
.FIN_sw_list: |
dd .final_processing ; TCPS_CLOSED |
dd .final_processing ; TCPS_LISTEN |
dd .final_processing ; TCPS_SYN_SENT |
dd .fin_syn_est ; TCPS_SYN_RECEIVED |
dd .fin_syn_est ; TCPS_ESTABLISHED |
dd .final_processing ; TCPS_CLOSE_WAIT |
dd .fin_wait1 ; TCPS_FIN_WAIT_1 |
dd .final_processing ; TCPS_CLOSING |
dd .final_processing ; TCPS_LAST_ACK |
dd .fin_wait2 ; TCPS_FIN_WAIT_2 |
dd .fin_timed ; TCPS_TIMED_WAIT |
.fin_syn_est: |
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT |
jmp .final_processing |
.fin_wait1: |
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSING |
jmp .final_processing |
.fin_wait2: |
mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
mov eax, ebx |
call TCP_cancel_timers |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
call SOCKET_is_disconnected |
jmp .final_processing |
.fin_timed: |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
jmp .final_processing |
.drop_after_ack: |
DEBUGF 1,"TCP_input: Drop after ACK\n" |
push edx ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax edx |
test [edx + TCP_header.Flags], TH_RST |
jnz .dumpit |
or [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
jmp .need_output |
.drop_with_reset: |
DEBUGF 1,"TCP_input: Drop with reset\n" |
push ebx edx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop edx ebx |
test [edx + TCP_header.Flags], TH_RST |
jnz .dumpit |
;;; if its a multicast/broadcast, also drop |
test [edx + TCP_header.Flags], TH_ACK |
jnz .respond_ack |
test [edx + TCP_header.Flags], TH_SYN |
jnz .respond_syn |
jmp .dumpit |
;----------------- |
; Final processing |
.final_processing: |
DEBUGF 1,"TCP_input: Final processing\n" |
push ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax |
test [eax + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
jnz .need_output |
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
jz .dumpit |
DEBUGF 1,"TCP_input: ACK now!\n" |
.need_output: |
DEBUGF 1,"TCP_input: need output\n" |
call TCP_output |
.dumpit: |
DEBUGF 1,"TCP_input: dumping\n" |
call kernel_free |
add esp, 4 |
jmp .loop |
;--------- |
; Respond |
.respond_ack: |
push ebx |
mov cl, TH_RST |
call TCP_respond |
pop ebx |
jmp .destroy_new_socket |
.respond_syn: |
push ebx |
mov cl, TH_RST + TH_ACK |
call TCP_respond |
pop ebx |
jmp .destroy_new_socket |
.respond_seg_reset: |
test [edx + TCP_header.Flags], TH_RST |
jnz .drop_no_socket |
;;; TODO: if its a multicast/broadcast, also drop |
test [edx + TCP_header.Flags], TH_ACK |
jnz .respond_seg_ack |
test [edx + TCP_header.Flags], TH_SYN |
jnz .respond_seg_syn |
jmp .drop_no_socket |
.respond_seg_ack: |
mov cl, TH_RST |
call TCP_respond_segment |
jmp .drop_no_socket |
.respond_seg_syn: |
mov cl, TH_RST + TH_ACK |
call TCP_respond_segment |
jmp .drop_no_socket |
;----- |
; Drop |
.drop: |
DEBUGF 1,"TCP_input: Dropping segment\n" |
pusha |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
popa |
.destroy_new_socket: |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET |
jz .drop_no_socket |
mov eax, ebx |
call SOCKET_free |
.drop_no_socket: |
DEBUGF 1,"TCP_input: Drop (no socket)\n" |
call kernel_free |
add esp, 4 |
jmp .loop |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/tcp_output.inc |
---|
0,0 → 1,621 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3289 $ |
;----------------------------------------------------------------- |
; |
; TCP_output |
; |
; IN: eax = socket pointer |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
TCP_output: |
DEBUGF 1,"TCP_output: socket=%x\n", eax |
push eax |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
pop eax |
; We'll detect the length of the data to be transmitted, and flags to be used |
; If there is some data, or any critical controls to send (SYN / RST), then transmit |
; Otherwise, investigate further |
mov ebx, [eax + TCP_SOCKET.SND_MAX] |
cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
jbe .not_idle |
mov ebx, [eax + TCP_SOCKET.t_idle] |
cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
jbe .not_idle |
; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
; Slow start to get ack "clock" running again. |
mov ebx, [eax + TCP_SOCKET.t_maxseg] |
mov [eax + TCP_SOCKET.SND_CWND], ebx |
.not_idle: |
.again: |
mov [eax + TCP_SOCKET.temp_bits], 0 |
mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
jb @f ; |
mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
@@: ; |
call TCP_outflags ; flags in dl |
;------------------------ |
; data being forced out ? |
; If in persist timeout with window of 0, send 1 byte. |
; Otherwise, if window is small but nonzero, and timer expired, |
; we will send what we can and go to transmit state |
cmp [eax + TCP_SOCKET.t_force], 0 |
je .no_force |
DEBUGF 1,"TCP_output: forcing data out\n" |
test ecx, ecx |
jnz .no_zero_window |
cmp ebx, [eax + STREAM_SOCKET.snd.size] |
jae @f |
and dl, not (TH_FIN) |
@@: |
inc ecx |
jmp .no_force |
.no_zero_window: |
mov [eax + TCP_SOCKET.timer_persist], 0 |
mov [eax + TCP_SOCKET.t_rxtshift], 0 |
.no_force: |
;-------------------------------- |
; Calculate how much data to send (106) |
mov esi, [eax + STREAM_SOCKET.snd.size] |
cmp esi, ecx |
jb @f |
mov esi, ecx |
@@: |
sub esi, ebx |
;------------------------ |
; check for window shrink (107) |
; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
; Otherwise, window shrank after we sent into it. |
jae .not_persist |
; enter persist state |
xor esi, esi |
; If window shrank to 0 |
test ecx, ecx |
jnz @f |
; cancel pending retransmit |
mov [eax + TCP_SOCKET.timer_retransmission], 0 |
; pull SND_NXT back to (closed) window, We will enter persist state below. |
push [eax + TCP_SOCKET.SND_UNA] |
pop [eax + TCP_SOCKET.SND_NXT] |
@@: |
; If window didn't close completely, just wait for an ACK |
.not_persist: |
;--------------------------- |
; Send one segment at a time (124) |
cmp esi, [eax + TCP_SOCKET.t_maxseg] |
jbe @f |
mov esi, [eax + TCP_SOCKET.t_maxseg] |
or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
@@: |
;-------------------------------------------- |
; Turn of FIN flag if send buffer not emptied (128) |
mov edi, [eax + TCP_SOCKET.SND_NXT] |
add edi, esi |
sub edi, [eax + TCP_SOCKET.SND_UNA] |
cmp edi, [eax + STREAM_SOCKET.snd.size] |
jae @f |
and dl, not (TH_FIN) |
@@: |
;------------------------------- |
; calculate window advertisement (130) |
mov ecx, SOCKET_MAXDATA |
sub ecx, [eax + STREAM_SOCKET.rcv.size] |
;------------------------------ |
; Sender silly window avoidance (131) |
test esi, esi |
jz .len_zero |
cmp esi, [eax + TCP_SOCKET.t_maxseg] |
je TCP_send |
add ebx, esi ; offset + length |
cmp ebx, [eax + STREAM_SOCKET.snd.size] |
jb @f |
test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
jnz TCP_send |
mov ebx, [eax + TCP_SOCKET.SND_MAX] |
cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
je TCP_send |
@@: |
test [eax + TCP_SOCKET.t_force], -1 ;;; |
jnz TCP_send |
mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
shr ebx, 1 |
cmp esi, ebx |
jae TCP_send |
mov ebx, [eax + TCP_SOCKET.SND_NXT] |
cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
jb TCP_send |
.len_zero: |
;---------------------------------------- |
; Check if a window update should be sent (154) |
DEBUGF 1,"TCP_output: window=%d\n", ecx |
; Compare available window to amount of window known to peer (as advertised window less next expected input) |
; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
; Then we want to send a window update to the peer. |
test ecx, ecx |
jz .no_window |
push ecx |
mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
mov ebx, TCP_max_win |
shl ebx, cl |
pop ecx |
cmp ebx, ecx |
jb @f |
mov ebx, ecx |
@@: |
sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
add ebx, [eax + TCP_SOCKET.RCV_NXT] |
mov edi, [eax + TCP_SOCKET.t_maxseg] |
shl edi, 1 |
; cmp ebx, edi |
; jae TCP_send |
; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
; jae TCP_send |
.no_window: |
;-------------------------- |
; Should a segment be sent? (174) |
DEBUGF 1,"TCP_output: 174\n" |
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
jnz TCP_send |
test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
jnz TCP_send |
mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
ja TCP_send |
test dl, TH_FIN |
jz .enter_persist ; no reason to send, enter persist state |
; FIN was set, only send if not already sent, or on retransmit |
test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
jz TCP_send |
mov ebx, [eax + TCP_SOCKET.SND_NXT] |
cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
je TCP_send |
;-------------------- |
; Enter persist state (191) |
.enter_persist: |
cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
jne @f |
cmp [eax + TCP_SOCKET.timer_retransmission], 0 |
jne @f |
cmp [eax + TCP_SOCKET.timer_persist], 0 ; Persist timer already expired? |
jne @f |
DEBUGF 1,"TCP_output: Entering persist state\n" |
mov [eax + TCP_SOCKET.t_rxtshift], 0 |
call TCP_set_persist |
@@: |
;---------------------------- |
; No reason to send a segment (219) |
DEBUGF 1,"TCP_output: No reason to send a segment\n" |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
popa |
; Fixme: returnvalue? |
ret |
;----------------------------------------------- |
; |
; Send a segment (222) |
; |
; eax = socket pointer |
; esi = data len |
; dl = flags |
; |
;----------------------------------------------- |
align 4 |
TCP_send: |
DEBUGF 1,"TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
push eax ; save socket ptr |
push esi ; and data length too |
mov edi, sizeof.TCP_header ; edi will contain headersize |
;------------------------------------ |
; Send options with first SYN segment |
test dl, TH_SYN |
jz .options_done |
push [eax + TCP_SOCKET.ISS] |
pop [eax + TCP_SOCKET.SND_NXT] |
test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
jnz .options_done |
mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
bswap ecx |
push ecx |
add di, 4 |
DEBUGF 1,"TCP_send: added maxseg option\n" |
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
jz .no_scale |
test dl, TH_ACK |
jz .scale_opt |
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
jz .no_scale |
.scale_opt: |
mov cl, [eax + TCP_SOCKET.request_r_scale] |
mov ch, TCP_OPT_NOP |
pushw cx |
pushw TCP_OPT_WINDOW + 3 shl 8 |
add di, 4 |
DEBUGF 1,"TCP_send: added scale option\n" |
.no_scale: |
.no_syn: |
;------------------------------------ |
; Make the timestamp option if needed |
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
jz .no_timestamp |
test dl, TH_RST |
jnz .no_timestamp |
test dl, TH_ACK |
jz .timestamp |
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
jz .no_timestamp |
.timestamp: |
pushd 0 |
pushd [timer_ticks] |
pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
add di, 12 |
DEBUGF 1,"TCP_send: added timestamp\n" |
.no_timestamp: |
; <Add additional options here> |
.options_done: |
; eax = socket ptr |
; edx = flags |
; edi = header size |
; esi = data len |
;--------------------------------------------- |
; check if we dont exceed the max segment size (270) |
add esi, edi ; total TCP segment size |
cmp esi, [eax + TCP_SOCKET.t_maxseg] |
jbe .no_overflow |
mov esi, [eax + TCP_SOCKET.t_maxseg] |
or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
.no_overflow: |
;----------------------------------------------------------------- |
; Start by pushing all TCP header values in reverse order on stack |
; (essentially, creating the tcp header on the stack!) |
pushw 0 ; .UrgentPointer dw ? |
pushw 0 ; .Checksum dw ? |
pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) |
shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
shl dx, 8 |
or dx, di ; .Flags db ? |
pushw dx |
shr edi, 2 ; .DataOffset db ? |
push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
ntohd [esp] |
push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
ntohd [esp] |
push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
push edi ; header size |
;--------------------- |
; Create the IP packet |
mov ecx, esi |
mov ebx, [eax + SOCKET.device] |
mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
mov di, IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
jz .ip_error |
;----------------------------------------- |
; Move TCP header from stack to TCP packet |
push ecx |
mov ecx, [esp + 4] |
lea esi, [esp + 8] |
shr ecx, 2 ; count is in bytes, we will work with dwords |
rep movsd |
pop ecx ; full TCP packet size |
pop esi ; headersize |
add esp, esi ; remove it from stack |
push edx ; packet size for send proc |
push eax ; packet ptr for send proc |
mov edx, edi ; begin of data |
sub edx, esi ; begin of packet (edi = begin of data) |
push ecx |
sub ecx, esi ; data size |
;-------------- |
; Copy the data |
; eax = ptr to ring struct |
; ecx = buffer size |
; edi = ptr to buffer |
mov eax, [esp + 16] ; get socket ptr |
push edx |
push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
test ecx, ecx |
jz .nodata |
mov edx, [eax + TCP_SOCKET.SND_NXT] |
add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME |
sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
add eax, STREAM_SOCKET.snd |
call SOCKET_ring_read |
.nodata: |
pop edi |
pop esi ; begin of data |
pop ecx ; full packet size |
mov eax, [esp + 12] ; socket ptr |
;---------------------------------- |
; initialize retransmit timer (400) |
;TODO: check t_force and persist |
test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
jz @f |
inc [eax + TCP_SOCKET.SND_NXT] |
test [esi + TCP_header.Flags], TH_FIN |
jz @f |
or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
@@: |
mov edx, [eax + TCP_SOCKET.SND_NXT] |
cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
jbe @f |
mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
je @f |
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
mov [eax + TCP_SOCKET.t_rtseq], edi |
;TODO: update stats |
@@: |
; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
cmp [eax + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME |
ja .retransmit_set |
cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
je .retransmit_set |
mov edx, [eax + TCP_SOCKET.t_rxtcur] |
mov [eax + TCP_SOCKET.timer_retransmission], edx |
cmp [eax + TCP_SOCKET.timer_persist], 0 |
jne .retransmit_set |
mov [eax + TCP_SOCKET.timer_persist], 0 |
mov [eax + TCP_SOCKET.t_rxtshift], 0 |
.retransmit_set: |
;-------------------- |
; Create the checksum |
TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
mov [esi + TCP_header.Checksum], dx |
;---------------- |
; Send the packet |
DEBUGF 1,"TCP_send: Sending with device %x\n", ebx |
call [ebx + NET_DEVICE.transmit] |
jnz .send_error |
;--------------- |
; Ok, data sent! |
pop ecx |
pop eax |
inc [TCP_segments_tx] ; FIXME: correct interface? |
; update advertised receive window |
test ecx, ecx |
jz @f |
add ecx, [eax + TCP_SOCKET.RCV_NXT] |
cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
jbe @f |
mov [eax + TCP_SOCKET.RCV_ADV], ecx |
@@: |
; update last ack sent |
push [eax + TCP_SOCKET.RCV_NXT] |
pop [eax + TCP_SOCKET.last_ack_sent] |
; and flags |
and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
;-------------- |
; unlock socket |
push eax |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
pop eax |
;----------------------------- |
; Check if we need more output |
test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
jnz TCP_output.again |
DEBUGF 1,"TCP_send: success!\n" |
xor eax, eax |
ret |
.ip_error: |
pop ecx |
add esp, ecx |
add esp, 4 |
pop eax |
mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
DEBUGF 1,"TCP_send: IP error\n" |
or eax, -1 |
ret |
.send_error: |
add esp, 8 |
lea ecx, [eax + SOCKET.mutex] |
call mutex_unlock |
DEBUGF 1,"TCP_send: sending failed\n" |
or eax, -2 |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/tcp_subr.inc |
---|
0,0 → 1,546 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3514 $ |
align 4 |
iglobal |
TCP_backoff db 0,1,2,3,4,5,6,6,6,6,6,6,6 |
endg |
macro TCP_checksum IP1, IP2 { |
;------------- |
; Pseudoheader |
; protocol type |
mov edx, IP_PROTO_TCP |
; source address |
add dl, byte [IP1+1] |
adc dh, byte [IP1+0] |
adc dl, byte [IP1+3] |
adc dh, byte [IP1+2] |
; destination address |
adc dl, byte [IP2+1] |
adc dh, byte [IP2+0] |
adc dl, byte [IP2+3] |
adc dh, byte [IP2+2] |
; size |
adc dl, cl |
adc dh, ch |
adc edx, 0 |
;--------------------- |
; Real header and data |
push esi |
call checksum_1 |
call checksum_2 |
pop esi |
} ; returns in dx only |
macro TCP_sendseqinit ptr { |
push edi ;;;; i dont like this static use of edi |
mov edi, [ptr + TCP_SOCKET.ISS] |
mov [ptr + TCP_SOCKET.SND_UP], edi |
mov [ptr + TCP_SOCKET.SND_MAX], edi |
mov [ptr + TCP_SOCKET.SND_NXT], edi |
mov [ptr + TCP_SOCKET.SND_UNA], edi |
pop edi |
} |
macro TCP_rcvseqinit ptr { |
push edi |
mov edi, [ptr + TCP_SOCKET.IRS] |
inc edi |
mov [ptr + TCP_SOCKET.RCV_NXT], edi |
mov [ptr + TCP_SOCKET.RCV_ADV], edi |
pop edi |
} |
macro TCP_init_socket socket { |
mov [socket + TCP_SOCKET.t_maxseg], TCP_mss_default |
mov [socket + TCP_SOCKET.t_flags], TF_REQ_SCALE or TF_REQ_TSTMP |
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default |
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4 |
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min |
;;; TODO: TCP_time_rangeset |
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift |
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift |
} |
;--------------------------- |
; |
; TCP_pull_out_of_band |
; |
; IN: eax = |
; ebx = socket ptr |
; edx = tcp packet ptr |
; |
; OUT: / |
; |
;--------------------------- |
align 4 |
TCP_pull_out_of_band: |
DEBUGF 1,"TCP_pull_out_of_band\n" |
;;;; 1282-1305 |
ret |
;------------------------- |
; |
; TCP_drop |
; |
; IN: eax = socket ptr |
; ebx = error number |
; |
; OUT: eax = socket ptr |
; |
;------------------------- |
align 4 |
TCP_drop: |
DEBUGF 1,"TCP_drop: %x\n", eax |
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED |
jb .no_syn_received |
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED |
call TCP_output |
;;; TODO: update stats |
jmp TCP_close |
.no_syn_received: |
;;; TODO: update stats |
;;; TODO: check if error code is "Connection timed out' and handle accordingly |
mov [eax + SOCKET.errorcode], ebx |
;------------------------- |
; |
; TCP_close |
; |
; IN: eax = socket ptr |
; OUT: eax = socket ptr |
; |
;------------------------- |
align 4 |
TCP_close: |
DEBUGF 1,"TCP_close: %x\n", eax |
;;; TODO: update RTT and mean deviation |
;;; TODO: update slow start threshold |
call SOCKET_is_disconnected |
call SOCKET_free |
ret |
;------------------------- |
; |
; TCP_outflags |
; |
; IN: eax = socket ptr |
; |
; OUT: edx = flags |
; |
;------------------------- |
align 4 |
TCP_outflags: |
mov edx, [eax + TCP_SOCKET.t_state] |
movzx edx, byte [edx + .flaglist] |
DEBUGF 1,"TCP_outflags: socket=%x flags=%x\n", eax, dl |
ret |
.flaglist: |
db TH_RST + TH_ACK ; TCPS_CLOSED |
db 0 ; TCPS_LISTEN |
db TH_SYN ; TCPS_SYN_SENT |
db TH_SYN + TH_ACK ; TCPS_SYN_RECEIVED |
db TH_ACK ; TCPS_ESTABLISHED |
db TH_ACK ; TCPS_CLOSE_WAIT |
db TH_FIN + TH_ACK ; TCPS_FIN_WAIT_1 |
db TH_FIN + TH_ACK ; TCPS_CLOSING |
db TH_FIN + TH_ACK ; TCPS_LAST_ACK |
db TH_ACK ; TCPS_FIN_WAIT_2 |
db TH_ACK ; TCPS_TIMED_WAIT |
;--------------------------------------- |
; |
; The fast way to send an ACK/RST/keepalive segment |
; |
; TCP_respond |
; |
; IN: ebx = socket ptr |
; cl = flags |
; |
;-------------------------------------- |
align 4 |
TCP_respond: |
DEBUGF 1,"TCP_respond_socket: socket=%x flags=%x\n", ebx, cl |
;--------------------- |
; Create the IP packet |
push cx ebx |
mov eax, [ebx + IP_SOCKET.RemoteIP] |
mov edx, [ebx + IP_SOCKET.LocalIP] |
mov ecx, sizeof.TCP_header |
mov di, IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
test edi, edi |
jz .error |
pop esi cx |
push edx eax |
;----------------------------------------------- |
; Fill in the TCP header by using the socket ptr |
mov ax, [esi + TCP_SOCKET.LocalPort] |
stosw |
mov ax, [esi + TCP_SOCKET.RemotePort] |
stosw |
mov eax, [esi + TCP_SOCKET.SND_NXT] |
bswap eax |
stosd |
mov eax, [esi + TCP_SOCKET.RCV_NXT] |
bswap eax |
stosd |
mov al, 0x50 ; Dataoffset: 20 bytes (TCP_header.DataOffset) |
stosb |
mov al, cl |
stosb |
; mov ax, [esi + TCP_SOCKET.RCV_WND] |
; rol ax, 8 |
mov ax, 0x00a0 ;;;;;;; FIXME |
stosw ; window |
xor eax, eax |
stosd ; checksum + urgentpointer |
;--------------------- |
; Fill in the checksum |
.checksum: |
sub edi, sizeof.TCP_header |
mov ecx, sizeof.TCP_header |
xchg esi, edi |
TCP_checksum (edi + IP_SOCKET.LocalIP), (edi + IP_SOCKET.RemoteIP) |
mov [esi+TCP_header.Checksum], dx |
;-------------------- |
; And send the segment |
call [ebx + NET_DEVICE.transmit] |
ret |
.error: |
DEBUGF 1,"TCP_respond_socket: failed\n" |
add esp, 2 + 4 |
ret |
;------------------------- |
; TCP_respond_segment: |
; |
; IN: edx = segment ptr (a previously received segment) |
; edi = ptr to dest and src IPv4 addresses |
; cl = flags |
align 4 |
TCP_respond_segment: |
DEBUGF 1,"TCP_respond_segment: frame=%x flags=%x\n", edx, cl |
;--------------------- |
; Create the IP packet |
push cx edx |
mov ebx, [edi + 4] |
mov eax, [edi] |
mov ecx, sizeof.TCP_header |
mov di, IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
jz .error |
pop esi cx |
push edx eax |
;--------------------------------------------------- |
; Fill in the TCP header by using a received segment |
mov ax, [esi + TCP_header.DestinationPort] |
stosw |
mov ax, [esi + TCP_header.SourcePort] |
stosw |
mov eax, [esi + TCP_header.AckNumber] |
bswap eax |
stosd |
xor eax, eax |
stosd |
mov al, 0x50 ; Dataoffset: 20 bytes (sizeof.TCP_header/4 shl 4) |
stosb |
mov al, cl |
stosb |
mov ax, 1280 |
rol ax, 8 |
stosw ; window |
xor eax, eax |
stosd ; checksum + urgentpointer |
;--------------------- |
; Fill in the checksum |
lea esi, [edi - sizeof.TCP_header] |
mov ecx, sizeof.TCP_header |
TCP_checksum (esi - sizeof.IPv4_header + IPv4_header.DestinationAddress),\ ; FIXME |
(esi - sizeof.IPv4_header + IPv4_header.SourceAddress) |
mov [esi + TCP_header.Checksum], dx |
;-------------------- |
; And send the segment |
call [ebx + NET_DEVICE.transmit] |
ret |
.error: |
DEBUGF 1,"TCP_respond_segment: failed\n" |
add esp, 2+4 |
ret |
macro TCPT_RANGESET timer, value, min, max { |
local .min |
local .max |
local .done |
cmp value, min |
jb .min |
cmp value, max |
ja .max |
mov timer, value |
jmp .done |
.min: |
mov timer, value |
jmp .done |
.max: |
mov timer, value |
jmp .done |
.done: |
} |
align 4 |
TCP_set_persist: |
DEBUGF 1,"TCP_set_persist\n" |
; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive |
cmp [eax + TCP_SOCKET.timer_retransmission], 0 |
ja @f |
; calculate RTO |
push ebx |
mov ebx, [eax + TCP_SOCKET.t_srtt] |
shr ebx, 2 |
add ebx, [eax + TCP_SOCKET.t_rttvar] |
shr ebx, 1 |
mov cl, [eax + TCP_SOCKET.t_rxtshift] |
shl ebx, cl |
; Start/restart persistance timer. |
TCPT_RANGESET [eax + TCP_SOCKET.timer_persist], ebx, TCP_time_pers_min, TCP_time_pers_max |
pop ebx |
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift |
jae @f |
inc [eax + TCP_SOCKET.t_rxtshift] |
@@: |
ret |
; eax = rtt |
; ebx = socket ptr |
align 4 |
TCP_xmit_timer: |
DEBUGF 1,"TCP_xmit_timer: socket=%x rtt=%d0ms\n", ebx, eax |
;TODO: update stats |
cmp [ebx + TCP_SOCKET.t_rtt], 0 |
je .no_rtt_yet |
; srtt is stored as a fixed point with 3 bits after the binary point. |
; The following magic is equivalent of the smoothing algorithm in rfc793 with an alpha of .875 |
; (srtt = rtt/8 + srtt*7/8 in fixed point) |
; Adjust rtt to origin 0. |
push ecx |
mov ecx, [ebx + TCP_SOCKET.t_srtt] |
shr ecx, TCP_RTT_SHIFT |
sub eax, ecx |
dec eax |
pop ecx |
add [ebx + TCP_SOCKET.t_srtt], eax |
ja @f |
mov [ebx + TCP_SOCKET.t_srtt], 1 |
@@: |
; We accumulate a smoothed rtt variance (actually, a smoothed mean difference), |
; then set the retransmit timer to smoothed rtt + 4 times the smoothed variance. |
; rttvar is stored as fixed point with 2 bits after the binary point. |
; The following is equivalent to rfc793 smoothing with an alpha of .75 |
; (rttvar = rttvar*3/4 + delta/4) (delta = eax) |
; get abs(eax) |
push edx |
cdq |
xor eax, edx |
sub eax, edx |
mov edx, [ebx + TCP_SOCKET.t_rttvar] |
shr edx, TCP_RTTVAR_SHIFT |
sub eax, edx |
pop edx |
add [ebx + TCP_SOCKET.t_rttvar], eax |
ja @f |
mov [ebx + TCP_SOCKET.t_rttvar], 1 |
@@: |
ret |
.no_rtt_yet: |
push ecx |
mov ecx, eax |
shl ecx, TCP_RTT_SHIFT |
mov [ebx + TCP_SOCKET.t_srtt], ecx |
shl eax, TCP_RTTVAR_SHIFT - 1 |
mov [ebx + TCP_SOCKET.t_rttvar], eax |
pop ecx |
ret |
; eax = max segment size |
; ebx = socket ptr |
align 4 |
TCP_mss: |
cmp eax, 1420 ; FIXME |
jbe @f |
mov eax, 1420 |
@@: |
mov [ebx + TCP_SOCKET.t_maxseg], eax |
ret |
; ebx = socket ptr |
; edx = segment ptr |
align 4 |
TCP_reassemble: |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/tcp_timer.inc |
---|
0,0 → 1,168 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 3143 $ |
;---------------------- |
; 160 ms timer |
;---------------------- |
macro TCP_timer_160ms { |
local .loop |
local .exit |
mov ebx, net_sockets |
.loop: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .exit |
cmp [ebx + SOCKET.Domain], AF_INET4 |
jne .loop |
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP |
jne .loop |
test [ebx + TCP_SOCKET.t_flags], TF_DELACK |
jz .loop |
and [ebx + TCP_SOCKET.t_flags], not (TF_DELACK) |
push ebx |
mov cl, TH_ACK |
call TCP_respond |
; and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW ;; |
; mov eax, ebx ;; |
; call TCP_output ;; |
pop ebx |
jmp .loop |
.exit: |
} |
;---------------------- |
; 640 ms timer |
;---------------------- |
macro TCP_timer_640ms { |
local .loop |
local .exit |
; Update TCP sequence number |
add [TCP_sequence_num], 64000 |
; scan through all the active TCP sockets, decrementing ALL timers |
; timers do not have the chance to wrap because the keepalive timer will kill the socket when it expires |
mov eax, net_sockets |
.loop: |
mov eax, [eax + SOCKET.NextPtr] |
.check_only: |
or eax, eax |
jz .exit |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne .loop |
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP |
jne .loop |
inc [eax + TCP_SOCKET.t_idle] |
dec [eax + TCP_SOCKET.timer_retransmission] |
jnz .check_more2 |
DEBUGF 1,"socket %x: Retransmission timer expired\n", eax |
push eax |
call TCP_output |
pop eax |
.check_more2: |
dec [eax + TCP_SOCKET.timer_keepalive] |
jnz .check_more3 |
DEBUGF 1,"socket %x: Keepalive expired\n", eax |
cmp [eax + TCP_SOCKET.state], TCPS_ESTABLISHED |
ja .dont_kill |
push eax |
call TCP_disconnect |
pop eax |
jmp .loop |
.dont_kill: |
test [eax + SOCKET.options], SO_KEEPALIVE |
jz .reset_keepalive |
push eax |
mov ebx, eax |
xor cl, cl |
call TCP_respond ; send keepalive |
pop eax |
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval |
jmp .check_more3 |
.reset_keepalive: |
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle |
.check_more3: |
dec [eax + TCP_SOCKET.timer_timed_wait] |
jnz .check_more5 |
DEBUGF 1,"socket %x: 2MSL timer expired\n", eax |
.check_more5: |
dec [eax + TCP_SOCKET.timer_persist] |
jnz .loop |
DEBUGF 1,"socket %x: persist timer expired\n", eax |
call TCP_set_persist |
mov [eax + TCP_SOCKET.t_force], 1 |
push eax |
call TCP_output |
pop eax |
mov [eax + TCP_SOCKET.t_force], 0 |
jmp .loop |
.exit: |
} |
; eax = socket |
TCP_cancel_timers: |
push eax edi |
lea edi, [eax + TCP_SOCKET.timer_retransmission] |
xor eax, eax |
stosd |
stosd |
stosd |
stosd |
stosd |
pop edi eax |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/tcp_usreq.inc |
---|
0,0 → 1,102 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;------------------------- |
; |
; TCP_usrclose |
; |
; Move connection to next state, based on process close. |
; |
; IN: eax = socket ptr |
; |
;------------------------- |
align 4 |
TCP_usrclosed: |
DEBUGF 1,"TCP_usrclosed: %x\n", eax |
push ebx |
mov ebx, [eax + TCP_SOCKET.t_state] |
mov ebx, dword [.switch + ebx*4] |
jmp ebx |
.switch: |
dd .close ; TCPS_CLOSED |
dd .close ; TCPS_LISTEN |
dd .close ; TCPS_SYN_SENT |
dd .wait1 ; TCPS_SYN_RECEIVED |
dd .wait1 ; TCPS_ESTABLISHED |
dd .last_ack ; TCPS_CLOSE_WAIT |
dd .ret ; TCPS_FIN_WAIT_1 |
dd .ret ; TCPS_CLOSING |
dd .ret ; TCPS_LAST_ACK |
dd .disc ; TCPS_FIN_WAIT_2 |
dd .disc ; TCPS_TIMED_WAIT |
.close: |
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED |
call TCP_close |
pop ebx |
ret |
.wait1: |
mov [eax + TCP_SOCKET.t_state], TCPS_FIN_WAIT_1 |
; TODO: set timer? |
pop ebx |
ret |
.last_ack: |
mov [eax + TCP_SOCKET.t_state], TCPS_LAST_ACK |
pop ebx |
ret |
.disc: |
call SOCKET_is_disconnected |
; TODO: set timer? |
.ret: |
pop ebx |
ret |
;------------------------- |
; |
; TCP_disconnect |
; |
; IN: eax = socket ptr |
; OUT: eax = socket ptr |
; |
;------------------------- |
align 4 |
TCP_disconnect: |
DEBUGF 1,"TCP_disconnect: %x\n", eax |
cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED |
jb TCP_close |
; TODO: implement LINGER ? |
call SOCKET_is_disconnecting |
call TCP_usrclosed |
call TCP_output |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/network/udp.inc |
---|
1,153 → 1,325 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; ;; |
;; UDP.INC ;; |
;; ;; |
;; UDP Processes for Menuet OS TCP/IP stack ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; See file COPYING for details ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
$Revision: 2995 $ |
;******************************************************************* |
; Interface |
; |
; udp_rx Handles received IP packets with the UDP protocol |
; |
;******************************************************************* |
struct UDP_header |
SourcePort dw ? |
DestinationPort dw ? |
Length dw ? ; Length of (UDP Header + Data) |
Checksum dw ? |
ends |
align 4 |
uglobal |
UDP_PACKETS_TX rd MAX_NET_DEVICES |
UDP_PACKETS_RX rd MAX_NET_DEVICES |
endg |
;----------------------------------------------------------------- |
; |
; UDP Payload ( Data field in IP datagram ) |
; UDP_init |
; |
; 0 1 2 3 |
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
; This function resets all UDP variables |
; |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | Source Port | Destination Port | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | Length ( UDP Header + Data ) | Checksum | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | UDP Data | |
; +-+-+-.......... -+ |
; |
;----------------------------------------------------------------- |
macro UDP_init { |
struc UDP_PACKET |
{ .SourcePort dw ? ;+00 |
.DestinationPort dw ? ;+02 |
.Length dw ? ;+04 - Length of (UDP Header + Data) |
.Checksum dw ? ;+06 |
.Data db ? ;+08 |
xor eax, eax |
mov edi, UDP_PACKETS_TX |
mov ecx, 2*MAX_NET_DEVICES |
rep stosd |
} |
virtual at 0 |
UDP_PACKET UDP_PACKET |
end virtual |
macro UDP_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx |
;*************************************************************************** |
; Function |
; udp_rx [by Johnny_B] |
; Pseudoheader |
mov edx, IP_PROTO_UDP |
add dl, [IP1+1] |
adc dh, [IP1+0] |
adc dl, [IP1+3] |
adc dh, [IP1+2] |
adc dl, [IP2+1] |
adc dh, [IP2+0] |
adc dl, [IP2+3] |
adc dh, [IP2+2] |
adc dl, cl ; byte[esi+UDP_header.Length+1] |
adc dh, ch ; byte[esi+UDP_header.Length+0] |
; Done with pseudoheader, now do real header |
adc dl, byte[esi+UDP_header.SourcePort+1] |
adc dh, byte[esi+UDP_header.SourcePort+0] |
adc dl, byte[esi+UDP_header.DestinationPort+1] |
adc dh, byte[esi+UDP_header.DestinationPort+0] |
adc dl, byte[esi+UDP_header.Length+1] |
adc dh, byte[esi+UDP_header.Length+0] |
adc edx, 0 |
; Done with header, now do data |
push esi |
movzx ecx, [esi+UDP_header.Length] |
rol cx , 8 |
sub cx , sizeof.UDP_header |
add esi, sizeof.UDP_header |
call checksum_1 |
call checksum_2 |
pop esi |
add [esi+UDP_header.Checksum], dx ; this final instruction will set or clear ZF :) |
} |
;----------------------------------------------------------------- |
; |
; Description |
; UDP protocol handler |
; This is a kernel function, called by ip_rx |
; IP buffer address given in edx |
; IP buffer number in eax |
; Free up (or re-use) IP buffer when finished |
; UDP_input: |
; |
;*************************************************************************** |
; Called by IPv4_input, |
; this procedure will inject the udp data diagrams in the application sockets. |
; |
; IN: [esp] = Pointer to buffer |
; [esp+4] = size of buffer |
; ebx = ptr to device struct |
; ecx = UDP Packet size |
; esi = ptr to UDP header |
; edi = ptr to ipv4 source and dest address |
; |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
UDP_input: |
proc udp_rx stdcall |
push eax |
DEBUGF 1,"UDP_input: size=%u\n", ecx |
; First validate the header & checksum. Discard buffer if error |
; First validate, checksum |
neg [esi + UDP_header.Checksum] ; substract checksum from 0 |
jz .no_checksum ; if checksum is zero, it is considered valid |
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct |
UDP_checksum (edi), (edi+4) |
jnz .checksum_mismatch |
.no_checksum: |
DEBUGF 1,"UDP_input: checksum ok\n" |
; Convert length to little endian |
rol [esi + UDP_header.Length], 8 |
; Look for a socket where |
; IP Packet UDP Destination Port = local Port |
; IP Packet SA = Remote IP |
mov ax, [edx + 20 + UDP_PACKET.DestinationPort] ; get the local port from |
; the IP packet's UDP header |
mov cx, [esi + UDP_header.SourcePort] |
mov dx, [esi + UDP_header.DestinationPort] |
mov edi, [edi + 4] ; ipv4 source address |
mov eax, net_sockets |
mov ebx, net_sockets |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .exit ; No match, so exit |
cmp [ebx + SOCKET.LocalPort], ax ; ax will hold the 'wrong' value, |
; but the comparision is correct |
jne .next_socket ; Return back if no match |
mov eax, [eax + SOCKET.NextPtr] |
or eax, eax |
jz .dump |
; For dhcp, we must allow any remote server to respond. |
; I will accept the first incoming response to be the one |
; I bind to, if the socket is opened with a destination IP address of |
; 255.255.255.255 |
cmp [ebx + SOCKET.RemoteIP], 0xffffffff |
je @f |
cmp [eax + SOCKET.Domain], AF_INET4 |
jne .next_socket |
mov eax, [edx + IP_PACKET.SourceAddress] ; get the Source address from the IP packet |
cmp [ebx + SOCKET.RemoteIP], eax |
jne .exit ; Quit if the source IP is not valid |
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP |
jne .next_socket |
@@: ; OK - we have a valid UDP packet for this socket. |
; First, update the sockets remote port number with the incoming msg |
; - it will have changed |
; from the original ( 69 normally ) to allow further connects |
mov ax, [edx + 20 + UDP_PACKET.SourcePort] ; get the UDP source port |
; ( was 69, now new ) |
mov [ebx + SOCKET.RemotePort], ax |
cmp [eax + UDP_SOCKET.LocalPort], dx |
jne .next_socket |
; Now, copy data to socket. We have socket address as [eax + sockets]. |
; We have IP packet in edx |
DEBUGF 1,"UDP_input: socket=%x\n", eax |
; get # of bytes in ecx |
movzx ecx, [edx + IP_PACKET.TotalLength] ; total length of IP packet. Subtract |
xchg cl, ch ; 20 + 8 gives data length |
sub ecx, 28 |
;;; TODO: when packet is processed, check more sockets! |
mov eax, [ebx + SOCKET.rxDataCount] ; get # of bytes already in buffer |
add [ebx + SOCKET.rxDataCount], ecx ; increment the count of bytes in buffer |
; cmp [eax + IP_SOCKET.RemoteIP], 0xffffffff |
; je @f |
; cmp [eax + IP_SOCKET.RemoteIP], edi |
; jne .next_socket |
; @@: |
; |
; FIXME: UDP should check remote IP, but not under all circumstances! |
; ecx has count, edx points to data |
cmp [eax + UDP_SOCKET.firstpacket], 0 |
je .updateport |
add edx, 28 ; edx now points to the data |
lea edi, [ebx + eax + SOCKETHEADERSIZE] |
mov esi, edx |
cmp [eax + UDP_SOCKET.RemotePort], cx |
jne .dump |
cld |
rep movsb ; copy the data across |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
; flag an event to the application |
mov eax, [ebx + SOCKET.PID] ; get socket owner PID |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
.updatesock: |
inc [UDP_PACKETS_RX] ; Fixme: correct interface? |
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
movzx ecx, [esi + UDP_header.Length] |
sub ecx, sizeof.UDP_header |
add esi, sizeof.UDP_header |
jmp .exit |
jmp SOCKET_input |
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event |
.updateport: |
pusha |
lea ecx, [eax + SOCKET.mutex] |
call mutex_lock |
popa |
.exit: |
pop eax |
call freeBuff ; Discard the packet |
DEBUGF 1,"UDP_input: new remote port=%x\n", cx ; FIXME: find a way to print big endian values with debugf |
mov [eax + UDP_SOCKET.RemotePort], cx |
inc [eax + UDP_SOCKET.firstpacket] |
jmp .updatesock |
.checksum_mismatch: |
DEBUGF 2,"UDP_input: checksum mismatch\n" |
.dump: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
DEBUGF 2,"UDP_input: dumping\n" |
ret |
endp |
;----------------------------------------------------------------- |
; |
; UDP_output |
; |
; IN: eax = socket pointer |
; ecx = number of bytes to send |
; esi = pointer to data |
; |
;----------------------------------------------------------------- |
align 4 |
UDP_output: |
DEBUGF 1,"UDP_output: socket=%x bytes=%u data_ptr=%x\n", eax, ecx, esi |
mov dx, [eax + UDP_SOCKET.RemotePort] |
DEBUGF 1,"UDP_output: remote port=%x, ", dx ; FIXME: find a way to print big endian values with debugf |
rol edx, 16 |
mov dx, [eax + UDP_SOCKET.LocalPort] |
DEBUGF 1,"local port=%x\n", dx |
sub esp, 8 ; Data ptr and data size will be placed here |
push edx esi |
mov ebx, [eax + SOCKET.device] |
mov edx, [eax + IP_SOCKET.LocalIP] |
mov eax, [eax + IP_SOCKET.RemoteIP] |
mov di, IP_PROTO_UDP shl 8 + 128 |
add ecx, sizeof.UDP_header |
call IPv4_output |
jz .fail |
mov [esp + 8], eax ; pointer to buffer start |
mov [esp + 8 + 4], edx ; buffer size |
mov [edi + UDP_header.Length], cx |
rol [edi + UDP_header.Length], 8 |
pop esi |
push edi ecx |
sub ecx, sizeof.UDP_header |
add edi, sizeof.UDP_header |
shr ecx, 2 |
rep movsd |
mov ecx, [esp] |
and ecx, 3 |
rep movsb |
pop ecx edi |
pop dword [edi + UDP_header.SourcePort] |
; Checksum |
mov esi, edi |
mov [edi + UDP_header.Checksum], 0 |
UDP_checksum (edi-4), (edi-8) ; FIXME: IPv4 packet could have options.. |
DEBUGF 1,"UDP_output: sending with device %x\n", ebx |
call [ebx + NET_DEVICE.transmit] |
test eax, eax |
jnz @f |
inc [UDP_PACKETS_TX] ; FIXME: correct device? |
@@: |
ret |
.fail: |
DEBUGF 1,"UDP_output: failed\n" |
add esp, 4+4+8 |
or eax, -1 |
ret |
;--------------------------------------------------------------------------- |
; |
; UDP_API |
; |
; This function is called by system function 75 |
; |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
; OUT: |
; |
;--------------------------------------------------------------------------- |
align 4 |
UDP_api: |
movzx eax, bh |
shl eax, 2 |
test bl, bl |
jz .packets_tx ; 0 |
dec bl |
jz .packets_rx ; 1 |
.error: |
mov eax, -1 |
ret |
.packets_tx: |
mov eax, [UDP_PACKETS_TX + eax] |
ret |
.packets_rx: |
mov eax, [UDP_PACKETS_RX + eax] |
ret |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |