Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1513 → Rev 1514

/kernel/branches/net/network/tcp.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; TCP.INC ;;
8,25 → 8,57
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; Inspired by the TCP code of Mike Hibbit for MenuetOS ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; 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
; Socket states
TCB_CLOSED equ 0
TCB_LISTEN equ 1
TCB_SYN_SENT equ 2
TCB_SYN_RECEIVED equ 3
TCB_ESTABLISHED equ 4
TCB_CLOSE_WAIT equ 5
TCB_FIN_WAIT_1 equ 6
TCB_CLOSING equ 7
TCB_LAST_ACK equ 8
TCB_FIN_WAIT_2 equ 9
TCB_TIMED_WAIT equ 10
 
TCP_MAX_ACKS equ 16
; Socket Flags
TF_ACKNOW equ 1 shl 0 ; ack peer immediately
TF_DELACK equ 1 shl 1 ; ack, but try to delay it
TF_NODELAY equ 1 shl 2 ; don't delay packets to coalesce
TF_NOOPT equ 1 shl 3 ; don't use tcp options
TF_SENTFIN equ 1 shl 4 ; have sent FIN
TF_REQ_SCALE equ 1 shl 5 ; have/will request window scaling
TF_RCVD_SCALE equ 1 shl 6 ; other side has requested scaling
TF_REQ_TSTMP equ 1 shl 7 ; have/will request timestamps
TF_RCVD_TSTMP equ 1 shl 8 ; a timestamp was received in SYN
TF_SACK_PERMIT equ 1 shl 9 ; other side said I could SACK
 
; Segment flags
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
 
struct TCP_Packet
; Segment header options
TCP_OPT_EOL equ 0 ; End of option list.
TCP_OPT_NOP equ 1 ; No-Operation.
TCP_OPT_MAXSEG equ 2 ; Maximum Segment Size.
TCP_OPT_WINDOW equ 3 ; window scale
TCP_OPT_TIMESTAMP equ 8
 
struct TCP_segment
.SourcePort dw ?
.DestinationPort dw ?
.SequenceNumber dd ?
36,15 → 68,13
.Window dw ?
.Checksum dw ?
.UrgentPointer dw ?
; .Options rb 3
; .Padding db ?
.Data:
.Data: ; ..or options
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
.offset dd ?
.size:
ends
 
51,47 → 81,20
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
 
TCP_ACKS dd ?
TCP_ACK_LIST rd 3*TCP_MAX_ACKS
TCP_segments_tx rd IP_MAX_INTERFACES
TCP_segments_rx rd IP_MAX_INTERFACES
TCP_bytes_rx rq IP_MAX_INTERFACES
TCP_bytes_tx rq IP_MAX_INTERFACES
TCP_sequence_num dd ?
endg
 
align 4
iglobal
TCPstateHandler:
 
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
106,73 → 109,60
TCP_init:
 
xor eax, eax
mov edi, TCP_PACKETS_TX
mov ecx, 2*MAX_IP
mov edi, TCP_segments_tx
mov ecx, (6*IP_MAX_INTERFACES)
rep stosd
 
init_queue TCP_IN_QUEUE
mov [TCP_sequence_num],1
 
; 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+2+2+3*TCP_MAX_ACKS
rep stosd
 
ret
 
 
;-----------------------------------------------------------------
;
; TCP_decrease_socket_ttls
; decrease socket ttls
;
; IN: /
; OUT: /
;
; destroys: eax
;
;-----------------------------------------------------------------
align 4
TCP_decrease_socket_ttls:
; scan through all the sockets, decrementing active timers
TCP_timer_1000ms:
; scan through all the active TCP 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
mov eax, net_sockets
.loop:
mov eax, [eax + SOCKET.NextPtr]
.check_only:
or eax, eax
jz .exit
 
cmp [ebx + SOCKET_head.Type], IP_PROTO_TCP
jne .next_socket
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jne .loop
 
; 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
cmp [eax + TCP_SOCKET.t_timer], 0
jne .decrement_tcb
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 0
;;;;;; cmp [eax + TCP_SOCKET.wndsizeTimer], 0
jne .decrement_wnd
jmp .next_socket
jmp .loop
 
.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
dec [eax + TCP_SOCKET.t_timer]
jnz .loop
 
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
jne .next_socket
cmp [eax + TCP_SOCKET.t_state], TCB_TIMED_WAIT
jne .loop
 
push [ebx + SOCKET_head.PrevPtr]
stdcall net_socket_free, ebx
pop ebx
jmp .next_socket
push [eax + SOCKET.NextPtr]
call SOCKET_free
pop eax
jmp .check_only
 
.decrement_wnd:
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer]
jmp .next_socket
;;;;;; dec [eax + TCP_SOCKET.wndsizeTimer]
 
.exit:
ret
179,1067 → 169,1815
 
 
 
;-----------------------------------------------------------------
;----------------------
;
; TCP_send_queued:
; TCP_500ms
;
; 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:
TCP_500ms:
 
cmp [TCP_OUT_QUEUE], 0
je .exit
add [TCP_sequence_num], 64000
 
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
 
mov eax, TCP_QUEUE_SIZE
mov ecx, [TCP_OUT_QUEUE]
mov esi, TCP_OUT_QUEUE+8
 
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jnz .found_one
add esi, tcp_out_queue_entry.size
loop .loop
.exit:
mov [TCP_OUT_QUEUE+4], 0
ret
 
.found_one:
dec [esi + tcp_out_queue_entry.ttl]
jz .send_it
cmp [esi + tcp_out_queue_entry.data_ptr], -1
jz .is_ack
.find_next:
add esi, tcp_out_queue_entry.size
dec eax
jz .exit
test ecx, ecx
jnz .loop
mov [TCP_OUT_QUEUE+4], 0
ret
 
.send_it:
pusha
mov ebx, [esi + tcp_out_queue_entry.owner]
pushd [esi + tcp_out_queue_entry.data_size]
pushd [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
popa
 
dec [esi + tcp_out_queue_entry.retries]
jz .remove_it
;----------------------
;
; TCP_10ms
;
;----------------------
align 4
TCP_10ms:
 
mov [esi + tcp_out_queue_entry.ttl], TCP_PACKET_TTL
jmp .find_next
; todo: decrease timers
 
.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
ret
 
.is_ack:
pusha
mov eax, [esi + tcp_out_queue_entry.socket]
mov ebx, [esi + tcp_out_queue_entry.owner]
mov ecx, [esi + tcp_out_queue_entry.size]
call TCP_send_ack
popa
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
jmp .find_next
 
 
 
;-----------------------------------------------------------------
;
; TCP_handler:
; TCP_input:
;
; Called by IPv4_handler,
; this procedure will inject the tcp data diagrams in the application sockets.
; IN: [esp] = ptr to buffer
; [esp+4] = buffer size
; ebx = ptr to device struct
; ecx = segment size
; edx = ptr to TCP segment
;
; 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
; esi = ipv4 source address
; edi = ipv4 dest address
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_handler :
TCP_input:
 
DEBUGF 1,"TCP_Handler\n"
DEBUGF 1,"TCP_input\n"
 
; TODO: validate checksum
; Offset must be greater than or equal to the size of the standard TCP header (20) and less than or equal to the TCP length.
 
; Find a matching socket for received packet, all following expressions must be valid:
;
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 0xf0
shr al , 2
 
DEBUGF 1,"data offset: %u\n", eax
 
cmp eax, 20
jl .drop
 
cmp eax, ecx
jg .drop
 
;-------------------------------
; Now, re-calculate the checksum
 
push eax edx ebx
 
push edi
push esi
mov esi, edx
call TCP_checksum ; this destroys edx, ecx and esi (but not edi! :)
 
pop ebx edx eax
 
cmp [edx + TCP_segment.Checksum], 0
jnz .drop
 
DEBUGF 1,"Checksum is correct\n"
 
;-----------------------------------------------------------------------------------------
; Check if this packet has a timestamp option (We do it here so we can process it quickly)
 
cmp eax, 20 + 12 ; Timestamp option is 12 bytes
jl .no_timestamp
je .is_ok
 
cmp byte [edx + TCP_segment.Data + 12], 0 ; end of option list
jne .no_timestamp
 
.is_ok:
test [edx + TCP_segment.Flags], TH_SYN ; SYN flag must not be set
jnz .no_timestamp
 
cmp dword [edx + TCP_segment.Data], 0x0101080a ; Timestamp header
jne .no_timestamp
 
DEBUGF 1,"timestamp ok\n"
 
; TODO: Parse the options
; TODO: Set a Bit in the TCP to tell all options are parsed
 
ret
 
.no_timestamp:
 
;-------------------------------------------
; Convert Big-endian values to little endian
 
ntohld [edx + TCP_segment.SequenceNumber]
ntohld [edx + TCP_segment.AckNumber]
 
ntohlw [edx + TCP_segment.Window]
ntohlw [edx + TCP_segment.UrgentPointer]
 
;------------------------------------------------------------
; Next thing to do is find the TCB (thus, the socket pointer)
 
; IP Packet TCP Destination Port = local Port
; (IP Packet SA = Remote IP) OR (Remote IP = 0)
; (IP Packet SenderAddress = Remote IP) OR (Remote IP = 0)
; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0)
 
mov ebx, net_sockets
 
.socket_loop:
mov ebx, [ebx + SOCKET_head.NextPtr]
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .dump
jz .drop_with_reset
 
mov ax, [edx + TCP_Packet.DestinationPort]
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], ax
cmp [ebx + SOCKET.Type], IP_PROTO_TCP
jne .socket_loop
 
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
mov ax, [edx + TCP_segment.DestinationPort]
cmp [ebx + TCP_SOCKET.LocalPort], ax
jne .socket_loop
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
cmp eax, esi
je @f
test eax, eax
jne .socket_loop
jnz .socket_loop
@@:
 
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort]
cmp [edx + TCP_Packet.SourcePort] , ax
mov ax, [ebx + TCP_SOCKET.RemotePort]
cmp [edx + TCP_segment.SourcePort] , ax
je .found_socket
test ax, ax
jnz .socket_loop
.found_socket:
DEBUGF 1,"Found valid socket for packet\n"
DEBUGF 1,"Socket ptr: %x\n", ebx
 
inc [TCP_PACKETS_RX]
; ebx now contains the pointer to the socket
 
add ebx, SOCKET_head.lock
;----------------------------
; Check if socket isnt closed
 
cmp [TCP_SOCKET.t_state], TCB_CLOSED
je .drop
 
;----------------
; Lock the socket
 
add ebx, SOCKET.lock ; TODO: figure out if we should lock now already
call wait_mutex
sub ebx, SOCKET_head.lock
sub ebx, SOCKET.lock
 
;-------------------------------
; ebx is pointer to socket
; ecx is size of tcp packet
; edx is pointer to tcp packet
;---------------------------------------
; unscale the window into a 32 bit value ;;;;;;
 
; calculate header length
movzx eax, [edx + TCP_Packet.DataOffset]
movzx eax, [edx + TCP_segment.Window]
xchg al, ah
 
test [edx + TCP_segment.Flags], TH_SYN
jnz .no_syn
 
mov cl , [ebx + TCP_SOCKET.SND_SCALE]
shl eax, cl
 
.no_syn:
 
;-----------------------------------
; Is this socket a listening socket?
 
; If so, create a new socket
 
test [ebx + SOCKET.options], SO_ACCEPTCON
jz .no_accept_conn
 
 
; TODO: create a new socket
 
 
.no_accept_conn:
 
;----------------------------
; Compute window scale factor
 
 
; TODO
 
 
;-------------------------------------
; Reset idle timer and keepalive timer
 
 
; TODO
 
;-----------------------------------------
; Process TCP options if not in LISTEN state
 
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN
jz .dont_do_options
 
call TCP_do_options
 
.dont_do_options:
 
;-----------------------------------------------------------------------
; Time to do some header prediction (Original Principle by Van Jacobson)
 
 
; There are two common cases for an uni-directional data transfer.
;
; General rule: the packets has no control flags, is in-sequence,
; window width didnt change and we're not retransmitting.
;
; Second rules:
; - If the length is 0 and the ACK moved forward, we're the sender side of the transfer.
; In this case we'll free the ACK'ed data and notify higher levels that we have free space in buffer
;
; - If the length is not 0 and the ACK didn't move, we're the receiver side of the transfer.
; If the packets are in order (data queue is empty), add the data to the socket buffer and request a delayed ACK
 
cmp [TCP_SOCKET.t_state], TCB_ESTABLISHED
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_ACK
jz .not_uni_xfer
 
