forked from KolibriOS/kolibrios
aa79e1a449
git-svn-id: svn://kolibrios.org@8988 a494cfbc-eb01-0410-851d-a64ba20cac60
620 lines
20 KiB
PHP
620 lines
20 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; Part of the TCP/IP network stack for KolibriOS ;;
|
|
;; ;;
|
|
;; Written by hidnplayr@kolibrios.org ;;
|
|
;; ;;
|
|
;; Based on the code of 4.4BSD ;;
|
|
;; ;;
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
;; Version 2, June 1991 ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
$Revision$
|
|
|
|
align 4
|
|
iglobal
|
|
TCP_backoff db 0,1,2,3,4,5,6,6,6,6,6,6,6
|
|
endg
|
|
|
|
macro tcp_checksum IP1, IP2 {
|
|
|
|
;-------------
|
|
; Pseudoheader
|
|
|
|
; protocol type
|
|
mov edx, IP_PROTO_TCP
|
|
|
|
; source address
|
|
add dl, byte [IP1+1]
|
|
adc dh, byte [IP1+0]
|
|
adc dl, byte [IP1+3]
|
|
adc dh, byte [IP1+2]
|
|
|
|
; destination address
|
|
adc dl, byte [IP2+1]
|
|
adc dh, byte [IP2+0]
|
|
adc dl, byte [IP2+3]
|
|
adc dh, byte [IP2+2]
|
|
|
|
; size
|
|
adc dl, cl
|
|
adc dh, ch
|
|
|
|
adc edx, 0
|
|
|
|
;---------------------
|
|
; Real header and data
|
|
|
|
push esi
|
|
call checksum_1
|
|
call checksum_2
|
|
pop esi
|
|
|
|
} ; returns in dx only
|
|
|
|
|
|
|
|
|
|
macro tcp_sendseqinit ptr {
|
|
|
|
push edi ;;;; FIXME: i dont like this static use of edi
|
|
mov edi, [ptr + TCP_SOCKET.ISS]
|
|
mov [ptr + TCP_SOCKET.SND_UP], edi
|
|
mov [ptr + TCP_SOCKET.SND_MAX], edi
|
|
mov [ptr + TCP_SOCKET.SND_NXT], edi
|
|
mov [ptr + TCP_SOCKET.SND_UNA], edi
|
|
pop edi
|
|
|
|
}
|
|
|
|
|
|
|
|
macro tcp_rcvseqinit ptr {
|
|
|
|
push edi
|
|
mov edi, [ptr + TCP_SOCKET.IRS]
|
|
inc edi ; SYN ocupies a sequence number
|
|
mov [ptr + TCP_SOCKET.RCV_NXT], edi
|
|
mov [ptr + TCP_SOCKET.RCV_ADV], edi
|
|
pop edi
|
|
|
|
}
|
|
|
|
|
|
|
|
macro tcp_init_socket socket {
|
|
|
|
; new tcp control block
|
|
|
|
mov [socket + TCP_SOCKET.t_maxseg], TCP_mss_default
|
|
mov [socket + TCP_SOCKET.t_flags], TF_REQ_SCALE or TF_REQ_TSTMP
|
|
|
|
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default
|
|
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4
|
|
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min
|
|
;;; TODO: TCP_time_rangeset
|
|
|
|
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift
|
|
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift
|
|
|
|
mov [socket + TCP_SOCKET.RCV_SCALE], 0
|
|
mov [socket + TCP_SOCKET.SND_SCALE], 0
|
|
|
|
}
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_pull_out_of_band ;
|
|
; ;
|
|
; IN: eax = ? ;
|
|
; ebx = socket ptr ;
|
|
; edx = tcp packet ptr ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_pull_out_of_band:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "tcp_pull_out_of_band\n"
|
|
|
|
;;;; 1282-1305
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_drop ;
|
|
; ;
|
|
; IN: eax = socket ptr ;
|
|
; ebx = error number ;
|
|
; ;
|
|
; OUT: eax = socket ptr ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_drop:
|
|
|
|
;;; TODO: check if error code is "Connection timed out' and handle accordingly
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "tcp_drop: %x\n", eax
|
|
|
|
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
|
|
jb .no_syn_received
|
|
|
|
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED
|
|
|
|
push eax
|
|
call tcp_output
|
|
pop eax
|
|
|
|
inc [TCPS_drops]
|
|
|
|
mov [eax + SOCKET.errorcode], ebx
|
|
jmp tcp_close
|
|
|
|
.no_syn_received:
|
|
inc [TCPS_conndrops]
|
|
|
|
mov [eax + SOCKET.errorcode], ebx
|
|
jmp tcp_close
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_disconnect ;
|
|
; ;
|
|
; IN: eax = socket ptr ;
|
|
; ;
|
|
; OUT: eax = socket ptr / 0 ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_disconnect:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_disconnect: %x\n", eax
|
|
|
|
cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED
|
|
jb tcp_close ; Connection not yet synchronised, just get rid of the socket
|
|
|
|
test [eax + SOCKET.options], SO_LINGER
|
|
jz .nolinger
|
|
|
|
; TODO: implement LINGER
|
|
; cmp [eax + SOCKET.so_linger], 0
|
|
; je TCP_drop
|
|
|
|
.nolinger:
|
|
call socket_is_disconnecting
|
|
|
|
push eax
|
|
add eax, STREAM_SOCKET.rcv
|
|
mov ecx, [eax + RING_BUFFER.size]
|
|
call socket_ring_free
|
|
pop eax
|
|
|
|
call tcp_usrclosed
|
|
|
|
test eax, eax
|
|
jz @f
|
|
push eax
|
|
call tcp_output
|
|
pop eax
|
|
@@:
|
|
ret
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_close ;
|
|
; ;
|
|
; IN: eax = socket ptr ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_close:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_close: %x\n", eax
|
|
|
|
;;; TODO: update RTT and mean deviation
|
|
;;; TODO: update slow start threshold
|
|
|
|
call socket_is_disconnected
|
|
call socket_free
|
|
|
|
inc [TCPS_closed]
|
|
|
|
xor eax, eax
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_outflags ;
|
|
; ;
|
|
; IN: eax = socket ptr ;
|
|
; ;
|
|
; OUT: edx = flags ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_outflags:
|
|
|
|
mov edx, [eax + TCP_SOCKET.t_state]
|
|
movzx edx, byte[edx + .flaglist]
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_outflags: socket=%x flags=%x\n", eax, dl
|
|
|
|
ret
|
|
|
|
.flaglist:
|
|
db TH_RST + TH_ACK ; TCPS_CLOSED
|
|
db 0 ; TCPS_LISTEN
|
|
db TH_SYN ; TCPS_SYN_SENT
|
|
db TH_SYN + TH_ACK ; TCPS_SYN_RECEIVED
|
|
db TH_ACK ; TCPS_ESTABLISHED
|
|
db TH_ACK ; TCPS_CLOSE_WAIT
|
|
db TH_FIN + TH_ACK ; TCPS_FIN_WAIT_1
|
|
db TH_FIN + TH_ACK ; TCPS_CLOSING
|
|
db TH_FIN + TH_ACK ; TCPS_LAST_ACK
|
|
db TH_ACK ; TCPS_FIN_WAIT_2
|
|
db TH_ACK ; TCPS_TIME_WAIT
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; TCP_respond: Fast way to send an ACK/RST/keepalive segment. ;
|
|
; ;
|
|
; IN: ebx = socket ptr ;
|
|
; cl = flags ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_respond:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_socket: socket=%x flags=%x\n", ebx, cl
|
|
|
|
;---------------------
|
|
; Create the IP packet
|
|
|
|
push cx ebx
|
|
mov edx, [ebx + IP_SOCKET.LocalIP]
|
|
mov edi, [ebx + IP_SOCKET.RemoteIP]
|
|
mov al, [ebx + IP_SOCKET.ttl]
|
|
mov ah, IP_PROTO_TCP
|
|
mov ecx, sizeof.TCP_header
|
|
mov ebx, [ebx + IP_SOCKET.device]
|
|
call ipv4_output
|
|
jz .error
|
|
pop esi cx
|
|
push eax
|
|
|
|
;-----------------------------------------------
|
|
; Fill in the TCP header by using the socket ptr
|
|
|
|
mov ax, [esi + TCP_SOCKET.LocalPort]
|
|
stosw
|
|
mov ax, [esi + TCP_SOCKET.RemotePort]
|
|
stosw
|
|
mov eax, [esi + TCP_SOCKET.SND_NXT]
|
|
bswap eax
|
|
stosd
|
|
mov eax, [esi + TCP_SOCKET.RCV_NXT]
|
|
bswap eax
|
|
stosd
|
|
mov al, 0x50 ; Dataoffset: 20 bytes (TCP_header.DataOffset)
|
|
stosb
|
|
mov al, cl
|
|
stosb
|
|
mov eax, SOCKET_BUFFER_SIZE
|
|
sub eax, [esi + STREAM_SOCKET.rcv.size]
|
|
cmp eax, TCP_max_win
|
|
jbe .lessthanmax
|
|
mov eax, TCP_max_win
|
|
.lessthanmax:
|
|
mov cl, [esi + TCP_SOCKET.RCV_SCALE]
|
|
shr eax, cl
|
|
|
|
xchg al, ah
|
|
stosw ; window
|
|
xor eax, eax
|
|
stosd ; checksum + urgentpointer
|
|
|
|
;---------------------
|
|
; Fill in the checksum
|
|
|
|
.checksum:
|
|
sub edi, sizeof.TCP_header
|
|
mov ecx, sizeof.TCP_header
|
|
xchg esi, edi
|
|
tcp_checksum (edi + IP_SOCKET.LocalIP), (edi + IP_SOCKET.RemoteIP)
|
|
mov [esi+TCP_header.Checksum], dx
|
|
|
|
;--------------------
|
|
; And send the segment
|
|
|
|
call [ebx + NET_DEVICE.transmit]
|
|
test eax, eax
|
|
jnz @f
|
|
call net_ptr_to_num4
|
|
inc [TCP_segments_tx + edi]
|
|
@@:
|
|
ret
|
|
|
|
.error:
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_socket: failed\n"
|
|
add esp, 2 + 4
|
|
|
|
ret
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_respond_segment ;
|
|
; ;
|
|
; IN: ebx = device ptr ;
|
|
; edx = segment ptr (a previously received segment) ;
|
|
; edi = ptr to IPv4 header ;
|
|
; cl = flags ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_respond_segment:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_segment: frame=%x flags=%x\n", edx, cl
|
|
|
|
;---------------------
|
|
; Create the IP packet
|
|
|
|
push cx edx
|
|
mov edx, [edi + IPv4_header.DestinationAddress]
|
|
mov edi, [edi + IPv4_header.SourceAddress]
|
|
mov ecx, sizeof.TCP_header
|
|
mov ax, IP_PROTO_TCP shl 8 + 128
|
|
call ipv4_output
|
|
jz .error
|
|
pop esi cx
|
|
|
|
push eax
|
|
|
|
;---------------------------------------------------
|
|
; Fill in the TCP header by using a received segment
|
|
|
|
mov ax, [esi + TCP_header.DestinationPort]
|
|
stosw
|
|
mov ax, [esi + TCP_header.SourcePort]
|
|
stosw
|
|
mov eax, [esi + TCP_header.AckNumber]
|
|
bswap eax
|
|
stosd
|
|
xor eax, eax
|
|
stosd
|
|
mov al, 0x50 ; Dataoffset: 20 bytes (sizeof.TCP_header/4 shl 4)
|
|
stosb
|
|
mov al, cl
|
|
stosb
|
|
mov ax, 1280
|
|
rol ax, 8
|
|
stosw ; window
|
|
xor eax, eax
|
|
stosd ; checksum + urgentpointer
|
|
|
|
;---------------------
|
|
; Fill in the checksum
|
|
|
|
lea esi, [edi - sizeof.TCP_header]
|
|
mov ecx, sizeof.TCP_header
|
|
tcp_checksum (esi - sizeof.IPv4_header + IPv4_header.DestinationAddress),\ ; FIXME
|
|
(esi - sizeof.IPv4_header + IPv4_header.SourceAddress)
|
|
mov [esi + TCP_header.Checksum], dx
|
|
|
|
;--------------------
|
|
; And send the segment
|
|
|
|
call [ebx + NET_DEVICE.transmit]
|
|
test eax, eax
|
|
jnz @f
|
|
call net_ptr_to_num4
|
|
inc [TCP_segments_tx + edi]
|
|
@@:
|
|
ret
|
|
|
|
.error:
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_segment: failed\n"
|
|
add esp, 2+4
|
|
|
|
ret
|
|
|
|
|
|
macro tcpt_rangeset timer, value, min, max {
|
|
|
|
local .min
|
|
local .max
|
|
local .done
|
|
|
|
cmp value, min
|
|
jb .min
|
|
cmp value, max
|
|
ja .max
|
|
mov timer, value
|
|
jmp .done
|
|
.min:
|
|
mov timer, min
|
|
jmp .done
|
|
.max:
|
|
mov timer, max
|
|
.done:
|
|
}
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_set_persist ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_set_persist:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_set_persist\n"
|
|
|
|
; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive
|
|
|
|
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
|
|
jnz .exit
|
|
|
|
; calculate RTO
|
|
push ebx
|
|
mov ebx, [eax + TCP_SOCKET.t_srtt]
|
|
shr ebx, 2
|
|
add ebx, [eax + TCP_SOCKET.t_rttvar]
|
|
shr ebx, 1
|
|
|
|
mov cl, [eax + TCP_SOCKET.t_rxtshift]
|
|
shl ebx, cl
|
|
|
|
; Start/restart persistance timer.
|
|
|
|
tcpt_rangeset [eax + TCP_SOCKET.timer_persist], ebx, TCP_time_pers_min, TCP_time_pers_max
|
|
or [ebx + TCP_SOCKET.timer_flags], timer_flag_persist
|
|
pop ebx
|
|
|
|
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift
|
|
jae @f
|
|
inc [eax + TCP_SOCKET.t_rxtshift]
|
|
@@:
|
|
.exit:
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_xmit_timer: Calculate new smoothed RTT. ;
|
|
; ;
|
|
; IN: eax = rtt ;
|
|
; ebx = socket ptr ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_xmit_timer:
|
|
|
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_xmit_timer: socket=0x%x rtt=%d0ms\n", ebx, eax
|
|
|
|
inc [TCPS_rttupdated]
|
|
|
|
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
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_mss: Update maximum segment size ;
|
|
; ;
|
|
; IN: eax = max segment size ;
|
|
; ebx = socket ptr ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_mss:
|
|
|
|
cmp eax, 1420 ; FIXME
|
|
jbe @f
|
|
mov eax, 1420
|
|
@@:
|
|
mov [ebx + TCP_SOCKET.t_maxseg], eax
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------;
|
|
; ;
|
|
; tcp_reassemble ;
|
|
; ;
|
|
; IN: ebx = socket ptr ;
|
|
; edx = segment ptr ;
|
|
; ;
|
|
; OUT: / ;
|
|
; ;
|
|
;-----------------------------------------------------------------;
|
|
align 4
|
|
tcp_reassemble:
|
|
|
|
;;;;; TODO
|
|
|
|
ret
|
|
|