forked from KolibriOS/kolibrios
1303 lines
32 KiB
PHP
1303 lines
32 KiB
PHP
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; ;;
|
||
|
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
|
||
|
;; Distributed under terms of the GNU General Public License ;;
|
||
|
;; ;;
|
||
|
;; TCP.INC ;;
|
||
|
;; ;;
|
||
|
;; TCP Processes for Menuet OS TCP/IP stack ;;
|
||
|
;; ;;
|
||
|
;; Version 0.6 4th July 2004 ;;
|
||
|
;; ;;
|
||
|
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
|
||
|
;; ;;
|
||
|
;; 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 ;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
|
||
|
$Revision$
|
||
|
|
||
|
|
||
|
; 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_TIME_WAIT equ 10
|
||
|
TCB_CLOSED equ 11
|
||
|
|
||
|
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 x hs
|
||
|
|
||
|
;*******************************************************************
|
||
|
; 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
|
||
|
;
|
||
|
;*******************************************************************
|
||
|
|
||
|
|
||
|
; 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
|
||
|
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
virtual at 0
|
||
|
TCP_PACKET TCP_PACKET
|
||
|
end virtual
|
||
|
|
||
|
|
||
|
|
||
|
;***************************************************************************
|
||
|
; Function
|
||
|
; tcp_tcb_handler
|
||
|
;
|
||
|
; Description
|
||
|
; Handles sockets in the timewait state, closing them
|
||
|
; when the TCB timer expires
|
||
|
;
|
||
|
;***************************************************************************
|
||
|
tcp_tcb_handler:
|
||
|
; scan through all the sockets, decrementing active timers
|
||
|
|
||
|
mov eax, SOCKETBUFFSIZE * NUM_SOCKETS
|
||
|
mov ecx, NUM_SOCKETS
|
||
|
|
||
|
tth1:
|
||
|
sub eax, SOCKETBUFFSIZE
|
||
|
cmp [eax + sockets + 32], dword 0
|
||
|
jne tth2
|
||
|
|
||
|
tth1a:
|
||
|
cmp [eax + sockets + 72], dword 0
|
||
|
jne tth4
|
||
|
|
||
|
loop tth1
|
||
|
ret
|
||
|
|
||
|
tth2:
|
||
|
; decrement it, delete socket if TCB timer = 0 & socket in timewait state
|
||
|
pusha
|
||
|
dec dword [eax + sockets + 32]
|
||
|
cmp [eax + sockets + 32], dword 0
|
||
|
jne tth3
|
||
|
|
||
|
cmp [eax + sockets + 28], dword TCB_TIME_WAIT
|
||
|
jne tth3
|
||
|
|
||
|
; OK, delete socket
|
||
|
mov edi, eax
|
||
|
add edi, sockets
|
||
|
|
||
|
xor eax, eax
|
||
|
mov ecx, SOCKETHEADERSIZE
|
||
|
cld
|
||
|
rep stosb
|
||
|
|
||
|
tth3:
|
||
|
popa
|
||
|
|
||
|
jmp tth1a
|
||
|
|
||
|
loop tth1
|
||
|
ret
|
||
|
|
||
|
; TODO - prove it works!
|
||
|
tth4:
|
||
|
dec dword [eax + sockets + 72]
|
||
|
loop tth1
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
tth_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
;***************************************************************************
|
||
|
; Function
|
||
|
; tcp_tx_handler
|
||
|
;
|
||
|
; Description
|
||
|
; Handles queued TCP data
|
||
|
; This is a kernel function, called by stack_handler
|
||
|
;
|
||
|
;***************************************************************************
|
||
|
tcp_tx_handler:
|
||
|
; 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
|
||
|
|
||
|
mov esi, resendQ
|
||
|
mov ecx, 0
|
||
|
|
||
|
tth001:
|
||
|
cmp ecx, NUMRESENDENTRIES
|
||
|
je tth003 ; None left
|
||
|
cmp [esi], byte 0xFF
|
||
|
jne tth002 ; found one
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp tth001
|
||
|
|
||
|
tth002:
|
||
|
; we have one. decrement it's timer by 1
|
||
|
dec word [esi+2]
|
||
|
mov ax, [esi+2]
|
||
|
cmp ax, 0
|
||
|
je tth002a
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp tth001 ; Timer not zero, so move on
|
||
|
|
||
|
tth002a:
|
||
|
mov bl, 0xff
|
||
|
; 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]
|
||
|
mov al, [esi+1]
|
||
|
cmp al, 0
|
||
|
jne tth004
|
||
|
|
||
|
; retries now 0, so delete from queue
|
||
|
xchg [esi], bl
|
||
|
tth004:
|
||
|
|
||
|
; resend packet
|
||
|
pusha
|
||
|
|
||
|
mov eax, EMPTY_QUEUE
|
||
|
call dequeue
|
||
|
cmp ax, NO_BUFFER
|
||
|
jne tth004z
|
||
|
|
||
|
; TODO - try again in 10ms.
|
||
|
cmp bl, 0xff
|
||
|
jne tth004za
|
||
|
mov [esi], bl
|
||
|
|
||
|
tth004za:
|
||
|
; Mark it to expire in 10ms - 1 tick
|
||
|
mov [esi+1], byte 1
|
||
|
mov [esi+2], word 1
|
||
|
jmp tth005
|
||
|
|
||
|
tth004z:
|
||
|
; we have a buffer # in ax
|
||
|
|
||
|
push eax
|
||
|
push ecx
|
||
|
mov ecx, IPBUFFSIZE
|
||
|
mul ecx
|
||
|
add eax, IPbuffs
|
||
|
|
||
|
; we have the buffer address in eax
|
||
|
mov edi, eax
|
||
|
pop ecx
|
||
|
; get resend data address
|
||
|
inc ecx
|
||
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
||
|
mov esi, resendBuffer - IPBUFFSIZE
|
||
|
tth004a:
|
||
|
add esi, IPBUFFSIZE
|
||
|
loop tth004a
|
||
|
|
||
|
; we have resend buffer location in esi
|
||
|
mov ecx, IPBUFFSIZE
|
||
|
|
||
|
; copy data across
|
||
|
cld
|
||
|
rep movsb
|
||
|
|
||
|
; queue packet
|
||
|
|
||
|
|
||
|
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ edi + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne tth004b
|
||
|
mov eax, IPIN_QUEUE
|
||
|
|
||
|
tth004b:
|
||
|
pop ebx
|
||
|
|
||
|
call queue
|
||
|
|
||
|
|
||
|
tth005:
|
||
|
popa
|
||
|
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp tth001
|
||
|
|
||
|
tth003:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
;***************************************************************************
|
||
|
; 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_rx:
|
||
|
; 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 eax, SOCKETBUFFSIZE * NUM_SOCKETS
|
||
|
mov ecx, NUM_SOCKETS
|
||
|
ss1:
|
||
|
sub eax, SOCKETBUFFSIZE
|
||
|
movzx ebx, word [edx + 22] ; get the dest. port from the TCP hdr
|
||
|
cmp [eax + sockets + 12], bx ; compare with socket's local port
|
||
|
jnz nxttst1 ; different - try next socket
|
||
|
|
||
|
movzx ebx, word [edx + 20] ; get the source port from the TCP hdr
|
||
|
cmp [eax + sockets + 20], bx ; compare with socket's remote port
|
||
|
jnz nxttst1 ; different - try next socket
|
||
|
|
||
|
|
||
|
mov ebx, [edx + 12] ; get the source IP Addr from the IP hdr
|
||
|
cmp [eax + sockets + 16], ebx ; compare with socket's remote IP
|
||
|
jnz nxttst1 ; different - try next socket
|
||
|
|
||
|
; We have a complete match - use this socket
|
||
|
jmp tcprx_001
|
||
|
|
||
|
nxttst1:
|
||
|
loop ss1 ; Return back if no match
|
||
|
|
||
|
; 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 eax, SOCKETBUFFSIZE * NUM_SOCKETS
|
||
|
mov ecx, NUM_SOCKETS
|
||
|
|
||
|
ss2:
|
||
|
sub eax, SOCKETBUFFSIZE
|
||
|
|
||
|
movzx ebx, word [edx + 22] ; get the dest. port from the TCP hdr
|
||
|
cmp [eax + sockets + 12], bx ; compare with socket's local port
|
||
|
jnz nxttst2 ; different - try next socket
|
||
|
|
||
|
mov ebx, [edx + 12] ; get the source IP Addr from the IP hdr
|
||
|
cmp [eax + sockets + 16], ebx ; compare with socket's remote IP
|
||
|
jnz nxttst2 ; different - try next socket
|
||
|
|
||
|
mov ebx, 0
|
||
|
cmp [eax + sockets + 20], bx ; only match a remote socket of 0
|
||
|
jnz nxttst2 ; different - try next socket
|
||
|
|
||
|
; We have a complete match - use this socket
|
||
|
jmp tcprx_001
|
||
|
|
||
|
nxttst2:
|
||
|
loop ss2 ; Return back if no match
|
||
|
|
||
|
; 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 eax, SOCKETBUFFSIZE * NUM_SOCKETS
|
||
|
mov ecx, NUM_SOCKETS
|
||
|
|
||
|
ss3:
|
||
|
sub eax, SOCKETBUFFSIZE
|
||
|
|
||
|
movzx ebx, word [edx + 22] ; get destination port from the TCP hdr
|
||
|
cmp [eax + sockets + 12], bx ; compare with socket's local port
|
||
|
jnz nxttst3 ; different - try next socket
|
||
|
|
||
|
mov ebx, 0
|
||
|
cmp [eax + sockets + 20], bx ; only match a remote socket of 0
|
||
|
jnz nxttst3 ; different - try next socket
|
||
|
|
||
|
mov ebx, 0
|
||
|
cmp [eax + sockets + 16], ebx ; only match a socket remote IP of 0
|
||
|
jnz nxttst3 ; different - try next socket
|
||
|
|
||
|
; We have a complete match - use this socket
|
||
|
jmp tcprx_001
|
||
|
|
||
|
nxttst3:
|
||
|
loop ss3 ; Return back if no match
|
||
|
|
||
|
; If we got here, we need to reject the packet
|
||
|
inc dword [dumped_rx_count]
|
||
|
jmp tcprx_exit
|
||
|
|
||
|
tcprx_001:
|
||
|
; We have a valid socket/TCB, so call the TCB State Machine for that skt.
|
||
|
; socket is pointed to by [eax + sockets]
|
||
|
; IP packet is pointed to by [edx]
|
||
|
; IP buffer number is on stack ( it will be popped at the end)
|
||
|
call tcpStateMachine
|
||
|
|
||
|
tcprx_exit:
|
||
|
pop eax
|
||
|
call freeBuff
|
||
|
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
;***************************************************************************
|
||
|
; Function
|
||
|
; buildTCPPacket
|
||
|
;
|
||
|
; 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
|
||
|
;
|
||
|
;***************************************************************************
|
||
|
buildTCPPacket:
|
||
|
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 + 33], bl ; TCP flags
|
||
|
|
||
|
mov ebx, [sktAddr]
|
||
|
|
||
|
; 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 + 8]
|
||
|
mov [edx + 12], eax ; source IP
|
||
|
mov eax, [ebx + 16]
|
||
|
mov [edx + 16], eax ; Destination IP
|
||
|
|
||
|
mov al, 0x45
|
||
|
mov [edx], al ; Version, IHL
|
||
|
xor al, al
|
||
|
mov [edx + 1], al ; Type of service
|
||
|
|
||
|
pop eax ; Get the TCP data length
|
||
|
push eax
|
||
|
|
||
|
add eax, 20 + 20 ; add IP header and TCP header lengths
|
||
|
mov [edx + 2], ah
|
||
|
mov [edx + 3], al
|
||
|
xor al, al
|
||
|
mov [edx + 4], al
|
||
|
mov [edx + 5], al
|
||
|
mov al, 0x40
|
||
|
mov [edx + 6], al
|
||
|
xor al, al
|
||
|
mov [edx + 7], al
|
||
|
mov al, 0x20
|
||
|
mov [edx + 8], al
|
||
|
mov al, 6 ; TCP protocol
|
||
|
mov [edx + 9], al
|
||
|
|
||
|
; Checksum left unfilled
|
||
|
xor ax, ax
|
||
|
mov [edx + 10], ax
|
||
|
|
||
|
; Fill in the TCP header ( some data is in the socket descriptor)
|
||
|
mov ax, [ebx + 12]
|
||
|
mov [edx + 20], ax ; Local Port
|
||
|
|
||
|
mov ax, [ebx + 20]
|
||
|
mov [edx + 20 + 2], ax ; desitination Port
|
||
|
|
||
|
; Checksum left unfilled
|
||
|
xor ax, ax
|
||
|
mov [edx + 20 + 16], ax
|
||
|
|
||
|
; sequence number
|
||
|
mov eax, [ebx + 48]
|
||
|
mov [edx + 20 + 4], eax
|
||
|
|
||
|
; ack number
|
||
|
mov eax, [ebx + 56]
|
||
|
mov [edx + 20 + 8], eax
|
||
|
|
||
|
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size)
|
||
|
; 768 bytes seems better
|
||
|
mov ax, 0x0003
|
||
|
mov [edx + 20 + 14], ax
|
||
|
|
||
|
; Urgent pointer (0)
|
||
|
mov ax, 0
|
||
|
mov [edx + 20 + 18], ax
|
||
|
|
||
|
; data offset ( 0x50 )
|
||
|
mov al, 0x50
|
||
|
mov [edx + 20 + 12], al
|
||
|
|
||
|
pop ecx ; count of bytes to send
|
||
|
mov ebx, ecx ; need the length later
|
||
|
|
||
|
cmp ebx, 0
|
||
|
jz btp_001
|
||
|
|
||
|
mov edi, edx
|
||
|
add edi, 40
|
||
|
cld
|
||
|
rep movsb ; copy the data across
|
||
|
|
||
|
btp_001:
|
||
|
; we have edx as IPbuffer ptr.
|
||
|
; Fill in the TCP checksum
|
||
|
; First, fill in pseudoheader
|
||
|
mov eax, [edx + 12]
|
||
|
mov [pseudoHeader], eax
|
||
|
mov eax, [edx + 16]
|
||
|
mov [pseudoHeader+4], eax
|
||
|
mov ax, 0x0600 ; 0 + protocol
|
||
|
mov [pseudoHeader+8], ax
|
||
|
add ebx, 20
|
||
|
mov eax, ebx
|
||
|
mov [pseudoHeader+10], ah
|
||
|
mov [pseudoHeader+11], al
|
||
|
|
||
|
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
|
||
|
|
||
|
call checksum
|
||
|
|
||
|
; store it in the TCP checksum ( in the correct order! )
|
||
|
mov ax, [checkResult]
|
||
|
|
||
|
mov [edx + 20 + 16], ah
|
||
|
mov [edx + 20 + 17], al
|
||
|
|
||
|
; Fill in the IP header checksum
|
||
|
GET_IHL eax,edx ; get IP-Header length
|
||
|
stdcall checksum_jb,edx,eax ; buf_ptr, buf_size
|
||
|
|
||
|
mov [edx + 10], ah
|
||
|
mov [edx + 11], al
|
||
|
|
||
|
ret
|
||
|
|
||
|
|
||
|
; Increments the 32 bit value pointed to by esi in internet order
|
||
|
inc_inet_esi:
|
||
|
push eax
|
||
|
add esi, 3
|
||
|
mov al, byte[esi]
|
||
|
inc al
|
||
|
mov byte[esi], al
|
||
|
cmp al, 0
|
||
|
jnz iie_exit
|
||
|
dec esi
|
||
|
mov al, byte[esi]
|
||
|
inc al
|
||
|
mov byte[esi], al
|
||
|
cmp al, 0
|
||
|
jnz iie_exit
|
||
|
dec esi
|
||
|
mov al, byte[esi]
|
||
|
inc al
|
||
|
mov byte[esi], al
|
||
|
cmp al, 0
|
||
|
jnz iie_exit
|
||
|
dec esi
|
||
|
mov al, byte[esi]
|
||
|
inc al
|
||
|
mov byte[esi], al
|
||
|
|
||
|
iie_exit:
|
||
|
pop eax
|
||
|
ret
|
||
|
|
||
|
|
||
|
; Increments the 32 bit value pointed to by esi in internet order
|
||
|
; by the value in ecx
|
||
|
add_inet_esi:
|
||
|
push eax
|
||
|
|
||
|
mov al, [esi]
|
||
|
shl eax, 8
|
||
|
inc esi
|
||
|
mov al, [esi]
|
||
|
shl eax, 8
|
||
|
inc esi
|
||
|
mov al, [esi]
|
||
|
shl eax, 8
|
||
|
inc esi
|
||
|
mov al, [esi]
|
||
|
add eax, ecx
|
||
|
mov [esi], al
|
||
|
dec esi
|
||
|
shr eax, 8
|
||
|
mov [esi], al
|
||
|
dec esi
|
||
|
shr eax, 8
|
||
|
mov [esi], al
|
||
|
dec esi
|
||
|
shr eax, 8
|
||
|
mov [esi], al
|
||
|
pop eax
|
||
|
ret
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
;***************************************************************************
|
||
|
; Function
|
||
|
; tcpStateMachine
|
||
|
;
|
||
|
; Description
|
||
|
; TCP state machine
|
||
|
; This is a kernel function, called by tcp_rx
|
||
|
;
|
||
|
; IP buffer address given in edx
|
||
|
; Socket/TCB address in [eax + sockets]
|
||
|
;
|
||
|
; The IP buffer will be released by the caller
|
||
|
;***************************************************************************
|
||
|
tcpStateMachine:
|
||
|
mov ebx, sockets
|
||
|
add ebx, eax
|
||
|
mov [sktAddr], ebx
|
||
|
|
||
|
; as a packet has been received, update the TCB timer
|
||
|
mov ecx, TWOMSL
|
||
|
mov [ebx + 32], ecx
|
||
|
|
||
|
; If the received packet has an ACK bit set,
|
||
|
; remove any packets in the resend queue that this
|
||
|
; received packet acknowledges
|
||
|
pusha
|
||
|
mov cl, [edx + 33]
|
||
|
and cl, 0x10
|
||
|
cmp cl, 0x10
|
||
|
jne tsm001 ; No ACK, so no data yet
|
||
|
|
||
|
|
||
|
; get skt number in al
|
||
|
shr eax, 12
|
||
|
|
||
|
; The ack number is in [edx + 28], inet format
|
||
|
; skt in al
|
||
|
|
||
|
mov esi, resendQ
|
||
|
mov ecx, 0
|
||
|
|
||
|
t001:
|
||
|
cmp ecx, NUMRESENDENTRIES
|
||
|
je t003 ; None left
|
||
|
cmp [esi], al
|
||
|
je t002 ; found one
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp t001
|
||
|
|
||
|
t002: ; Can we delete this buffer?
|
||
|
|
||
|
; If yes, goto t004. No, goto t001
|
||
|
; Get packet data address
|
||
|
|
||
|
push ecx
|
||
|
inc ecx
|
||
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
||
|
mov edi, resendBuffer - IPBUFFSIZE
|
||
|
t002a:
|
||
|
add edi, IPBUFFSIZE
|
||
|
loop t002a
|
||
|
|
||
|
; we have dest buffer location in edi. incoming packet in edx.
|
||
|
; Get this packets sequence number
|
||
|
; preserve al, ecx, esi, edx
|
||
|
|
||
|
mov cl, [edi + 24]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edi + 25]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edi + 26]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edi + 27]
|
||
|
movzx ebx, byte [edi + 3]
|
||
|
mov bh, [edi + 2]
|
||
|
sub ebx, 40
|
||
|
add ecx, ebx ; ecx is now seq# of last byte +1, intel format
|
||
|
|
||
|
; get recievd ack #, in intel format
|
||
|
mov bl, [edx + 28]
|
||
|
shl ebx, 8
|
||
|
mov bl, [edx + 29]
|
||
|
shl ebx, 8
|
||
|
mov bl, [edx + 30]
|
||
|
shl ebx, 8
|
||
|
mov bl, [edx + 31]
|
||
|
|
||
|
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 t004 ; if rx > old, delete old
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp t001
|
||
|
|
||
|
|
||
|
t004:
|
||
|
dec dword [arp_rx_count] ; ************ TEST ONLY!
|
||
|
|
||
|
mov [esi], byte 0xFF
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp t001
|
||
|
|
||
|
t003:
|
||
|
|
||
|
tsm001:
|
||
|
popa
|
||
|
|
||
|
; Call handler for given TCB state
|
||
|
mov ebx, [eax + sockets+28]
|
||
|
cmp ebx, TCB_LISTEN
|
||
|
jb tsm_exit
|
||
|
cmp ebx, TCB_CLOSED
|
||
|
ja tsm_exit
|
||
|
|
||
|
dec ebx
|
||
|
call dword [TCBStateHandler+ebx*4]
|
||
|
|
||
|
tsm_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
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
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x02
|
||
|
cmp bl, 0x02
|
||
|
jnz stl_exit
|
||
|
|
||
|
; We have a SYN. update the socket with this IP packets details,
|
||
|
; And send a response
|
||
|
|
||
|
mov ebx, [edx + 12] ; IP source address
|
||
|
mov [eax + sockets + 16], ebx
|
||
|
mov bx, [edx + 20] ; IP source port
|
||
|
mov [eax + sockets + 20], bx
|
||
|
mov ebx, [edx + 24] ; IRS
|
||
|
mov [eax + sockets + 40], ebx
|
||
|
mov [eax + sockets + 56], ebx
|
||
|
mov esi, sockets
|
||
|
add esi, eax
|
||
|
add esi, 56
|
||
|
call inc_inet_esi ; RCV.NXT
|
||
|
mov ebx, [eax + sockets + 36] ; ISS
|
||
|
mov [eax + sockets + 48], ebx ; SND.NXT
|
||
|
|
||
|
; Now construct the response, and queue for sending by IP
|
||
|
mov eax, EMPTY_QUEUE
|
||
|
call dequeue
|
||
|
cmp ax, NO_BUFFER
|
||
|
je stl_exit
|
||
|
|
||
|
push eax
|
||
|
mov bl, 0x12 ; SYN + ACK
|
||
|
mov ecx, 0
|
||
|
mov esi, 0
|
||
|
|
||
|
call buildTCPPacket
|
||
|
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ sktAddr ]
|
||
|
mov ecx, [ ecx + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne stl_notlocal
|
||
|
mov eax, IPIN_QUEUE
|
||
|
|
||
|
stl_notlocal:
|
||
|
; Send it.
|
||
|
pop ebx
|
||
|
call queue
|
||
|
|
||
|
|
||
|
mov ebx, TCB_SYN_RECEIVED
|
||
|
mov esi, [sktAddr]
|
||
|
mov [esi + 28], ebx
|
||
|
|
||
|
; increament SND.NXT in socket
|
||
|
add esi, 48
|
||
|
call inc_inet_esi
|
||
|
|
||
|
stl_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_SYN_SENT:
|
||
|
; We are awaiting an ACK to our SYN, with a SYM
|
||
|
; Look at control flags - expecting an ACK
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x12
|
||
|
cmp bl, 0x12
|
||
|
jnz stss_exit
|
||
|
|
||
|
mov ebx, TCB_ESTABLISHED
|
||
|
mov esi, [sktAddr]
|
||
|
mov [esi + 28], ebx
|
||
|
|
||
|
; Store the recv.nxt field
|
||
|
mov eax, [edx + 24]
|
||
|
|
||
|
; Update our recv.nxt field
|
||
|
mov esi, [sktAddr]
|
||
|
add esi, 56
|
||
|
mov [esi], eax
|
||
|
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
|
||
|
je stss_exit
|
||
|
|
||
|
push eax
|
||
|
|
||
|
mov bl, 0x10 ; ACK
|
||
|
mov ecx, 0
|
||
|
mov esi, 0
|
||
|
|
||
|
call buildTCPPacket
|
||
|
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ sktAddr ]
|
||
|
mov ecx, [ ecx + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne stss_notlocal
|
||
|
mov eax, IPIN_QUEUE
|
||
|
|
||
|
stss_notlocal:
|
||
|
; Send it.
|
||
|
pop ebx
|
||
|
call queue
|
||
|
|
||
|
stss_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
; Look at control flags - expecting an ACK
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x10
|
||
|
cmp bl, 0x10
|
||
|
jnz stsr_exit
|
||
|
|
||
|
mov ebx, TCB_ESTABLISHED
|
||
|
mov esi, [sktAddr]
|
||
|
mov [esi + 28], ebx
|
||
|
|
||
|
stsr_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_ESTABLISHED:
|
||
|
; Here we are expecting data, or a request to close
|
||
|
; OR both...
|
||
|
|
||
|
; Did we receive a FIN or RST?
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x05
|
||
|
cmp bl, 0
|
||
|
je ste_chkack
|
||
|
|
||
|
; It was a fin or reset.
|
||
|
|
||
|
; Remove resend entries from the queue - I dont want to send any more data
|
||
|
pusha
|
||
|
|
||
|
mov ebx, [sktAddr]
|
||
|
sub ebx, sockets
|
||
|
shr ebx, 12 ; get skt #
|
||
|
|
||
|
mov esi, resendQ
|
||
|
mov ecx, 0
|
||
|
|
||
|
ste001:
|
||
|
cmp ecx, NUMRESENDENTRIES
|
||
|
je ste003 ; None left
|
||
|
cmp [esi], bl
|
||
|
je ste002 ; found one
|
||
|
inc ecx
|
||
|
add esi, 4
|
||
|
jmp ste001
|
||
|
|
||
|
ste002:
|
||
|
dec dword [arp_rx_count] ; ************ TEST ONLY!
|
||
|
|
||
|
mov [esi], byte 0xFF
|
||
|
jmp ste001
|
||
|
|
||
|
ste003:
|
||
|
popa
|
||
|
|
||
|
; was it a reset?
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x04
|
||
|
cmp bl, 0x04
|
||
|
jne ste003a
|
||
|
|
||
|
mov esi, [sktAddr]
|
||
|
mov ebx, TCB_CLOSED
|
||
|
mov [esi + 28], ebx
|
||
|
jmp ste_exit
|
||
|
|
||
|
ste003a:
|
||
|
; Send an ACK to that fin, and enter closewait state
|
||
|
|
||
|
mov esi, [sktAddr]
|
||
|
mov ebx, TCB_CLOSE_WAIT
|
||
|
mov [esi + 28], ebx
|
||
|
add esi, 56
|
||
|
mov eax, [esi] ; save original
|
||
|
call inc_inet_esi
|
||
|
;; jmp ste_ack - NO, there may be data
|
||
|
|
||
|
ste_chkack:
|
||
|
; Check that we received an ACK
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x10
|
||
|
cmp bl, 0x10
|
||
|
jnz ste_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 ch, [edx + 34]
|
||
|
mov cl, [edx + 35]
|
||
|
cmp cx, 1024
|
||
|
ja ste004
|
||
|
|
||
|
mov ecx, [sktAddr]
|
||
|
mov [ecx+72], dword 1
|
||
|
|
||
|
ste004:
|
||
|
|
||
|
; 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 inext format
|
||
|
; recv seq is in [sktAddr]+56, in inet format
|
||
|
; just do a comparision
|
||
|
mov ecx, [sktAddr]
|
||
|
add ecx, 56
|
||
|
|
||
|
cmp [ecx - 56 + 28], dword TCB_CLOSE_WAIT
|
||
|
mov ecx, [ecx]
|
||
|
jne stenofin
|
||
|
mov ecx, eax
|
||
|
|
||
|
stenofin:
|
||
|
cmp ecx, [edx+24]
|
||
|
jne ste_ack
|
||
|
|
||
|
|
||
|
; Read the data bytes, store in socket buffer
|
||
|
xor ecx, ecx
|
||
|
mov ch, [edx + 2]
|
||
|
mov cl, [edx + 3]
|
||
|
sub ecx, 40 ; Discard 40 bytes of header
|
||
|
|
||
|
cmp ecx, 0
|
||
|
jnz ste_data ; Read data, if any
|
||
|
|
||
|
; If we had received a fin, we need to ACK it.
|
||
|
mov esi, [sktAddr]
|
||
|
mov ebx, [esi + 28]
|
||
|
cmp ebx, TCB_CLOSE_WAIT
|
||
|
jz ste_ack
|
||
|
jnz ste_exit
|
||
|
|
||
|
ste_data:
|
||
|
push ecx
|
||
|
mov esi, [sktAddr]
|
||
|
|
||
|
add [esi + 24], ecx ; increment the count of bytes in buffer
|
||
|
|
||
|
mov eax, [esi + 4] ; get socket owner PID
|
||
|
push eax
|
||
|
|
||
|
mov eax, [esi + 24] ; get # of bytes already in buffer
|
||
|
|
||
|
; point to the location to store the data
|
||
|
add esi, eax
|
||
|
sub esi, ecx
|
||
|
add esi, SOCKETHEADERSIZE
|
||
|
|
||
|
add edx, 40 ; edx now points to the data
|
||
|
mov edi, esi
|
||
|
mov esi, edx
|
||
|
|
||
|
cld
|
||
|
rep movsb ; copy the data across
|
||
|
|
||
|
; flag an event to the application
|
||
|
pop eax
|
||
|
mov ecx,1
|
||
|
mov esi,TASK_DATA+TASKDATA.pid
|
||
|
|
||
|
news:
|
||
|
cmp [esi],eax
|
||
|
je foundPID1
|
||
|
inc ecx
|
||
|
add esi,0x20
|
||
|
cmp ecx,[TASK_COUNT]
|
||
|
jbe news
|
||
|
|
||
|
foundPID1:
|
||
|
shl ecx,8
|
||
|
or dword [ecx+SLOT_BASE+APPDATA.event_mask],dword 10000000b ; stack event
|
||
|
|
||
|
pop ecx
|
||
|
|
||
|
; Update our recv.nxt field
|
||
|
mov esi, [sktAddr]
|
||
|
add esi, 56
|
||
|
call add_inet_esi
|
||
|
|
||
|
ste_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 ste_exit
|
||
|
|
||
|
push eax
|
||
|
|
||
|
mov bl, 0x10 ; ACK
|
||
|
mov ecx, 0
|
||
|
mov esi, 0
|
||
|
|
||
|
call buildTCPPacket
|
||
|
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ sktAddr ]
|
||
|
mov ecx, [ ecx + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne ste_notlocal
|
||
|
mov eax, IPIN_QUEUE
|
||
|
ste_notlocal:
|
||
|
|
||
|
; Send it.
|
||
|
pop ebx
|
||
|
call queue
|
||
|
|
||
|
ste_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_FIN_WAIT_1:
|
||
|
; We can either receive an ACK of a fin, or a fin
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x10
|
||
|
cmp bl, 0x10
|
||
|
jnz stfw1_001
|
||
|
|
||
|
; It was an ACK
|
||
|
mov esi, [sktAddr]
|
||
|
mov ebx, TCB_FIN_WAIT_2
|
||
|
mov [esi + 28], ebx
|
||
|
jmp stfw1_exit
|
||
|
|
||
|
stfw1_001:
|
||
|
; It must be a fin then
|
||
|
mov esi, [sktAddr]
|
||
|
mov ebx, TCB_CLOSING
|
||
|
mov [esi + 28], ebx
|
||
|
add esi, 56
|
||
|
call inc_inet_esi
|
||
|
|
||
|
; Send an ACK
|
||
|
mov eax, EMPTY_QUEUE
|
||
|
call dequeue
|
||
|
cmp ax, NO_BUFFER
|
||
|
je stfw1_exit
|
||
|
|
||
|
push eax
|
||
|
|
||
|
mov bl, 0x10 ; ACK
|
||
|
mov ecx, 0
|
||
|
mov esi, 0
|
||
|
|
||
|
call buildTCPPacket
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ sktAddr ]
|
||
|
mov ecx, [ ecx + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne stfw1_notlocal
|
||
|
mov eax, IPIN_QUEUE
|
||
|
|
||
|
stfw1_notlocal:
|
||
|
; Send it.
|
||
|
pop ebx
|
||
|
call queue
|
||
|
|
||
|
stfw1_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_FIN_WAIT_2:
|
||
|
mov esi, [sktAddr]
|
||
|
|
||
|
; Get data length
|
||
|
xor ecx, ecx
|
||
|
mov ch, [edx+2]
|
||
|
mov cl, [edx+3]
|
||
|
sub ecx, 40
|
||
|
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x01
|
||
|
cmp bl, 0x01
|
||
|
jne stfw2001
|
||
|
|
||
|
; Change state, as we have a fin
|
||
|
mov ebx, TCB_TIME_WAIT
|
||
|
mov [esi + 28], ebx
|
||
|
|
||
|
inc ecx ; FIN is part of the sequence space
|
||
|
|
||
|
stfw2001:
|
||
|
add esi, 56
|
||
|
call add_inet_esi
|
||
|
|
||
|
; Send an ACK
|
||
|
mov eax, EMPTY_QUEUE
|
||
|
call dequeue
|
||
|
cmp ax, NO_BUFFER
|
||
|
je stfw2_exit
|
||
|
|
||
|
push eax
|
||
|
|
||
|
mov bl, 0x10 ; ACK
|
||
|
mov ecx, 0
|
||
|
mov esi, 0
|
||
|
|
||
|
call buildTCPPacket
|
||
|
|
||
|
mov eax, NET1OUT_QUEUE
|
||
|
|
||
|
mov edx, [stack_ip]
|
||
|
mov ecx, [ sktAddr ]
|
||
|
mov ecx, [ ecx + 16 ]
|
||
|
cmp edx, ecx
|
||
|
jne stfw2_notlocal
|
||
|
mov eax, IPIN_QUEUE
|
||
|
|
||
|
stfw2_notlocal:
|
||
|
; Send it.
|
||
|
pop ebx
|
||
|
call queue
|
||
|
|
||
|
; Only delete the socket if we received the FIN
|
||
|
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x01
|
||
|
cmp bl, 0x01
|
||
|
jne stfw2_exit
|
||
|
|
||
|
; mov edi, [sktAddr]
|
||
|
|
||
|
; delete the socket. Should really wait for 2MSL
|
||
|
; xor eax, eax
|
||
|
; mov ecx,SOCKETHEADERSIZE
|
||
|
; cld
|
||
|
; rep stosb
|
||
|
|
||
|
stfw2_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_CLOSE_WAIT:
|
||
|
; Intentionally left empty
|
||
|
; socket_close_tcp handles this
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_CLOSING:
|
||
|
; We can either receive an ACK of a fin, or a fin
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x10
|
||
|
cmp bl, 0x10
|
||
|
jnz stc_exit
|
||
|
|
||
|
; It was an ACK
|
||
|
|
||
|
mov edi, [sktAddr]
|
||
|
|
||
|
; delete the socket
|
||
|
xor eax, eax
|
||
|
mov ecx,SOCKETHEADERSIZE
|
||
|
cld
|
||
|
rep stosb
|
||
|
|
||
|
stc_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_LAST_ACK:
|
||
|
; Look at control flags - expecting an ACK
|
||
|
mov bl, [edx + 33]
|
||
|
and bl, 0x10
|
||
|
cmp bl, 0x10
|
||
|
jnz stla_exit
|
||
|
|
||
|
mov edi, [sktAddr]
|
||
|
|
||
|
; delete the socket
|
||
|
xor eax, eax
|
||
|
mov ecx,SOCKETHEADERSIZE
|
||
|
cld
|
||
|
rep stosb
|
||
|
|
||
|
stla_exit:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_TIME_WAIT:
|
||
|
ret
|
||
|
|
||
|
|
||
|
|
||
|
stateTCB_CLOSED:
|
||
|
ret
|