mov eax, [edx + TCP_segment.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
movzx eax, [edx + TCP_segment.Window] ;;;;;
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .not_uni_xfer
 
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jne .not_uni_xfer
 
;-------------------------------------------------------------------------------
; If last ACK falls within this segment's sequence number, record the timestamp.
 
; TODO: check if it has a timestamp
 
 
 
 
;---------------------------------------
; check if we are sender in the uni-xfer
 
; If the following 4 conditions are all true, this segment is a pure ACK.
;
; - The segment contains no data (ti_len is 0).
 
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 11110000b
shr eax, 2
DEBUGF 1,"TCP header size: %u\n", eax
sub ecx, eax
jnz .not_sender
 
;-------------------------------
; ecx is size of tcp data
; - The acknowledgment field in the segment (ti_ack) is greater than the largest unacknowledged sequence number (snd_una).
; Since this test is "greater than" and not "greater than or equal to," it is true only if some positive amount of data is acknowledged by the ACK.
 
; as a Packet has been received, update the TCB timer
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jle .not_uni_xfer
 
; 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
; - The acknowledgment field in the segment (ti_ack) is less than or equal to the maximum sequence number sent (snd_max).
 
; Calculate ACK number, in intel byte order
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
; mov eax, [edx + TCP_segment.Ack]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .not_uni_xfer
 
; Dequeue all acknowledged packets
cmp [TCP_OUT_QUEUE], 0 ; first, check if any packets are queued at all
je .no_ack
; - The congestion window (snd_cwnd) is greater than or equal to the current send window (snd_wnd).
; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance.
 
push ebx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
pop ebx
mov eax, [ebx + TCP_SOCKET.SND_CWND]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jl .not_uni_xfer
 
push ecx
DEBUGF 1,"Removing all queued packets with smaller ACK\n"
mov ecx, TCP_QUEUE_SIZE
mov esi, TCP_OUT_QUEUE+8
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
je .maybe_next
DEBUGF 1,"Header prediction: we are sender\n"
 
cmp [esi + tcp_out_queue_entry.socket], ebx
jne .maybe_next
;---------------------------------
; Packet is a pure ACK, process it
 
cmp [esi + tcp_out_queue_entry.seq_num], edi
jg .maybe_next
; Update RTT estimators
 
DEBUGF 1,"Removing a queued packet\n"
; Delete acknowledged bytes from send buffer
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
; Stop retransmit timer
 
.maybe_next:
add esi, tcp_out_queue_entry.size
loop .loop
; Awaken waiting processes
 
mov [TCP_OUT_QUEUE+4], 0
pop ecx
; Generate more output
 
; 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
 
call dword [TCPstateHandler+eax*4-4]
 
.dump:
DEBUGF 1,"Dumping TCP packet\n"
call kernel_free
add esp, 4 ; pop (balance stack)
jmp .drop
 
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
;
;-----------------------------------------------------------------
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
.not_sender:
; The amount of data in the segment (ti_len) is greater than 0 (data count is in ecx)
 
 
; The acknowledgment field (ti_ack) equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jne .not_uni_xfer
 
; The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp).
;;;;
jnz .not_uni_xfer
 
; There is room in the receive buffer for the data in the segment.
;;;;
jnz .not_uni_xfer
 
;-------------------------------------
; Complete processing of received data
 
DEBUGF 1,"header prediction: we are receiver\nreceiving %u bytes of data\n", ecx
 
; The next expected receive sequence number (rcv_nxt) is incremented by the number of bytes of data.
 
add [ebx + TCP_SOCKET.RCV_NXT], ecx
 
; Add the data to the socket buffer
 
; The receiving process is awakened (by sorwakeup).
 
; The delayed-ACK flag is set and the input processing is complete.
 
jmp .drop
 
 
 
 
 
;----------------------------------------------------
; Header prediction failed, doing it the slow way..
 
.not_uni_xfer:
 
DEBUGF 1,"Header prediction failed\n"
 
;------------------------
; calculate header length ;;;;; we already calculated this before!
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 0xf0
shr eax, 2
 
; Update edx to point to data..
add edx, eax
; ..and ecx to give data size
sub ecx, eax
 
;------------------------------
; Calculate receive window size
 
;;;;
 
 
;-------------------------
; TCP slow input procedure
 
DEBUGF 1,"TCP slow input procedure\n"
 
cmp [eax + TCP_SOCKET.t_state], TCB_LISTEN
je .LISTEN
 
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
je .SYN_SENT
 
 
;--------------------------------------------
; Protection Against Wrapped Sequence Numbers
 
 
; First, check timestamp if present
 
;;;; TODO
 
; Then, check if at least some bytes of data are within window
 
;;;; TODO
 
jmp .trim_then_step6
 
align 4
TCP_send:
.LISTEN:
 
DEBUGF 1,"Creating TCP packet, socket: %x, flags: %x\n",eax, bl
DEBUGF 1,"TCP state: listen\n"
 
mov di , IP_PROTO_TCP
add ecx, TCP_Packet.Data
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
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]
test [edx + TCP_segment.Flags], TH_ACK
jnz .drop_with_reset
 
call IPv4_create_packet
cmp edi, -1
je .fail
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; If there is any data, copy it first
pop esi
push edi
add edi, TCP_Packet.Data
sub ecx, TCP_Packet.Data
; TODO: check if it's a broadcast or multicast, and drop if so
 
shr ecx, 1
jnc .nb
movsb
.nb: shr ecx, 1
jnc .nw
movsw
.nw: test ecx, ecx
jz .nd
;;; 28.6
 
; create a new socket and fill in the nescessary variables
 
;; Exit if backlog queue is full
; mov ax, [ebx + TCP_SOCKET.backlog_cur]
; cmp ax, [ebx + TCP_SOCKET.backlog]
; jae .exit
 
; Allocate new socket
call SOCKET_alloc
;;; jz .fail
 
; Copy structure from current socket to new, (including lock!)
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket
lea esi, [edx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4
rep movsd
.nd:
pop edi
 
; Fill in the TCP header
pop esi
;; Push pointer to new socket to queue
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur]
; inc [ebx + TCP_SOCKET.backlog_cur]
; mov [ebx + TCP_SOCKET.end + ecx*4], eax
 
; fill in tcp sequence number
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address
 
; Fill in local and remote ports
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
mov cx, [edx + TCP_segment.SourcePort]
mov [eax + TCP_SOCKET.RemotePort], cx
 
; Acknumber
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
pop [edi + TCP_Packet.AckNumber]
mov ecx, [edx + TCP_segment.SequenceNumber]
mov [eax + TCP_SOCKET.IRS], ecx
 
; 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
mov ecx, [eax + TCP_SOCKET.ISS]
mov [eax + TCP_SOCKET.SND_NXT], ecx
 
; 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
jmp .trim_then_step6
 
; Now, calculate the checksum
xchg cl, ch
pushw cx
xchg cl, ch
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 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]
align 4
.SYN_SENT:
 
pop cx ; get the length from packet, back from pseudoheader
pop edi
DEBUGF 1,"TCP state: syn_sent\n"
 
cmp cx, TCP_Packet.Data shl 8 ; if the packet has no data
je .only_one ; send it only once
test [edx + TCP_segment.Flags], TH_ACK
jz @f
 
and ecx, 0x0000ffff
xchg cl, ch
sub cx, TCP_Packet.Data
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.ISS]
jle .drop_with_reset
 
add_INET (edi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT) ; todo: this should only happen when packet was queued successful
mov ecx, TCP_RETRIES
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .drop_with_reset
@@:
 
jmp .go_for_it
 
.only_one:
mov ecx, 1
.go_for_it:
test [edx + TCP_segment.Flags], TH_RST
jz @f
 
mov [edi + SOCKET_head.lock], 0
jmp TCP_queue ; At last send the packet!
test [edx + TCP_segment.Flags], TH_ACK
jz .drop
 
.fail:
add esp, 2+4
or eax, -1
ret
;tp = tcp_drop(tp, ECONNREFUSED)
 
jmp .drop
@@:
 
;-----------------------------------------------------------------
;
; Queue a TCP packet for sending
;
; IN: [esp] pointer to buffer
; [esp + 4] size of buffer
; ebx = driver struct
; edx = sequence number of this packet in intel byte order
; esi = sender proc
; edi = socket number
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; OUT: /
;
;-----------------------------------------------------------------
; now, process received SYN in response to an active open
test [edx + TCP_segment.Flags], TH_ACK
jz @f
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jle @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
 
