forked from KolibriOS/kolibrios
* Updates in TCP code
* Splitted TCP code into multiple files * cleanup git-svn-id: svn://kolibrios.org@1733 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
409
kernel/branches/net/network/tcp_output.inc
Normal file
409
kernel/branches/net/network/tcp_output.inc
Normal file
@@ -0,0 +1,409 @@
|
||||
;-----------------------------------------------------------------
|
||||
;
|
||||
; TCP_output
|
||||
;
|
||||
; IN: eax = socket pointer
|
||||
;
|
||||
; OUT: /
|
||||
;
|
||||
;-----------------------------------------------------------------
|
||||
align 4
|
||||
TCP_output:
|
||||
|
||||
DEBUGF 1,"TCP_output, socket: %x\n", eax
|
||||
|
||||
|
||||
; 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 ebx, [eax + TCP_SOCKET.SND_MAX]
|
||||
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
|
||||
jne .not_idle
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.t_idle]
|
||||
cmp ebx, [eax + TCP_SOCKET.t_rxtcur]
|
||||
jle .not_idle
|
||||
|
||||
; 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.
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.t_maxseg]
|
||||
mov [eax + TCP_SOCKET.SND_CWND], ebx
|
||||
|
||||
.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 ; in dl
|
||||
|
||||
; 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
|
||||
|
||||
test ecx, ecx
|
||||
jnz .no_zero_window
|
||||
|
||||
cmp ebx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
|
||||
jge @f
|
||||
|
||||
and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before?
|
||||
|
||||
@@:
|
||||
inc ecx
|
||||
jmp .no_persist_timeout
|
||||
|
||||
.no_zero_window:
|
||||
|
||||
mov [eax + TCP_SOCKET.timer_persist], 0
|
||||
mov [eax + TCP_SOCKET.t_rxtshift], 0
|
||||
|
||||
.no_persist_timeout:
|
||||
|
||||
;;;106
|
||||
|
||||
mov esi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
|
||||
cmp esi, ecx
|
||||
jl @f
|
||||
mov esi, ecx
|
||||
@@:
|
||||
sub esi, ebx
|
||||
|
||||
cmp esi, -1
|
||||
jne .not_minus_one
|
||||
|
||||
; 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
|
||||
|
||||
xor esi, esi
|
||||
|
||||
test ecx, ecx
|
||||
jnz @f
|
||||
|
||||
mov [eax + TCP_SOCKET.timer_retransmission], 0 ; cancel retransmit
|
||||
|
||||
push [eax + TCP_SOCKET.SND_UNA]
|
||||
pop [eax + TCP_SOCKET.SND_NXT]
|
||||
@@:
|
||||
|
||||
.not_minus_one:
|
||||
|
||||
;;; 124
|
||||
|
||||
cmp esi, [eax + TCP_SOCKET.t_maxseg]
|
||||
jle @f
|
||||
|
||||
mov esi, [eax + TCP_SOCKET.t_maxseg]
|
||||
;sendalot = 1
|
||||
|
||||
@@:
|
||||
|
||||
;;; 128
|
||||
|
||||
mov edi, [eax + TCP_SOCKET.SND_NXT]
|
||||
add edi, esi ; len
|
||||
sub edi, [eax + TCP_SOCKET.SND_UNA]
|
||||
add edi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
|
||||
cmp edi, 0
|
||||
jle @f
|
||||
|
||||
and dl, not (TH_FIN) ; clear the FIN flag
|
||||
|
||||
@@:
|
||||
|
||||
|
||||
; set ecx to space available in receive buffer
|
||||
; From now on, ecx will be the window we advertise to the other end
|
||||
|
||||
mov ecx, SOCKET_MAXDATA
|
||||
sub ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size]
|
||||
|
||||
;------------------------------
|
||||
; Sender silly window avoidance
|
||||
|
||||
cmp ecx, [eax + TCP_SOCKET.t_maxseg]
|
||||
je .send
|
||||
|
||||
;;; TODO: 144-145
|
||||
|
||||
test [eax + TCP_SOCKET.t_force], -1
|
||||
jnz .send
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.max_sndwnd]
|
||||
shr ebx, 1
|
||||
cmp ecx, ebx
|
||||
jge .send
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.SND_NXT]
|
||||
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
|
||||
jl .send
|
||||
|
||||
;----------------------------------------
|
||||
; Check if a window update should be sent
|
||||
|
||||
test ecx, ecx ; window
|
||||
jz .no_window
|
||||
|
||||
;;; TODO 154-172
|
||||
|
||||
.no_window:
|
||||
|
||||
;--------------------------
|
||||
; Should a segment be sent?
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW
|
||||
jnz .send
|
||||
|
||||
test dl, TH_SYN + TH_RST
|
||||
jnz .send
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.SND_UP]
|
||||
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
|
||||
jg .send
|
||||
|
||||
test dl, TH_FIN
|
||||
jz .enter_persist
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_SENTFIN
|
||||
jnz .send
|
||||
|
||||
mov ebx, [eax + TCP_SOCKET.SND_NXT]
|
||||
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
|
||||
je .send
|
||||
|
||||
;--------------------
|
||||
; Enter persist state
|
||||
|
||||
.enter_persist:
|
||||
|
||||
DEBUGF 1,"Entering persist state\n"
|
||||
|
||||
;--------------------------------------
|
||||
; No reason to send a segment, just ret
|
||||
|
||||
DEBUGF 1,"No reason to send a segment\n"
|
||||
|
||||
mov [ebx + SOCKET.lock], 0
|
||||
|
||||
ret
|
||||
|
||||
|
||||
;-----------------------------------------------
|
||||
;
|
||||
; Send a segment
|
||||
;
|
||||
; eax = socket pointer
|
||||
; dl = flags
|
||||
;
|
||||
;-----------------------------------------------
|
||||
|
||||
.send:
|
||||
|
||||
DEBUGF 1,"Preparing to send a segment\n"
|
||||
|
||||
mov edi, TCP_segment.Data ; edi will contain headersize
|
||||
|
||||
sub esp, 8 ; create some space on stack
|
||||
push eax ; save this too..
|
||||
|
||||
;------------------------------------
|
||||
; Send options with first SYN segment
|
||||
|
||||
test dl, TH_SYN
|
||||
jz .no_options
|
||||
|
||||
push [eax + TCP_SOCKET.ISS]
|
||||
pop [eax + TCP_SOCKET.SND_NXT]
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_NOOPT
|
||||
jnz .no_options
|
||||
|
||||
mov ecx, 1460
|
||||
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
|
||||
bswap ecx
|
||||
push ecx
|
||||
add di, 4
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
|
||||
jz .no_syn
|
||||
|
||||
test dl, TH_ACK
|
||||
jnz .scale_opt
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
|
||||
jz .no_syn
|
||||
|
||||
.scale_opt:
|
||||
movzx ecx, byte [eax + TCP_SOCKET.request_r_scale]
|
||||
or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
|
||||
bswap ecx
|
||||
pushd ecx
|
||||
add di, 4
|
||||
|
||||
.no_syn:
|
||||
|
||||
;------------------------------------
|
||||
; Make the timestamp option if needed
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
|
||||
jz .no_timestamp
|
||||
|
||||
test dl, TH_RST
|
||||
jnz .no_timestamp
|
||||
|
||||
test dl, TH_ACK
|
||||
jz .timestamp
|
||||
|
||||
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
|
||||
jz .no_timestamp
|
||||
|
||||
.timestamp:
|
||||
mov esi, [timer_ticks]
|
||||
bswap esi
|
||||
push esi
|
||||
pushw 0
|
||||
pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
|
||||
add di, 10
|
||||
|
||||
.no_timestamp:
|
||||
;; TODO: check if we dont exceed the max segment size
|
||||
|
||||
.no_options:
|
||||
; eax = socket ptr
|
||||
; edx = flags
|
||||
; ecx = data size
|
||||
; edi = header size
|
||||
; esi = snd ring buff ptr
|
||||
|
||||
mov ecx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
|
||||
cmp ecx, [eax + TCP_SOCKET.t_maxseg] ;;; right?
|
||||
jle @f
|
||||
mov ecx, [eax + TCP_SOCKET.t_maxseg]
|
||||
@@:
|
||||
add ecx, edi ; total TCP segment size
|
||||
|
||||
; Start by pushing all TCP header values in reverse order on stack
|
||||
; (essentially, creating the tcp header!)
|
||||
|
||||
pushw 0 ; .UrgentPointer dw ?
|
||||
pushw 0 ; .Checksum dw ?
|
||||
pushw 0x00a0 ; .Window dw ? ;;;;;;;
|
||||
shl edi, 2 ; .DataOffset db ? only 4 left-most bits
|
||||
shl dx, 8
|
||||
or dx, di ; .Flags db ?
|
||||
pushw dx
|
||||
shr edi, 2 ; .DataOffset db ? ;;;;
|
||||
|
||||
push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ?
|
||||
ntohd [esp]
|
||||
|
||||
push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ?
|
||||
ntohd [esp]
|
||||
|
||||
push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ?
|
||||
ntohw [esp]
|
||||
|
||||
push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ?
|
||||
ntohw [esp]
|
||||
|
||||
push edi ; header size
|
||||
|
||||
; Create the IP packet
|
||||
mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip
|
||||
mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
|
||||
mov di, IP_PROTO_TCP shl 8 + 128
|
||||
call IPv4_output
|
||||
jz .fail
|
||||
|
||||
;-----------------------------------------
|
||||
; Move TCP header from stack to TCP packet
|
||||
|
||||
push ecx
|
||||
mov ecx, [esp+4]
|
||||
lea esi, [esp+4+4]
|
||||
shr ecx, 2
|
||||
rep movsd
|
||||
pop ecx ; full TCP packet size
|
||||
|
||||
pop esi ; headersize
|
||||
add esp, esi
|
||||
|
||||
mov [esp + 4], eax ; packet ptr
|
||||
mov [esp + 4+4], edx ; packet size
|
||||
|
||||
mov edx, edi ; begin of data
|
||||
sub edx, esi ; begin of packet (edi = begin of data)
|
||||
push ecx
|
||||
sub ecx, esi ; data size
|
||||
|
||||
;--------------
|
||||
; Copy the data
|
||||
|
||||
; eax = ptr to ring struct
|
||||
; ecx = buffer size
|
||||
; edi = ptr to buffer
|
||||
|
||||
; test ecx, ecx
|
||||
mov eax, [esp+4] ; socket ptr
|
||||
add [eax + TCP_SOCKET.SND_NXT], ecx
|
||||
add eax, STREAM_SOCKET.snd
|
||||
push edx
|
||||
call SOCKET_ring_read
|
||||
pop esi
|
||||
pop ecx
|
||||
pop eax
|
||||
|
||||
test [esi + TCP_segment.Flags], TH_SYN + TH_FIN
|
||||
jz @f
|
||||
inc [eax + TCP_SOCKET.SND_NXT]
|
||||
;;; TODO: update sentfin flag
|
||||
@@:
|
||||
|
||||
mov edx, [eax + TCP_SOCKET.SND_NXT]
|
||||
cmp edx, [eax + TCP_SOCKET.SND_MAX]
|
||||
jle @f
|
||||
mov [eax + TCP_SOCKET.SND_MAX], edx
|
||||
|
||||
;;;; TODO: time transmission (420)
|
||||
@@:
|
||||
|
||||
;;; TODO: set retransmission timer
|
||||
|
||||
;--------------------
|
||||
; Create the checksum
|
||||
|
||||
DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx
|
||||
|
||||
TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
|
||||
mov [esi+TCP_segment.Checksum], dx
|
||||
|
||||
;----------------
|
||||
; Send the packet
|
||||
|
||||
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
|
||||
call [ebx + NET_DEVICE.transmit]
|
||||
ret
|
||||
|
||||
|
||||
.fail:
|
||||
pop ecx
|
||||
add esp, ecx
|
||||
add esp, 4+8
|
||||
DEBUGF 1,"TCP_output: failed\n"
|
||||
ret
|
||||
|
||||
|
Reference in New Issue
Block a user