Beginning implementation of timestamps and Round Trip Time in TCP.

git-svn-id: svn://kolibrios.org@2937 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
hidnplayr 2012-08-27 13:55:15 +00:00
parent 7957e16d02
commit ead0b600bf
5 changed files with 357 additions and 192 deletions

View File

@ -75,7 +75,7 @@ struct TCP_SOCKET IP_SOCKET
SND_WND dd ? ; send window SND_WND dd ? ; send window
; receive sequence ; receive sequence
RCV_WND dw ? ; receive window RCV_WND dw ? ; receive window ; FIXME: better use a dword?
RCV_NXT dd ? ; next receive sequence number to use RCV_NXT dd ? ; next receive sequence number to use
RCV_UP dd ? ; urgent pointer RCV_UP dd ? ; urgent pointer
IRS dd ? ; initial receive sequence number IRS dd ? ; initial receive sequence number
@ -132,8 +132,9 @@ struct TCP_SOCKET IP_SOCKET
; extra ; extra
sendalot db ? ; also used as 'need output'
ts_ecr dd ? ; timestamp echo reply ts_ecr dd ? ; timestamp echo reply
ts_val dd ?
temp_bits db ?
ends ends

View File

@ -67,6 +67,7 @@ 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_keep_interval = 118 ; between probes when no response (75,52s)
TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s) TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s)
TCP_time_srtt_default = 0 ; TCP_time_srtt_default = 0 ;
TCP_time_max_idle = 8*TCP_time_keep_interval ; FIXME
; timer constants ; timer constants
TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK
@ -80,6 +81,20 @@ TCP_re_xmit_thresh = 3
TCP_mss_default = 1480 ; default max segment size TCP_mss_default = 1480 ; default max segment size
; 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
struct TCP_header struct TCP_header
SourcePort dw ? SourcePort dw ?

View File