; TODO: turn off retransmission timer
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.IRS], eax
 
; TODO: set socket state to connected
 
mov [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED
 
; TODO: check if we should scale the connection (567-572)
; TODO: update RTT estimators
 
 
@@:
 
; We have received a syn but no ACK, so we are having a simultaneous open..
mov [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
 
;-------------------------------------
; Common processing for receipt of SYN
 
.trimthenstep6:
 
inc [edx + TCP_segment.SequenceNumber]
 
cmp cx, [ebx + TCP_SOCKET.RCV_WND]
jle @f
 
movzx eax, cx
sub ax, [ebx + TCP_SOCKET.RCV_WND]
; TODO: 592
mov cx, [ebx + TCP_SOCKET.RCV_WND]
; TODO...
@@:
;;;;;
;;; jmp .step6
 
 
 
 
 
align 4
TCP_queue:
.trim_then_step6:
 
DEBUGF 1,"Adding packet to TCP queue, buffer: %x, size: %u, driver: %x, acknum: %u\n", [esp], [esp+4], ebx, edx
DEBUGF 1,"Trim, then step 6\n"
 
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
jge .full
;----------------------------
; trim any data not in window
 
push ebx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
pop ebx
mov eax, [ebx + TCP_SOCKET.RCV_NXT]
sub eax, [edx + TCP_segment.SequenceNumber]
 
mov ecx, TCP_QUEUE_SIZE
mov eax, TCP_OUT_QUEUE+8
.loop:
cmp [eax + tcp_out_queue_entry.data_ptr], 0
je .found_it
add eax, tcp_out_queue_entry.size
loop .loop
test eax, eax
jz .no_drop
 
add esp, 4
.full: ; silently discard the packet
DEBUGF 1,"TCP queue is full!\n"
test [edx + TCP_segment.Flags], TH_SYN
jz .no_drop
 
and [edx + TCP_segment.Flags], not (TH_SYN)
inc [edx + TCP_segment.SequenceNumber]
 
cmp [edx + TCP_segment.UrgentPointer], 1
jl @f
 
dec [edx + TCP_segment.UrgentPointer]
 
jmp .no_drop
@@:
 
and [edx + TCP_segment.Flags], not (TH_URG)
dec eax
 
.no_drop:
 
; eax holds number of bytes to drop
 
 
;----------------------------------
; Check for entire duplicate packet
 
cmp eax, ecx
jge .duplicate
 
;;; TODO: figure 28.30
 
;; inc [TCP_segments_rx]
 
;; add dword [TCP_bytes_rx], ecx
;; adc dword [TCP_bytes_rx+4], 0
 
;------------------------
; Check for duplicate FIN
 
test [edx + TCP_segment.Flags], TH_FIN
jz @f
inc ecx
cmp eax, ecx
dec ecx
jne @f
 
mov eax, ecx
and [edx + TCP_segment.Flags], not TH_FIN
;;; TODO: set ACKNOW flag
 
jmp .no_duplicate
@@:
 
; Handle the case when a bound socket connects to itself
; Allow packets with a SYN and an ACKto continue with the processing
 
 
;-------------------------------------
; Generate duplicate ACK if nescessary
 
; This code also handles simultaneous half-open or self-connects
 
test eax, eax
jnz .drop_after_ack
 
cmp [edx + TCP_segment.Flags], TH_ACK
jz .drop_after_ack
 
.duplicate:
 
;----------------------------------------
; Update statistics for duplicate packets
 
;;; TODO
 
;;; DROP the packet ??
 
.no_duplicate:
 
;-----------------------------------------------
; Remove duplicate data and update urgent offset
 
add [edx + TCP_segment.SequenceNumber], eax
 
;;; TODO
 
sub [edx + TCP_segment.UrgentPointer], ax
jg @f
 
and [edx + TCP_segment.Flags], not (TH_URG)
mov [edx + TCP_segment.UrgentPointer], 0
@@:
 
;--------------------------------------------------
; Handle data that arrives after process terminates
 
cmp [ebx + SOCKET.PID], 0
jge @f
 
cmp [ebx + TCP_SOCKET.t_state], TCB_CLOSE_WAIT
jle @f
 
test ecx, ecx
jz @f
 
;;; Close the socket
;;; update stats
 
jmp .drop_with_reset
 
@@:
 
;----------------------------------------
; Remove data beyond right edge of window
 
mov eax, [edx + TCP_segment.SequenceNumber]
add eax, ecx
sub eax, [ebx + TCP_SOCKET.RCV_NXT]
sub ax, [ebx + TCP_SOCKET.RCV_WND]
 
; eax now holds the number of bytes to drop
 
jle .no_excess_data
 
;;; TODO: update stats
 
cmp eax, ecx
jl .dont_drop_all
 
;;; TODO 700-736
 
.dont_drop_all:
 
.no_excess_data:
 
 
;-----------------
; Record timestamp
 
;;; TODO 737-746
 
;------------------
; Process RST flags
 
test [edx + TCP_segment.Flags], TH_RST
jz .rst_skip
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .rst_sw_list]
 
.rst_sw_list:
dd .rst_skip ;TCB_CLOSED
dd .rst_skip ;TCB_LISTEN
dd .rst_skip ;TCB_SYN_SENT
dd .econnrefused ;TCB_SYN_RECEIVED
dd .econnreset ;TCB_ESTABLISHED
dd .econnreset ;TCB_CLOSE_WAIT
dd .econnreset ;TCB_FIN_WAIT_1
dd .rst_close ;TCB_CLOSING
dd .rst_close ;TCB_LAST_ACK
dd .econnreset ;TCB_FIN_WAIT_2
dd .rst_close ;TCB_TIMED_WAIT
 
.econnrefused:
 
;;; TODO: debug info
 
jmp .close
 
.econnreset:
 
;;; TODO: debug info
.close:
 
;;; update stats
 
.rst_close:
 
;;; Close the socket
jmp .drop
 
.rst_skip:
 
;--------------------------------------
; handle SYN-full and ACK-less segments
 
test [edx + TCP_segment.Flags], TH_SYN
jz @f
 
;;; tcp_drop ( ECONNRESET)
jmp .drop_with_reset
 
test [edx + TCP_segment.Flags], TH_ACK
jz .drop
 
;----------------
; Process the ACK
 
cmp [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
jg .ack_dup
jl .ack_nodup
 
; dd .ack_nodup ;TCB_CLOSED
; dd .ack_nodup ;TCB_LISTEN
; dd .ack_nodup ;TCB_SYN_SENT
; dd .ack_syn_rcvd ;TCB_SYN_RECEIVED
; dd .ack_dup ;TCB_ESTABLISHED
; dd .ack_dup ;TCB_CLOSE_WAIT
; dd .ack_dup ;TCB_FIN_WAIT_1
; dd .ack_dup ;TCB_CLOSING
; dd .ack_dup ;TCB_LAST_ACK
; dd .ack_dup ;TCB_FIN_WAIT_2
; dd .ack_dup ;TCB_TIMED_WAIT
 
;;;;;
 
.ack_dup:
 
;;;;
 
.ack_nodup:
 
;;;; 887
 
;-------------------------------------------------
; If the congestion window was infalted to account
; for the other side's cached packets, retrace it
 
;;;; 888 - 902
 
 
;------------------------------------------
; RTT measurements and retransmission timer
 
;;;;; 903 - 926
 
 
;-------------------------------------------
; Open congestion window in response to ACKs
 
;;;;
 
 
;------------------------------------------
; Remove acknowledged data from send buffer
 
;;;; 943 - 956
 
;---------------------------------------
; Wake up process waiting on send buffer
 
;;;;;
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .ACK_sw_list]
 
.ACK_sw_list:
dd .step6 ;TCB_CLOSED
dd .step6 ;TCB_LISTEN
dd .step6 ;TCB_SYN_SENT
dd .step6 ;TCB_SYN_RECEIVED
dd .step6 ;TCB_ESTABLISHED
dd .step6 ;TCB_CLOSE_WAIT
dd ._963 ;TCB_FIN_WAIT_1
dd ._958 ;TCB_CLOSING
dd ._999 ;TCB_LAST_ACK
dd .step6 ;TCB_FIN_WAIT_2
dd ._1010 ;TCB_TIMED_WAIT
 
 
._963:
 
 
jmp .step6
 
 
._958:
 
jmp .step6
 
._999:
 
jmp .step6
 
 
._1010:
 
jmp .step6
 
 
 
align 4
.step6:
 
DEBUGF 1,"step 6\n"
 
;--------------------------
; update window information
 
test [edx + TCP_segment.Flags], TH_ACK
jz .no_window_update
 
mov eax, [ebx + TCP_SOCKET.SND_WL1]
cmp eax, [edx + TCP_segment.SequenceNumber]
 
;;;; 1021
 
;----------------------------------
; Keep track of pure window updates
 
test ecx, ecx
jz @f
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_segment.AckNumber]
jne @f
 
