1,1176 → 1,224 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; TCP.INC ;; |
;; Part of the TCP/IP network stack for KolibriOS ;; |
;; ;; |
;; TCP Processes for Menuet OS TCP/IP stack ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; ;; |
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; |
;; Based on the code of 4.4BSD ;; |
;; ;; |
;; 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 ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
$Revision$ |
$Revision: 3406 $ |
|
; Socket states |
TCPS_CLOSED = 0 |
TCPS_LISTEN = 1 |
TCPS_SYN_SENT = 2 |
TCPS_SYN_RECEIVED = 3 |
TCPS_ESTABLISHED = 4 |
TCPS_CLOSE_WAIT = 5 |
TCPS_FIN_WAIT_1 = 6 |
TCPS_CLOSING = 7 |
TCPS_LAST_ACK = 8 |
TCPS_FIN_WAIT_2 = 9 |
TCPS_TIMED_WAIT = 10 |
|
; TCP TCB states |
TCB_LISTEN equ 1 |
TCB_SYN_SENT equ 2 |
TCB_SYN_RECEIVED equ 3 |
TCB_ESTABLISHED equ 4 |
TCB_FIN_WAIT_1 equ 5 |
TCB_FIN_WAIT_2 equ 6 |
TCB_CLOSE_WAIT equ 7 |
TCB_CLOSING equ 8 |
TCB_LAST_ACK equ 9 |
TCB_TIMED_WAIT equ 10 |
TCB_CLOSED equ 11 |
; Socket Flags |
TF_ACKNOW = 1 shl 0 ; ack peer immediately |
TF_DELACK = 1 shl 1 ; ack, but try to delay it |
TF_NODELAY = 1 shl 2 ; don't delay packets to coalesce |
TF_NOOPT = 1 shl 3 ; don't use tcp options |
TF_SENTFIN = 1 shl 4 ; have sent FIN |
TF_REQ_SCALE = 1 shl 5 ; have/will request window scaling |
TF_RCVD_SCALE = 1 shl 6 ; other side has requested scaling |
TF_REQ_TSTMP = 1 shl 7 ; have/will request timestamps |
TF_RCVD_TSTMP = 1 shl 8 ; a timestamp was received in SYN |
TF_SACK_PERMIT = 1 shl 9 ; other side said I could SACK |
|
TH_FIN = 0x01 |
TH_SYN = 0x02 |
TH_RST = 0x04 |
TH_PUSH = 0x08 |
TH_ACK = 0x10 |
TH_URG = 0x20 |
; Segment flags |
TH_FIN = 1 shl 0 |
TH_SYN = 1 shl 1 |
TH_RST = 1 shl 2 |
TH_PUSH = 1 shl 3 |
TH_ACK = 1 shl 4 |
TH_URG = 1 shl 5 |
|
TWOMSL equ 10 ; # of secs to wait before closing socket |
; Segment header options |
TCP_OPT_EOL = 0 ; End of option list. |
TCP_OPT_NOP = 1 ; No-Operation. |
TCP_OPT_MAXSEG = 2 ; Maximum Segment Size. |
TCP_OPT_WINDOW = 3 ; window scale |
TCP_OPT_SACK_PERMIT = 4 ; Selective Acknowledgement |
TCP_OPT_SACK = 5 |
TCP_OPT_TIMESTAMP = 8 |
|
TCP_RETRIES equ 5 ; Number of times to resend a packet |
TCP_TIMEOUT equ 20 ; resend if not replied to in x hs |
; Fundamental timer values |
TCP_time_MSL = 47 ; max segment lifetime (30s) |
TCP_time_re_min = 2 ; min retransmission (1,28s) |
TCP_time_re_max = 100 ; max retransmission (64s) |
TCP_time_pers_min = 8 ; min persist (5,12s) |
TCP_time_pers_max = 94 ; max persist (60,16s) |
TCP_time_keep_init = 118 ; connection establishment (75,52s) |
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h) |
TCP_time_keep_interval = 118 ; between probes when no response (75,52s) |
TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s) |
TCP_time_srtt_default = 0 ; |
TCP_time_max_idle = 8*TCP_time_keep_interval ; FIXME |
|
;******************************************************************* |
; 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 |
; |
;******************************************************************* |
; timer constants |
TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK |
TCP_max_keepcnt = 8 ; max keepalive probes |
|
|
; TCP Payload ( Data field in IP datagram ) |
; |
; 0 1 2 3 |
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;20 | Source Port | Destination Port | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;24 | Sequence Number | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;28 | Acknowledgment Number | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;32 | Data | |U|A|P|R|S|F| | |
; | Offset| Reserved |R|C|S|S|Y|I| Window | |
; | | |G|K|H|T|N|N| | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;36 | Checksum | Urgent Pointer | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
;40 | Options | Padding | |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
; | data |
TCP_max_winshift = 14 |
TCP_max_win = 65535 |
|
TCP_re_xmit_thresh = 3 |
|
struc TCP_PACKET |
{ .SourcePort dw ? ;+00 |
.DestinationPort dw ? ;+02 |
.SequenceNumber dd ? ;+04 |
.AckNumber dd ? ;+08 |
.DataOffset db ? ;+12 - DataOffset[0-3 bits] and Reserved[4-7] |
.Flags db ? ;+13 - Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN |
.Window dw ? ;+14 |
.Checksum dw ? ;+16 |
.UrgentPointer dw ? ;+18 |
.Options rb 3 ;+20 |
.Padding db ? ;+23 |
.Data db ? ;+24 |
} |
TCP_mss_default = 1480 ; default max segment size |
|
virtual at 0 |
TCP_PACKET TCP_PACKET |
end virtual |
; smoothed round trip time and estimated variance are stored as fixed point numbers, |
; shifted by the value below. |
; With these scales, srtt has 3 bits to the right of the binary point, and thus an "alpha" |
; of .875. rttvar has 2 bits to the right and thus "alpha" of 0.75 |
TCP_RTT_SHIFT = 3 |
TCP_RTTVAR_SHIFT = 2 |
|
; bits used by tcp_input and tcp_output |
TCP_BIT_NEEDOUTPUT = 1 shl 0 |
TCP_BIT_TIMESTAMP = 1 shl 1 |
TCP_BIT_DROPSOCKET = 1 shl 2 |
|
TCP_BIT_SENDALOT = 1 shl 0 |
|
;*************************************************************************** |
; Function |
; tcp_tcb_handler |
; |
; Description |
; Handles sockets in the timewait state, closing them |
; when the TCB timer expires |
; |
;*************************************************************************** |
TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds |
|
proc tcp_tcb_handler stdcall uses ebx |
; scan through all the sockets, decrementing active timers |
TCP_QUEUE_SIZE = 50 |
|
mov ebx, net_sockets |
struct TCP_header |
|
cmp [ebx + SOCKET.NextPtr], 0 |
je .exit |
;DEBUGF 1, "K : sockets:\n" |
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 ? |
|
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .exit |
ends |
|
;DEBUGF 1, "K : %x-%x: %x-%x-%x-%u\n", [ebx + SOCKET.PID]:2, [ebx + SOCKET.Number]:2, [ebx + SOCKET.LocalPort]:4, [ebx + SOCKET.RemoteIP], [ebx + SOCKET.RemotePort]:4, [ebx + SOCKET.TCBState] |
struct TCP_queue_entry |
|
cmp [ebx + SOCKET.TCBTimer], 0 |
jne .decrement_tcb |
cmp [ebx + SOCKET.wndsizeTimer], 0 |
jne .decrement_wnd |
jmp .next_socket |
ip_ptr dd ? |
segment_ptr dd ? |
segment_size dd ? |
device_ptr dd ? |
|
.decrement_tcb: |
; decrement it, delete socket if TCB timer = 0 & socket in timewait state |
dec [ebx + SOCKET.TCBTimer] |
jnz .next_socket |
buffer_ptr dd ? |
timestamp dd ? |
|
cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
jne .next_socket |
ends |
|
push [ebx + SOCKET.PrevPtr] |
stdcall net_socket_free, ebx |
pop ebx |
jmp .next_socket |
align 4 |
uglobal |
TCP_segments_tx rd MAX_NET_DEVICES |
TCP_segments_rx rd MAX_NET_DEVICES |
TCP_segments_missed rd MAX_NET_DEVICES |
TCP_segments_dumped rd MAX_NET_DEVICES |
; TCP_bytes_rx rq MAX_NET_DEVICES |
; TCP_bytes_tx rq MAX_NET_DEVICES |
TCP_sequence_num dd ? |
TCP_queue rd TCP_QUEUE_SIZE*sizeof.TCP_queue_entry/4 |
TCP_input_event dd ? |
endg |
|
.decrement_wnd: |
; TODO - prove it works! |
dec [ebx + SOCKET.wndsizeTimer] |
jmp .next_socket |
|
.exit: |
ret |
endp |
|
|
;*************************************************************************** |
; Function |
; tcp_tx_handler |
;----------------------------------------------------------------- |
; |
; Description |
; Handles queued TCP data |
; This is a kernel function, called by stack_handler |
; TCP_init |
; |
;*************************************************************************** |
; This function resets all TCP variables |
; |
;----------------------------------------------------------------- |
macro TCP_init { |
|
proc tcp_tx_handler stdcall |
; 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 |
xor eax, eax |
mov edi, TCP_segments_tx |
mov ecx, (6*MAX_NET_DEVICES) |
rep stosd |
|
mov esi, resendQ |
mov ecx, 0 |
pseudo_random eax |
mov [TCP_sequence_num], eax |
|
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .exit ; None left |
cmp dword[esi + 4], 0 |
jne @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
init_queue TCP_queue |
|
@@: ; we have one. decrement it's timer by 1 |
dec word[esi + 2] |
jz @f |
inc ecx |
add esi, 8 |
jmp .next_resendq ; Timer not zero, so move on |
|
@@: |
xor ebx, ebx |
; 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] |
jnz @f |
|
; retries now 0, so delete from queue |
xchg [esi + 4], ebx |
|
@@: ; resend packet |
pushad |
|
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
jne .tth004z |
|
; TODO - try again in 10ms. |
test ebx, ebx |
jnz @f |
mov [esi + 4], ebx |
|
@@: ; Mark it to expire in 10ms - 1 tick |
mov byte[esi + 1], 1 |
mov word[esi + 2], 1 |
jmp .tth005 |
|
.tth004z: |
; we have a buffer # in ax |
push eax ecx |
mov ecx, IPBUFFSIZE |
mul ecx |
add eax, IPbuffs |
|
; we have the buffer address in eax |
mov edi, eax |
pop ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
imul esi, ecx, IPBUFFSIZE |
add esi, resendBuffer |
|
; we have resend buffer location in esi |
mov ecx, IPBUFFSIZE |
|
; copy data across |
push edi |
cld |
rep movsb |
pop edi |
|
; queue packet |
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
cmp edx, [edi + IP_PACKET.DestinationAddress] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
push 1 |
pop ebx |
call queue |
mov ecx, TCP_process_input |
call new_sys_threads |
|
.tth005: |
popad |
} |
|
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
.exit: |
ret |
endp |
include 'tcp_timer.inc' |
include 'tcp_subr.inc' |
include 'tcp_usreq.inc' |
include 'tcp_input.inc' |
include 'tcp_output.inc' |
|
|
;*************************************************************************** |
; 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_API |
; |
;*************************************************************************** |
|
proc tcp_rx stdcall uses ebx |
; 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 ebx, net_sockets |
|
.next_socket.1: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.1.exit |
|
; DEBUGF 1, "K : tcp_rx - 1.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
|
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; get the dest. port from the TCP hdr |
jne .next_socket.1 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 1.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP] |
|
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP |
jne .next_socket.1 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 1.sport: %x - %x\n", [edx + 20 + TCP_PACKET.SourcePort]:4, [ebx + SOCKET.RemotePort]:4 |
|
mov ax, [edx + 20 + TCP_PACKET.SourcePort] ; get the source port from the TCP hdr |
cmp [ebx + SOCKET.RemotePort], ax ; compare with socket's remote port |
jne .next_socket.1 ; different - try next socket |
|
; We have a complete match - use this socket |
jmp .change_state |
|
.next_socket.1.exit: |
|
; 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 ebx, net_sockets |
|
.next_socket.2: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.2.exit |
|
; DEBUGF 1, "K : tcp_rx - 2.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
|
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.2 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 2.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP] |
|
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP |
jne .next_socket.2 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 2.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
|
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.2 ; different - try next socket |
|
; We have a complete match - use this socket |
jmp .change_state |
|
.next_socket.2.exit: |
|
; 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 ebx, net_sockets |
|
.next_socket.3: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.3.exit |
|
; DEBUGF 1, "K : tcp_rx - 3.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
|
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get destination port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.3 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 3.addr: 00000000 - %x\n", [ebx + SOCKET.RemoteIP] |
|
cmp [ebx + SOCKET.RemoteIP], 0 ; only match a socket remote IP of 0 |
jne .next_socket.3 ; different - try next socket |
|
; DEBUGF 1, "K : tcp_rx - 3.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
|
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.3 ; different - try next socket |
|
; We have a complete match - use this socket |
jmp .change_state |
|
.next_socket.3.exit: |
|
; If we got here, we need to reject the packet |
|
DEBUGF 1, "K : tcp_rx - dumped\n" |
DEBUGF 1, "K : --------: %x-%x-%x (flags: %x)\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [edx + IP_PACKET.SourceAddress], [edx + 20 + TCP_PACKET.SourcePort]:4, [edx + 20 + TCP_PACKET.Flags]:2 |
|
inc [dumped_rx_count] |
jmp .exit |
|
.change_state: |
|
; We have a valid socket/TCB, so call the TCB State Machine for that skt. |
; socket is pointed to by ebx |
; IP packet is pointed to by edx |
; IP buffer number is on stack ( it will be popped at the end) |
|
stdcall tcpStateMachine, ebx |
|
.exit: |
pop eax |
call freeBuff |
ret |
endp |
|
|
;*************************************************************************** |
; Function |
; buildTCPPacket |
; This function is called by system function 76 |
; |
; 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 |
; IN: subfunction number in bl |
; device number in bh |
; ecx, edx, .. depends on subfunction |
; |
;*************************************************************************** |
|
proc build_tcp_packet stdcall, sockAddr:DWORD |
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 + 20 + TCP_PACKET.Flags], bl ; TCP flags |
|
mov ebx, [sockAddr] |
|
; 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 + SOCKET.LocalIP] |
mov [edx + IP_PACKET.SourceAddress], eax |
mov eax, [ebx + SOCKET.RemoteIP] |
mov [edx + IP_PACKET.DestinationAddress], eax |
|
mov [edx + IP_PACKET.VersionAndIHL], 0x45 |
mov [edx + IP_PACKET.TypeOfService], 0 |
|
pop eax ; Get the TCP data length |
push eax |
|
add eax, 20 + 20 ; add IP header and TCP header lengths |
rol ax, 8 |
mov [edx + IP_PACKET.TotalLength], ax |
mov [edx + IP_PACKET.Identification], 0 |
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040 |
mov [edx + IP_PACKET.TimeToLive], 0x20 |
mov [edx + IP_PACKET.Protocol], PROTOCOL_TCP |
|
; Checksum left unfilled |
mov [edx + IP_PACKET.HeaderChecksum], 0 |
|
; Fill in the TCP header (some data is in the socket descriptor) |
mov ax, [ebx + SOCKET.LocalPort] |
mov [edx + 20 + TCP_PACKET.SourcePort], ax ; Local Port |
|
mov ax, [ebx + SOCKET.RemotePort] |
mov [edx + 20 + TCP_PACKET.DestinationPort], ax ; desitination Port |
|
; Checksum left unfilled |
mov [edx + 20 + TCP_PACKET.Checksum], 0 |
|
; sequence number |
mov eax, [ebx + SOCKET.SND_NXT] |
mov [edx + 20 + TCP_PACKET.SequenceNumber], eax |
|
; ack number |
mov eax, [ebx + SOCKET.RCV_NXT] |
mov [edx + 20 + TCP_PACKET.AckNumber], eax |
|
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size) |
; 768 bytes seems better |
mov [edx + 20 + TCP_PACKET.Window], 0x0003 |
|
; Urgent pointer (0) |
mov [edx + 20 + TCP_PACKET.UrgentPointer], 0 |
|
; data offset ( 0x50 ) |
mov [edx + 20 + TCP_PACKET.DataOffset], 0x50 |
|
pop ecx ; count of bytes to send |
mov ebx, ecx ; need the length later |
|
cmp ebx, 0 |
jz @f |
|
mov edi, edx |
add edi, 40 |
cld |
rep movsb ; copy the data across |
|
@@: ; we have edx as IPbuffer ptr. |
; Fill in the TCP checksum |
; First, fill in pseudoheader |
mov eax, [edx + IP_PACKET.SourceAddress] |
mov [pseudoHeader], eax |
mov eax, [edx + IP_PACKET.DestinationAddress] |
mov [pseudoHeader + 4], eax |
mov word[pseudoHeader + 8], PROTOCOL_TCP shl 8 + 0 |
add ebx, 20 |
mov [pseudoHeader + 10], bh |
mov [pseudoHeader + 11], bl |
|
mov eax, pseudoHeader |
mov [checkAdd1], eax |
mov word[checkSize1], 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] |
rol ax, 8 |
mov [edx + 20 + TCP_PACKET.Checksum], ax |
|
; Fill in the IP header checksum |
GET_IHL eax, edx ; get IP-Header length |
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size |
rol ax, 8 |
mov [edx + IP_PACKET.HeaderChecksum], ax |
|
ret |
endp |
|
|
; Increments the 32 bit value pointed to by esi in internet order |
proc inc_inet_esi stdcall |
push eax |
mov eax, [esi] |
bswap eax |
inc eax |
bswap eax |
mov [esi], eax |
pop eax |
ret |
endp |
|
|
; Increments the 32 bit value pointed to by esi in internet order |
; by the value in ecx |
proc add_inet_esi stdcall |
push eax |
mov eax, [esi] |
bswap eax |
add eax, ecx |
bswap eax |
mov [esi], eax |
pop eax |
ret |
endp |
|
|
iglobal |
TCBStateHandler dd \ |
stateTCB_LISTEN, \ |
stateTCB_SYN_SENT, \ |
stateTCB_SYN_RECEIVED, \ |
stateTCB_ESTABLISHED, \ |
stateTCB_FIN_WAIT_1, \ |
stateTCB_FIN_WAIT_2, \ |
stateTCB_CLOSE_WAIT, \ |
stateTCB_CLOSING, \ |
stateTCB_LAST_ACK, \ |
stateTCB_TIME_WAIT, \ |
stateTCB_CLOSED |
endg |
|
|
;*************************************************************************** |
; Function |
; tcpStateMachine |
; OUT: |
; |
; Description |
; TCP state machine |
; This is a kernel function, called by tcp_rx |
; |
; IP buffer address given in edx |
; Socket/TCB address in ebx |
; |
; The IP buffer will be released by the caller |
;*************************************************************************** |
;--------------------------------------------------------------------------- |
align 4 |
TCP_api: |
|
proc tcpStateMachine stdcall, sockAddr:DWORD |
; as a packet has been received, update the TCB timer |
mov [ebx + SOCKET.TCBTimer], TWOMSL |
movzx eax, bh |
shl eax, 2 |
|
; If the received packet has an ACK bit set, |
; remove any packets in the resend queue that this |
; received packet acknowledges |
pushad |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .call_handler ; No ACK, so no data yet |
test bl, bl |
jz .packets_tx ; 0 |
dec bl |
jz .packets_rx ; 1 |
dec bl |
jz .packets_missed ; 2 |
dec bl |
jz .packets_dumped ; 3 |
|
; get skt number in eax |
stdcall net_socket_addr_to_num, ebx |
|
; The ack number is in [edx + 28], inet format |
; skt in eax |
|
mov esi, resendQ |
xor ecx, ecx |
|
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .call_handler ; None left |
cmp [esi + 4], eax |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
@@: ; Can we delete this buffer? |
|
; If yes, goto @@. No, goto .next_resendq |
; Get packet data address |
|
push ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
imul edi, ecx, IPBUFFSIZE |
add edi, resendBuffer |
|
; we have dest buffer location in edi. incoming packet in edx. |
; Get this packets sequence number |
; preserve al, ecx, esi, edx |
mov ecx, [edi + 20 + TCP_PACKET.SequenceNumber] |
bswap ecx |
movzx ebx, word[edi + 2] |
xchg bl, bh |
sub ebx, 40 |
add ecx, ebx ; ecx is now seq# of last byte +1, intel format |
|
; get recievd ack #, in intel format |
mov ebx, [edx + 20 + TCP_PACKET.AckNumber] |
bswap ebx |
|
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 @f ; if rx > old, delete old |
|
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
@@: |
mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
.call_handler: |
popad |
|
; Call handler for given TCB state |
|
mov eax, [ebx + SOCKET.TCBState] |
cmp eax, TCB_LISTEN |
jb .exit |
cmp eax, TCB_CLOSED |
ja .exit |
|
stdcall [TCBStateHandler + (eax - 1) * 4], [sockAddr] |
|
.exit: |
.error: |
mov eax, -1 |
ret |
endp |
|
;*************************************************************************** |
; Function |
; signal_network_event |
; |
; Description |
; Signals about network event to socket owner |
; This is a kernel function, called from TCP handler |
; |
; Socket/TCB address in ebx |
;*************************************************************************** |
proc signal_network_event |
push ecx esi eax |
mov eax, [ebx + SOCKET.PID] |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
|
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
|
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event |
pop eax esi ecx |
.packets_tx: |
mov eax, [TCP_segments_tx + eax] |
ret |
endp |
|
proc stateTCB_LISTEN stdcall, sockAddr:DWORD |
; In this case, we are expecting a SYN packet |
; For now, if the packet is a SYN, process it, and send a response |
; If not, ignore it |
|
; Look at control flags |
test [edx + 20 + TCP_PACKET.Flags], TH_SYN |
jz .exit |
|
; We have a SYN. update the socket with this IP packets details, |
; And send a response |
|
mov eax, [edx + IP_PACKET.SourceAddress] |
mov [ebx + SOCKET.RemoteIP], eax |
mov ax, [edx + 20 + TCP_PACKET.SourcePort] |
mov [ebx + SOCKET.RemotePort], ax |
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
mov [ebx + SOCKET.IRS], eax |
mov [ebx + SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi ; RCV.NXT |
mov eax, [ebx + SOCKET.ISS] |
mov [ebx + SOCKET.SND_NXT], eax |
|
; Now construct the response, and queue for sending by IP |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
|
push ebx |
push eax |
mov bl, TH_SYN + TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
|
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
; Send it. |
pop ebx |
call queue |
|
pop ebx |
mov esi, [sockAddr] |
mov [esi + SOCKET.TCBState], TCB_SYN_RECEIVED |
call signal_network_event |
|
; increment SND.NXT in socket |
add esi, SOCKET.SND_NXT |
call inc_inet_esi |
|
.exit: |
.packets_rx: |
mov eax, [TCP_segments_rx + eax] |
ret |
endp |
|
|
proc stateTCB_SYN_SENT stdcall, sockAddr:DWORD |
; We are awaiting an ACK to our SYN, with a SYM |
; Look at control flags - expecting an ACK |
|
mov al, [edx + 20 + TCP_PACKET.Flags] |
and al, TH_SYN + TH_ACK |
cmp al, TH_SYN + TH_ACK |
je .syn_ack |
|
test al, TH_SYN |
jz .exit |
|
mov [ebx + SOCKET.TCBState], TCB_SYN_RECEIVED |
push TH_SYN + TH_ACK |
jmp .send |
|
.syn_ack: |
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
push TH_ACK |
|
.send: |
call signal_network_event |
; Store the recv.nxt field |
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
|
; Update our recv.nxt field |
mov [ebx + SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET.RCV_NXT] |
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 |
pop ebx |
je .exit |
|
push eax |
|
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
|
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
; Send it. |
pop ebx |
call queue |
|
.exit: |
.packets_missed: |
mov eax, [TCP_segments_missed + eax] |
ret |
endp |
|
|
proc stateTCB_SYN_RECEIVED stdcall, sockAddr:DWORD |
; In this case, we are expecting an ACK packet |
; For now, if the packet is an ACK, process it, |
; If not, ignore it |
|
test [edx + 20 + TCP_PACKET.Flags], TH_RST |
jz .check_ack |
|
push [ebx + SOCKET.OrigRemotePort] [ebx + SOCKET.OrigRemoteIP] |
pop [ebx + SOCKET.RemoteIP] [ebx + SOCKET.RemotePort] |
|
mov [ebx + SOCKET.TCBState], TCB_LISTEN |
jmp .signal |
|
.check_ack: |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
|
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
.signal: |
call signal_network_event |
|
.exit: |
.packets_dumped: |
mov eax, [TCP_segments_dumped + eax] |
ret |
endp |
|
|
proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD |
; Here we are expecting data, or a request to close |
; OR both... |
|
; Ignore all packets with sequnce number other than next expected |
|
; recv.nxt is in dword [edx+24], in inet format |
; recv seq is in [sktAddr]+56, in inet format |
; just do a comparision |
mov eax, [ebx + SOCKET.RCV_NXT] |
cmp eax, [edx + 20 + TCP_PACKET.SequenceNumber] |
jne .exit |
|
; Did we receive a FIN or RST? |
test [edx + 20 + TCP_PACKET.Flags], TH_FIN+TH_RST |
jz .check_ack |
|
; It was a fin or reset. |
|
; Remove resend entries from the queue - I dont want to send any more data |
pushad |
|
; get skt # |
stdcall net_socket_addr_to_num, ebx |
|
mov esi, resendQ |
mov ecx, 0 |
|
.next_resendq: |
cmp ecx, NUMRESENDENTRIES |
je .last_resendq ; None left |
cmp [esi + 4], eax |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
@@: |
mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
|
.last_resendq: |
popad |
|
@@: ; Send an ACK to that fin, and enter closewait state |
|
mov [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
test [edx + 20 + TCP_PACKET.Flags], TH_RST |
je @f |
mov [ebx + SOCKET.TCBState], TCB_CLOSED |
@@: |
call signal_network_event |
lea esi, [ebx + SOCKET.RCV_NXT] |
mov eax, [esi] ; save original |
call inc_inet_esi |
;; jmp ste_ack - NO, there may be data |
|
.check_ack: |
; Check that we received an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .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 cx, [edx + 20 + TCP_PACKET.Window] |
xchg cl, ch |
cmp cx, 1024 |
ja @f |
|
mov [ebx + SOCKET.wndsizeTimer], 1 |
|
@@: ; OK, here is the deal |
|
|
; Read the data bytes, store in socket buffer |
movzx ecx, [edx + IP_PACKET.TotalLength] |
xchg cl, ch |
sub ecx, 40 ; Discard 40 bytes of header |
ja .data ; Read data, if any |
|
; If we had received a fin, we need to ACK it. |
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
je .ack |
jmp .exit |
|
.data: |
push ecx |
push ecx edx |
lea ecx, [ebx+SOCKET.mutex] |
call mutex_lock |
pop edx ecx |
|
push ebx |
mov eax, [ebx + SOCKET.rxDataCount] |
add eax, ecx |
cmp eax, SOCKETBUFFSIZE - SOCKETHEADERSIZE |
ja .overflow |
|
mov [ebx + SOCKET.rxDataCount], eax ; increment the count of bytes in buffer |
|
; point to the location to store the data |
lea edi, [ebx + eax + SOCKETHEADERSIZE] |
sub edi, ecx |
|
add edx, 40 ; edx now points to the data |
mov esi, edx |
|
cld |
rep movsb ; copy the data across |
|
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
|
; flag an event to the application |
pop ebx |
call signal_network_event |
|
pop ecx |
|
; Update our recv.nxt field |
lea esi, [ebx + SOCKET.RCV_NXT] |
call add_inet_esi |
|
.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 .exit |
|
push eax |
|
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
|
mov eax, NET1OUT_QUEUE |
|
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
; Send it. |
pop ebx |
call queue |
|
.exit: |
ret |
.overflow: |
; no place in buffer |
; so simply restore stack and exit |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax ecx |
ret |
endp |
|
|
proc stateTCB_FIN_WAIT_1 stdcall, sockAddr:DWORD |
; We can either receive an ACK of a fin, or a fin |
mov al, [edx + 20 + TCP_PACKET.Flags] |
and al, TH_FIN + TH_ACK |
|
cmp al, TH_ACK |
jne @f |
|
; It was an ACK |
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_2 |
jmp .exit |
|
@@: |
mov [ebx + SOCKET.TCBState], TCB_CLOSING |
cmp al, TH_FIN |
je @f |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
|
@@: |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
|
; Send an ACK |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
|
push eax |
|
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
|
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
; Send it. |
pop ebx |
call queue |
|
.exit: |
ret |
endp |
|
|
proc stateTCB_FIN_WAIT_2 stdcall, sockAddr:DWORD |
test [edx + 20 + TCP_PACKET.Flags], TH_FIN |
jz .exit |
|
; Change state, as we have a fin |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
|
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
|
; Send an ACK |
mov eax, EMPTY_QUEUE |
call dequeue |
cmp ax, NO_BUFFER |
je .exit |
|
push eax |
|
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
stdcall build_tcp_packet, [sockAddr] |
|
mov eax, NET1OUT_QUEUE |
mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
mov eax, IPIN_QUEUE |
|
.not_local: |
; Send it. |
pop ebx |
call queue |
|
.exit: |
ret |
endp |
|
|
proc stateTCB_CLOSE_WAIT stdcall, sockAddr:DWORD |
; Intentionally left empty |
; socket_close_tcp handles this |
ret |
endp |
|
|
proc stateTCB_CLOSING stdcall, sockAddr:DWORD |
; We can either receive an ACK of a fin, or a fin |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
|
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
|
.exit: |
ret |
endp |
|
|
proc stateTCB_LAST_ACK stdcall, sockAddr:DWORD |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_PACKET.Flags], TH_ACK |
jz .exit |
|
; delete the socket |
stdcall net_socket_free, ebx |
|
.exit: |
ret |
endp |
|
|
proc stateTCB_TIME_WAIT stdcall, sockAddr:DWORD |
ret |
endp |
|
|
proc stateTCB_CLOSED stdcall, sockAddr:DWORD |
ret |
endp |
Property changes: |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |