;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2012. 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$ 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 ;;;; 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 mov [ptr + TCP_SOCKET.RCV_NXT], edi mov [ptr + TCP_SOCKET.RCV_ADV], edi pop edi } macro TCP_init_socket socket { mov [socket + TCP_SOCKET.t_maxseg], TCP_mss_default mov [socket + TCP_SOCKET.t_flags], 0 ; we could also request scale and timestamp 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 } ;--------------------------- ; ; TCP_pull_out_of_band ; ; IN: eax = ; ebx = socket ptr ; edx = tcp packet ptr ; ; OUT: / ; ;--------------------------- align 4 TCP_pull_out_of_band: DEBUGF 1,"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: DEBUGF 1,"TCP_drop\n" cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED jb .no_syn_received mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED call TCP_output ;;; TODO: update stats jmp TCP_close .no_syn_received: ;;; TODO: update stats ;;; TODO: check if error code is "Connection timed out' and handle accordingly mov [eax + SOCKET.errorcode], ebx ;------------------------- ; ; TCP_close ; ; IN: eax = socket ptr ; OUT: eax = socket ptr ; ;------------------------- align 4 TCP_close: DEBUGF 1,"TCP_close: %x\n", eax ;;; TODO: update RTT and mean deviation ;;; TODO: update slow start threshold ;;; TODO: release connection resources call SOCKET_is_disconnected call SOCKET_free ret ;------------------------- ; ; TCP_disconnect ; ; IN: eax = socket ptr ; OUT: eax = socket ptr ; ;------------------------- align 4 TCP_disconnect: DEBUGF 1,"TCP_disconnect\n" cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED jb TCP_close ; TODO: implement LINGER ? call SOCKET_is_disconnecting call TCP_usrclosed call TCP_output ret ;------------------------- ; ; TCP_usrclose ; ; IN: eax = socket ptr ; ;------------------------- align 4 TCP_usrclosed: push ebx mov ebx, [eax + TCP_SOCKET.t_state] mov ebx, dword [.switch + ebx*4] jmp ebx .switch: dd .close ; TCPS_CLOSED dd .close ; TCPS_LISTEN dd .close ; TCPS_SYN_SENT dd .wait1 ; TCPS_SYN_RECEIVED dd .wait1 ; TCPS_ESTABLISHED dd .last_ack ; TCPS_CLOSE_WAIT dd .ret ; TCPS_FIN_WAIT_1 dd .ret ; TCPS_CLOSING dd .ret ; TCPS_LAST_ACK dd .disc ; TCPS_FIN_WAIT_2 dd .disc ; TCPS_TIMED_WAIT .close: pop ebx mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED call TCP_close ret .wait1: pop ebx mov [eax + TCP_SOCKET.t_state], TCPS_FIN_WAIT_1 ret .last_ack: pop ebx mov [eax + TCP_SOCKET.t_state], TCPS_LAST_ACK ret .disc: call SOCKET_is_disconnected .ret: pop ebx 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 1,"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_SYN + TH_ACK ; TCPS_FIN_WAIT_1 db TH_SYN + TH_ACK ; TCPS_CLOSING db TH_SYN + TH_ACK ; TCPS_LAST_ACK db TH_ACK ; TCPS_FIN_WAIT_2 db TH_ACK ; TCPS_TIMED_WAIT ;--------------------------------------- ; ; The fast way to send an ACK/RST/keepalive segment ; ; TCP_respond_socket: ; ; IN: ebx = socket ptr ; cl = flags ; ;-------------------------------------- align 4 TCP_respond_socket: DEBUGF 1,"TCP_respond_socket\n" ;--------------------- ; Create the IP packet push cx ebx mov eax, [ebx + IP_SOCKET.RemoteIP] mov edx, [ebx + IP_SOCKET.LocalIP] mov ecx, sizeof.TCP_header mov di, IP_PROTO_TCP shl 8 + 128 call IPv4_output test edi, edi jz .error pop esi cx push edx eax ;----------------------------------------------- ; Fill in the TCP header by using the socket ptr mov ax, [esi + TCP_SOCKET.LocalPort] rol ax, 8 stosw mov ax, [esi + TCP_SOCKET.RemotePort] rol ax, 8 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 ax, [esi + TCP_SOCKET.RCV_WND] ; rol ax, 8 mov ax, 0x00a0 ;;;;;;; FIXME 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] ret .error: DEBUGF 1,"TCP_respond failed\n" add esp, 2+4 ret ;------------------------- ; TCP_respond.segment: ; ; IN: edx = segment ptr (a previously received segment) ; edi = ptr to dest and src IPv4 addresses ; cl = flags align 4 TCP_respond_segment: DEBUGF 1,"TCP_respond_segment\n" ;--------------------- ; Create the IP packet push cx edx mov ebx, [edi + 4] mov eax, [edi] mov ecx, sizeof.TCP_header mov di , IP_PROTO_TCP shl 8 + 128 call IPv4_output jz .error pop esi cx push edx eax ;--------------------------------------------------- ; Fill in the TCP header by using a received segment mov ax, [esi + TCP_header.DestinationPort] rol ax, 8 stosw mov ax, [esi + TCP_header.SourcePort] rol ax, 8 stosw mov eax, [esi + TCP_header.AckNumber] bswap eax stosd xor eax, eax stosd mov al, 0x50 ; Dataoffset: 20 bytes (sizeof.TCP_header) stosb mov al, cl stosb mov ax, 1280 rol ax, 8 stosw ; window xor eax, eax stosd ; checksum + urgentpointer ;--------------------- ; Fill in the checksum .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] ret .error: DEBUGF 1,"TCP_respond failed\n" add esp, 2+4 ret macro TCP_set_persist socket { ;int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; ;int tt; ; ;tp->t_flags &= ~TF_PREVVALID; ; ;if (tcp_timer_active(tp, TT_REXMT)) ; panic("tcp_setpersist: retransmit pending"); ; ;; Start/restart persistance timer. ; ;TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift], TCPTV_PERSMIN, TCPTV_PERSMAX); ;tcp_timer_activate(tp, TT_PERSIST, tt); ; ;if (tp->t_rxtshift < TCP_MAXRXTSHIFT) ; tp->t_rxtshift++; }