;; mov eax, tiwin
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jle @f
 
;;; update stats
 
@@:
 
;; mov eax, incoming window
cmp eax, [ebx + TCP_SOCKET.max_sndwnd]
jle @f
mov [ebx + TCP_SOCKET.max_sndwnd], eax
@@:
mov [ebx + TCP_SOCKET.SND_WND], eax
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_WL2], eax
 
;;; needoutput = 1
 
.no_window_update:
 
 
;-----------------
; process URG flag
 
test [edx + TCP_segment.Flags], TH_URG
jz .not_urgent
 
cmp [edx + TCP_segment.UrgentPointer], 0
jz .not_urgent
 
cmp [ebx + TCP_SOCKET.t_state], TCB_TIMED_WAIT
je .not_urgent
 
; Ignore bogus urgent offsets
 
;;; 1040-1050
 
movzx eax, [edx + TCP_segment.UrgentPointer]
add eax, [ebx + SOCKET.SO_RCV.SB_CC]
cmp eax, SOCKET_MAXDATA
jle .not_urgent
 
mov [edx + TCP_segment.UrgentPointer], 0
and [edx + TCP_segment.Flags], not (TH_URG)
jmp .do_data
 
.not_urgent:
 
;--------------------------------------
; processing of received urgent pointer
 
;;; 1051-1093
 
align 4
.do_data:
 
DEBUGF 1,"Do data:\n"
 
; process the data in the segment
 
test [edx + TCP_segment.Flags], TH_FIN
jz .process_fin
 
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 ;;;;;
jge .dont_do_data
 
DEBUGF 1,"Processing data in segment\n"
 
;;; NOW, process the data
 
jmp .final_processing
 
 
.dont_do_data:
 
 
;---------------
; FIN processing
 
.process_fin:
 
DEBUGF 1,"Processing FIN\n"
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .FIN_sw_list]
 
.FIN_sw_list:
dd .no_fin ;TCB_CLOSED
dd .no_fin ;TCB_LISTEN
dd .no_fin ;TCB_SYN_SENT
dd ._1131 ;TCB_SYN_RECEIVED
dd ._1131 ;TCB_ESTABLISHED
dd .no_fin ;TCB_CLOSE_WAIT
dd ._1139 ;TCB_FIN_WAIT_1
dd .no_fin ;TCB_CLOSING
dd .no_fin ;TCB_LAST_ACK
dd ._1147 ;TCB_FIN_WAIT_2
dd ._1156 ;TCB_TIMED_WAIT
 
 
 
._1131:
 
._1139:
 
._1147:
 
._1156:
 
 
.no_fin:
 
;-----------------
; Final processing
 
.final_processing:
 
DEBUGF 1,"Final processing\n"
 
;;; if debug enabled, output packet
 
;test ;;;needoutput = 1
;jnz .outputnow
 
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jz .ret
 
.outputnow:
call TCP_output
 
.ret:
mov [ebx + SOCKET.lock], 0
 
call kernel_free
add esp, 4
ret 4
 
ret
;------------------------------------------
; Generate an ACK, droping incoming segment
 
.found_it: ; eax points to empty queue entry
align 4
.drop_after_ack:
 
mov [eax + tcp_out_queue_entry.retries], TCP_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
DEBUGF 1,"Drop after ACK\n"
 
inc [TCP_OUT_QUEUE]
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
sub eax, TCP_OUT_QUEUE+8
shr eax, 5
DEBUGF 1,"Added to queue in pos %u, total queued packets: %u\n", eax, [TCP_OUT_QUEUE+8]
and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
mov [TCP_OUT_QUEUE+4], 0
call TCP_output
 
ret
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
;-----------------------------------------------------------------
 
;-------------------------------------------
; Generate an RST, dropping incoming segment
 
align 4
.drop_with_reset:
 
DEBUGF 1,"Drop with reset\n"
 
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
;;; if its a multicast/broadcast, also drop
 
test [edx + TCP_segment.Flags], TH_ACK
jnz .respond_ack
 
test [edx + TCP_segment.Flags], TH_SYN
jnz .respond_syn
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
.respond_ack:
 
;;;;
 
call TCP_respond
 
jmp .destroy_new_socket
 
 
.respond_syn:
 
;;;;
 
call TCP_respond
 
jmp .destroy_new_socket
 
;-----
; Drop
 
align 4
.drop:
 
DEBUGF 1,"Dropping packet\n"
 
;;;; If debugging options are enabled, output the packet somwhere
 
.destroy_new_socket:
 
;;;; kill the newly created socket
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
;---------------------
;
; IN: ebx = socket
; ecx = ack number
; TCP_do_options
;
; OUT: /
;
;-----------------------------------------------------------------
;-------------------
 
align 4
TCP_queue_ack:
TCP_do_options:
 
DEBUGF 1,"Adding ACK to TCP queue, socket: %x, acknum: %u\n", ebx, ecx
DEBUGF 1,"TCP_do_options\n"
 
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
jge .full
push eax
sub eax, 20
jz .no_options
 
