0,0 → 1,1243 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; 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 ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
|
;******************************************************************* |
; 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 |
; |
;******************************************************************* |
|
|
|
;*************************************************************************** |
; 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 |
mov eax, edx |
mov [checkAdd1], eax |
mov [checkSize1], word 20 |
mov [checkAdd2], dword 0 |
mov [checkSize2], word 0 |
|
call checksum |
|
mov ax, [checkResult] |
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,0x3020+0x4 |
|
news: |
cmp [esi],eax |
je foundPID1 |
inc ecx |
add esi,0x20 |
cmp ecx,[0x3004] |
jbe news |
|
foundPID1: |
shl ecx,8 |
or dword [ecx+0x80000+0xA8],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 |