Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1535 → Rev 1536

/kernel/branches/net/network/ethernet.inc
167,6 → 167,7
 
.adjust_size:
mov edx, 60
test edx, edx ; clear zero flag
ret
 
.out_of_ram:
/kernel/branches/net/network/socket.inc
35,6 → 35,9
.options dd ?
.state dd ?
 
.snd_proc dd ?
.rcv_proc dd ?
 
.end:
end virtual
 
261,6 → 264,7
 
ret
 
align 4
sock_sysfn_table:
dd SOCKET_open ; 0
dd SOCKET_close ; 1
288,7 → 292,7
align 4
SOCKET_open:
 
DEBUGF 1,"socket_open: domain: %u, type: %u protocol: %x\n", ecx, edx, esi
DEBUGF 1,"SOCKET_open: domain: %u, type: %u protocol: %x\n", ecx, edx, esi
 
call SOCKET_alloc
jz s_error
297,17 → 301,26
mov [eax + SOCKET.Type], edx
mov [eax + SOCKET.Protocol], esi
 
mov [esp+32], edi
mov [esp+32], edi ; return socketnumber
 
cmp ecx, AF_INET4
jnz .no_stream
jne .no_inet4
 
push [IP_LIST] ;;;;
pop [eax + IP_SOCKET.LocalIP] ;;;;
push [IP_LIST]
pop [eax + IP_SOCKET.LocalIP] ; fill in local ip number
 
call SOCKET_find_port ; fill in a local port number, application may change it later, or use this one
 
cmp edx, IP_PROTO_UDP
je .udp
 
cmp edx, IP_PROTO_TCP
jnz .no_stream
je .tcp
 
.no_inet4:
ret
 
.tcp:
mov ebx, eax
 
lea eax, [ebx + STREAM_SOCKET.snd]
316,14 → 329,19
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create
 
mov [ebx + SOCKET.snd_proc], SOCKET_send_tcp
mov [ebx + SOCKET.rcv_proc], SOCKET_receive_tcp
 
ret
 
.no_stream:
 
push edi
.udp:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION)
pop edi
pop eax
 
mov [eax + SOCKET.snd_proc], SOCKET_send_udp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_udp
 
ret
 
 
375,22 → 393,16
 
mov bx, word [edx + 2]
test bx, bx
jz .find_free
jz .use_preset_port
 
call SOCKET_check_port
; test bx, bx
jz s_error
jmp .got_port
 
.find_free:
call SOCKET_find_port
; test bx, bx
jz s_error
 
.got_port:
DEBUGF 1,"using local port: %u\n", bx
mov word [eax + UDP_SOCKET.LocalPort], bx
 
.use_preset_port:
 
DEBUGF 1,"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
464,14 → 476,6
mov ebx, dword [edx + 4]
mov [eax + IP_SOCKET.RemoteIP], ebx
 
; check if local port and IP is ok
 
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
call SOCKET_find_port
@@:
DEBUGF 1,"local port: %u\n", [eax + TCP_SOCKET.LocalPort]:2
 
;;;;;
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
572,10 → 576,10
test ebx, ebx
jz .unlock_err
 
dec [eax + TCP_SOCKET.backlog_cur]
mov eax, [eax + TCP_SOCKET.end + (ebx-1)*4]
mov [eax + SOCKET.lock], 0
mov dword [esp+32], 0
dec [eax + TCP_SOCKET.backlog_cur] ;;;;
mov eax, [eax + TCP_SOCKET.end + (ebx-1)*4] ;;;;;
mov [eax + SOCKET.lock], 0 ;;;;
mov dword [esp+32], 0 ;;;;
 
call TCP_output ;;;;;
 
617,17 → 621,14
jmp s_error
 