push ebx ecx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
lea esi, [edx + TCP_segment.Data]
 
mov ecx, TCP_QUEUE_SIZE
mov eax, TCP_OUT_QUEUE+8
 
;-------------------------------------------
; Begin the loop by checking for EOL and NOP
 
.loop:
cmp [eax + tcp_out_queue_entry.data_ptr], 0
je .found_it
add eax, tcp_out_queue_entry.size
loop .loop
 
add esp, 8
.full: ; silently discard the packet
DEBUGF 1,"TCP queue is full!\n"
ret
cmp byte [esi], TCP_OPT_EOL ; end of option list?
jz .no_options
 
.found_it: ; eax points to empty queue entry
cmp byte [esi], TCP_OPT_NOP ; nop ?
;;; cmove edi, 1 ; if so, set option size to 1
jz .continue ; and continue scanning
 
pop [eax + tcp_out_queue_entry.data_size] ; ACK number
mov [eax + tcp_out_queue_entry.data_ptr], -1 ; ACK packet
pop [eax + tcp_out_queue_entry.socket]
mov [eax + tcp_out_queue_entry.retries], 1
mov [eax + tcp_out_queue_entry.ttl], 20 ; 200 ms
;------------------
; We have an option
 
inc [TCP_OUT_QUEUE]
movzx edi, byte [esi + 1] ; get the length of this option in edi
 
sub eax, TCP_OUT_QUEUE+8
shr eax, 5
DEBUGF 1,"Added to queue in pos %u, total queued packets: %u\n", eax, [TCP_OUT_QUEUE+8]
 
mov [TCP_OUT_QUEUE+4], 0
;--------------------------------------
; Check for Maximum segment size option
 
ret
cmp byte [esi], TCP_OPT_MAXSEG
jne .no_maxseg
 
cmp edi, 4 ; option length
jne .continue
 
; IN: eax = socket pointer
; ebx = device structure
; ecx = ack number
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
align 4
TCP_send_ack:
; Now parse the option...
 
DEBUGF 1,"Creating TCP ACK packet, socket: %x, acknum: %x\n", eax, ecx
jmp .continue
 
push ecx eax
.no_maxseg:
 
mov di , IP_PROTO_TCP
mov ecx, TCP_Packet.Data
; 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]
;------------------------
; Check for Window option
 
call IPv4_create_packet
cmp edi, -1
je .fail
cmp byte [esi], TCP_OPT_WINDOW
jne .no_window
 
pop ecx
cmp edi, 3 ; option length
jne .continue
 
; fill in tcp sequence number
push [ecx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
; Fill in local and remote ports
push dword [ecx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
; ...
 
; Acknumber
pop [edi + TCP_Packet.AckNumber]
jmp .continue
 
; Fill in other tcp options
mov [edi + TCP_Packet.Flags], TH_ACK
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
.no_window:
 
; Push pointer to and size of total packet (needed for send procedure)
push edx eax esi
;---------------------------
; Check for Timestamp option
 
; Now, calculate the checksum
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
cmp byte [esi], TCP_OPT_TIMESTAMP
jne .no_timestamp
 
xor edx, edx
mov ecx, 12
mov esi, esp
call checksum_1
call checksum_2
mov [edi + TCP_Packet.Checksum], dx
add esp, 12 ; remove the pseudoheader from stack
cmp edi, 10 ; option length
jne .continue
 
; ...
 
 
jmp .continue
 
.no_timestamp:
 
;----------------------------------
; Future options may be placed here
 
 
 
 
;------------------------------
; Continue scanning for options
 
.continue:
add esi, edi
sub eax, edi
jg .loop
 
.no_options:
 
pop eax
call eax
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
.fail:
add esp, 8
 
 
 
;---------------------------
;
; TCP_pull_out_of_band
;
; IN: eax =
; ebx = socket ptr
; edx = tcp packet ptr
;
; OUT: /
;
;---------------------------
 
align 4
TCP_pull_out_of_band:
 
DEBUGF 1,"TCP_pull_out_of_band\n"
 
;;;; 1282-1305
 
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
 
 
;-----------------------------------------------------------------
;
; Remove all queued TCP packets for a specified socket
; TCP_output
;
; IN: eax = socket number
; IN: eax = socket pointer
;; esi = ptr to data
;; ecx = number of data bytes
;
; OUT: /
;
; destoys esi and ecx
;
;-----------------------------------------------------------------
 
align 4
TCP_remove_socket:
TCP_output:
 
cmp [TCP_OUT_QUEUE], 0
je .skip
DEBUGF 1,"TCP_output, socket: %x\n", eax
 
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
; We'll detect the length of the data to be transmitted, and flags to be used
; If there is some data, or any critical controls to send (SYN / RST), then transmit
; Otherwise, investigate further
 
mov eax, TCP_QUEUE_SIZE
mov ecx, [TCP_OUT_QUEUE]
mov esi, TCP_OUT_QUEUE+8
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
jne .not_idle
 
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jz .maybenext
cmp [esi + tcp_out_queue_entry.socket], eax
jnz .maybenext
mov ebx, [eax + TCP_SOCKET.t_idle]
cmp ebx, [eax + TCP_SOCKET.t_rxtcur]
jle .not_idle
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
; We have been idle for a while and no ACKS are expected to clock out any data we send..
; Slow start to get ack "clock" running again.
 
.maybenext:
add esi, tcp_out_queue_entry.size
loop .loop
mov ebx, [eax + TCP_SOCKET.t_maxseg]
mov [eax + TCP_SOCKET.SND_CWND], ebx
 
mov [TCP_OUT_QUEUE+4], 0
.skip:
ret
.not_idle:
.again:
mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset
sub ebx, [eax + TCP_SOCKET.SND_UNA] ;
 
mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window
cmp ecx, [eax + TCP_SOCKET.SND_CWND] ;
jl @f ;
mov ecx, [eax + TCP_SOCKET.SND_CWND] ;
@@: ;
 
call TCP_outflags
 
; If in persist timeout with window of 0, send 1 byte.
; Otherwise, if window is small but nonzero, and timer expired,
; we will send what we can and go to transmit state
 
test [eax + TCP_SOCKET.t_force], -1
jz .no_persist_timeout
 
;---------- TCB state handlers start here
test ecx, ecx
jnz .no_zero_window
 
cmp ebx, [eax + SOCKET.SO_SND.SB_CC]
jge @f
 
and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before?
 
@@:
inc ecx
jmp .no_persist_timeout
 
align 4
stateTCB_LISTEN:
.no_zero_window:
 
DEBUGF 1,"TCBStateHandler: Listen\n"
;;; mov [eax + TCP_SOCKET.t_timer....TCPT_PERSIST], 0
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
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
.no_persist_timeout:
 
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
;;;106
 
mov [ebx + SOCKET_head.lock], 0
mov esi, [eax + SOCKET.SO_SND.SB_CC]
cmp esi, ecx
jl @f
mov esi, ecx
@@:
sub esi, ebx
 
push eax
; Now construct the response
mov bl, TH_SYN + TH_ACK
xor ecx, ecx
call TCP_send
pop eax
cmp esi, -1
jne .not_minus_one
 
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
; If FIN has been set, but not ACKed, and we havent been called to retransmit,
; len (esi) will be -1
; Otherwise, window shrank after we sent into it.
; If window shrank to 0, cancel pending retransmit and pull SND_NXT back to (closed) window
; We will enter persist state below.
; If window didn't close completely, just wait for an ACK
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
xor esi, esi
 
.fail:
add esp, 8
mov [ebx + SOCKET_head.lock], 0
ret
test ecx, ecx
jnz @f
 
;;; mov [eax + TCP_SOCKET.t_timer..TCPT_REXMT], 0
 
align 4
stateTCB_SYN_SENT:
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
@@:
 
DEBUGF 1,"TCBStateHandler: Syn_Sent\n"
.not_minus_one:
 
; We are awaiting an ACK to our SYN, with a SYM
; Look at control flags - expecting an ACK
;;; 124
 
mov al, [edx + TCP_Packet.Flags]
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jle @f
 
test al, TH_RST
jnz .reset ; jump if RST bit set
mov esi, [eax + TCP_SOCKET.t_maxseg]
;sendalot = 1
 
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) ;;
@@:
 
;;; 128
 
push [edx + TCP_Packet.AckNumber] ;;;;;;
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] ;;;;;;
mov edi, [eax + TCP_SOCKET.SND_NXT]
add edi, esi ; len
sub edi, [eax + TCP_SOCKET.SND_UNA]
add edi, [eax + SOCKET.SO_SND.SB_CC]
cmp edi, 0
jle @f
 