@ -37,7 +37,7 @@ TCP_input:
DEBUGF 1,"TCP_input: size=%u\n", ecx DEBUGF 1,"TCP_input: size=%u\n", ecx
; First, record the current time ; First, record the current time
mov eax, [timer_ticks] mov eax, [timer_ticks] ; in 1/100 seconds
mov [esp+4], eax mov [esp+4], eax
; then, re-calculate the checksum (if not already done by hw) ; then, re-calculate the checksum (if not already done by hw)
@ -48,7 +48,7 @@ TCP_input:
pushw [esi + TCP_header.Checksum] pushw [esi + TCP_header.Checksum]
mov [esi + TCP_header.Checksum], 0 mov [esi + TCP_header.Checksum], 0
TCP_checksum (edi), (edi+4) TCP_checksum (edi), (edi+4)
pop cx ; previous checksum pop cx ; previous checksum
cmp cx, dx cmp cx, dx
pop edx ecx pop edx ecx
jne .drop_no_socket jne .drop_no_socket
@ -83,6 +83,7 @@ TCP_input:
; (IP Packet SenderAddress = 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) ; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0)
.findpcb:
mov ebx, net_sockets mov ebx, net_sockets
mov si, [edx + TCP_header.DestinationPort] mov si, [edx + TCP_header.DestinationPort]
@ -124,7 +125,7 @@ TCP_input:
; Check if socket isnt closed ; Check if socket isnt closed
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSED cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
je .drop_not_locked je .drop_no_socket
;---------------- ;----------------
; Lock the socket ; Lock the socket
@ -136,10 +137,10 @@ TCP_input:
DEBUGF 1,"TCP_input: socket locked\n" DEBUGF 1,"TCP_input: socket locked\n"
;---------------------- ;---------------------------
; set need_output to 0 ; disable all temporary bits
mov [ebx + TCP_SOCKET.sendalot], 0 mov [ebx + TCP_SOCKET.temp_bits], 0
;--------------------------------------- ;---------------------------------------
; unscale the window into a 32 bit value ; unscale the window into a 32 bit value
@ -197,7 +198,7 @@ TCP_input:
cmp byte [esi], TCP_OPT_TIMESTAMP cmp byte [esi], TCP_OPT_TIMESTAMP
je .opt_timestamp je .opt_timestamp
jmp .no_options ; If we reach here, some unknown options were received, skip them all! jmp .no_options ; If we reach here, some unknown options were received, skip them all!
.opt_nop: .opt_nop:
inc esi inc esi
@ -205,7 +206,7 @@ TCP_input:
.opt_maxseg: .opt_maxseg:
cmp byte [esi+1], 4 cmp byte [esi+1], 4
jne .no_options ; error occured, ignore all options! jne .no_options ; error occured, ignore all options!
test [edx + TCP_header.Flags], TH_SYN test [edx + TCP_header.Flags], TH_SYN
jz @f jz @f
@ -237,16 +238,16 @@ TCP_input:
.opt_timestamp: .opt_timestamp:
cmp byte [esi+1], 10 ; length must be 10 cmp byte [esi+1], 10 ; length must be 10
jne .no_options jne .no_options
DEBUGF 1,"TCP_input: Got timestamp option\n" DEBUGF 1,"TCP_input: Got timestamp option\n"
push dword [esi + 2] ; timestamp push dword [esi + 2] ; timestamp
pop [ebx + TCP_SOCKET.ts_recent] pop [ebx + TCP_SOCKET.ts_val]
push dword [esi + 6] ; timestamp echo reply
push dword [esi + 6] ; timestamp echo reply
pop [ebx + TCP_SOCKET.ts_ecr] pop [ebx + TCP_SOCKET.ts_ecr]
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
add esi, 10 add esi, 10
jmp .opt_loop jmp .opt_loop
@ -327,16 +328,24 @@ TCP_input:
; Update RTT estimators ; Update RTT estimators
; if ts_present test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
; mov eax, [esp + 4] ; timestamp when this segment was received jz .no_timestamp_rtt
; sub eax, [ebx + TCP_SOCKET.ts_ecr] mov eax, [esp + 4] ; timestamp when this segment was received
; inc eax sub eax, [ebx + TCP_SOCKET.ts_ecr]
; call TCP_xmit_timer inc eax
call TCP_xmit_timer
jmp .rtt_done
; else if (t_rtt && SEG_GT(ti_ack - t_rtsec)) .no_timestamp_rtt:
; mov eax, [ebx + t_rtt] cmp [ebx + TCP_SOCKET.t_rtt], 0
; call TCP_xmit_timer je .rtt_done
; end if mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done
mov eax, [ebx + TCP_SOCKET.t_rtt]
call TCP_xmit_timer
.rtt_done:
; update window pointers ; update window pointers
mov eax, [edx + TCP_header.AckNumber] mov eax, [edx + TCP_header.AckNumber]
@ -357,7 +366,7 @@ TCP_input:
; Generate more output ; Generate more output
call TCP_output call TCP_output
jmp .drop_not_locked jmp .drop_no_socket
;------------------------------------------------- ;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then.. ; maybe we are the receiver in the uni-xfer then..
@ -408,15 +417,17 @@ TCP_input:
; Calculate receive window size ; Calculate receive window size
; mov eax, [ebx + STREAM_SOCKET.rcv.size] mov eax, SOCKETBUFFSIZE
; neg eax sub eax, [ebx + STREAM_SOCKET.rcv.size]
; add eax, SOCKETBUFFSIZE mov edx, [ebx + TCP_SOCKET.RCV_ADV]
; mov edx, [ebx + TCP_SOCKET.RCV_ADV] sub edx, [ebx + TCP_SOCKET.RCV_NXT]
; sub edx, [ebx + TCP_SOCKET.RCV_NXT] cmp eax, edx
; cmp eax, edx ja @f
; jae @f mov eax, edx
; mov eax, edx @@:
; @@: mov [ebx + TCP_SOCKET.RCV_WND], ax
; If listen or Syn sent, go to that specific code right away
cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN
je .LISTEN je .LISTEN
@ -473,68 +484,34 @@ TCP_input:
dec eax dec eax
.no_dup_syn: .no_dup_syn:
; eax holds number of bytes to drop ; Check for entire duplicate segment
cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size
; Check for entire duplicate packet jb .duplicate
jnz @f
cmp eax, ecx
jae .duplicate
DEBUGF 1,"TCP_input: Going to drop %u out of %u bytes\n", eax, ecx
;;; TODO: apply figure 28.30
; Check for duplicate FIN
test [edx + TCP_header.Flags], TH_FIN test [edx + TCP_header.Flags], TH_FIN
jz .no_fin2 jnz .duplicate
inc ecx @@:
cmp eax, ecx
jne @f
mov eax, ecx ; Any valid FIN must be to the left of the window.
; At this point the FIN must be out of sequence or a duplicate, drop it
and [edx + TCP_header.Flags], not TH_FIN and [edx + TCP_header.Flags], not TH_FIN
; send an ACK and resynchronize and drop any data.
; But keep on processing for RST or ACK
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
dec ecx mov eax, ecx
jmp .no_duplicate ;TODO: update stats
@@:
dec ecx
.no_fin2:
; Handle the case when a bound socket connects to itself
; Allow packets with a SYN and an ACK to 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_header.Flags], TH_ACK
jz .drop_after_ack
.duplicate:
DEBUGF 1,"TCP_input: Duplicate received\n"
;----------------------------------------
; Update statistics for duplicate packets
;;; TODO
jmp .drop_after_ack
.no_duplicate:
;----------------------------------------------- ;-----------------------------------------------
; Remove duplicate data and update urgent offset ; Remove duplicate data and update urgent offset
.duplicate:
;;; TODO: 677
add [edx + TCP_header.SequenceNumber], eax add [edx + TCP_header.SequenceNumber], eax
sub ecx, eax ;;;;;;;; Checkme sub ecx, eax
sub [edx + TCP_header.UrgentPointer], ax sub [edx + TCP_header.UrgentPointer], ax
ja @f ja @f
and [edx + TCP_header.Flags], not (TH_URG) and [edx + TCP_header.Flags], not (TH_URG)
mov [edx + TCP_header.UrgentPointer], 0 mov [edx + TCP_header.UrgentPointer], 0
@@: @@:
@ -542,49 +519,87 @@ TCP_input:
;-------------------------------------------------- ;--------------------------------------------------
; Handle data that arrives after process terminates ; Handle data that arrives after process terminates
.no_duplicate:
cmp [ebx + SOCKET.PID], 0 cmp [ebx + SOCKET.PID], 0
ja @f jne .not_terminated
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT
jbe @f jbe .not_terminated
test ecx, ecx test ecx, ecx
jz @f jz .not_terminated
;;; Close the socket
;;; update stats
mov eax, ebx
call TCP_close
;;;TODO: update stats
jmp .drop_with_reset jmp .drop_with_reset
@@:
;---------------------------------------- ;----------------------------------------
; Remove data beyond right edge of window ; Remove data beyond right edge of window (700-736)
.not_terminated:
mov eax, [edx + TCP_header.SequenceNumber] mov eax, [edx + TCP_header.SequenceNumber]
add eax, ecx add eax, ecx
sub eax, [ebx + TCP_SOCKET.RCV_NXT] sub eax, [ebx + TCP_SOCKET.RCV_NXT]
sub ax, [ebx + TCP_SOCKET.RCV_WND] sub ax, [ebx + TCP_SOCKET.RCV_WND] ; eax now holds the number of bytes to drop
; eax now holds the number of bytes to drop
jbe .no_excess_data jbe .no_excess_data
;;; TODO: update stats ;;; TODO: update stats
cmp eax, ecx cmp eax, ecx
jb .dont_drop_all jb .dont_drop_all
; If a new connection request is received while in TIME_WAIT, drop the old connection and start over,
; if the sequence numbers are above the previous ones
;;; TODO 700-736 test [edx + TCP_header.Flags], TH_SYN
jz .no_new_request
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
jne .no_new_request
mov edx, [ebx + TCP_SOCKET.RCV_NXT]
cmp edx, [edx + TCP_header.SequenceNumber]
add edx, 64000 ; TCP_ISSINCR
mov eax, ebx
call TCP_close
jmp .findpcb ; FIXME: skip code for unscaling window, ...
.no_new_request:
; If window is closed can only take segments at window edge, and have to drop data and PUSH from
; incoming segments. Continue processing, but remember to ACK. Otherwise drop segment and ACK
cmp [ebx + TCP_SOCKET.RCV_WND], 0
jne .drop_after_ack
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .drop_after_ack
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
;;; TODO: update stats
jmp .no_excess_data
.dont_drop_all: .dont_drop_all:
;;; TODO: update stats
;;; TODO: 733
sub ecx, eax
and [ebx + TCP_SOCKET.t_flags], not (TH_PUSH or TH_FIN)
.no_excess_data: .no_excess_data:
;----------------- ;-----------------
; Record timestamp ; Record timestamp (737-746) TODO
;;; TODO 737-746 ; If last ACK falls within this segments sequence numbers, record its timestamp
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp
mov eax, [ebx + TCP_SOCKET.last_ack_sent]
sub eax, [edx + TCP_header.SequenceNumber]
jb .no_timestamp
test [ebx + TCP_header.Flags], TH_SYN or TH_FIN ; syn and fin occupy one byte
jz @f
dec eax
@@:
sub eax, ecx
jae .no_timestamp
mov eax, [esp + 4] ; tcp_now
mov [ebx + TCP_SOCKET.ts_recent_age], eax
mov eax, [ebx + TCP_SOCKET.ts_val]
mov [ebx + TCP_SOCKET.ts_recent], eax
.no_timestamp:
;------------------ ;------------------
; Process RST flags ; Process RST flags
@ -626,7 +641,7 @@ TCP_input:
DEBUGF 1,"TCP_input: Closing connection\n" DEBUGF 1,"TCP_input: Closing connection\n"
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
;;; TODO: update stats (tcp drops) ;;; TODO: update stats (tcp drops)
mov eax, ebx mov eax, ebx
call TCP_close call TCP_close
jmp .drop jmp .drop
@ -644,13 +659,13 @@ TCP_input:
; handle SYN-full and ACK-less segments ; handle SYN-full and ACK-less segments
test [edx + TCP_header.Flags], TH_SYN test [edx + TCP_header.Flags], TH_SYN
jz @f jz .not_syn_full
mov eax, ebx mov eax, ebx
mov ebx, ECONNRESET mov ebx, ECONNRESET
call TCP_drop call TCP_drop
jmp .drop_with_reset jmp .drop_with_reset
@@: .not_syn_full:
;--------------- ;---------------
; ACK processing ; ACK processing
@ -659,7 +674,8 @@ TCP_input:
jz .drop jz .drop
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
jnz .no_syn_rcv jb .ack_processed ; states: closed, listen, syn_sent
ja .no_syn_rcv ; established, fin_wait_1, fin_wait_2, close_wait, closing, last_ack, time_wait
DEBUGF 1,"TCP_input: state=syn_received\n" DEBUGF 1,"TCP_input: state=syn_received\n"
@ -694,7 +710,8 @@ TCP_input:
.no_syn_rcv: .no_syn_rcv:
; check for duplicate ACK ;-------------------------
; check for duplicate ACKs
mov eax, [edx + TCP_header.AckNumber] mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA] cmp eax, [ebx + TCP_SOCKET.SND_UNA]
@ -709,8 +726,12 @@ TCP_input:
DEBUGF 1,"TCP_input: Processing duplicate ACK\n" DEBUGF 1,"TCP_input: Processing duplicate ACK\n"
cmp [ebx + TCP_SOCKET.timer_retransmission], 10000 ;;;; FIXME ; If we have outstanidn data, other than a window probe, this is a completely duplicate ACK
ja @f ; (window info didnt change) The ACK is the biggest we've seen and we've seen exactly our rexmt threshold of them,
; assume a packet has been dropped and retransmit it. Kludge snd_nxt & the congestion window so we send only this one packet.
cmp [ebx + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME
jg @f
mov eax, [edx + TCP_header.AckNumber] mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA] cmp eax, [ebx + TCP_SOCKET.SND_UNA]
@ -735,8 +756,9 @@ TCP_input:
xor edx, edx xor edx, edx
div [ebx + TCP_SOCKET.t_maxseg] div [ebx + TCP_SOCKET.t_maxseg]
cmp eax, 2 cmp eax, 2
jae @f ja @f
mov ax, 2 xor eax, eax
mov al, 2
@@: @@:
mul [ebx + TCP_SOCKET.t_maxseg] mul [ebx + TCP_SOCKET.t_maxseg]
pop edx pop edx
@ -813,23 +835,55 @@ TCP_input:
;;; TODO: update stats ;;; TODO: update stats
DEBUGF 1,"TCP_input: acceptable ACK for %u bytes\n", edi DEBUGF 1,"TCP_input: acceptable ACK for %u bytes\n", edi
;------------------------------------------ ;------------------------------------------
; RTT measurements and retransmission timer ; RTT measurements and retransmission timer (912-926)
;;;;; 912 - 926 ; If we have a timestamp, update smoothed RTT
mov [ebx + TCP_SOCKET.timer_retransmission], 0 test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
jne .timestamp_not_present
mov eax, [esp+4]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
inc eax
call TCP_xmit_timer
jmp .rtt_done_
; If no timestamp but transmit timer is running and timed sequence number was acked,
; update smoothed RTT. Since we now have an RTT measurement, cancel the timer backoff
; (Phil Karn's retransmit algo)
; Recompute the initial retransmit timer
.timestamp_not_present:
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done_
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
jz .rtt_done_
call TCP_xmit_timer
.rtt_done_:
; If all outstanding data is acked, stop retransmit timer and remember to restart (more output or persist)
; If there is more data to be acked, restart retransmit timer, using current (possible backed-off) value.
mov eax, [ebx + TCP_SOCKET.SND_MAX] mov eax, [ebx + TCP_SOCKET.SND_MAX]
cmp eax, [edx + TCP_header.AckNumber] cmp eax, [edx + TCP_header.AckNumber]
je .all_outstanding jne .more_data
mov [ebx + TCP_SOCKET.timer_retransmission], 120 ;;;; TODO: correct this value (use a macro for it) mov [ebx + TCP_SOCKET.timer_retransmission], 0
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
jmp .no_restart
.more_data:
cmp [ebx + TCP_SOCKET.timer_persist], 0
jne .no_restart
mov eax, [ebx + TCP_SOCKET.t_rxtcur]
mov [ebx + TCP_SOCKET.timer_retransmission], ax
.no_restart:
.all_outstanding:
inc [ebx + TCP_SOCKET.sendalot] ; need output
;------------------------------------------- ;-------------------------------------------
; Open congestion window in response to ACKs ; Open congestion window in response to ACKs
@ -939,9 +993,8 @@ TCP_input:
jnz @f jnz @f
mov eax, ebx mov eax, ebx
call SOCKET_is_disconnected call SOCKET_is_disconnected
;;; mov [ebx + TCP_SOCKET.timer_timed_wait], TCP_time_max_idle ; FIXME mov [ebx + TCP_SOCKET.timer_timed_wait], TCP_time_max_idle
@@: @@:
mov [ebx + TCP_SOCKET.t_state], TCPS_FIN_WAIT_2 mov [ebx + TCP_SOCKET.t_state], TCPS_FIN_WAIT_2
jmp .ack_processed jmp .ack_processed
@ -994,6 +1047,8 @@ align 4
test eax, eax test eax, eax
jz .drop jz .drop
mov [eax + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET ;;; FIXME: should we take over bits from previous socket?
push dword [edi + 4] ; Ipv4 destination addres push dword [edi + 4] ; Ipv4 destination addres
pop [eax + IP_SOCKET.LocalIP] pop [eax + IP_SOCKET.LocalIP]
@ -1007,7 +1062,7 @@ align 4
DEBUGF 1,"TCP_input: state=listen\n" DEBUGF 1,"TCP_input: state=listen\n"
test [edx + TCP_header.Flags], TH_RST ;;; TODO: kill new socket on error test [edx + TCP_header.Flags], TH_RST
jnz .drop jnz .drop
test [edx + TCP_header.Flags], TH_ACK test [edx + TCP_header.Flags], TH_ACK
@ -1045,6 +1100,8 @@ align 4
lea eax, [ebx + STREAM_SOCKET.rcv] lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create call SOCKET_ring_create
and [ebx + TCP_SOCKET.temp_bits], not TCP_BIT_DROPSOCKET
;;; call SOCKET_notify_owner ;;; call SOCKET_notify_owner
jmp .trim_then_step6 jmp .trim_then_step6
@ -1062,7 +1119,7 @@ align 4
; Active Open ; Active Open
align 4 align 4
.SYN_SENT: .SYN_SENT:
DEBUGF 1,"TCP_input: state=syn_sent\n" DEBUGF 1,"TCP_input: state=syn_sent\n"
@ -1108,7 +1165,6 @@ align 4
@@: @@:
.no_syn_ack: .no_syn_ack:
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission
push [edx + TCP_header.SequenceNumber] push [edx + TCP_header.SequenceNumber]
@ -1133,11 +1189,22 @@ align 4
mov [ebx + SOCKET.state], SS_ISCONNECTED mov [ebx + SOCKET.state], SS_ISCONNECTED
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
;;; TODO: check if we should scale the connection (567-572) ; Do window scaling on this connection ?
mov [ebx + TCP_SOCKET.SND_SCALE], 0 mov eax, [ebx + TCP_SOCKET.t_flags]
and eax, TF_REQ_SCALE or TF_RCVD_SCALE
cmp eax, TF_REQ_SCALE or TF_RCVD_SCALE
jne .no_scaling
;;; TODO: update RTT estimators mov ax, word [ebx + TCP_SOCKET.requested_s_scale]
mov word [ebx + TCP_SOCKET.SND_SCALE], ax
.no_scaling:
;;; TODO: reassemble packets queue
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
je .trim_then_step6
call TCP_xmit_timer
jmp .trim_then_step6 jmp .trim_then_step6
.simultaneous_open: .simultaneous_open:
@ -1160,8 +1227,6 @@ align 4
dec eax dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax mov [ebx + TCP_SOCKET.SND_WL1], eax
jmp .ack_processed
.ack_processed: ; (step 6) .ack_processed: ; (step 6)
@ -1212,7 +1277,7 @@ align 4
push [edx + TCP_header.AckNumber] push [edx + TCP_header.AckNumber]
pop [ebx + TCP_SOCKET.SND_WL2] pop [ebx + TCP_SOCKET.SND_WL2]
inc [ebx + TCP_SOCKET.sendalot] or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
.no_window_update: .no_window_update:
@ -1296,17 +1361,17 @@ align 4
jmp dword [eax + .FIN_sw_list] jmp dword [eax + .FIN_sw_list]
.FIN_sw_list: .FIN_sw_list:
dd .final_processing ; TCPS_CLOSED dd .final_processing ; TCPS_CLOSED
dd .final_processing ; TCPS_LISTEN dd .final_processing ; TCPS_LISTEN
dd .final_processing ; TCPS_SYN_SENT dd .final_processing ; TCPS_SYN_SENT
dd .fin_syn_est ; TCPS_SYN_RECEIVED dd .fin_syn_est ; TCPS_SYN_RECEIVED
dd .fin_syn_est ; TCPS_ESTABLISHED dd .fin_syn_est ; TCPS_ESTABLISHED
dd .final_processing ; TCPS_CLOSE_WAIT dd .final_processing ; TCPS_CLOSE_WAIT
dd .fin_wait1 ; TCPS_FIN_WAIT_1 dd .fin_wait1 ; TCPS_FIN_WAIT_1
dd .final_processing ; TCPS_CLOSING dd .final_processing ; TCPS_CLOSING
dd .final_processing ; TCPS_LAST_ACK dd .final_processing ; TCPS_LAST_ACK
dd .fin_wait2 ; TCPS_FIN_WAIT_2 dd .fin_wait2 ; TCPS_FIN_WAIT_2
dd .fin_timed ; TCPS_TIMED_WAIT dd .fin_timed ; TCPS_TIMED_WAIT
.fin_syn_est: .fin_syn_est:
@ -1379,8 +1444,8 @@ align 4
call mutex_unlock call mutex_unlock
pop eax pop eax
cmp [eax + TCP_SOCKET.sendalot], 0 test [eax + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
jne .need_output jnz .need_output
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW test [eax + TCP_SOCKET.t_flags], TF_ACKNOW
jz .dumpit jz .dumpit
@ -1397,11 +1462,7 @@ align 4
ret ret
.respond_ack: .respond_ack:
push ebx push ebx
mov cl, TH_RST mov cl, TH_RST
call TCP_respond_socket call TCP_respond_socket
@ -1410,7 +1471,6 @@ align 4
.respond_syn: .respond_syn:
push ebx push ebx
mov cl, TH_RST + TH_ACK mov cl, TH_RST + TH_ACK
call TCP_respond_socket call TCP_respond_socket
@ -1419,27 +1479,25 @@ align 4
;----- ;-----
; Drop ; Drop
.drop: .drop:
DEBUGF 1,"TCP_input: Dropping packet\n"
pusha pusha
lea ecx, [ebx + SOCKET.mutex] lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock call mutex_unlock
popa popa
.drop_not_locked:
DEBUGF 1,"TCP_input: Dropping packet\n"
;;;; If debugging options are enabled, output the packet somwhere
.destroy_new_socket: .destroy_new_socket:
;;;; kill the newly created socket test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET
jz .drop_no_socket
mov eax, ebx
call SOCKET_free
.drop_no_socket: .drop_no_socket:
DEBUGF 1,"TCP_input: Drop (no socket)\n" DEBUGF 1,"TCP_input: Drop (no socket)\n"

View File

@ -55,7 +55,7 @@ TCP_output:
.not_idle: .not_idle:
.again: .again:
mov [eax + TCP_SOCKET.sendalot], 0 mov [eax + TCP_SOCKET.temp_bits], 0
mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71)
sub ebx, [eax + TCP_SOCKET.SND_UNA] ; sub ebx, [eax + TCP_SOCKET.SND_UNA] ;
@ -141,7 +141,7 @@ TCP_output:
jbe @f jbe @f
mov esi, [eax + TCP_SOCKET.t_maxseg] mov esi, [eax + TCP_SOCKET.t_maxseg]
inc [eax + TCP_SOCKET.sendalot] or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
@@: @@:
;-------------------------------------------- ;--------------------------------------------
@ -301,6 +301,7 @@ TCP_send:
DEBUGF 1,"TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl DEBUGF 1,"TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl
push eax ; save socket ptr push eax ; save socket ptr
push esi ; and data length too
mov edi, sizeof.TCP_header ; edi will contain headersize mov edi, sizeof.TCP_header ; edi will contain headersize
;------------------------------------ ;------------------------------------
@ -381,7 +382,7 @@ TCP_send:
jbe .no_overflow jbe .no_overflow
mov esi, [eax + TCP_SOCKET.t_maxseg] mov esi, [eax + TCP_SOCKET.t_maxseg]
inc [eax + TCP_SOCKET.sendalot] or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
.no_overflow: .no_overflow:
;----------------------------------------------------------------- ;-----------------------------------------------------------------
@ -451,48 +452,54 @@ TCP_send:
; ecx = buffer size ; ecx = buffer size
; edi = ptr to buffer ; edi = ptr to buffer
mov eax, [esp + 12] ; get socket ptr mov eax, [esp + 16] ; get socket ptr
push edx push edx
push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission
test ecx, ecx test ecx, ecx
jz .nodata jz .nodata
mov edx, [eax + TCP_SOCKET.SND_NXT] mov edx, [eax + TCP_SOCKET.SND_NXT]
add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME
sub edx, [eax + TCP_SOCKET.SND_UNA] sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset
add eax, STREAM_SOCKET.snd add eax, STREAM_SOCKET.snd
call SOCKET_ring_read call SOCKET_ring_read
.nodata: .nodata:
pop edi
pop esi ; begin of data pop esi ; begin of data
pop ecx ; full packet size pop ecx ; full packet size
mov eax, [esp + 8] mov eax, [esp + 8]
;---------------------------------- ;----------------------------------
; update sequence number and timers (400) ; initialize retransmit timer (400)
test [esi + TCP_header.Flags], TH_SYN + TH_FIN ;TODO: check t_force and persist
test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number
jz @f jz @f
inc [eax + TCP_SOCKET.SND_NXT] ; syn and fin take a sequence number inc [eax + TCP_SOCKET.SND_NXT]
test [esi + TCP_header.Flags], TH_FIN test [esi + TCP_header.Flags], TH_FIN
jz @f jz @f
or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag
@@: @@:
mov edx, [eax + TCP_SOCKET.SND_NXT] mov edx, [eax + TCP_SOCKET.SND_NXT]
cmp edx, [eax + TCP_SOCKET.SND_MAX] cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission?
jbe @f jbe @f
mov [eax + TCP_SOCKET.SND_MAX], edx mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it
;;;; TODO: time transmission (420)
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything?
je @f
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer
mov [eax + TCP_SOCKET.t_rtseq], edi
;TODO: update stats
@@: @@:
; set retransmission timer if not already set, and not doing an ACK or keepalive probe ; set retransmission timer if not already set, and not doing an ACK or keepalive probe
cmp [eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; FIXME cmp [eax + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME
jb .retransmit_set ja .retransmit_set
cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx = [eax + TCP_SOCKET.SND_NXT] cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT]
je .retransmit_set je .retransmit_set
mov edx, [eax + TCP_SOCKET.t_rxtcur] mov edx, [eax + TCP_SOCKET.t_rxtcur]
@ -517,19 +524,33 @@ TCP_send:
DEBUGF 1,"TCP_send: Sending with device %x\n", ebx DEBUGF 1,"TCP_send: Sending with device %x\n", ebx
call [ebx + NET_DEVICE.transmit] call [ebx + NET_DEVICE.transmit]
jnz .send_error jnz .send_error
;---------------
; Ok, data sent!
pop ecx
pop eax pop eax
inc [TCP_segments_tx] ; FIXME: correct interface? inc [TCP_segments_tx] ; FIXME: correct interface?
;;; TODO: (485) ; update advertised receive window
test ecx, ecx
jz @f
add ecx, [eax + TCP_SOCKET.RCV_NXT]
cmp ecx, [eax + TCP_SOCKET.RCV_ADV]
jbe @f
mov [eax + TCP_SOCKET.RCV_ADV], ecx
@@:
; update last ack sent
push [eax + TCP_SOCKET.RCV_NXT] push [eax + TCP_SOCKET.RCV_NXT]
pop [eax + TCP_SOCKET.last_ack_sent] pop [eax + TCP_SOCKET.last_ack_sent]
; and flags
and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK)
cmp [eax + TCP_SOCKET.sendalot], 0 test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
jne TCP_output.again jnz TCP_output.again
; unlock socket ; unlock socket
lea ecx, [eax + SOCKET.mutex] lea ecx, [eax + SOCKET.mutex]
@ -544,6 +565,7 @@ TCP_send:
.ip_error: .ip_error:
pop ecx pop ecx
add esp, ecx add esp, ecx
add esp, 4
pop eax pop eax
mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min
@ -558,7 +580,9 @@ TCP_send:
ret ret
.send_error: .send_error:
add esp, 4
pop eax pop eax
; unlock socket ; unlock socket
lea ecx, [eax + SOCKET.mutex] lea ecx, [eax + SOCKET.mutex]
call mutex_unlock call mutex_unlock

View File

@ -470,30 +470,97 @@ TCP_respond_segment:
macro TCP_set_persist socket { macro TCP_set_persist socket {
;int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; ; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive
;int tt;
; ; cmp [socket + TCP_socket.timer_retransmission]
;tp->t_flags &= ~TF_PREVVALID;
; ; calculate RTO
; mov ecx, [socket + TCP_socket.t_srtt]
; shr ecx, 2
; add ecx, [socket + TCP_socket.t_rttvar]
; shr ecx, 1
; and [socket + TCP_socket.t_flags], not TF_PREVVALID
;if (tcp_timer_active(tp, TT_REXMT)) ;if (tcp_timer_active(tp, TT_REXMT))
; panic("tcp_setpersist: retransmit pending"); ; panic("tcp_setpersist: retransmit pending");
;
;; Start/restart persistance timer. ; Start/restart persistance timer.
;
;TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift], TCPTV_PERSMIN, TCPTV_PERSMAX); ;TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift], TCPTV_PERSMIN, TCPTV_PERSMAX);
;tcp_timer_activate(tp, TT_PERSIST, tt); ;tcp_timer_activate(tp, TT_PERSIST, tt);
;
;if (tp->t_rxtshift < TCP_MAXRXTSHIFT) ; cmp [socket + TCP_socket.t_rxtshift], TCP_MAXRXTSHIFT
; tp->t_rxtshift++; ; jae @f
; inc [socket + TCP_socket.t_rxtshift]
; @@:
} }
; eax = rtt ; eax = rtt
; ebx = socket ptr ; ebx = socket ptr
align 4 align 4
TCP_xmit_timer: TCP_xmit_timer:
;TODO: update srtt and rttvar ;TODO: update stats
ret cmp [ebx + TCP_SOCKET.t_rtt], 0
je .no_rtt_yet
; srtt is stored as a fixed point with 3 bits after the binary point.
; The following magic is equivalent of the smoothing algorithm in rfc793 with an alpha of .875
; (srtt = rtt/8 + srtt*7/8 in fixed point)
; Adjust rtt to origin 0.
push ecx
mov ecx, [ebx + TCP_SOCKET.t_srtt]
shr ecx, TCP_RTT_SHIFT
sub eax, ecx
dec eax
pop ecx
add [ebx + TCP_SOCKET.t_srtt], eax
ja @f
mov [ebx + TCP_SOCKET.t_srtt], 1
@@:
; We accumulate a smoothed rtt variance (actually, a smoothed mean difference),
; then set the retransmit timer to smoothed rtt + 4 times the smoothed variance.
; rttvar is stored as fixed point with 2 bits after the binary point.
; The following is equivalent to rfc793 smoothing with an alpha of .75
; (rttvar = rttvar*3/4 + delta/4) (delta = eax)
; get abs(eax)
push edx
cdq
xor eax, edx
sub eax, edx
mov edx, [ebx + TCP_SOCKET.t_rttvar]
shr edx, TCP_RTTVAR_SHIFT
sub eax, edx
pop edx
add [ebx + TCP_SOCKET.t_rttvar], eax
ja @f
mov [ebx + TCP_SOCKET.t_rttvar], 1
@@:
ret
.no_rtt_yet:
push ecx
mov ecx, eax
shl ecx, TCP_RTT_SHIFT
mov [ebx + TCP_SOCKET.t_srtt], ecx
shl eax, TCP_RTTVAR_SHIFT - 1
mov [ebx + TCP_SOCKET.t_rttvar], eax
pop ecx
ret