forked from KolibriOS/kolibrios
602924a5b5
fixed bug in netcfg created in last revision netcfg gives error msg when driver is not loaded zeroconfig now works with latest version of libini also fixed use of static and link-local ip in zeroconfig initial IPv4 variables are now 0.0.0.0 instead of 255.255.255.255 created kernel function that shows number of active network devices fixed the use of temp mac variable in IPV4.inc (variable is now in stack) rewrite of ARP code, needs full testing/debugging (new application needed: ARP manager) port numbers are now in INET byte order, as is in posix standards git-svn-id: svn://kolibrios.org@1196 a494cfbc-eb01-0410-851d-a64ba20cac60
1400 lines
33 KiB
PHP
1400 lines
33 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License ;;
|
|
;; ;;
|
|
;; TCP.INC ;;
|
|
;; ;;
|
|
;; Part of the tcp/ip network stack for KolibriOS ;;
|
|
;; ;;
|
|
;; Written by hidnplayr@kolibrios.org ;;
|
|
;; ;;
|
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
|
;; Version 2, June 1991 ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
$Revision: 1019 $
|
|
|
|
|
|
; TCP TCB states
|
|
TCB_LISTEN equ 1
|
|
TCB_SYN_SENT equ 2
|
|
TCB_SYN_RECEIVED equ 3
|
|
TCB_ESTABLISHED equ 4
|
|
TCB_FIN_WAIT_1 equ 5
|
|
TCB_FIN_WAIT_2 equ 6
|
|
TCB_CLOSE_WAIT equ 7
|
|
TCB_CLOSING equ 8
|
|
TCB_LAST_ACK equ 9
|
|
TCB_TIMED_WAIT equ 10
|
|
TCB_CLOSED equ 11
|
|
|
|
TH_FIN equ 0x01
|
|
TH_SYN equ 0x02
|
|
TH_RST equ 0x04
|
|
TH_PUSH equ 0x08
|
|
TH_ACK equ 0x10
|
|
TH_URG equ 0x20
|
|
|
|
TWOMSL equ 10 ; # of secs to wait before closing socket
|
|
|
|
TCP_RETRIES equ 5 ; Number of times to resend a Packet
|
|
TCP_TIMEOUT equ 10 ; resend if not replied to in x hs
|
|
|
|
TCP_QUEUE_SIZE equ 16
|
|
|
|
struct TCP_Packet
|
|
.SourcePort dw ?
|
|
.DestinationPort dw ?
|
|
.SequenceNumber dd ?
|
|
.AckNumber dd ?
|
|
.DataOffset db ? ; DataOffset[0-3 bits] and Reserved[4-7]
|
|
.Flags db ? ; Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN
|
|
.Window dw ?
|
|
.Checksum dw ?
|
|
.UrgentPointer dw ?
|
|
.Options rb 3
|
|
.Padding db ?
|
|
.Data:
|
|
ends
|
|
|
|
align 4
|
|
uglobal
|
|
TCP_PACKETS_TX rd MAX_IP
|
|
TCP_PACKETS_RX rd MAX_IP
|
|
|
|
TCP_IN_QUEUE rd 3*TCP_QUEUE_SIZE+3
|
|
TCP_OUT_QUEUE rd 3*TCP_QUEUE_SIZE+3
|
|
endg
|
|
|
|
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; TCP_init
|
|
;
|
|
; This function resets all TCP variables
|
|
;
|
|
; IN: /
|
|
; OUT: /
|
|
;
|
|
;-----------------------------------------------------------------
|
|
|
|
align 4
|
|
TCP_init:
|
|
|
|
xor eax, eax
|
|
mov edi, TCP_PACKETS_TX
|
|
mov ecx, 2*MAX_IP
|
|
rep stosd
|
|
|
|
mov dword [TCP_IN_QUEUE], TCP_QUEUE_SIZE
|
|
mov dword [TCP_IN_QUEUE+4], TCP_IN_QUEUE + queue.data
|
|
mov dword [TCP_IN_QUEUE+8], TCP_IN_QUEUE + queue.data
|
|
|
|
mov dword [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
|
|
mov dword [TCP_OUT_QUEUE+4], TCP_OUT_QUEUE + queue.data
|
|
mov dword [TCP_OUT_QUEUE+8], TCP_OUT_QUEUE + queue.data
|
|
|
|
ret
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; tcp_tcb_handler
|
|
;
|
|
; Handles sockets in the timewait state, closing them
|
|
; when the TCB timer expires
|
|
;
|
|
;-----------------------------------------------------------------
|
|
|
|
align 4
|
|
tcp_tcb_handler:
|
|
; scan through all the sockets, decrementing active timers
|
|
|
|
mov ebx, net_sockets
|
|
|
|
cmp [ebx + SOCKET.NextPtr], 0
|
|
je .exit
|
|
DEBUGF 1, "K : sockets:\n"
|
|
|
|
.next_socket:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .exit
|
|
|
|
; DEBUGF 1, "K : %x-%x: %x-%x-%x-%u\n", [ebx + SOCKET.PID]:2, [ebx + SOCKET.Number]:2, [ebx + SOCKET.LocalPort]:4, [ebx + SOCKET.RemoteIP], [ebx + SOCKET.RemotePort]:4, [ebx + SOCKET.TCBState]
|
|
|
|
cmp [ebx + SOCKET.TCBTimer], 0
|
|
jne .decrement_tcb
|
|
cmp [ebx + SOCKET.wndsizeTimer], 0
|
|
jne .decrement_wnd
|
|
jmp .next_socket
|
|
|
|
.decrement_tcb:
|
|
; decrement it, delete socket if TCB timer = 0 & socket in timewait state
|
|
dec [ebx + SOCKET.TCBTimer]
|
|
jnz .next_socket
|
|
|
|
cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
|
|
jne .next_socket
|
|
|
|
push [ebx + SOCKET.PrevPtr]
|
|
stdcall net_socket_free, ebx
|
|
pop ebx
|
|
jmp .next_socket
|
|
|
|
.decrement_wnd:
|
|
; TODO - prove it works!
|
|
dec [ebx + SOCKET.wndsizeTimer]
|
|
jmp .next_socket
|
|
|
|
.exit:
|
|
ret
|
|
|
|
|
|
;***************************************************************************
|
|
; Function
|
|
; tcp_tx_handler
|
|
;
|
|
; Description
|
|
; Handles queued TCP data
|
|
; This is a kernel function, called by stack_handler
|
|
;
|
|
;***************************************************************************
|
|
|
|
align 4
|
|
tcp_tx_handler:
|
|
; decrement all resend buffers timers. If they
|
|
; expire, queue them for sending, and restart the timer.
|
|
; If the retries counter reach 0, delete the entry
|
|
|
|
mov esi, resendQ
|
|
mov ecx, 0
|
|
|
|
.next_resendq:
|
|
; cmp ecx, NUMRESENDENTRIES
|
|
je .exit ; None left
|
|
cmp dword[esi + 4], 0
|
|
jne @f ; found one
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: ; we have one. decrement it's timer by 1
|
|
dec word[esi + 2]
|
|
jz @f
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq ; Timer not zero, so move on
|
|
|
|
@@:
|
|
xor ebx, ebx
|
|
; restart timer, and decrement retries
|
|
; After the first resend, back of on next, by a factor of 5
|
|
mov [esi + 2], word TCP_TIMEOUT * 5
|
|
dec byte[esi + 1]
|
|
jnz @f
|
|
|
|
; retries now 0, so delete from queue
|
|
xchg [esi + 4], ebx
|
|
|
|
@@: ; resend Packet
|
|
pushad
|
|
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
jne .tth004z
|
|
|
|
; TODO - try again in 10ms.
|
|
test ebx, ebx
|
|
jnz @f
|
|
mov [esi + 4], ebx
|
|
|
|
@@: ; Mark it to expire in 10ms - 1 tick
|
|
mov byte[esi + 1], 1
|
|
mov word[esi + 2], 1
|
|
jmp .tth005
|
|
|
|
.tth004z:
|
|
; we have a buffer # in ax
|
|
; push eax ecx
|
|
; mov ecx, IPBUFFSIZE
|
|
; mul ecx
|
|
; add eax, IPbuffs
|
|
|
|
; we have the buffer address in eax
|
|
mov edi, eax
|
|
pop ecx
|
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
|
; mov esi, resendBuffer
|
|
; @@: add esi, IPBUFFSIZE
|
|
loop @b
|
|
|
|
; we have resend buffer location in esi
|
|
; mov ecx, IPBUFFSIZE
|
|
|
|
; copy data across
|
|
push edi
|
|
cld
|
|
rep movsb
|
|
pop edi
|
|
|
|
; queue Packet
|
|
; mov eax, NET1OUT_QUEUE
|
|
; mov edx, [IP_LIST]
|
|
; cmp edx, [edi + IP_Packet.DestinationAddress]
|
|
; jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
pop ebx
|
|
; call queue
|
|
|
|
.tth005:
|
|
popad
|
|
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
.exit:
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; TCP_Handler:
|
|
;
|
|
; Called by IPv4_handler,
|
|
; this procedure will inject the tcp data diagrams in the application sockets.
|
|
;
|
|
; IN: Pointer to buffer in [esp]
|
|
; size of buffer in [esp+4]
|
|
; pointer to device struct in ebx
|
|
; TCP Packet size in ecx
|
|
; pointer to TCP Packet data in edx
|
|
; SourceAddres in esi
|
|
; OUT: /
|
|
;
|
|
;-----------------------------------------------------------------
|
|
|
|
TCP_Handler :
|
|
|
|
|
|
DEBUGF 1,"TCP_Handler\n"
|
|
|
|
jmp .exit ;;;;
|
|
|
|
; Look for a socket where
|
|
; IP Packet TCP Destination Port = local Port
|
|
; IP Packet SA = Remote IP
|
|
; IP Packet TCP Source Port = remote Port
|
|
|
|
mov ebx, net_sockets
|
|
|
|
.next_socket.1:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .next_socket.1.exit
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 1.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
|
|
|
|
mov ax, [edx + TCP_Packet.DestinationPort] ; get the dest. port from the TCP hdr
|
|
cmp [ebx + SOCKET.LocalPort], ax ; get the dest. port from the TCP hdr
|
|
jne .next_socket.1 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 1.addr: %x - %x\n", [edx + IP_Packet.SourceAddress], [ebx + SOCKET.RemoteIP]
|
|
|
|
mov eax, esi ;[edx + IP_Packet.SourceAddress] ; get the source IP Addr from the IP hdr
|
|
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP
|
|
jne .next_socket.1 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 1.sport: %x - %x\n", [edx + 20 + TCP_Packet.SourcePort]:4, [ebx + SOCKET.RemotePort]:4
|
|
|
|
mov ax, [edx + TCP_Packet.SourcePort] ; get the source port from the TCP hdr
|
|
cmp [ebx + SOCKET.RemotePort], ax ; compare with socket's remote port
|
|
jne .next_socket.1 ; different - try next socket
|
|
|
|
; We have a complete match - use this socket
|
|
jmp .change_state
|
|
|
|
.next_socket.1.exit:
|
|
|
|
; If we got here, there was no match
|
|
; Look for a socket where
|
|
; IP Packet TCP Destination Port = local Port
|
|
; IP Packet SA = Remote IP
|
|
; socket remote Port = 0
|
|
|
|
mov ebx, net_sockets
|
|
|
|
.next_socket.2:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .next_socket.2.exit
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 2.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
|
|
|
|
mov ax, [edx + TCP_Packet.DestinationPort] ; get the dest. port from the TCP hdr
|
|
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port
|
|
jne .next_socket.2 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 2.addr: %x - %x\n", [edx + IP_Packet.SourceAddress], [ebx + SOCKET.RemoteIP]
|
|
|
|
; mov eax, esi ;[edx + IP_Packet.SourceAddress] ; get the source IP Addr from the IP hdr
|
|
cmp [ebx + SOCKET.RemoteIP], esi ; compare with socket's remote IP
|
|
jne .next_socket.2 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 2.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4
|
|
|
|
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0
|
|
jne .next_socket.2 ; different - try next socket
|
|
|
|
; We have a complete match - use this socket
|
|
jmp .change_state
|
|
|
|
.next_socket.2.exit:
|
|
|
|
; If we got here, there was no match
|
|
; Look for a socket where
|
|
; IP Packet TCP Destination Port = local Port
|
|
; socket Remote IP = 0
|
|
; socket remote Port = 0
|
|
|
|
mov ebx, net_sockets
|
|
|
|
.next_socket.3:
|
|
mov ebx, [ebx + SOCKET.NextPtr]
|
|
or ebx, ebx
|
|
jz .next_socket.3.exit
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 3.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
|
|
|
|
mov ax, [edx + TCP_Packet.DestinationPort] ; get destination port from the TCP hdr
|
|
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port
|
|
jne .next_socket.3 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 3.addr: 00000000 - %x\n", [ebx + SOCKET.RemoteIP]
|
|
|
|
cmp [ebx + SOCKET.RemoteIP], 0 ; only match a socket remote IP of 0
|
|
jne .next_socket.3 ; different - try next socket
|
|
|
|
; DEBUGF 1, "K : tcp_rx - 3.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4
|
|
|
|
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0
|
|
jne .next_socket.3 ; different - try next socket
|
|
|
|
; We have a complete match - use this socket
|
|
jmp .change_state
|
|
|
|
.next_socket.3.exit:
|
|
|
|
; If we got here, we need to reject the Packet
|
|
|
|
DEBUGF 1, "K : tcp_rx - dumped\n"
|
|
; DEBUGF 1, "K : --------: %x-%x-%x (flags: %x)\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [edx + IP_Packet.SourceAddress], [edx + 20 + TCP_Packet.SourcePort]:4, [edx + 20 + TCP_Packet.Flags]:2
|
|
|
|
; inc [dumped_rx_count]
|
|
jmp .exit
|
|
|
|
.change_state:
|
|
|
|
; We have a valid socket/TCB, so call the TCB State Machine for that skt.
|
|
; socket is pointed to by ebx
|
|
; IP Packet is pointed to by edx
|
|
; IP buffer number is on stack ( it will be popped at the end)
|
|
|
|
stdcall tcpStateMachine, ebx
|
|
|
|
.exit:
|
|
|
|
call kernel_free
|
|
add esp, 4 ; pop (balance stack)
|
|
|
|
ret
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; IN: eax = dest ip
|
|
; ebx = source ip
|
|
; ecx = data length
|
|
; edx = remote port shl 16 + local port
|
|
; esi = data offset
|
|
;
|
|
;-----------------------------------------------------------------
|
|
|
|
TCP_create_Packet:
|
|
|
|
DEBUGF 1,"Create TCP Packet\n"
|
|
;***************************************************************************
|
|
; Function
|
|
; buildTCPPacket
|
|
;
|
|
; Description
|
|
; builds an IP Packet with TCP data fully populated for transmission
|
|
; You may destroy any and all registers
|
|
; TCP control flags specified in bl
|
|
; This TCB is in [sktAddr]
|
|
; User data pointed to by esi
|
|
; Data length in ecx
|
|
; Transmit buffer number in eax
|
|
;
|
|
;***************************************************************************
|
|
|
|
push ecx ; Save data length
|
|
|
|
add ecx, UDP_Packet.Data
|
|
mov di , IP_PROTO_UDP
|
|
|
|
; dx = fragment id
|
|
|
|
call IPv4_create_Packet ; TODO: figure out a way to choose between IPv4 and IPv6
|
|
cmp edi, -1
|
|
je .exit
|
|
|
|
mov [edi + TCP_Packet.Flags], bl ; TCP flags
|
|
|
|
; mov ebx, [sockAddr];---------------------------------------------------------- eof
|
|
|
|
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
|
|
|
|
; Fill in the IP header ( some data is in the socket descriptor)
|
|
mov eax, [ebx + SOCKET.LocalIP]
|
|
; mov [edx + IP_Packet.SourceAddress], eax
|
|
mov eax, [ebx + SOCKET.RemoteIP]
|
|
; mov [edx + IP_Packet.DestinationAddress], eax
|
|
|
|
; mov [edx + IP_Packet.VersionAndIHL], 0x45
|
|
; mov [edx + IP_Packet.TypeOfService], 0
|
|
|
|
pop eax ; Get the TCP data length
|
|
push eax
|
|
|
|
add eax, 20 + 20 ; add IP header and TCP header lengths
|
|
rol ax, 8
|
|
; mov [edx + IP_Packet.TotalLength], ax
|
|
; mov [edx + IP_Packet.Identification], 0
|
|
; mov [edx + IP_Packet.FlagsAndFragmentOffset], 0x0040
|
|
; mov [edx + IP_Packet.TimeToLive], 0x20
|
|
; mov [edx + IP_Packet.Protocol], PROTOCOL_TCP
|
|
|
|
; Checksum left unfilled
|
|
; mov [edx + IP_Packet.HeaderChecksum], 0
|
|
|
|
; Fill in the TCP header (some data is in the socket descriptor)
|
|
mov ax, [ebx + SOCKET.LocalPort]
|
|
mov [edx + 20 + TCP_Packet.SourcePort], ax ; Local Port
|
|
|
|
mov ax, [ebx + SOCKET.RemotePort]
|
|
mov [edx + 20 + TCP_Packet.DestinationPort], ax ; desitination Port
|
|
|
|
; Checksum left unfilled
|
|
mov [edx + 20 + TCP_Packet.Checksum], 0
|
|
|
|
; sequence number
|
|
mov eax, [ebx + SOCKET.SND_NXT]
|
|
mov [edx + 20 + TCP_Packet.SequenceNumber], eax
|
|
|
|
; ack number
|
|
mov eax, [ebx + SOCKET.RCV_NXT]
|
|
mov [edx + 20 + TCP_Packet.AckNumber], eax
|
|
|
|
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size)
|
|
; 768 bytes seems better
|
|
mov [edx + 20 + TCP_Packet.Window], 0x0003
|
|
|
|
; Urgent pointer (0)
|
|
mov [edx + 20 + TCP_Packet.UrgentPointer], 0
|
|
|
|
; data offset ( 0x50 )
|
|
mov [edx + 20 + TCP_Packet.DataOffset], 0x50
|
|
|
|
pop ecx ; count of bytes to send
|
|
mov ebx, ecx ; need the length later
|
|
|
|
cmp ebx, 0
|
|
jz @f
|
|
|
|
mov edi, edx
|
|
add edi, 40
|
|
cld
|
|
rep movsb ; copy the data across
|
|
|
|
@@: ; we have edx as IPbuffer ptr.
|
|
; Fill in the TCP checksum
|
|
; First, fill in pseudoheader
|
|
; mov eax, [edx + IP_Packet.SourceAddress]
|
|
; mov [pseudoHeader], eax
|
|
; mov eax, [edx + IP_Packet.DestinationAddress]
|
|
; mov [pseudoHeader + 4], eax
|
|
; mov word[pseudoHeader + 8], PROTOCOL_TCP shl 8 + 0
|
|
; add ebx, 20
|
|
; mov [pseudoHeader + 10], bh
|
|
; mov [pseudoHeader + 11], bl
|
|
;
|
|
; mov eax, pseudoHeader
|
|
; mov [checkAdd1], eax
|
|
; mov word[checkSize1], 12
|
|
; mov eax, edx
|
|
; add eax, 20
|
|
; mov [checkAdd2], eax
|
|
; mov eax, ebx
|
|
; mov [checkSize2], ax
|
|
;
|
|
; call checksum
|
|
|
|
; store it in the TCP checksum ( in the correct order! )
|
|
; mov ax, [checkResult]
|
|
; rol ax, 8
|
|
; mov [edx + 20 + TCP_Packet.Checksum], ax
|
|
|
|
; Fill in the IP header checksum
|
|
; movzx eax, byte [edx + IP_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
|
|
; and eax, 0x0000000F ;
|
|
; shl eax, 2 ;
|
|
;
|
|
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size
|
|
rol ax, 8
|
|
; mov [edx + IP_Packet.HeaderChecksum], ax
|
|
|
|
|
|
.exit:
|
|
|
|
call kernel_free
|
|
add esp, 4 ; pop (balance stack)
|
|
|
|
ret
|
|
;endp
|
|
|
|
|
|
; Increments the 32 bit value pointed to by esi in internet order
|
|
proc inc_inet_esi stdcall
|
|
; push eax
|
|
; mov eax, [esi]
|
|
; bswap eax
|
|
; inc eax
|
|
; bswap eax
|
|
; mov [esi], eax
|
|
; pop eax
|
|
; ret
|
|
inc byte[esi+0]
|
|
adc byte[esi+1],0
|
|
adc byte[esi+2],0
|
|
adc byte[esi+3],0
|
|
endp
|
|
|
|
|
|
; Increments the 32 bit value pointed to by esi in internet order
|
|
; by the value in ecx
|
|
proc add_inet_esi stdcall
|
|
push eax
|
|
mov eax, [esi]
|
|
bswap eax
|
|
add eax, ecx
|
|
bswap eax
|
|
mov [esi], eax
|
|
pop eax
|
|
ret
|
|
endp
|
|
|
|
|
|
iglobal
|
|
TCBStateHandler dd \
|
|
stateTCB_LISTEN, \
|
|
stateTCB_SYN_SENT, \
|
|
stateTCB_SYN_RECEIVED, \
|
|
stateTCB_ESTABLISHED, \
|
|
stateTCB_FIN_WAIT_1, \
|
|
stateTCB_FIN_WAIT_2, \
|
|
stateTCB_CLOSE_WAIT, \
|
|
stateTCB_CLOSING, \
|
|
stateTCB_LAST_ACK, \
|
|
stateTCB_TIME_WAIT, \
|
|
stateTCB_CLOSED
|
|
endg
|
|
|
|
|
|
;***************************************************************************
|
|
; Function
|
|
; tcpStateMachine
|
|
;
|
|
; Description
|
|
; TCP state machine
|
|
; This is a kernel function, called by tcp_rx
|
|
;
|
|
; IP buffer address given in edx
|
|
; Socket/TCB address in ebx
|
|
;
|
|
; The IP buffer will be released by the caller
|
|
;***************************************************************************
|
|
|
|
proc tcpStateMachine stdcall, sockAddr:DWORD
|
|
; as a Packet has been received, update the TCB timer
|
|
mov [ebx + SOCKET.TCBTimer], TWOMSL
|
|
|
|
; If the received Packet has an ACK bit set,
|
|
; remove any Packets in the resend queue that this
|
|
; received Packet acknowledges
|
|
pushad
|
|
test [edx + 20 + TCP_Packet.Flags], TH_ACK
|
|
jz .call_handler ; No ACK, so no data yet
|
|
|
|
; get skt number in eax
|
|
stdcall net_socket_addr_to_num, ebx
|
|
|
|
; The ack number is in [edx + 28], inet format
|
|
; skt in eax
|
|
|
|
mov esi, resendQ
|
|
xor ecx, ecx
|
|
|
|
.next_resendq:
|
|
; cmp ecx, NUMRESENDENTRIES
|
|
je .call_handler ; None left
|
|
cmp [esi + 4], eax
|
|
je @f ; found one
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: ; Can we delete this buffer?
|
|
|
|
; If yes, goto @@. No, goto .next_resendq
|
|
; Get Packet data address
|
|
|
|
push ecx
|
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
|
; imul edi, ecx, IPBUFFSIZE
|
|
; add edi, resendBuffer
|
|
|
|
; we have dest buffer location in edi. incoming Packet in edx.
|
|
; Get this Packets sequence number
|
|
; preserve al, ecx, esi, edx
|
|
mov ecx, [edi + 20 + TCP_Packet.SequenceNumber]
|
|
bswap ecx
|
|
movzx ebx, word[edi + 2]
|
|
xchg bl, bh
|
|
sub ebx, 40
|
|
add ecx, ebx ; ecx is now seq# of last byte +1, intel format
|
|
|
|
; get recievd ack #, in intel format
|
|
mov ebx, [edx + 20 + TCP_Packet.AckNumber]
|
|
bswap ebx
|
|
|
|
cmp ebx, ecx ; Finally. ecx = rx'ed ack. ebx = last byte in que
|
|
; DANGER! need to handle case that we have just
|
|
; passed the 2**32, and wrapped round!
|
|
pop ecx
|
|
jae @f ; if rx > old, delete old
|
|
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: mov dword[esi + 4], 0
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
.call_handler:
|
|
popad
|
|
|
|
; Call handler for given TCB state
|
|
|
|
mov eax, [ebx + SOCKET.TCBState]
|
|
cmp eax, TCB_LISTEN
|
|
jb .exit
|
|
cmp eax, TCB_CLOSED
|
|
ja .exit
|
|
|
|
stdcall [TCBStateHandler + (eax - 1) * 4], [sockAddr]
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_LISTEN stdcall, sockAddr:DWORD
|
|
; In this case, we are expecting a SYN Packet
|
|
; For now, if the Packet is a SYN, process it, and send a response
|
|
; If not, ignore it
|
|
|
|
; Look at control flags
|
|
test [edx + 20 + TCP_Packet.Flags], TH_SYN
|
|
jz .exit
|
|
|
|
; We have a SYN. update the socket with this IP Packets details,
|
|
; And send a response
|
|
|
|
; mov eax, [edx + IP_Packet.SourceAddress]
|
|
; mov [ebx + SOCKET.RemoteIP], eax
|
|
; mov ax, [edx + 20 + TCP_Packet.SourcePort]
|
|
; mov [ebx + SOCKET.RemotePort], ax
|
|
; mov eax, [edx + 20 + TCP_Packet.SequenceNumber]
|
|
; mov [ebx + SOCKET.IRS], eax
|
|
; mov [ebx + SOCKET.RCV_NXT], eax
|
|
; lea esi, [ebx + SOCKET.RCV_NXT]
|
|
; call inc_inet_esi ; RCV.NXT
|
|
; mov eax, [ebx + SOCKET.ISS]
|
|
; mov [ebx + SOCKET.SND_NXT], eax
|
|
;
|
|
; Now construct the response, and queue for sending by IP
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
; je .exit
|
|
|
|
push eax
|
|
mov bl, TH_SYN + TH_ACK
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
;;; mov edx, [stack_ip]
|
|
mov ecx, [sockAddr]
|
|
cmp edx, [ecx + SOCKET.RemoteIP]
|
|
jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
;;; call queue
|
|
|
|
mov esi, [sockAddr]
|
|
mov [esi + SOCKET.TCBState], TCB_SYN_RECEIVED
|
|
|
|
; increment SND.NXT in socket
|
|
add esi, SOCKET.SND_NXT
|
|
call inc_inet_esi
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_SYN_SENT stdcall, sockAddr:DWORD
|
|
; We are awaiting an ACK to our SYN, with a SYM
|
|
; Look at control flags - expecting an ACK
|
|
|
|
mov al, [edx + 20 + TCP_Packet.Flags]
|
|
and al, TH_SYN + TH_ACK
|
|
cmp al, TH_SYN + TH_ACK
|
|
je .syn_ack
|
|
|
|
test al, TH_SYN
|
|
jz .exit
|
|
|
|
mov [ebx + SOCKET.TCBState], TCB_SYN_RECEIVED
|
|
push TH_SYN + TH_ACK
|
|
jmp .send
|
|
|
|
.syn_ack:
|
|
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED
|
|
push TH_ACK
|
|
|
|
.send:
|
|
; Store the recv.nxt field
|
|
mov eax, [edx + 20 + TCP_Packet.SequenceNumber]
|
|
|
|
; Update our recv.nxt field
|
|
mov [ebx + SOCKET.RCV_NXT], eax
|
|
lea esi, [ebx + SOCKET.RCV_NXT]
|
|
call inc_inet_esi
|
|
|
|
; Send an ACK
|
|
; Now construct the response, and queue for sending by IP
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
pop ebx
|
|
je .exit
|
|
|
|
push eax
|
|
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
;;; mov edx, [stack_ip]
|
|
; mov ecx, [sockAddr]
|
|
; cmp edx, [ecx + SOCKET.RemoteIP]
|
|
; jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
;;; call queue
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_SYN_RECEIVED stdcall, sockAddr:DWORD
|
|
; In this case, we are expecting an ACK Packet
|
|
; For now, if the Packet is an ACK, process it,
|
|
; If not, ignore it
|
|
|
|
test [edx + 20 + TCP_Packet.Flags], TH_RST
|
|
jz .check_ack
|
|
|
|
push [ebx + SOCKET.OrigRemotePort] [ebx + SOCKET.OrigRemoteIP]
|
|
pop [ebx + SOCKET.RemoteIP] [ebx + SOCKET.RemotePort]
|
|
|
|
mov [ebx + SOCKET.TCBState], TCB_LISTEN
|
|
jmp .exit
|
|
|
|
.check_ack:
|
|
; Look at control flags - expecting an ACK
|
|
test [edx + 20 + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD
|
|
; Here we are expecting data, or a request to close
|
|
; OR both...
|
|
|
|
; Did we receive a FIN or RST?
|
|
test [edx + 20 + TCP_Packet.Flags], TH_FIN
|
|
jz .check_ack
|
|
|
|
; It was a fin or reset.
|
|
|
|
; Remove resend entries from the queue - I dont want to send any more data
|
|
pushad
|
|
|
|
; get skt #
|
|
stdcall net_socket_addr_to_num, ebx
|
|
|
|
mov esi, resendQ
|
|
mov ecx, 0
|
|
|
|
.next_resendq:
|
|
; cmp ecx, NUMRESENDENTRIES
|
|
; je .last_resendq ; None left
|
|
; cmp [esi + 4], eax
|
|
; je @f ; found one
|
|
; inc ecx
|
|
; add esi, 8
|
|
; jmp .next_resendq
|
|
|
|
@@: mov dword[esi + 4], 0
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
.last_resendq:
|
|
popad
|
|
|
|
@@: ; Send an ACK to that fin, and enter closewait state
|
|
|
|
mov [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT
|
|
lea esi, [ebx + SOCKET.RCV_NXT]
|
|
mov eax, [esi] ; save original
|
|
call inc_inet_esi
|
|
;; jmp ste_ack - NO, there may be data
|
|
|
|
.check_ack:
|
|
; Check that we received an ACK
|
|
test [edx + 20 + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
; TODO - done, I think!
|
|
; First, look at the incoming window. If this is less than or equal to 1024,
|
|
; Set the socket window timer to 1. This will stop an additional Packets being queued.
|
|
; ** I may need to tweak this value, since I do not know how many Packets are already queued
|
|
mov cx, [edx + 20 + TCP_Packet.Window]
|
|
xchg cl, ch
|
|
cmp cx, 1024
|
|
ja @f
|
|
|
|
mov [ebx + SOCKET.wndsizeTimer], 1
|
|
|
|
@@: ; OK, here is the deal
|
|
; My recv.nct field holds the seq of the expected next rec byte
|
|
; if the recevied sequence number is not equal to this, do not
|
|
; increment the recv.nxt field, do not copy data - just send a
|
|
; repeat ack.
|
|
|
|
; recv.nxt is in dword [edx+24], in inet format
|
|
; recv seq is in [sktAddr]+56, in inet format
|
|
; just do a comparision
|
|
mov ecx, [ebx + SOCKET.RCV_NXT]
|
|
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT
|
|
jne @f
|
|
mov ecx, eax
|
|
|
|
@@: cmp ecx, [edx + 20 + TCP_Packet.SequenceNumber]
|
|
jne .ack
|
|
|
|
|
|
; Read the data bytes, store in socket buffer
|
|
; movzx ecx, [edx + IP_Packet.TotalLength]
|
|
xchg cl, ch
|
|
sub ecx, 40 ; Discard 40 bytes of header
|
|
ja .data ; Read data, if any
|
|
|
|
; If we had received a fin, we need to ACK it.
|
|
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT
|
|
je .ack
|
|
jmp .exit
|
|
|
|
.data:
|
|
push ebx
|
|
add ebx, SOCKET.lock
|
|
call wait_mutex
|
|
pop ebx
|
|
|
|
push ecx
|
|
push [ebx + SOCKET.PID] ; get socket owner PID
|
|
mov eax, [ebx + SOCKET.rxDataCount]
|
|
add eax, ecx
|
|
cmp eax, SOCKETBUFFSIZE - SOCKETHEADERSIZE
|
|
ja .overflow
|
|
|
|
mov [ebx + SOCKET.rxDataCount], eax ; increment the count of bytes in buffer
|
|
|
|
; point to the location to store the data
|
|
lea edi, [ebx + eax + SOCKETHEADERSIZE]
|
|
sub edi, ecx
|
|
|
|
add edx, 40 ; edx now points to the data
|
|
mov esi, edx
|
|
|
|
cld
|
|
rep movsb ; copy the data across
|
|
mov [ebx + SOCKET.lock], 0 ; release mutex
|
|
|
|
; flag an event to the application
|
|
pop eax
|
|
mov ecx, 1
|
|
mov esi, TASK_DATA + TASKDATA.pid
|
|
|
|
.next_pid:
|
|
cmp [esi], eax
|
|
je .found_pid
|
|
inc ecx
|
|
add esi, 0x20
|
|
cmp ecx, [TASK_COUNT]
|
|
jbe .next_pid
|
|
|
|
.found_pid:
|
|
shl ecx, 8
|
|
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
|
|
|
|
pop ecx
|
|
|
|
; Update our recv.nxt field
|
|
lea esi, [ebx + SOCKET.RCV_NXT]
|
|
call add_inet_esi
|
|
|
|
.ack:
|
|
; Send an ACK
|
|
; Now construct the response, and queue for sending by IP
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
je .exit
|
|
|
|
push eax
|
|
|
|
mov bl, TH_ACK
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
|
|
;;; mov edx, [stack_ip]
|
|
; mov ecx, [sockAddr]
|
|
; cmp edx, [ecx + SOCKET.RemoteIP]
|
|
; jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
;;; call queue
|
|
|
|
.exit:
|
|
ret
|
|
.overflow:
|
|
; no place in buffer
|
|
; so simply restore stack and exit
|
|
pop eax ecx
|
|
mov [ebx + SOCKET.lock], 0
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_FIN_WAIT_1 stdcall, sockAddr:DWORD
|
|
; We can either receive an ACK of a fin, or a fin
|
|
mov al, [edx + 20 + TCP_Packet.Flags]
|
|
and al, TH_FIN + TH_ACK
|
|
|
|
cmp al, TH_ACK
|
|
jne @f
|
|
|
|
; It was an ACK
|
|
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_2
|
|
jmp .exit
|
|
|
|
@@: mov [ebx + SOCKET.TCBState], TCB_CLOSING
|
|
cmp al, TH_FIN
|
|
je @f
|
|
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
|
|
|
|
@@: lea esi, [ebx + SOCKET.RCV_NXT]
|
|
call inc_inet_esi
|
|
|
|
; Send an ACK
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
je .exit
|
|
|
|
push eax
|
|
|
|
mov bl, TH_ACK
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
;;; mov edx, [stack_ip]
|
|
; mov ecx, [sockAddr]
|
|
; cmp edx, [ecx + SOCKET.RemoteIP]
|
|
; jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
;;; call queue
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_FIN_WAIT_2 stdcall, sockAddr:DWORD
|
|
test [edx + 20 + TCP_Packet.Flags], TH_FIN
|
|
jz .exit
|
|
|
|
; Change state, as we have a fin
|
|
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
|
|
|
|
lea esi, [ebx + SOCKET.RCV_NXT]
|
|
call inc_inet_esi
|
|
|
|
; Send an ACK
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
;; cmp ax, NO_BUFFER
|
|
; je .exit
|
|
|
|
push eax
|
|
|
|
mov bl, TH_ACK
|
|
xor ecx, ecx
|
|
xor esi, esi
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
;;; mov edx, [stack_ip]
|
|
mov ecx, [sockAddr]
|
|
cmp edx, [ecx + SOCKET.RemoteIP]
|
|
jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
; Send it.
|
|
pop ebx
|
|
;;; call queue
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_CLOSE_WAIT stdcall, sockAddr:DWORD
|
|
; Intentionally left empty
|
|
; socket_close_tcp handles this
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_CLOSING stdcall, sockAddr:DWORD
|
|
; We can either receive an ACK of a fin, or a fin
|
|
test [edx + 20 + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_LAST_ACK stdcall, sockAddr:DWORD
|
|
; Look at control flags - expecting an ACK
|
|
test [edx + 20 + TCP_Packet.Flags], TH_ACK
|
|
jz .exit
|
|
|
|
; delete the socket
|
|
stdcall net_socket_free, ebx
|
|
|
|
.exit:
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_TIME_WAIT stdcall, sockAddr:DWORD
|
|
ret
|
|
endp
|
|
|
|
|
|
proc stateTCB_CLOSED stdcall, sockAddr:DWORD
|
|
ret
|
|
endp
|
|
|
|
|
|
|
|
;; [53.7] Send data through STREAM socket
|
|
;
|
|
; @param EBX is socket number
|
|
; @param ECX is application data size (number of bytes to send)
|
|
; @param EDX is pointer to application data buffer
|
|
; @return 0 (sent successfully) or -1 (error) in EAX
|
|
;;
|
|
;proc socket_write_tcp stdcall
|
|
;local sockAddr dd ?
|
|
|
|
; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx
|
|
stdcall net_socket_num_to_addr, ebx
|
|
or eax, eax
|
|
jz .error
|
|
|
|
mov ebx, eax
|
|
; mov [sockAddr], ebx
|
|
|
|
; If the sockets window timer is nonzero, do not queue Packet
|
|
cmp [ebx + SOCKET.wndsizeTimer], 0
|
|
jne .error
|
|
|
|
; mov eax, EMPTY_QUEUE
|
|
; call dequeue
|
|
; cmp ax, NO_BUFFER
|
|
; je .error
|
|
|
|
push eax
|
|
|
|
; Get the address of the callers data
|
|
mov edi, [TASK_BASE]
|
|
add edi, TASKDATA.mem_start
|
|
add edx, [edi]
|
|
mov esi, edx
|
|
|
|
pop eax
|
|
push eax
|
|
|
|
push ecx
|
|
mov bl, TH_ACK
|
|
; stdcall build_tcp_Packet, [sockAddr]
|
|
pop ecx
|
|
|
|
; Check destination IP address.
|
|
; If it is the local host IP, route it back to IP_RX
|
|
|
|
pop ebx
|
|
push ecx
|
|
|
|
; mov eax, NET1OUT_QUEUE
|
|
;;; TODO: get device id in edx
|
|
xor edx, edx
|
|
|
|
shl edx, 2
|
|
mov edx, [IP_LIST+edx]
|
|
; mov ecx, [sockAddr]
|
|
; cmp edx, [ecx + SOCKET.RemoteIP]
|
|
; jne .not_local
|
|
; mov eax, IPIN_QUEUE
|
|
|
|
.not_local:
|
|
pop ecx
|
|
push ebx ; save ipbuffer number
|
|
|
|
;;;; call queue
|
|
|
|
; mov esi, [sockAddr]
|
|
|
|
; increament SND.NXT in socket
|
|
; Amount to increment by is in ecx
|
|
add esi, SOCKET.SND_NXT
|
|
call add_inet_esi
|
|
|
|
pop ebx
|
|
|
|
; Copy the IP buffer to a resend queue
|
|
; If there isn't one, dont worry about it for now
|
|
mov esi, resendQ
|
|
mov ecx, 0
|
|
|
|
.next_resendq:
|
|
; cmp ecx, NUMRESENDENTRIES
|
|
je .exit ; None found
|
|
cmp dword[esi + 4], 0
|
|
je @f ; found one
|
|
inc ecx
|
|
add esi, 8
|
|
jmp .next_resendq
|
|
|
|
@@: push ebx
|
|
|
|
; OK, we have a buffer descriptor ptr in esi.
|
|
; resend entry # in ecx
|
|
; Populate it
|
|
; socket #
|
|
; retries count
|
|
; retry time
|
|
; fill IP buffer associated with this descriptor
|
|
|
|
; stdcall net_socket_addr_to_num, [sockAddr]
|
|
mov [esi + 4], eax
|
|
mov byte[esi + 1], TCP_RETRIES
|
|
mov word[esi + 2], TCP_TIMEOUT
|
|
|
|
inc ecx
|
|
; Now get buffer location, and copy buffer across. argh! more copying,,
|
|
; mov edi, resendBuffer - IPBUFFSIZE
|
|
|
|
; @@: add edi, IPBUFFSIZE
|
|
loop @b
|
|
|
|
; we have dest buffer location in edi
|
|
pop eax
|
|
; convert source buffer pointer eax to the absolute address
|
|
; mov ecx, IPBUFFSIZE
|
|
; mul ecx
|
|
; add eax, IPbuffs
|
|
; mov esi, eax
|
|
|
|
; do copy
|
|
; mov ecx, IPBUFFSIZE
|
|
; cld
|
|
rep movsb
|
|
|
|
.exit:
|
|
xor eax, eax
|
|
ret
|
|
|
|
.error:
|
|
or eax, -1
|
|
ret
|
|
;endp
|
|
|
|
|
|
|
|
;***************************************************************************
|
|
; Function
|
|
; checksum
|
|
;
|
|
; Description
|
|
; checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult
|
|
; Dont break anything; Most registers are used by the caller
|
|
; This code is derived from the 'C' source, cksum.c, in the book
|
|
; Internetworking with TCP/IP Volume II by D.E. Comer
|
|
;
|
|
;***************************************************************************
|
|
|
|
|
|
checksum:
|
|
pusha
|
|
; mov eax, [checkAdd1]
|
|
xor edx, edx ; edx is the accumulative checksum
|
|
xor ebx, ebx
|
|
; mov cx, [checkSize1]
|
|
shr cx, 1
|
|
jz cs1_1
|
|
|
|
cs1:
|
|
mov bh, [eax]
|
|
mov bl, [eax + 1]
|
|
|
|
add eax, 2
|
|
add edx, ebx
|
|
|
|
loopw cs1
|
|
|
|
cs1_1:
|
|
; and word [checkSize1], 0x01
|
|
jz cs_test2
|
|
|
|
mov bh, [eax]
|
|
xor bl, bl
|
|
|
|
add edx, ebx
|
|
|
|
cs_test2:
|
|
; mov cx, [checkSize2]
|
|
cmp cx, 0
|
|
jz cs_exit ; Finished if no 2nd buffer
|
|
|
|
; mov eax, [checkAdd2]
|
|
|
|
shr cx, 1
|
|
jz cs2_1
|
|
|
|
cs2:
|
|
mov bh, [eax]
|
|
mov bl, [eax + 1]
|
|
|
|
add eax, 2
|
|
add edx, ebx
|
|
|
|
loopw cs2
|
|
|
|
cs2_1:
|
|
; and word [checkSize2], 0x01
|
|
jz cs_exit
|
|
|
|
mov bh, [eax]
|
|
xor bl, bl
|
|
|
|
add edx, ebx
|
|
|
|
cs_exit:
|
|
mov ebx, edx
|
|
|
|
shr ebx, 16
|
|
and edx, 0xffff
|
|
add edx, ebx
|
|
mov eax, edx
|
|
shr eax, 16
|
|
add edx, eax
|
|
not dx
|
|
|
|
; mov [checkResult], dx
|
|
popa
|
|
ret
|
|
|