and al, TH_SYN + TH_ACK
jz .exit ; jump if none of the following is set: RST, SYN, ACK
and dl, not (TH_FIN) ; clear the FIN flag
 
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
;;;; 130 TODO: set window (ecx) to space in send buffer
 
.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
;------------------------------
; Sender silly window avoidance
 
.reset:
; TODO: ....
test esi, esi
jz .zero_length
 
; 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
cmp esi, [eax + TCP_SOCKET.t_maxseg]
je .send
 
.onlysyn:
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
pushw TH_SYN + TH_ACK
jmp .send
;;; TODO: 144-145
 
test [eax + TCP_SOCKET.t_force], -1
jnz .send
 
;;; TODO: 149..152
 
align 4
stateTCB_SYN_RECEIVED:
.zero_length:
 
DEBUGF 1,"TCBStateHandler: Syn_received\n"
 
test [edx + TCP_Packet.Flags], TH_RST ; reset connection? => LISTEN
jz .check_ack
;----------------------------------------
; Check if a window update should be sent
 
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]
cmp ecx, 0 ; window
jle .no_window
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_LISTEN
jmp .exit
;;; TODO 154-172
 
.check_ack:
test [edx + TCP_Packet.Flags], TH_ACK ; ACK? => connection established!
jz .exit
.no_window:
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
mov eax, ebx
call notify_network_event
;--------------------------
; Should a segment be sent?
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jnz .send
 
test dl, TH_SYN + TH_RST
jnz .send
 
if 0
mov eax, [ebx + TCP_SOCKET.SND_UP]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jg .send
 
test dl, TH_FIN
jz .enter_persist
 
align 4
stateTCB_ESTABLISHED:
test [ebx + TCP_SOCKET.t_flags], TF_SENTFIN
jnz .send
 
DEBUGF 1,"TCBStateHandler: Established\n"
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
je .send
 
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 ;;;;;;
;--------------------
; Enter persist state
 
; check if we received an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .no_ack
.enter_persist:
 
mov ax, [edx + TCP_Packet.Window]
xchg al, ah
cmp ax, 1024
ja @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 1
@@:
.no_ack:
DEBUGF 1,"Entering pesist state\n"
 
; Now, see if we received any data
test ecx, ecx
jz .nodata
 
; Calculate next sequencenumber
add_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT)
 
push edx
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
;--------------------------------------
; No reason to send a segment, just ret
 
add esp, 4
pop esi ; pointer to buffer
add esp, 4
DEBUGF 1,"No reason to send a segment\n"
 
sub edx, esi
mov edi, edx ; offset
mov eax, ebx ; socket ptr
ret
 
call socket_internal_receiver ; Place the data from packet into socket
 
; lea ebx, [eax + SOCKET_head.lock]
; call wait_mutex
mov ebx, eax
pop edx
 
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jz .ack
 
.nodata:
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jz .exit
 
; Send an ACK to that fin, and enter closewait state
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSE_WAIT
; Remove all resend entries from the queue
mov eax, ebx
call TCP_remove_socket
;-----------------------------------------------
;
; Send a segment
;
; ebx = socket pointer
; dl = flags
;
;-----------------------------------------------
 
.ack:
push ebx
mov ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
call TCP_queue_ack
pop ebx
.send:
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
DEBUGF 1,"Preparing to send a segment\n"
 
xor edi, edi ; edi will contain the number of header option bytes
 
end if
;------------------------------------
; Send options with first SYN segment
 
test dl, TH_SYN
jz .no_options
 
align 4
stateTCB_ESTABLISHED:
mov eax, [ebx + TCP_SOCKET.ISS]
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
DEBUGF 1,"TCBStateHandler: Established\n"
test [ebx + TCP_SOCKET.t_flags], TF_NOOPT
jnz .no_options
 
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
mov eax, TCP_OPT_MAXSEG shl 24 + 4 shl 16
mov ax, 1280 ;;;;;;
bswap eax
DEBUGF 1,"RCV_NXT is set to:%u\n", eax
push eax
 
mov di, 4
 
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz .no_syn
 
test dl, TH_ACK
jnz .scale_opt
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz .no_syn
 
.scale_opt:
 
mov eax, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP
mov ah, byte [ebx + TCP_SOCKET.request_r_scale]
bswap eax
cmp eax, [edx + TCP_Packet.SequenceNumber]
jne .exit
push eax
 
; Calculate next sequencenumber
add_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT)
add di, 4
 
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jnz .fin
.no_syn:
 
.check_ack:
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
;------------------------------------
; Make the timestamp option if needed
 
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
test [ebx + TCP_SOCKET.t_flags], TF_REQ_TSTMP
jz .no_timestamp
 
; Now, see if we received any data
test dl, TH_RST
jnz .no_timestamp
 
test dl, TH_ACK
jz .timestamp
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
jz .no_timestamp
 
.timestamp:
 
DEBUGF 1,"Creating a timestamp\n"
 
push dword (TCP_OPT_TIMESTAMP shl 8 + 10 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24)
pushw 0
mov eax, [timer_ticks]
bswap eax
push eax
 
add di, 10
 
.no_timestamp:
 
;; TODO: check if we dont exceed the max segment size
 
.no_options:
add edi, TCP_segment.Data
 
;-----------------------------------
; Check if we have some data to send
 
