kolibrios/kernel/branches/net/network/tcp.inc
hidnplayr 7f9d0c6697 Changes in net branch:
Things changed: sockets data organisation, queue macro's are more universal, new checksum routines, changed socket structures, ...
What's new: UDP checksum generation & validation
Rough TCP code has been written, but not debugged yet.

git-svn-id: svn://kolibrios.org@1249 a494cfbc-eb01-0410-851d-a64ba20cac60
2009-11-06 20:45:08 +00:00

879 lines
20 KiB
PHP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; TCP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
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
TH_FIN equ 1 shl 0
TH_SYN equ 1 shl 1
TH_RST equ 1 shl 2
TH_PUSH equ 1 shl 3
TH_ACK equ 1 shl 4
TH_URG equ 1 shl 5
TWOMSL equ 10 ; # of secs to wait before closing socket
TCP_RETRIES equ 5 ; Number of times to resend a Packet
TCP_TIMEOUT equ 10 ; resend if not replied to in 1/100 s
TCP_QUEUE_SIZE equ 16
struct TCP_Packet
.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 ?
.Options rb 3
.Padding db ?
.Data:
ends
align 4
uglobal
TCP_PACKETS_TX rd MAX_IP
TCP_PACKETS_RX rd MAX_IP
TCP_IN_QUEUE rd (tcp_in_queue_entry.size*TCP_QUEUE_SIZE+queue.data)/4
TCP_OUT_QUEUE dd ?
rd (tcp_out_queue_entry.size*TCP_QUEUE_SIZE)/4
endg
align 4
iglobal
TCBStateHandler:
dd stateTCB_LISTEN
dd stateTCB_SYN_SENT
dd stateTCB_SYN_RECEIVED
dd stateTCB_ESTABLISHED
dd stateTCB_FIN_WAIT_1
dd stateTCB_FIN_WAIT_2
dd stateTCB_CLOSE_WAIT
dd stateTCB_CLOSING
dd stateTCB_LAST_ACK
dd stateTCB_TIME_WAIT
dd stateTCB_CLOSED
endg
macro inc_INET reg {
inc byte [reg + 0]
adc byte [reg + 1], 0
adc byte [reg + 2], 0
adc byte [reg + 3], 0
}
macro add_INET reg {
rol ecx, 16
adc byte [reg + 0], ch
adc byte [reg + 1], cl
rol ecx, 16
adc byte [reg + 2], ch
adc byte [reg + 3], cl
}
;-----------------------------------------------------------------
;
; TCP_init
;
; This function resets all TCP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_init:
xor eax, eax
mov edi, TCP_PACKETS_TX
mov ecx, 2*MAX_IP
rep stosd
init_queue TCP_IN_QUEUE
init_queue TCP_OUT_QUEUE
ret
;-----------------------------------------------------------------
;
; TCP_decrease_socket_ttls
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_decrease_socket_ttls:
; scan through all the sockets, decrementing active timers
mov ebx, net_sockets
cmp [ebx + SOCKET_head.NextPtr], 0
je .exit
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
or ebx, ebx
jz .exit
cmp [ebx + SOCKET_head.Type], IP_PROTO_TCP
jne .next_socket
; 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]
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer], 0
jne .decrement_tcb
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 0
jne .decrement_wnd
jmp .next_socket
.decrement_tcb:
; decrement it, delete socket if TCB timer = 0 & socket in timewait state
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer]
jnz .next_socket
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT
jne .next_socket
push [ebx + SOCKET_head.PrevPtr]
stdcall net_socket_free, ebx
pop ebx
jmp .next_socket
.decrement_wnd:
; TODO - prove it works!
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer]
jmp .next_socket
.exit:
ret
;-----------------------------------------------------------------
;
; TCP_send_queued:
;
; Decreases 'ttl' of tcp packets queued.
; if 'ttl' reaches 0, resend the packet and decrease 'retries'
; if 'retries' reaches zero, remove the queued packet
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_send_queued:
cmp [TCP_OUT_QUEUE], 0
je .exit
mov eax, TCP_QUEUE_SIZE
mov ecx, [TCP_OUT_QUEUE]
mov esi, TCP_OUT_QUEUE+4
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jnz .found_one
add esi, tcp_out_queue_entry.size
loop .loop
.exit:
ret
.found_one:
dec [esi + tcp_out_queue_entry.ttl]
jz .send_it
.find_next:
dec eax
jz .exit
jmp .loop
.send_it:
push eax ecx esi
push [esi + tcp_out_queue_entry.data_size]
push [esi + tcp_out_queue_entry.data_ptr]
mov ebx, [esi + tcp_out_queue_entry.owner]
call [esi + tcp_out_queue_entry.sendproc]
pop esi ecx eax
dec [esi + tcp_out_queue_entry.retries]
jz .remove_it
mov [esi + tcp_out_queue_entry.ttl], TCP_TIMEOUT
jmp .find_next
.remove_it:
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
jmp .find_next
;-----------------------------------------------------------------
;
; TCP_add_to_queue:
;
; Queue a TCP packet for sending
;
; IN: [esp] pointer to buffer
; [esp + 4] size of buffer
; ebx = driver struct
; esi = sender proc
; edx = acknum
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_add_to_queue:
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
jge .full
mov ecx, TCP_QUEUE_SIZE
mov eax, TCP_OUT_QUEUE+4
.loop:
cmp [eax + tcp_out_queue_entry.data_ptr], 0
je .found_it
add eax, tcp_out_queue_entry.size
loop .loop
.full: ; silently discard the packet
call kernel_free
add esp, 4
ret
.found_it: ; eax point to empty queue entry
pop [eax + tcp_out_queue_entry.data_ptr]
pop [eax + tcp_out_queue_entry.data_size]
mov [eax + tcp_out_queue_entry.ttl], 1 ; send immediately
mov [eax + tcp_out_queue_entry.retries], TCP_RETRIES
mov [eax + tcp_out_queue_entry.owner], ebx
mov [eax + tcp_out_queue_entry.sendproc], esi
mov [eax + tcp_out_queue_entry.ack_num], edx
ret
;-----------------------------------------------------------------
;
; TCP_handler:
;
; Called by IPv4_handler,
; this procedure will inject the tcp data diagrams in the application sockets.
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; TCP Packet size in ecx
; pointer to TCP Packet data in edx
; SourceAddres in esi
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_handler :
DEBUGF 1,"TCP_Handler\n"
; IP Packet TCP Destination Port = local Port
; IP Packet SA = Remote IP OR = 0
; IP Packet TCP Source Port = remote Port OR = 0
mov ebx, net_sockets
.socket_loop:
mov ebx, [ebx + SOCKET_head.NextPtr]
or ebx, ebx
jz .dump
mov ax, [edx + TCP_Packet.DestinationPort]
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], ax
jne .socket_loop
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
cmp eax, esi
je @f
test eax, eax
jne .socket_loop
@@:
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort]
cmp [edx + TCP_Packet.SourcePort] , ax
je .change_state
test ax, ax
jne .socket_loop
.change_state:
push ebx
lea ebx, [ebx + SOCKET_head.lock]
call wait_mutex
pop ebx
;----------------------------------
; ebx is pointer to socket
; ecx is size of tcp packet
; edx is pointer to tcp packet
; as a Packet has been received, update the TCB timer
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer], TWOMSL
; If the received Packet has an ACK bit set, remove any Packets in the resend queue that this received Packet acknowledges
test [edx + TCP_Packet.Flags], TH_ACK
jz .call_handler ; No ACK, so no data yet
mov eax, [edx + TCP_Packet.SequenceNumber] ; Calculate sequencenumber in eax
bswap eax ;
add eax, ecx ;
cmp [TCP_OUT_QUEUE], 0
je .call_handler
push ecx
mov ecx, TCP_QUEUE_SIZE
mov esi, TCP_OUT_QUEUE+4
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jne .maybe_next
cmp [esi + tcp_out_queue_entry.ack_num], eax
jg .maybe_next
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
.maybe_next:
add esi, tcp_out_queue_entry.size
loop .loop
pop ecx
.call_handler:
; Call handler for given TCB state
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState]
cmp eax, TCB_LISTEN
jb .exit
cmp eax, TCB_CLOSED
ja .exit
shl eax, 2
add eax, TCBStateHandler - 4
push .exit
jmp eax
.exit:
mov [ebx + SOCKET_head.lock], 0
.dump:
DEBUGF 1,"Dumping TCP packet\n"
call kernel_free
add esp, 4 ; pop (balance stack)
ret
;-----------------------------------------------------------------
;
; TCP_socket_send
;
; IN: eax = socket pointer
; ecx = number of bytes to send
; esi = pointer to data
;
;-----------------------------------------------------------------
align 4
TCP_socket_send:
DEBUGF 1,"Creating TCP Packet\n"
mov di , IP_PROTO_TCP
; Create an IPv4 Packet of the correct size
push eax
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
; meanwhile, create the pseudoheader in stack,
; (now that we still have all the variables that are needed.)
push cx
push di
push eax
push ebx
push ecx esi eax ; save some variables for later
add ecx, TCP_Packet.Data
call IPv4_create_packet
cmp edi, -1
je .fail
pop esi
; Now add the TCP header to the IPv4 packet
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.AckNumber]
mov al, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.flags]
mov [edi + TCP_Packet.Flags], al
mov [edi + TCP_Packet.Window], 0x0005 ; 1280 bytes ;;; TODO: read RFC !
mov [edi + TCP_Packet.UrgentPointer], 0
mov [edi + TCP_Packet.DataOffset], 0x50
mov [edi + TCP_Packet.Checksum], 0
; Copy the data
mov esi, [esp]
mov ecx, [esp+4]
add edi, TCP_Packet.Data
shr ecx, 1
jnc .nb
movsb
.nb: shr ecx, 1
jnc .nw
movsw
.nw: rep movsd
; Now, calculate the checksum for pseudoheader
xor edx, edx
mov ecx, 12
mov esi, esp
call checksum_1
add esp, 12 ; remove the pseudoheader from stack
; And that of the data
pop esi
pop ecx
call checksum_1
; Now create the final checksum and store it in TCP header
call checksum_2
mov [edi + TCP_Packet.Checksum], dx
; And now, send it!
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
mov esi, ETH_sender
mov edx, [edi + TCP_Packet.AckNumber]
jmp TCP_add_to_queue
.fail:
add esp, 12+4
ret
;-----------------------------------------------------------------
;
; TCP_send_ack
;
; IN: eax = socket pointer
; bl = flags
;
;-----------------------------------------------------------------
align 4
TCP_send_ack:
DEBUGF 1,"Creating TCP ACK\n"
mov di , IP_PROTO_TCP
mov cx , TCP_Packet.Data
push bx eax
; Create an IPv4 Packet of the correct size
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
call IPv4_create_packet
cmp edi, -1
je .fail
; Fill in the TCP header
pop esi
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.AckNumber]
pop cx
mov [edi + TCP_Packet.Flags], cl
mov [edi + TCP_Packet.Window], 0x0005 ; 1280 bytes
mov [edi + TCP_Packet.UrgentPointer], 0
mov [edi + TCP_Packet.DataOffset], 0x50
push eax edx
push word TCP_Packet.Data shl 8
push IP_PROTO_TCP
push [esi + SOCKET_head.end + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
push [esi + SOCKET_head.end + SOCKET_head.end + IPv4_SOCKET.LocalIP]
; Now, calculate the checksum for pseudoheader
xor edx, edx
mov ecx, 12
mov esi, esp
call checksum_1
add esp, 12 ; remove the pseudoheader from stack
; Now create the final checksum and store it in TCP header
call checksum_2
mov [edi + TCP_Packet.Checksum], dx
; And now, send it!
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
mov esi, ETH_sender
mov edx, [edi + TCP_Packet.AckNumber]
jmp TCP_add_to_queue
.fail:
add esp, 12+4
ret
align 4
stateTCB_LISTEN:
; 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 + TCP_Packet.Flags], TH_SYN
jz .exit
; We have a SYN. update the socket with this IP Packets details,
; And send a response
mov [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP], esi ; IP source address
mov ax, [edx + TCP_Packet.SourcePort]
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], ax
mov eax, [edx + TCP_Packet.SequenceNumber]
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.IRS], eax
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], eax
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
inc_INET esi ; RCV.NXT
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.ISS]
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT], eax
; Now construct the response
mov bl, TH_SYN + TH_ACK
call TCP_send_ack
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_SYN_RECEIVED
; increment SND.NXT in socket
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
inc_INET esi
.exit:
ret
align 4
stateTCB_SYN_SENT:
; We are awaiting an ACK to our SYN, with a SYM
; Look at control flags - expecting an ACK
mov al, [edx + 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_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_SYN_RECEIVED
push TH_SYN + TH_ACK
jmp .send
.syn_ack:
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_ESTABLISHED
push TH_ACK
.send:
; Store the recv.nxt field
mov eax, [edx + TCP_Packet.SequenceNumber]
; Update our recv.nxt field
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], eax
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
inc_INET esi
; Send an ACK
pop ebx
call TCP_send_ack
.exit:
ret
align 4
stateTCB_SYN_RECEIVED:
; 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 + TCP_Packet.Flags], TH_RST
jz .check_ack
push [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.OrigRemotePort]
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort]
push [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.OrigRemoteIP]
pop [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_LISTEN
jmp .exit
.check_ack:
; Look at control flags - expecting an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_ESTABLISHED
.exit:
ret
align 4
stateTCB_ESTABLISHED:
; Here we are expecting data, or a request to close
; OR both...
; Did we receive a FIN or RST?
test [edx + TCP_Packet.Flags], TH_FIN
jz .check_ack
; It was a fin or reset.
; Remove resend entries from the queue - I dont want to send any more data
; Send an ACK to that fin, and enter closewait state
.check_ack:
; Check that we received an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
; 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 + TCP_Packet.Window]
xchg cl, ch
cmp cx, 1024
ja @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 1
@@: ; OK, here is the deal
; My recv.nct field holds the seq of the expected next rec byte
; if the recevied sequence number is not equal to this, do not
; increment the recv.nxt field, do not copy data - just send a
; repeat ack.
; recv.nxt is in dword [edx+24], in inet format
; recv seq is in [sktAddr]+56, in inet format
; just do a comparision
mov ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSE_WAIT
jne @f
mov ecx, eax
@@: cmp ecx, [edx + TCP_Packet.SequenceNumber]
jne .ack
test ecx, ecx
jnz .data
; If we had received a fin, we need to ACK it.
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSE_WAIT
je .ack
jmp .exit
.data:
mov esi, [esp + 4]
sub edx, esi
mov edi, edx
call socket_internal_receiver
.ack:
; Send an ACK
mov bl, TH_ACK
call TCP_send_ack
.exit:
ret
align 4
stateTCB_FIN_WAIT_1:
; We can either receive an ACK of a fin, or a fin
mov al, [edx + TCP_Packet.Flags]
and al, TH_FIN + TH_ACK
cmp al, TH_ACK
jne @f
; It was an ACK
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_FIN_WAIT_2
jmp .exit
@@: mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSING
cmp al, TH_FIN
je @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT
@@: lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
inc_INET esi
; Send an ACK
mov bl, TH_ACK
call TCP_send_ack
.exit:
ret
align 4
stateTCB_FIN_WAIT_2:
test [edx + TCP_Packet.Flags], TH_FIN
jz .exit
; Change state, as we have a fin
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
inc_INET esi
; Send an ACK
mov bl, TH_ACK
call TCP_send_ack
.exit:
ret
align 4
stateTCB_CLOSE_WAIT:
; Intentionally left empty
; socket_close_tcp handles this
ret
align 4
stateTCB_CLOSING:
; We can either receive an ACK of a fin, or a fin
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT
.exit:
ret
align 4
stateTCB_LAST_ACK:
; Look at control flags - expecting an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
; delete the socket
stdcall net_socket_free, ebx
.exit:
ret
align 4
stateTCB_TIME_WAIT:
ret
align 4
stateTCB_CLOSED:
ret