Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 1732 → Rev 1733

/kernel/branches/net/network/tcp_output.inc
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
 
 
Property changes:
Added: svn:keywords
+Revision
\ No newline at end of property