;;; mov ecx, [huppeldepup]
 
test ecx, ecx
jz .exit
jz .no_data
 
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
;;; 278-316
 
add esp, 4
pop esi ; pointer to buffer
add esp, 4
jmp .header
 
sub edx, esi
mov edi, edx ; offset
mov eax, ebx ; socket ptr
.no_data:
 
call socket_internal_receiver ; Place the data from packet into socket
;;; 317-338
 
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
;----------
 
push di dx ebx
 
add ecx, edi ; total TCP segment size
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
mov ebx, [ebx + IP_SOCKET.LocalIP]
mov di , IP_PROTO_TCP
call IPv4_create_packet
 
;;;; jz .fail
 
push edx eax
call NET_send
ret
 
.fin: ; we received a FIN or RESET
; 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 !
;-------------------------------
; Now, create the 20-byte header
 
DEBUGF 1,"Removing a queued packet\n"
.header:
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
;-----------------------
; Fill in the TCP header
pop esi
 
.maybe_next:
add esi, tcp_out_queue_entry.size
loop .removeloop
push [esi + TCP_SOCKET.SND_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.SequenceNumber]
 
; Send an ACK to that fin, and enter closewait state
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSING
jmp .check_ack
push [esi + TCP_SOCKET.RCV_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.AckNumber]
 
push [esi + TCP_SOCKET.LocalPort]
rol word [esp], 8
pop [edi + TCP_segment.SourcePort]
 
align 4
stateTCB_FIN_WAIT_1:
push [esi + TCP_SOCKET.RemotePort]
rol word [esp], 8
pop [edi + TCP_segment.DestinationPort]
 
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
mov [edi + TCP_segment.Window], 0x0005
; 1280 bytes
mov [edi + TCP_segment.UrgentPointer], 0
 
cmp al, TH_ACK
jne @f
mov [edi + TCP_segment.DataOffset], 0x50
 
; It was an ACK
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_FIN_WAIT_2
jmp .exit
mov [edi + TCP_segment.Flags], cl
 
@@: 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
mov [edi + TCP_segment.Checksum], 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
 
;--------------
; Copy the data
 
pop esi
push edi
add edi, TCP_segment.Data ;;
sub ecx, TCP_segment.Data ;;;
 
align 4
stateTCB_FIN_WAIT_2:
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop edi
 
DEBUGF 1,"TCBStateHandler: Fin_wait_2\n"
;--------------------
; Create the checksum
 
test [edx + TCP_Packet.Flags], TH_FIN
jz .exit
push [ebx + IP_SOCKET.LocalIP]
push [ebx + IP_SOCKET.RemoteIP]
call TCP_checksum
 
; Change state, as we have a fin
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
;----------------
; Send the packet
 
; 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
 
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
mov esi, [ebx + ETH_DEVICE.transmit]
 
ret
 
 
 
;-------------------------
;
; TCP_outflags
;
; IN: eax = socket ptr
;
; OUT: edx = flags
;
;-------------------------
align 4
stateTCB_CLOSE_WAIT:
TCP_outflags:
 
DEBUGF 1,"TCBStateHandler: close_wait\n"
; Intentionally left empty
; socket_close_tcp handles this
mov edx, [eax + TCP_SOCKET.t_state]
movzx edx, byte [edx + .flaglist]
 
mov [ebx + SOCKET_head.lock], 0
DEBUGF 1,"TCP_outflags, socket: %x, flags: %x\n", eax, dl
 
ret
 
.flaglist:
 
db TH_RST + TH_ACK ; TCB_CLOSED
db 0 ; TCB_LISTEN
db TH_SYN ; TCB_SYN_SENT
db TH_SYN + TH_ACK ; TCB_SYN_RECEIVED
db TH_ACK ; TCB_ESTABLISHED
db TH_ACK ; TCB_CLOSE_WAIT
db TH_SYN + TH_ACK ; TCB_FIN_WAIT_1
db TH_SYN + TH_ACK ; TCB_CLOSING
db TH_SYN + TH_ACK ; TCB_LAST_ACK
db TH_ACK ; TCB_FIN_WAIT_2
db TH_ACK ; TCB_TIMED_WAIT
 
 
;-------------------------
;
; TCP_drop
;
; IN: eax = socket ptr
;
; OUT: /
;
;-------------------------
align 4
stateTCB_CLOSING:
TCP_drop:
 
DEBUGF 1,"TCBStateHandler: closingn\n"
DEBUGF 1,"TCP_drop\n"
 
; We can either receive an ACK of a fin, or a fin
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
; cmp [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
; jl .no_syn_received
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
mov [eax + TCP_SOCKET.t_state], TCB_CLOSED
 
.exit:
call TCP_output
 
mov [ebx + SOCKET_head.lock], 0
; .no_syn_received:
 
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
;---------------------------------------
;
; TCP_respond
;
; The easy way to send a RST/ACK segment
;
; IN: eax = socket ptr
;
; OUT: /
;
;---------------------------------------
align 4
TCP_respond:
 
; delete the socket
stdcall net_socket_free, ebx
DEBUGF 1,"TCP_respond\n"
 
.exit:
ret
 
 
 
;-----------------------------------------------------------------
;
; TCP_checksum
;
; This is the fast procedure to create or check a UDP header
; - To create a new checksum, the checksum field must be set to 0 before computation
; - To check an existing checksum, leave the checksum as is,
; and it will be 0 after this procedure, if it was correct
;
; IN: push source ip
; push dest ip
;
; esi = packet ptr
;
; OUT: checksum is filled in in packet! (but also in dx)
;
;-----------------------------------------------------------------
align 4
stateTCB_TIME_WAIT:
TCP_checksum:
 
DEBUGF 1,"TCBStateHandler: time_wait\n"
;-------------
; Pseudoheader
 
mov [ebx + SOCKET_head.lock], 0
; protocol type
mov edx, IP_PROTO_TCP ; NO shl 8 here ! (it took me ages to figure this one out)
 
ret
; source address
add dl, [esp+1+4]
adc dh, [esp+0+4]
adc dl, [esp+3+4]
adc dh, [esp+2+4]
 
; destination address
adc dl, [esp+1+8]
adc dh, [esp+0+8]
adc dl, [esp+3+8]
adc dh, [esp+2+8]
 
align 4
stateTCB_CLOSED:
; size
adc dl, cl
adc dh, ch
 
DEBUGF 1,"TCBStateHandler: closed\n"
;---------------------
; Real header and data
 
mov [ebx + SOCKET_head.lock], 0
push esi
call checksum_1
call checksum_2
pop esi
 
ret
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
 
ret 8 ; Remove the IPs from stack
 
 
 
 
;---------------------------------------------------------------------------
;
; TCP_API
1269,11 → 2007,11
ret
 
.packets_tx:
add eax, TCP_PACKETS_TX
add eax, TCP_segments_tx
mov eax, [eax]
ret
 
.packets_rx:
add eax, TCP_PACKETS_RX
add eax, TCP_segments_rx
mov eax, [eax]
ret