.tcp:
test [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ;;;;;;
jz .free
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ; state must be LISTEN, SYN_SENT or CLOSED
jl .free
 
;;; call TCP_output
call TCP_output
mov dword [esp+32], 0
 
;;; mov dword [esp+32], 0
ret
 
;;; ret
 
; state must be LISTEN, SYN_SENT, CLOSED or maybe even invalid
; so, we may destroy the socket
.free:
call SOCKET_free
mov dword [esp+32], 0
649,55 → 650,63
align 4
SOCKET_receive:
 
DEBUGF 1,"socket_receive: socknum: %u bufaddr: %x, buflength: %u, flags: %x\n", ecx, edx, esi, edi
DEBUGF 1,"SOCKET_receive: socknum: %u bufaddr: %x, buflength: %u, flags: %x, ", ecx, edx, esi, edi
 
call SOCKET_num_to_ptr
jz s_error
 
cmp [eax + SOCKET.Type], IP_PROTO_TCP ;;;;;;;;
je .tcp
jmp [eax + SOCKET.rcv_proc]
 
 
 
align 4
SOCKET_receive_udp:
 
DEBUGF 1,"type: UDP\n"
 
mov ebx, esi
get_from_queue (eax + SOCKET_QUEUE_LOCATION),\
SOCKET_QUEUE_SIZE,\
socket_queue_entry.size,\
s_error
; destroys esi and ecx
 
mov edi, edx ; addr to buffer
 
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, s_error ; destroys esi and ecx
 
mov ecx, [esi + socket_queue_entry.data_size]
DEBUGF 1,"Got %u bytes of data\n", ecx
 
cmp ecx, ebx
jle .large_enough
DEBUGF 1,"Buffer too small...\n"
jmp s_error
jg .too_small
 
.large_enough:
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,"Source buffer: %x, real addr: %x\n", [esp], esi
mov dword[esp+32+4], ecx ; return number of bytes copied in ebx
mov dword[esp+32+4], ecx ; return number of bytes copied
 
; copy the data
shr ecx, 1
jnc .nb
movsb
.nb: shr ecx, 1
.nb:
shr ecx, 1
jnc .nw
movsw
.nw: test ecx, ecx
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
; remove the packet ;;; TODO: only if it is empty!!
 
call kernel_free
call kernel_free ; remove the packet
ret
 
.tcp:
.too_small:
 
DEBUGF 1,"Buffer too small...\n"
jmp s_error
 
align 4
SOCKET_receive_tcp:
 
DEBUGF 1,"type: TCP\n"
 
mov ecx, esi
mov edi, edx
add eax, STREAM_SOCKET.rcv
704,7 → 713,7
call SOCKET_ring_read
call SOCKET_ring_free
 
mov dword[esp+32], ecx ; return number of bytes copied in ebx
mov dword[esp+32], ecx ; return number of bytes copied
 
ret
 
724,45 → 733,20
align 4
SOCKET_send:
 
DEBUGF 1,"socket_send: socknum: %u sockaddr: %x, length: %u, flags: %x\n", ecx, edx, esi, edi
DEBUGF 1,"SOCKET_send: socknum: %u sockaddr: %x, length: %u, flags: %x, ", ecx, edx, esi, edi
 
call SOCKET_num_to_ptr
jz s_error
 
cmp word [eax + SOCKET.Domain], AF_INET4
je .af_inet4
jmp [eax + SOCKET.snd_proc]
 
jmp s_error
 
.af_inet4:
DEBUGF 1,"af_inet4\n"
 
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
mov ebx, [IP_LIST] ;;;;
mov dword [eax + IP_SOCKET.LocalIP], ebx
@@:
align 4
SOCKET_send_udp:
 
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
jmp s_error
 
.udp:
DEBUGF 1,"type: UDP\n"
 
; check if local port is valid
cmp [eax + UDP_SOCKET.LocalPort], 0
jne @f
 
call SOCKET_find_port
jz s_error
 
; Now, send the packet
@@:
mov ecx, esi
mov esi, edx
 
771,18 → 755,12
mov dword [esp+32], 0
ret
 
.tcp:
DEBUGF 1,"type: TCP\n"
 
; check if local port is valid
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
align 4
SOCKET_send_tcp:
 
call SOCKET_find_port
jz s_error
DEBUGF 1,"type: TCP\n"
 
@@:
 
push eax
mov ecx, esi
mov esi, edx
789,6 → 767,7
add eax, STREAM_SOCKET.snd
call SOCKET_ring_write
pop eax
 
call TCP_output
 
mov [esp+32], eax
814,7 → 793,7
align 4
SOCKET_get_opt:
 
DEBUGF 1,"socket_get_opt\n"
DEBUGF 1,"SOCKET_get_opt\n"
 
call SOCKET_num_to_ptr
jz s_error
894,7 → 873,7
align 4
SOCKET_find_port:
 
DEBUGF 1,"socket_find_free_port\n"
DEBUGF 1,"SOCKET_find_port\n"
 
push ebx esi ecx
 
956,7 → 935,7
align 4
SOCKET_check_port:
 
DEBUGF 1,"socket_check_port\n"
DEBUGF 1,"SOCKET_check_port\n"
 
mov ecx, [eax + SOCKET.Type]
mov esi, net_sockets
987,7 → 966,7
;
; SOCKET_input
;
; Updates a socket with received data
; Updates a (stateless) socket with received data
;
; Note: the mutex should already be set !
;
1004,16 → 983,13
align 4
SOCKET_input:
 
DEBUGF 1,"socket_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
DEBUGF 1,"SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
 
mov dword[esp+4], ecx
push esi
mov esi, esp
 
add_to_queue (eax + SOCKET_QUEUE_LOCATION),\
SOCKET_QUEUE_SIZE,\
socket_queue_entry.size,\
SOCKET_input.full
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, SOCKET_input.full
 
DEBUGF 1,"Queued packet successfully\n"
add esp, socket_queue_entry.size
1181,7 → 1157,7
align 4
SOCKET_ring_free:
 
DEBUGF 1,"Trying to free %u bytes of data from ring %x\n", ecx, eax
DEBUGF 1,"SOCKET_ring_free: %u bytes from ring %x\n", ecx, eax
 
sub [eax + RING_BUFFER.size], ecx
jl .sumthinwong
1213,12 → 1189,12
align 4
SOCKET_notify_owner:
 
DEBUGF 1,"socket_notify_owner\n"
DEBUGF 1,"SOCKET_notify_owner: %x\n", eax
 
call SOCKET_check
jz .error
 
push ecx eax esi
push ecx esi
 
; socket exists, now try to flag an event to the application
 
1243,10 → 1219,11
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK
mov [check_idle_semaphore], 200
 
DEBUGF 1,"owner notified\n"
DEBUGF 1,"SOCKET_notify_owner: succes!\n"
 
.error2:
pop esi eax ecx
pop esi ecx
 
.error:
 
ret
1284,18 → 1261,22
rep stosd
pop edi 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 ebx, net_sockets
xor ecx, ecx
.next_socket_number:
inc ecx
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .last_socket
 
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
mov ebx, net_sockets
jmp .next_socket_number
 
.last_socket:
1320,7 → 1301,7
call wait_mutex
sub ebx, SOCKET.lock
mov [ebx + SOCKET.PrevPtr], eax
mov [ebx + SOCKET.lock], 0
mov [ebx + SOCKET.lock], 0 ; and unlock it again
@@:
 
mov [net_sockets + SOCKET.NextPtr], eax
1344,7 → 1325,7
align 4
SOCKET_free:
 
DEBUGF 1, "socket_free: %x\n", eax
DEBUGF 1, "SOCKET_free: %x\n", eax
 
call SOCKET_check
jz .error
1356,14 → 1337,16
DEBUGF 1, "freeing socket..\n"
 
cmp [eax + SOCKET.Domain], AF_INET4
jnz .no_stream
jnz .no_tcp
 
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jnz .no_stream
jnz .no_tcp
 
stdcall kernel_free, [eax + STREAM_SOCKET.rcv + RING_BUFFER.start_ptr]
stdcall kernel_free, [eax + STREAM_SOCKET.snd + RING_BUFFER.start_ptr]
.no_stream:
mov ebx, eax
stdcall kernel_free, [ebx + STREAM_SOCKET.rcv + RING_BUFFER.start_ptr]
stdcall kernel_free, [ebx + STREAM_SOCKET.snd + RING_BUFFER.start_ptr]
mov eax, ebx
.no_tcp:
 
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
1437,7 → 1420,7
align 4
SOCKET_num_to_ptr:
 
DEBUGF 1,"socket_num_to_ptr: %u ", ecx
DEBUGF 1,"SOCKET_num_to_ptr: %u ", ecx
 
mov eax, net_sockets
 
1469,7 → 1452,7
align 4
SOCKET_ptr_to_num:
 
DEBUGF 1,"socket_ptr_to_num: %x ", eax
DEBUGF 1,"SOCKET_ptr_to_num: %x ", eax
 
call SOCKET_check
jz .error
1496,7 → 1479,7
align 4
SOCKET_check:
 
DEBUGF 1,"socket_check\n"
DEBUGF 1,"SOCKET_check: %x\n", eax
 
push ebx
mov ebx, net_sockets
1530,7 → 1513,7
align 4
SOCKET_check_owner:
 
DEBUGF 1,"socket_check_owner\n"
DEBUGF 1,"SOCKET_check_owner: %x\n", eax
 
push ebx
mov ebx, [TASK_BASE]
1558,7 → 1541,7
align 4
SOCKET_process_end:
 
DEBUGF 1,"socket_process_end: %x\n", eax
DEBUGF 1,"SOCKET_process_end: %x\n", eax
 
push ebx
mov ebx, net_sockets
/kernel/branches/net/network/tcp.inc
90,20 → 90,6
.Data: ; ..or options
ends
 
struct tcp_in_queue_entry
.data_ptr dd ?
.data_size dd ?
.offset dd ?
.size:
ends
 
struct tcp_out_queue_entry
.data_ptr dd ?
.data_size dd ?
 
.size:
ends
 
align 4
uglobal
TCP_segments_tx rd IP_MAX_INTERFACES
557,36 → 543,24
cmp [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED
jnz .not_uni_xfer
 
DEBUGF 1,"1\n"
 
test [edx + TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
DEBUGF 1,"2\n"
 
test [edx + TCP_segment.Flags], TH_ACK
jz .not_uni_xfer
 
DEBUGF 1,"3\n"
 
mov eax, [edx + TCP_segment.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
DEBUGF 1,"4\n"
movzx eax, [edx + TCP_segment.Window] ;;;;; (should use pre-calculated value instead: todo: figure out where to store it)
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .not_uni_xfer
 
;; movzx eax, [edx + TCP_segment.Window] ;;;;; (should use pre-calculated value isntead: todo: figure out where to store it)
;; cmp eax, [ebx + TCP_SOCKET.SND_WND]
;; jne .not_uni_xfer
 
DEBUGF 1,"5\n"
 
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jne .not_uni_xfer
 
DEBUGF 1,"6\n"
 
;---------------------------------------
; check if we are sender in the uni-xfer
 
596,8 → 570,6
test ecx, ecx
jnz .not_sender
 
DEBUGF 1,"7\n"
 
; - 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]
604,15 → 576,11
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jl .not_uni_xfer
 
DEBUGF 1,"8\n"
 
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent.
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .not_uni_xfer
 
DEBUGF 1,"9\n"
 
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number.
sub eax, [ebx + TCP_SOCKET.SND_UNA]
jle .not_uni_xfer
625,11 → 593,15
; Update RTT estimators
 
; Delete acknowledged bytes from send buffer
; notice how ecx already holds number of bytes ack-ed
 
mov ecx, eax
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_free
 
; update window pointers
mov eax, [edx + TCP_segment.AckNumber]
dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
; Stop retransmit timer
mov [ebx + TCP_SOCKET.timer_ack], 0
 
637,11 → 609,13
mov eax, ebx
call SOCKET_notify_owner
 
; Generate more output
call TCP_output
;; Generate more output
;; mov eax, ebx
;; call TCP_output
;;
;; jmp .drop
jmp .step6
 
jmp .drop
 
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
648,8 → 622,6
.not_sender:
; - The amount of data in the segment is greater than 0 (data count is in ecx)
 
DEBUGF 1,"10\n"
 
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
1084,17 → 1056,15
 
;;;;;
 
.ack_dup:
.ack_nodup:
 
DEBUGF 1,"Duplicate ACK\n"
DEBUGF 1,"New ACK\n"
 
;;;;
 
.ack_nodup:
 
;;;; 887
.ack_dup:
 
DEBUGF 1,"New ACK\n"
;;;;
 
;-------------------------------------------------
; If the congestion window was inflated to account
1125,13 → 1095,27
;------------------------------------------
; Remove acknowledged data from send buffer
 
push ecx
mov ecx, [edx + TCP_segment.AckNumber] ;;;
sub ecx, [ebx + TCP_SOCKET.SND_UNA] ;;;
pusha
; Delete acknowledged bytes from send buffer
mov ecx, [edx + TCP_segment.AckNumber]
sub ecx, [ebx + TCP_SOCKET.SND_UNA]
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_free ;;;; 943 - 956
pop ecx
call SOCKET_ring_free
popa
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; code missing (943?)
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jl @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------
; Wake up process waiting on send buffer
 
1176,14 → 1160,12
jmp .step6
 
 
 
align 4
.step6:
 
DEBUGF 1,"step 6\n"
 
;--------------------------
; update window information
;----------------------------------------------
; check if we need to update window information
 
test [edx + TCP_segment.Flags], TH_ACK
jz .no_window_update
1190,28 → 1172,46
 
mov eax, [ebx + TCP_SOCKET.SND_WL1]
cmp eax, [edx + TCP_segment.SequenceNumber]
jl .update_window
jg @f
 
;;;; 1021
 
;----------------------------------
; Keep track of pure window updates
 
test ecx, ecx
jz @f
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_segment.AckNumber]
jne @f
jl .update_window
jg .no_window_update
@@:
 
;; mov eax, tiwin
mov eax, [ebx + TCP_SOCKET.SND_WL2] ;;;;
cmp eax, [edx + TCP_segment.AckNumber]
jne .no_window_update
 
movzx eax, [edx + TCP_segment.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jle @f
jle .no_window_update
 
;;; update stats
.update_window:
 
@@:
DEBUGF 1,"Updating window\n"
 
;; mov eax, incoming window
;----------------------------------
; Keep track of pure window updates
 
; test ecx, ecx
; jz @f
;
; mov eax, [ebx + TCP_SOCKET.SND_WL2]
; cmp eax, [edx + TCP_segment.AckNumber]
; jne @f
;
; ;; mov eax, tiwin
; cmp eax, [ebx + TCP_SOCKET.SND_WND]
; jle @f
;
; ;;; update stats
;
; @@:
 
movzx eax, [edx + TCP_segment.Window] ;;; FIXME: use pre-calculated value instead!
cmp eax, [ebx + TCP_SOCKET.max_sndwnd]
jle @f
mov [ebx + TCP_SOCKET.max_sndwnd], eax
1218,11 → 1218,11
@@:
mov [ebx + TCP_SOCKET.SND_WND], eax
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.SND_WL1], eax
push [edx + TCP_segment.SequenceNumber]
pop [ebx + TCP_SOCKET.SND_WL1]
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_WL2], eax
push [edx + TCP_segment.AckNumber]
pop [ebx + TCP_SOCKET.SND_WL2]
 
;;; needoutput = 1
 
1496,8 → 1496,6
; TCP_output
;
; IN: eax = socket pointer
;; esi = ptr to data
;; ecx = number of data bytes
;
; OUT: /
;
1849,11 → 1847,14
; ecx = buffer size
; edi = ptr to buffer
 
; test ecx, ecx
mov eax, [esp+4] ; socket ptr
add [eax + TCP_SOCKET.SND_NXT], ecx
add eax, STREAM_SOCKET.snd
push edx
add eax, STREAM_SOCKET.snd
call SOCKET_ring_read
pop esi ecx
pop esi
pop ecx
pop eax
 
test [esi + TCP_segment.Flags], TH_SYN + TH_FIN
1862,8 → 1863,6
;;; TODO: update sentfin flag
@@:
 
;; add [eax + TCP_SOCKET.SND_NXT], ecx
 
mov edx, [eax + TCP_SOCKET.SND_NXT]
cmp edx, [eax + TCP_SOCKET.SND_MAX]
jle @f
1893,7 → 1892,7
.fail:
pop ecx
add esp, ecx
add esp, 4+4+8+4
add esp, 4+8
DEBUGF 1,"TCP_output: failed\n"
ret