c14545104d
git-svn-id: svn://kolibrios.org@1281 a494cfbc-eb01-0410-851d-a64ba20cac60
1020 lines
24 KiB
PHP
1020 lines
24 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$
|
|
|
|
TCP_RETRIES equ 5 ; Number of times to resend a Packet
|
|
TCP_PACKET_TTL equ 50 ; resend if not replied to in 1/100 s
|
|
TCP_SOCKET_TTL equ 10 ; # of secs to wait before closing socket
|
|
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
|
|
|
|
struct tcp_in_queue_entry
|
|
.data_ptr dd ?
|
|
.data_size dd ?
|
|
.offset dd ? ; TODO: replace this in code by absolute address isntead of relative offset
|
|
.size:
|
|
ends
|
|
|
|
struct tcp_out_queue_entry
|
|
.data_ptr dd ?
|
|
.data_size dd ?
|
|
.ttl dd ?
|
|
.retries dd ?
|
|
.owner dd ?
|
|
.sendproc dd ?
|
|
.seq_num dd ?
|
|
.socket dd ?
|
|
.size:
|
|
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
|
|
stateHandler:
|
|
|
|
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
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; 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
|
|
|
|
; tcp_out_queue is a special type of queue:
|
|
; The first dword is a counter of total packets queued.
|
|
; The remaining bytes are socket 'slots' wich use tcp_out_queue_entry data structure.
|
|
; An empty slot is know by the fact that tcp_out_queue_entry.data_ptr (first dword of the slot) is set to 0
|
|
; There are TCP_OUT_QUEUE_SIZE number of slots
|
|
|
|
xor eax, eax
|
|
mov esi, TCP_OUT_QUEUE
|
|
mov ecx, TCP_QUEUE_SIZE*tcp_out_queue_entry/4+1
|
|
rep stosd
|
|
|
|
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.state]
|
|
|
|
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.timer], 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.timer]
|
|
jnz .next_socket
|
|
|
|
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
|
|
jne .next_socket
|
|
|
|
push [ebx + SOCKET_head.PrevPtr]
|
|
stdcall net_socket_free, ebx
|
|
pop ebx
|
|
jmp .next_socket
|
|
|
|
.decrement_wnd:
|
|
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:
|
|
add esi, tcp_out_queue_entry.size
|
|
dec eax
|
|
jz .exit
|
|
test ecx, ecx
|
|
jnz .loop
|
|
ret
|
|
|
|
.send_it:
|
|
push eax ecx esi
|
|
|
|
mov ebx, [esi + tcp_out_queue_entry.owner]
|
|
push [esi + tcp_out_queue_entry.data_size]
|
|
push [esi + tcp_out_queue_entry.data_ptr]
|
|
DEBUGF 1,"Now sending TCP packet %x, size: %u, owner: %x, sendproc %x\n", [esp], [esp+4], ebx, [esi + tcp_out_queue_entry.sendproc]
|
|
inc [TCP_PACKETS_TX]
|
|
call [esi + tcp_out_queue_entry.sendproc]
|
|
add esp, 8
|
|
pop esi ecx eax
|
|
|
|
dec [esi + tcp_out_queue_entry.retries]
|
|
jz .remove_it
|
|
|
|
mov [esi + tcp_out_queue_entry.ttl], TCP_PACKET_TTL
|
|
jmp .find_next
|
|
|
|
.remove_it:
|
|
push [esi + tcp_out_queue_entry.data_ptr]
|
|
mov [esi + tcp_out_queue_entry.data_ptr], 0
|
|
call kernel_free
|
|
dec [TCP_OUT_QUEUE]
|
|
jmp .find_next
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; 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 in edx
|
|
; SourceAddres (IPv4) in esi
|
|
; OUT: /
|
|
;
|
|
;-----------------------------------------------------------------
|
|
align 4
|
|
TCP_handler :
|
|
|
|
DEBUGF 1,"TCP_Handler\n"
|
|
|
|
; TODO: validate checksum
|
|
|
|
; 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 .found_socket
|
|
test ax, ax
|
|
jnz .socket_loop
|
|
.found_socket:
|
|
DEBUGF 1,"Found valid socket for packet\n"
|
|
|
|
inc [TCP_PACKETS_RX]
|
|
|
|
add ebx, SOCKET_head.lock
|
|
call wait_mutex
|
|
sub ebx, SOCKET_head.lock
|
|
|
|
;-------------------------------
|
|
; ebx is pointer to socket
|
|
; ecx is size of tcp packet
|
|
; edx is pointer to tcp packet
|
|
|
|
; calculate header length
|
|
movzx eax, [edx + TCP_Packet.DataOffset]
|
|
and eax, 11110000b
|
|
shr eax, 2
|
|
DEBUGF 1,"TCP header size: %u\n", eax
|
|
sub ecx, eax
|
|
|
|
;-------------------------------
|
|
; ecx is size of tcp data
|
|
|
|
; as a Packet has been received, update the TCB timer
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.timer], TCP_SOCKET_TTL
|
|
|
|
; 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 .no_ack ; No ACK, so no data yet
|
|
|
|
; Calculate ACK number
|
|
mov edi, [edx + TCP_Packet.AckNumber]
|
|
bswap edi
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.last_ack_number], edi
|
|
DEBUGF 1,"Setting last_ack_number to %u\n", edi
|
|
bswap edi
|
|
|
|
; Dequeue all acknowledged packets
|
|
cmp [TCP_OUT_QUEUE], 0 ; first, check if any packets are queued at all
|
|
je .no_ack
|
|
|
|
push ecx
|
|
DEBUGF 1,"Removing all queued packets with smaller ACK\n"
|
|
mov ecx, TCP_QUEUE_SIZE
|
|
mov esi, TCP_OUT_QUEUE+4
|
|
.loop:
|
|
cmp [esi + tcp_out_queue_entry.data_ptr], 0
|
|
je .maybe_next
|
|
|
|
cmp [esi + tcp_out_queue_entry.socket], ebx
|
|
jne .maybe_next
|
|
|
|
cmp [esi + tcp_out_queue_entry.seq_num], edi
|
|
jg .maybe_next
|
|
|
|
DEBUGF 1,"Removing a queued packet\n"
|
|
|
|
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
|
|
|
|
|
|
; Now call the correct handler, depending on the socket state
|
|
.no_ack:
|
|
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state]
|
|
|
|
cmp eax, TCB_LISTEN
|
|
jb .dump
|
|
cmp eax, TCB_CLOSED
|
|
ja .dump
|
|
|
|
dec eax
|
|
shl eax, 2
|
|
add eax, stateHandler
|
|
|
|
call dword[eax]
|
|
|
|
.dump:
|
|
DEBUGF 1,"Dumping TCP packet\n"
|
|
call kernel_free
|
|
add esp, 4 ; pop (balance stack)
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; TCP_send (Assumes socket mutex set)
|
|
;
|
|
; IN: eax = socket pointer
|
|
; bl = flags
|
|
; ecx = number of bytes to send, may be set to 0 (single ACK)
|
|
; esi = pointer to data
|
|
;
|
|
;-----------------------------------------------------------------
|
|
align 4
|
|
TCP_send:
|
|
|
|
DEBUGF 1,"Creating TCP packet, socket: %x, flags: %x\n",eax, bl
|
|
|
|
mov di , IP_PROTO_TCP
|
|
add ecx, TCP_Packet.Data
|
|
|
|
push ecx bx eax esi
|
|
; 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
|
|
|
|
; If there is any data, copy it first
|
|
pop esi
|
|
push edi
|
|
add edi, TCP_Packet.Data
|
|
sub ecx, TCP_Packet.Data
|
|
|
|
shr ecx, 1
|
|
jnc .nb
|
|
movsb
|
|
.nb: shr ecx, 1
|
|
jnc .nw
|
|
movsw
|
|
.nw: test ecx, ecx
|
|
jz .nd
|
|
rep movsd
|
|
.nd:
|
|
pop edi
|
|
|
|
; Fill in the TCP header
|
|
pop esi
|
|
|
|
; fill in tcp sequence number
|
|
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
|
|
pop [edi + TCP_Packet.SequenceNumber]
|
|
|
|
; Fill in local and remote ports
|
|
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
|
|
pop dword [edi + TCP_Packet.SourcePort]
|
|
|
|
; Acknumber
|
|
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
|
|
pop [edi + TCP_Packet.AckNumber]
|
|
|
|
; Fill in other tcp options
|
|
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
|
|
mov [edi + TCP_Packet.Checksum], 0
|
|
|
|
; Get size of total packet back in ecx
|
|
pop ecx
|
|
; Push pointer to and size of total packet (needed for send procedure)
|
|
push edx eax
|
|
; push socket number (for TCP_add_to_queue)
|
|
push esi
|
|
|
|
; Now, calculate the checksum
|
|
xchg cl, ch
|
|
pushw cx
|
|
xchg cl, ch
|
|
;; pushw TCP_Packet.Data shl 8
|
|
pushw IP_PROTO_TCP shl 8
|
|
pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options..
|
|
pushd [edi-8] ; source address
|
|
|
|
xor edx, edx
|
|
; mov ecx, TCP_Packet.Data
|
|
mov esi, edi
|
|
call checksum_1
|
|
mov ecx, 12
|
|
mov esi, esp
|
|
call checksum_1
|
|
; and store it in TCP header
|
|
call checksum_2
|
|
mov [edi + TCP_Packet.Checksum], dx
|
|
add esp, 10 ; remove the pseudoheader from stack
|
|
|
|
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
|
|
mov edx, [edi + TCP_Packet.SequenceNumber]
|
|
bswap edx
|
|
mov esi, [ebx + ETH_DEVICE.transmit]
|
|
|
|
pop cx ; get the length from packet, back from pseudoheader
|
|
pop edi
|
|
|
|
cmp cx, TCP_Packet.Data shl 8 ; if the packet has no data
|
|
je .only_one ; send it only once
|
|
|
|
and ecx, 0x0000ffff
|
|
xchg cl, ch
|
|
sub cx, TCP_Packet.Data
|
|
add_INET (edi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT)
|
|
|
|
mov ecx, TCP_RETRIES
|
|
|
|
jmp .go_for_it
|
|
|
|
.only_one:
|
|
; inc_INET (edi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT)
|
|
mov ecx, 1
|
|
.go_for_it:
|
|
|
|
mov [edi + SOCKET_head.lock], 0
|
|
jmp TCP_queue ; At last send the packet!
|
|
|
|
.fail:
|
|
add esp, 2+4
|
|
or eax, -1
|
|
ret
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; Queue a TCP packet for sending
|
|
;
|
|
; IN: [esp] pointer to buffer
|
|
; [esp + 4] size of buffer
|
|
; ebx = driver struct
|
|
; esi = sender proc
|
|
; edx = sequence number of this packet in normal byte order
|
|
; edi = socket number
|
|
; ecx = retries
|
|
; OUT: /
|
|
;
|
|
;-----------------------------------------------------------------
|
|
align 4
|
|
TCP_queue:
|
|
|
|
bswap edx
|
|
DEBUGF 1,"Adding packet to TCP queue, buffer: %x, size: %u, driver: %x, acknum: %u\n", [esp], [esp+4], ebx, edx
|
|
bswap edx
|
|
|
|
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
|
|
jge .full
|
|
|
|
push ecx
|
|
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
|
|
DEBUGF 1,"TCP queue is full!\n"
|
|
|
|
add esp, 4
|
|
call kernel_free
|
|
add esp, 4
|
|
|
|
ret
|
|
|
|
.found_it: ; eax points to empty queue entry
|
|
|
|
pop [eax + tcp_out_queue_entry.retries]
|
|
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.owner], ebx
|
|
mov [eax + tcp_out_queue_entry.sendproc], esi
|
|
mov [eax + tcp_out_queue_entry.seq_num], edx
|
|
mov [eax + tcp_out_queue_entry.socket], edi
|
|
|
|
inc [TCP_OUT_QUEUE]
|
|
|
|
sub eax, TCP_OUT_QUEUE+4
|
|
DEBUGF 1,"Added to queue in pos %u\n", eax
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
;---------- TCB state handlers start here
|
|
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_LISTEN:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Listen\n"
|
|
|
|
test [edx + TCP_Packet.Flags], TH_SYN ; SYN packet? => send syn+ack, open new socket and set connection to established
|
|
jz .exit
|
|
; Exit if backlog queue is full
|
|
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
|
|
cmp ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog]
|
|
jae .exit
|
|
; Allocate new socket
|
|
push esi edi
|
|
call net_socket_alloc
|
|
test eax, eax
|
|
jz .fail
|
|
; Copy structure from current socket to new, including lock
|
|
lea esi, [ebx + SOCKET_head.PID] ; yes, PID must also be copied
|
|
lea edi, [eax + SOCKET_head.PID]
|
|
mov ecx, ((SOCKET_head.end - SOCKET_head.PID) + IPv4_SOCKET.end + TCP_SOCKET.end + 3)/4
|
|
rep movsd
|
|
pop edi esi
|
|
; Push pointer to new socket to queue
|
|
movzx ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
|
|
inc [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.end + ecx*4], eax
|
|
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], esi ; IP source address
|
|
mov cx, [edx + TCP_Packet.SourcePort]
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], cx
|
|
mov ecx, [edx + TCP_Packet.SequenceNumber]
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.IRS], ecx
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], ecx
|
|
lea esi, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
|
|
inc_INET esi ; RCV.NXT
|
|
mov ecx, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.ISS]
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT], ecx
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
|
|
push eax
|
|
; Now construct the response
|
|
mov bl, TH_SYN + TH_ACK
|
|
xor ecx, ecx
|
|
call TCP_send
|
|
pop eax
|
|
|
|
mov [eax + SOCKET_head.lock], 0
|
|
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
|
|
call notify_network_event
|
|
ret
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
.fail:
|
|
add esp, 8
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
align 4
|
|
stateTCB_SYN_SENT:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Syn_Sent\n"
|
|
|
|
; We are awaiting an ACK to our SYN, with a SYM
|
|
; Look at control flags - expecting an ACK
|
|
|
|
mov al, [edx + TCP_Packet.Flags]
|
|
|
|
test al, TH_RST
|
|
jnz .reset ; jump if RST bit set
|
|
|
|
push [edx + TCP_Packet.SequenceNumber] ;;
|
|
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] ;;
|
|
inc_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT) ;;
|
|
|
|
|
|
push [edx + TCP_Packet.AckNumber] ;;;;;;
|
|
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] ;;;;;;
|
|
|
|
and al, TH_SYN + TH_ACK
|
|
jz .exit ; jump if none of the following is set: RST, SYN, ACK
|
|
|
|
test al, TH_ACK
|
|
jz .onlysyn ; jump if only SYN bit is set
|
|
|
|
; If we arrived here, SYN and ACK are set
|
|
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
|
|
pushw TH_ACK
|
|
|
|
.send: ; Send an ACK
|
|
mov eax, ebx
|
|
pop bx
|
|
push eax
|
|
xor ecx, ecx
|
|
call TCP_send
|
|
pop ebx
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
.reset:
|
|
; TODO: ....
|
|
|
|
; remove all queued TCP packets for this connection !
|
|
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSED
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
.onlysyn:
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
|
|
pushw TH_SYN + TH_ACK
|
|
jmp .send
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_SYN_RECEIVED:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Syn_received\n"
|
|
|
|
test [edx + TCP_Packet.Flags], TH_RST ; reset connection? => LISTEN
|
|
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.state], TCB_LISTEN
|
|
jmp .exit
|
|
|
|
.check_ack:
|
|
test [edx + TCP_Packet.Flags], TH_ACK ; ACK? => connection established!
|
|
jz .exit
|
|
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
|
|
mov eax, ebx
|
|
call notify_network_event
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_ESTABLISHED:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Established\n"
|
|
|
|
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
|
|
bswap eax
|
|
DEBUGF 1,"RCV_NXT is set to:%u\n", eax
|
|
bswap eax
|
|
cmp eax, [edx + TCP_Packet.SequenceNumber]
|
|
jne .exit
|
|
|
|
; Calculate next sequencenumber
|
|
;; test ecx, ecx
|
|
;; jnz @f
|
|
;; inc ecx
|
|
;; @@:
|
|
add_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT)
|
|
|
|
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST ;;;
|
|
jnz .fin
|
|
|
|
.check_ack:
|
|
test [edx + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
DEBUGF 1,"Received ACK\n"
|
|
; 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
|
|
push ecx
|
|
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
|
|
@@:
|
|
pop ecx
|
|
|
|
; Now, see if we received any data
|
|
test ecx, ecx
|
|
jz .exit
|
|
|
|
DEBUGF 1,"Got %u bytes data!\n", ecx
|
|
; calculate header length
|
|
movzx eax, [edx + TCP_Packet.DataOffset]
|
|
and eax, 11110000b
|
|
shr eax, 2
|
|
DEBUGF 1,"TCP header size: %u\n", eax
|
|
add edx, eax ; now edx points to data
|
|
|
|
add esp, 4
|
|
pop esi ; pointer to buffer
|
|
add esp, 4
|
|
|
|
sub edx, esi
|
|
mov edi, edx ; offset
|
|
mov eax, ebx ; socket ptr
|
|
|
|
call socket_internal_receiver ; Place the data from packet into socket
|
|
|
|
lea ebx, [eax + SOCKET_head.lock] ;;;;;
|
|
call wait_mutex ;;;;;
|
|
mov ebx, eax ;;;;
|
|
|
|
.ack:
|
|
mov eax, ebx
|
|
mov bl, TH_ACK
|
|
push eax
|
|
xor ecx, ecx
|
|
call TCP_send ; send the ack
|
|
pop ebx
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
.fin:
|
|
; Remove all resend entries from the queue
|
|
mov ecx, TCP_QUEUE_SIZE
|
|
mov esi, TCP_OUT_QUEUE+4
|
|
|
|
.removeloop:
|
|
cmp [esi + tcp_out_queue_entry.data_ptr], 0
|
|
je .maybe_next
|
|
|
|
; TODO: check if the packets belong to the same tcp connection !
|
|
|
|
DEBUGF 1,"Removing a queued packet\n"
|
|
|
|
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 .removeloop
|
|
|
|
; Send an ACK to that fin, and enter closewait state
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSE_WAIT
|
|
jmp .check_ack
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_FIN_WAIT_1:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Fin_wait_1\n"
|
|
|
|
; 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.state], TCB_FIN_WAIT_2
|
|
jmp .exit
|
|
|
|
@@: mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSING
|
|
cmp al, TH_FIN
|
|
je @f
|
|
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
|
|
|
|
@@:
|
|
|
|
; lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
|
|
; inc_INET esi
|
|
|
|
; Send an ACK
|
|
mov eax, ebx
|
|
mov bl, TH_ACK
|
|
push eax
|
|
xor ecx, ecx
|
|
call TCP_send
|
|
pop ebx
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_FIN_WAIT_2:
|
|
|
|
DEBUGF 1,"TCBStateHandler: Fin_wait_2\n"
|
|
|
|
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.state], TCB_TIMED_WAIT
|
|
|
|
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
|
|
inc_INET esi
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
|
|
; Send an ACK
|
|
mov eax, ebx
|
|
mov bl, TH_ACK
|
|
push eax
|
|
xor ecx, ecx
|
|
call TCP_send
|
|
pop ebx
|
|
|
|
.exit:
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_CLOSE_WAIT:
|
|
|
|
DEBUGF 1,"TCBStateHandler: close_wait\n"
|
|
; Intentionally left empty
|
|
; socket_close_tcp handles this
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
|
|
align 4
|
|
stateTCB_CLOSING:
|
|
|
|
DEBUGF 1,"TCBStateHandler: closingn\n"
|
|
|
|
; 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.state], TCB_TIMED_WAIT
|
|
|
|
.exit:
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
ret
|
|
|
|
|
|
align 4
|
|
stateTCB_LAST_ACK:
|
|
|
|
DEBUGF 1,"TCBStateHandler: last_ackn\n"
|
|
|
|
; Look at control flags - expecting an ACK
|
|
test [edx + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
|
|
; delete the socket
|
|
stdcall net_socket_free, ebx
|
|
|
|
.exit:
|
|
ret
|
|
|
|
|
|
align 4
|
|
stateTCB_TIME_WAIT:
|
|
|
|
DEBUGF 1,"TCBStateHandler: time_wait\n"
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
|
|
ret
|
|
|
|
|
|
align 4
|
|
stateTCB_CLOSED:
|
|
|
|
DEBUGF 1,"TCBStateHandler: closed\n"
|
|
|
|
mov [ebx + SOCKET_head.lock], 0
|
|
|
|
ret
|
|
|
|
|
|
|
|
;---------------------------------------------------------------------------
|
|
;
|
|
; TCP_API
|
|
;
|
|
; This function is called by system function 75
|
|
;
|
|
; IN: subfunction number in bl
|
|
; device number in bh
|
|
; ecx, edx, .. depends on subfunction
|
|
;
|
|
; OUT:
|
|
;
|
|
;---------------------------------------------------------------------------
|
|
align 4
|
|
TCP_API:
|
|
|
|
movzx eax, bh
|
|
shl eax, 2
|
|
|
|
test bl, bl
|
|
jz .packets_tx ; 0
|
|
dec bl
|
|
jz .packets_rx ; 1
|
|
|
|
.error:
|
|
mov eax, -1
|
|
ret
|
|
|
|
.packets_tx:
|
|
add eax, TCP_PACKETS_TX
|
|
mov eax, [eax]
|
|
ret
|
|
|
|
.packets_rx:
|
|
add eax, TCP_PACKETS_RX
|
|
mov eax, [eax]
|
|
ret
|