Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 1160 → Rev 1161

/kernel/branches/net/network/ARP.inc
0,0 → 1,725
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ARP.INC ;;
;; ;;
;; Address Resolution Protocol ;;
;; ;;
;; This file contains the following: ;;
;; arp_table_manager - Manages an ARPTable ;;
;; arp_request - Sends an ARP request on the ethernet ;;
;; arp_handler - Called when an ARP packet is received ;;
;; ;;
;; Changes history: ;;
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;;
;; 11.11.2006 - [Johnny_B] and [smb] ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 983 $
 
 
ARP_NO_ENTRY equ 0
ARP_VALID_MAPPING equ 1
ARP_AWAITING_RESPONSE equ 2
ARP_RESPONSE_TIMEOUT equ 3
 
ETHER_ARP equ 0x0608
 
ARP_REQ_OPCODE equ 0x0100 ; request
ARP_REP_OPCODE equ 0x0200 ; reply
 
ARP_TABLE_SIZE equ 20 ; Size of table
 
struct ARP_ENTRY
.IP dd ?
.MAC dp ?
.Status dw ?
.TTL dw ? ; in seconds
.size:
ends
 
struct ARP_Packet
.HardwareType dw ?
.ProtocolType dw ?
.HardwareSize db ?
.ProtocolSize db ?
.Opcode dw ?
.SenderMAC dp ?
.SenderIP dd ?
.TargetMAC dp ?
.TargetIP dd ?
ends
 
 
; The TTL field is decremented every second, and is deleted when it
; reaches 0. It is refreshed every time a packet is received
; If the TTL field is 0xFFFF it is a static entry and is never deleted
; The status field can be the following values:
; 0x0000 entry not used
; 0x0001 entry holds a valid mapping
; 0x0002 entry contains an IP address, awaiting ARP response
; 0x0003 No response received to ARP request.
; The last status value is provided to allow the network layer to delete
; a packet that is queued awaiting an ARP response
 
align 4
uglobal
 
NumARP dd ?
ARPTable rb ARP_ENTRY.size * ARP_TABLE_SIZE
 
ARP_PACKETS_TX rd MAX_NET_DEVICES
ARP_PACKETS_RX rd MAX_NET_DEVICES
 
 
endg
 
 
 
 
ARP_init:
 
xor eax, eax
 
mov [NumARP], eax
 
mov edi, ARP_PACKETS_TX
mov ecx, 2*MAX_NET_DEVICES
rep stosd
 
ret
 
 
 
 
;***************************************************************************
; Function
; arp_table_manager [by Johnny_B]
;
; Description
; Does a most required operations with ARP-table
; IN:
; Operation: see Opcode's constants below
; Index: Index of entry in the ARP-table
; Extra: Extra parameter for some Opcodes
; OUT:
; EAX = Returned value depends on opcodes, more detailed see below
;
;***************************************************************************
;Opcode's constants
ARP_TABLE_ADD equ 1
ARP_TABLE_DEL equ 2
ARP_TABLE_GET equ 3
ARP_TABLE_GET_ENTRIES_NUMBER equ 4
ARP_TABLE_IP_TO_MAC equ 5
ARP_TABLE_TIMER equ 6
 
;Index's constants
EXTRA_IS_ARP_PACKET_PTR equ 0 ;if Extra contain pointer to ARP_Packet
EXTRA_IS_ARP_ENTRY_PTR equ -1 ;if Extra contain pointer to ARP_ENTRY
 
align 4
proc arp_table_manager stdcall uses ebx esi edi ecx edx, Opcode:DWORD,Index:DWORD,Extra:DWORD
 
mov ebx, ARPTable ;ARPTable base
mov ecx, dword[NumARP] ;ARP-entries counter
 
mov eax, dword[Opcode]
 
cmp eax, ARP_TABLE_TIMER
je .timer
 
DEBUGF 1,"ARP table manager opcode:%u numARP:%u\n",eax,ecx
 
cmp eax, ARP_TABLE_ADD
je .add
cmp eax, ARP_TABLE_DEL
je .del
cmp eax, ARP_TABLE_GET
je .get
cmp eax, ARP_TABLE_IP_TO_MAC
je .ip_to_mac
cmp eax, ARP_TABLE_GET_ENTRIES_NUMBER
je .get_entries_number
jmp .exit ;if unknown opcode
 
 
;;BEGIN TIMER
;;Description: it must be callback every second. It is responsible for removing expired routes.
;;IN: Operation: ARP_TABLE_TIMER
;; Index: must be zero
;; Extra: must be zero
;;OUT:
;; EAX=not defined
;;
.timer:
test ecx, ecx
jz .exit ;if NumARP=0 nothing to do
; sub ecx, ARP_TABLE_ENTRIES ;ecx=dynamic entries number
; jz .exit ;if NumARP=number of static entries then exit
 
; add ebx, ARP_TABLE_ENTRIES*ARP_ENTRY_SIZE ;ebx=dynamic entries base
 
.timer_loop:
movsx esi, word [ebx + ARP_ENTRY.TTL]
cmp esi, 0xFFFFFFFF
je .timer_loop_end ;if TTL==0xFFFF then it's static entry
 
test esi, esi
jnz .timer_loop_end_with_dec ;if TTL!=0
 
; Ok, TTL is 0
;if Status==AWAITING_RESPONSE and TTL==0
;then we have to change it to ARP_RESPONSE_TIMEOUT
cmp word [ebx + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE
jne @f
 
mov word [ebx + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT
mov word [ebx + ARP_ENTRY.TTL], word 0x000A ;10 sec
jmp .timer_loop_end
 
@@:
;if TTL==0 and Status==VALID_MAPPING, we have to delete it
;if TTL==0 and Status==RESPONSE_TIMEOUT, delete too
mov esi, dword[NumARP]
sub esi, ecx ;esi=index of entry, will be deleted
stdcall arp_table_manager,ARP_TABLE_DEL,esi,0 ;opcode,index,extra
jmp .timer_loop_end
 
 
.timer_loop_end_with_dec:
dec word [ebx + ARP_ENTRY.TTL] ;decrease TTL
.timer_loop_end:
add ebx, ARP_ENTRY.size
loop .timer_loop
 
jmp .exit
;;END TIMER
 
;;BEGIN ADD
;;Description: it adds an entry in the table. If ARP-table already
;; contains same IP, it will be updated.
;;IN: Operation: ARP_TABLE_ADD
;; Index: specifies what contains Extra-parameter
;; Extra: if Index==EXTRA_IS_ARP_Packet_PTR,
;; then Extra contains pointer to ARP_Packet,
;; otherwise Extra contains pointer to ARP_ENTRY
;;OUT:
;; EAX=index of entry, that has been added
;;
.add:
 
DEBUGF 1,"1"
 
sub esp, ARP_ENTRY.size ;Allocate ARP_ENTRY_SIZE byte in stack
 
mov esi, [Extra] ;pointer
mov edi, [Index] ;opcode
 
cmp edi, EXTRA_IS_ARP_PACKET_PTR
je .ARP_Packet_to_entry ;if Extra contain ptr to ARP_Packet and we have to form arp-entry
;else it contain ptr to arp-entry
 
DEBUGF 1,"2"
 
cld
; esi already has been loaded
mov edi, esp ;ebx + eax=ARPTable_base + ARP-entry_base(where we will add)
mov ecx,ARP_ENTRY.size/2 ;ARP_ENTRY_SIZE must be even number!!!
rep movsw ;copy
jmp .search
 
.ARP_Packet_to_entry:
 
DEBUGF 1,"3"
mov edx, dword[esi + ARP_Packet.SenderIP] ;esi=base of ARP_Packet
mov [esp + ARP_ENTRY.IP], edx
 
cld
lea esi, [esi + ARP_Packet.SenderMAC]
lea edi, [esp + ARP_ENTRY.MAC]
movsd
movsw
mov word[esp + ARP_ENTRY.Status], ARP_VALID_MAPPING ; specify the type - a valid entry
mov word[esp + ARP_ENTRY.TTL], 0x0E10 ; = 1 hour
 
.search:
 
DEBUGF 1,"4"
mov edx, dword[esp + ARP_ENTRY.IP] ;edx=IP-address, which we'll search
mov ecx, dword[NumARP] ;ecx=ARP-entries counter
jecxz .add_to_end ;if ARP-entries number == 0
imul eax, ecx, ARP_ENTRY.size ;eax=current table size(in bytes)
@@:
sub eax, ARP_ENTRY.size
cmp dword[ebx + eax + ARP_ENTRY.IP], edx
loopnz @b
; jz .replace ; found, replace existing entry, ptr to it is in eax
 
; .add_to_end:
;
; DEBUGF 1,"5\n"
; ;else add to end
; or eax,-1 ;set eax=0xFFFFFFFF if adding is impossible
; mov ecx, dword[NumARP]
; cmp ecx, ARP_TABLE_SIZE
; je .add_exit ;if arp-entries number is equal to arp-table maxsize
 
; imul eax, dword[NumARP], ARP_ENTRY.size ;eax=ptr to end of ARPTable
; inc dword [NumARP] ;increase ARP-entries counter
 
; .replace:
DEBUGF 1,"Updating ARP entry: %x-%x-%x-%x-%x-%x = %u.%u.%u.%u to slot:%u\n",\
[esp + ARP_ENTRY.MAC]:2,[esp + ARP_ENTRY.MAC+1]:2,[esp + ARP_ENTRY.MAC+2]:2,[esp + ARP_ENTRY.MAC+3]:2,[esp + ARP_ENTRY.MAC+4]:2,[esp + ARP_ENTRY.MAC+5]:2,\
[esp + ARP_ENTRY.IP]:1,[esp + ARP_ENTRY.IP+1]:1,[esp + ARP_ENTRY.IP+2]:1,[esp + ARP_ENTRY.IP+3]:1,eax
 
cld
mov esi, esp ;esp=base of ARP-entry, that will be added
lea edi, [ebx + eax] ;ebx + eax=ARPTable_base + ARP-entry_base(where we will add)
mov ecx,ARP_ENTRY.size/2 ;ARP_ENTRY_SIZE must be even number!!!
rep movsw
 
mov ecx, ARP_ENTRY.size
xor edx, edx ;"div" takes operand from EDX:EAX
div ecx ;eax=index of entry, which has been added
 
 
 
.add_exit:
 
add esp, ARP_ENTRY.size ;free stack
jmp .exit
;;END ADD
 
;;BEGIN DEL
;;Description: it deletes an entry in the table.
;;IN: Operation: ARP_TABLE_DEL
;; Index: index of entry, that should be deleted
;; Extra: must be zero
;;OUT:
;; EAX=not defined
;;
.del:
mov esi, [Index]
imul esi, ARP_ENTRY.size
 
mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size
sub ecx, esi
 
lea edi, [ebx + esi] ;edi=ptr to entry that should be deleted
lea esi, [edi + ARP_ENTRY.size] ;esi=ptr to next entry
 
shr ecx,1 ;ecx/2 => ARP_ENTRY_SIZE MUST BE EVEN NUMBER!
cld
rep movsw
 
dec dword[NumARP] ;decrease arp-entries counter
jmp .exit
;;END DEL
 
;;BEGIN GET
;;Description: it reads an entry of table into buffer.
;;IN: Operation: ARP_TABLE_GET
;; Index: index of entry, that should be read
;; Extra: pointer to buffer for reading(size must be equal to ARP_ENTRY_SIZE)
;;OUT:
;; EAX=not defined
;;
.get:
mov esi, [Index]
imul esi, ARP_ENTRY.size ;esi=ptr to required ARP_ENTRY
mov edi, [Extra] ;edi=buffer for reading
mov ecx, ARP_ENTRY.size/2 ; must be even number!!!
cld
rep movsw
jmp .exit
;;END GET
 
;;BEGIN IP_TO_MAC
;;Description: it gets an IP from Index, scans each entry in the table and writes
;; MAC, that relates to specified IP, into buffer specified in Extra.
;; And if it cannot find an IP-address in the table, it does an ARP-request of that.
;;IN: Operation: ARP_TABLE_IP_TO_MAC
;; Index: IP that should be transformed into MAC
;; Extra: pointer to buffer where will be written the MAC-address.
;;OUT:
;; EAX=ARP table entry status code.
;; If EAX==ARP_NO_ENTRY, IP isn't found in the table and we have sent the request.
;; If EAX==ARP_AWAITING_RESPONSE, we wait the response from remote system.
;; If EAX==ARP_RESPONSE_TIMEOUT, remote system not responds too long.
;; If EAX==ARP_VALID_MAPPING, all is ok, we've got a true MAC.
;;
;; If MAC will equal to a zero, in the buffer. It means, that IP-address was not yet
;; resolved, or that doesn't exist. I recommend you, to do at most 3-5 calls of this
;; function with 1sec delay. sure, only if it not return a valid MAC after a first call.
;;
.ip_to_mac:
 
DEBUGF 1,"Trying to find MAC for %u.%u.%u.%u\n",[Index]:1,[Index+1]:1,[Index+2]:1,[Index+3]:1
 
xor eax, eax
mov edi, dword[Extra]
cld
stosd
stosw
 
 
; first, check destination IP to see if it is on 'this' network.
; The test is:
; if ( destIP & subnet_mask == stack_ip & subnet_mask )
; destination is local
; else
; destination is remote, so pass to gateway
 
 
;;; TODO: get device number ! (in edx)
xor edx, edx
 
mov eax, [Index] ;eax=required IP
mov esi, eax
and esi, [SUBNET_LIST+edx]
mov ecx, [IP_LIST+edx]
and ecx, [SUBNET_LIST+edx]
cmp esi, ecx
je @f ;if we and target IP are located in the same network
mov eax, [GATEWAY_LIST+edx]
mov [Index], eax
DEBUGF 1,"IP is not on subnet, using %u.%u.%u.%u instead\n",[Index]:1,[Index+1]:1,[Index+2]:1,[Index+3]:1
@@:
 
cmp dword[NumARP], 0
je .ip_to_mac_send_request ;if ARP-table not contain an entries, we have to request IP.
;EAX will be containing a zero, it's equal to ARP_NO_ENTRY
 
mov ecx, dword[NumARP]
imul esi, ecx, ARP_ENTRY.size ;esi=current ARP-table size
 
@@:
sub esi, ARP_ENTRY.size
cmp [ebx + esi + ARP_ENTRY.IP], eax ; ebx=ARPTable base
loopnz @b ; Return back if non match
jnz .ip_to_mac_send_request ; and request IP->MAC if none found in the table
 
; Return the entry status in eax
movzx eax, word[ebx + esi + ARP_ENTRY.Status]
 
DEBUGF 1,"MAC found: %x-%x-%x-%x-%x-%x status:%x in slot:%u\n",\
[ebx + esi + ARP_ENTRY.MAC]:2,[ebx + esi + ARP_ENTRY.MAC+1]:2,[ebx + esi + ARP_ENTRY.MAC+2]:2,[ebx + esi + ARP_ENTRY.MAC+3]:2,[ebx + esi + ARP_ENTRY.MAC+4]:2,[ebx + esi + ARP_ENTRY.MAC+5]:2, ax, esi
 
; esi holds index
cld
lea esi, [ebx + esi + ARP_ENTRY.MAC]
mov edi, [Extra] ;edi=ptr to buffer for write MAC
movsd
movsw
jmp .exit
 
.ip_to_mac_send_request:
;;; TODO: get device number ! (in edx)
xor edx, edx
mov edx, [ETH_DRV_LIST + 4*edx]
lea ecx, [edx + ETH_DEVICE.mac]
 
stdcall arp_request,[Index],[IP_LIST+edx],ecx ;TargetIP,SenderIP_ptr,SenderMAC_ptr
mov eax, ARP_NO_ENTRY
jmp .exit
 
;;END IP_TO_MAC
 
;;BEGIN GET_ENTRIES_NUMBER
;;Description: returns an ARP-entries number in the ARPTable
;;IN: Operation: ARP_TABLE_GET_ENTRIES_NUMBER
;; Index: must be zero
;; Extra: must be zero
;;OUT:
;; EAX=ARP-entries number in the ARPTable
.get_entries_number:
mov eax, dword[NumARP]
jmp .exit
;;END GET_ENTRIES_NUMBER
 
.exit:
ret
endp
 
 
;***************************************************************************
; Function
; arp_request [by Johnny_B]
;
; Description
; Sends an ARP request on the ethernet
; IN:
; TargetIP : requested IP address
; SenderIP_ptr : POINTER to sender's IP address(our system's address)
; SenderMAC_ptr : POINTER to sender's MAC address(our system's address)
; OUT:
; EAX=0 (if all is ok), otherwise EAX is not defined
;
; EBX,ESI,EDI will be saved
;
;***************************************************************************
proc arp_request stdcall uses ebx esi edi,\
TargetIP:DWORD, SenderIP_ptr:DWORD, SenderMAC_ptr:DWORD
 
DEBUGF 1,"Create ARP request\n"
 
 
stdcall kernel_alloc, 60 ; minimum eth packet size
test eax, eax
jz .exit
 
mov ebx, eax
 
mov word [ebx + ETH_FRAME.Data + ARP_Packet.HardwareType], 0x0100 ;Ethernet
mov word [ebx + ETH_FRAME.Data + ARP_Packet.ProtocolType], 0x0008 ;IP
mov byte [ebx + ETH_FRAME.Data + ARP_Packet.HardwareSize], 0x06 ;MAC-addr length
mov byte [ebx + ETH_FRAME.Data + ARP_Packet.ProtocolSize], 0x04 ;IP-addr length
mov word [ebx + ETH_FRAME.Data + ARP_Packet.Opcode], 0x0100 ;Request
 
DEBUGF 1,"1"
 
cld
mov esi, [SenderMAC_ptr]
lea edi, [ebx + ETH_FRAME.Data + ARP_Packet.SenderMAC] ;Our MAC-addr
movsd
movsw
 
DEBUGF 1,"2"
 
mov esi, [SenderIP_ptr]
mov [ebx + ETH_FRAME.Data + ARP_Packet.SenderIP], esi ;Our IP-addr
; movsd
 
DEBUGF 1,"3"
 
lea edi, [ebx + ETH_FRAME.Data + ARP_Packet.TargetMAC] ; Required MAC-addr
xor eax, eax
stosd
stosw
 
DEBUGF 1,"4"
 
lea edi, [ebx + ETH_FRAME.DstMAC]
stosd
stosw
 
DEBUGF 1,"5"
 
mov esi, [TargetIP]
mov dword [ebx + ETH_FRAME.Data + ARP_Packet.TargetIP], esi ;Required IP-addr(we get it as function parameter)
 
 
DEBUGF 1,"6"
 
mov esi, [SenderMAC_ptr]
lea edi, [ebx + ETH_FRAME.SrcMAC]
movsd
movsw
 
DEBUGF 1,"7"
 
mov ax , ETHER_ARP
stosw
 
DEBUGF 1,"8"
 
;;; TODO: get device number in edx !!
xor edx, edx
shl edx, 2
 
push dword .returnaddr
push dword 60
push ebx
mov ebx, [ETH_DRV_LIST + edx]
jmp [ebx + ETH_DEVICE.transmit]
.returnaddr:
 
; Add an entry in the ARP table, awaiting response
sub esp, ARP_ENTRY.size ;allocate memory for ARP-entry
 
mov esi, dword[TargetIP]
mov dword[esp + ARP_ENTRY.IP],esi
 
lea edi, [esp + ARP_ENTRY.MAC]
xor eax, eax
stosd
stosw
 
mov word[esp + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE
mov word[esp + ARP_ENTRY.TTL], 10 ; 10 seconds
 
stdcall arp_table_manager,ARP_TABLE_ADD,EXTRA_IS_ARP_ENTRY_PTR,esp
add esp, ARP_ENTRY.size ; free memory
 
.exit:
 
DEBUGF 1,"ARP request - end\n"
ret
endp
 
 
 
 
 
;-----------------------------------------------------
;
; ARP_Handler:
;
; This function handles ARP protocol over ethernet
; (other protocols may follow in the future)
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; packet size (without ethernet header) in ecx
; OUT: /
;
;-----------------------------------------------------
align 4
ARP_Handler:
 
DEBUGF 1,"ARP_Handler - start\n"
cmp ecx, 28
jl .exit
 
; Is this a REQUEST?
; Is this a request for My Host IP
; Yes - So construct a response message.
; Send this message to the ethernet card for transmission
 
; push ebx edx
stdcall arp_table_manager, ARP_TABLE_ADD, EXTRA_IS_ARP_PACKET_PTR, edx
; pop edx ebx
 
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet?
jne .exit
 
call ETH_struc2dev
DEBUGF 1,"Packet came from device: %u\n", edi
cmp edi, -1
jz .exit
 
mov eax, edi
shl eax, 2
add eax, IP_LIST
mov eax, [eax]
cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address?
jnz .exit
push eax
push edi
 
; DEBUGF 1,"ETH_ARP_Handler - request for %u.%u.%u.%u\n",[edi+0]:1,[edi+1]:1,[edi+2]:1,[edi+3]:1
 
; OK, it is a request for one of our MAC addresses. Build the frame and send it
; We can reuse the buffer.
 
cld
lea esi, [edx + ARP_Packet.SenderMAC]
lea edi, [edx + ARP_Packet.TargetMAC]
movsd ; Move Sender Mac to Dest MAC
movsw ;
movsd ; Move sender IP to Dest IP
 
pop esi
mov esi, [ETH_DRV_LIST + 4*esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_Packet.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
movsw ;
pop eax
stosd ; Write our IP
 
mov word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE
 
; Now, Fill in ETHERNET header
 
mov edi, [esp]
lea esi, [edx + ARP_Packet.TargetMAC]
movsd
movsw
lea esi, [edx + ARP_Packet.SenderMAC]
movsd
movsw
mov ax , ETHER_ARP
stosw
 
jmp ETH_Sender ; And send it!
 
.exit:
call kernel_free
add esp, 4 ; pop (balance stack)
 
DEBUGF 1,"ARP_Handler - fail\n"
ret
 
 
 
 
;---------------------------------------------------------------------------
;
; ARP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
 
align 4
ARP_API:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
dec bl
jz .entries ; 2
dec bl
jz .read ; 3
dec bl
jz .write ; 4
dec bl
jz .remove ; 5
dec bl
 
 
.error:
mov eax, -1
ret
 
.packets_tx:
add eax, ARP_PACKETS_TX
mov eax, [eax]
ret
 
.packets_rx:
add eax, ARP_PACKETS_RX
mov eax, [eax]
ret
 
.entries:
mov eax, [NumARP]
ret
 
.read:
; TODO: write code
ret
 
.write:
; TODO: write code
ret
 
.remove:
; TODO: write code
ret
 
/kernel/branches/net/network/IPv4.inc
0,0 → 1,742
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 922 $
 
; IP underlying protocols numbers
 
ETHER_IPv4 equ 0x0008 ; Reversed from 0800 for intel
 
MAX_FRAGMENTS equ 16
MAX_IP equ MAX_NET_DEVICES
 
struct IPv4_Packet
.VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits]
.TypeOfService db ?
.TotalLength dw ?
.Identification dw ?
.FlagsAndFragmentOffset dw ? ; Flags[0-2] and FragmentOffset[3-15]
.TimeToLive db ? ;
.Protocol db ?
.HeaderChecksum dw ?
.SourceAddress dd ?
.DestinationAddress dd ?
.DataOrOptional:
ends
 
struct FRAGMENT_slot
.ttl dw ? ; Time to live for this entry, 0 for empty slot's
.id dw ? ; Identification field from IP header
.SrcIP dd ? ; .. from IP header
.DstIP dd ? ; .. from IP header
.ptr dd ? ; Pointer to first packet
.size:
ends
 
struct FRAGMENT_entry ; This structure will replace the ethernet header in fragmented ip packets
.PrevPtr dd ? ; Pointer to previous fragment entry (-1 for first packet)
.NextPtr dd ? ; Pointer to next fragment entry (-1 for last packet)
.Owner dd ? ; Pointer to structure of driver
rb 2 ; to match ethernet header size
.Data: ; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends
 
align 4
uglobal
BROADCAST dd ?
IP_LIST rd MAX_IP
SUBNET_LIST rd MAX_IP
DNS_LIST rd MAX_IP
GATEWAY_LIST rd MAX_IP
IP_PACKETS_TX rd MAX_IP
IP_PACKETS_RX rd MAX_IP
FRAGMENT_LIST rb MAX_FRAGMENTS*FRAGMENT_slot.size
endg
 
 
;-----------------------------------------------------------------
;
; IPv4_init
;
; This function resets all IP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
IPv4_init:
 
or eax, -1
mov edi, BROADCAST
mov ecx, 1+4*MAX_IP
rep stosd
 
xor eax, eax
mov edi, FRAGMENT_LIST
mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP
rep stosd
 
ret
 
 
 
;-----------------------------------------------------------------
;
; IP_Handler:
;
; Called by eth_handler,
; will check if IP Packet isnt damaged
; and call appropriate handler. (TCP/UDP/ICMP/..)
;
; It will also re-construct fragmented packets
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; pointer to IP Packet data in edx
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
IPv4_Handler:
 
DEBUGF 1,"IP_Handler - start\n"
mov cx , [edx + IPv4_Packet.HeaderChecksum]
xchg ch , cl ; Get the checksum in intel format
mov word [edx + IPv4_Packet.HeaderChecksum], 0 ; Clear checksum field to recalculating checksum
 
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
and eax, 0x0000000F ;
shl eax, 2 ;
 
push edx
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size
pop edx
cmp cx , ax
jnz .dump ; if CHECKSUM isn't valid then dump Packet
 
mov eax, [edx + IPv4_Packet.DestinationAddress]
mov edi, BROADCAST
mov ecx, MAX_IP+1
repnz scasd
jz .ip_ok
 
not eax
test eax, 127 shl 24 ; 127.x.x.x
jz .ip_ok
 
; TODO: we need to check for broadcasts (other then 255.255.255.255)
 
jmp .dump
 
.ip_ok:
 
DEBUGF 1,"IP_Handler - packet from %u.%u.%u.%u\n",\
[edx + IPv4_Packet.SourceAddress]:1,[edx + IPv4_Packet.SourceAddress + 1]:1,[edx + IPv4_Packet.SourceAddress + 2]:1,[edx + IPv4_Packet.SourceAddress + 3]:1
 
mov al , [edx + IPv4_Packet.VersionAndIHL]
and al , 0x0f ; get IHL(header length)
cmp al , 0x05 ; IHL!= 5*4(20 bytes)
jnz .dump ; TODO: dont dump packets wich have optional fiels !!! /!\
 
cmp byte [edx + IPv4_Packet.TimeToLive], 0
je .dump
 
movzx eax, word [edx + IPv4_Packet.FlagsAndFragmentOffset]
xchg al , ah
 
test ax , 1 shl 13 ; Is 'more fragments' flag set ?
jnz .yes_fragments ; If so, we definately have a fragmented packet
 
test ax , 0x1fff ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
jnz .last_fragment
 
.handle_it: ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
and eax, 0x0000000F ;
shl eax, 2 ;
 
movzx ecx, word [edx + IPv4_Packet.TotalLength] ; Calculate length of encapsulated Packet
xchg cl , ch ;
sub ecx, eax ;
 
add eax, edx
push eax
mov al , [edx + IPv4_Packet.Protocol]
pop edx ; Offset to data (tcp/udp/icmp/.. Packet)
 
cmp al , IP_PROTO_TCP
; je TCP_Handler
 
cmp al , IP_PROTO_UDP
je UDP_Handler
 
cmp al , IP_PROTO_ICMP
je ICMP_Handler
 
DEBUGF 1,"IP_Handler - unknown protocol:%u\n",al
 
.dump:
DEBUGF 1,"IP_Handler - done\n"
; inc [dumped_rx_count]
call kernel_free
add esp, 4 ; pop (balance stack)
ret
 
 
.yes_fragments:
shl ax , 3
DEBUGF 1,"Fragmented packet, offset:%u, id:%x\n", ax, [edx + IPv4_Packet.Identification]:4
 
test ax , ax ; Is this the first packet of the fragment?
jnz .not_first_fragment
 
DEBUGF 1,"First fragmented packet received!\n"
; try to locate a free slot..
mov ecx, MAX_FRAGMENTS
mov esi, FRAGMENT_LIST
.find_free_slot:
cmp word [esi + FRAGMENT_slot.ttl], 0
je .found_free_slot
add esi, FRAGMENT_slot.size
loop .find_free_slot
jmp .dump ; If no free slot was found, dump the packet
 
.found_free_slot: ; We found a free slot, let's fill in the FRAGMENT_slot structure
mov word [esi + FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl
mov ax , word [edx + IPv4_Packet.Identification]
mov word [esi + FRAGMENT_slot.id], ax
mov eax, dword [edx + IPv4_Packet.SourceAddress]
mov dword [esi + FRAGMENT_slot.SrcIP], eax
mov eax, dword [edx + IPv4_Packet.DestinationAddress]
mov dword [esi + FRAGMENT_slot.DstIP], eax
pop eax
mov dword [esi + FRAGMENT_slot.ptr], eax
; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
mov [eax + FRAGMENT_entry.NextPtr], -1
mov [eax + FRAGMENT_entry.PrevPtr], -1
mov [eax + FRAGMENT_entry.Owner], ebx
 
add esp, 4 ; balance stack and exit
ret
 
 
 
.not_first_fragment:
DEBUGF 1,"Middle fragmented packet received!\n"
 
call .find_fragment_slot
cmp esi, -1
je .dump
 
mov word [esi + FRAGMENT_slot.ttl], 15 ; Reset the ttl
mov esi, [esi + FRAGMENT_slot.ptr]
or edi, -1
.find_last_entry: ; The following routine will try to find the last entry
cmp edi, [esi + FRAGMENT_entry.PrevPtr]
jne .destroy_slot ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov edi, esi
mov esi, [esi + FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .find_last_entry
; We found the last entry (pointer is noww in edi)
; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure
 
pop eax ; pointer to packet
mov [edi + FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry
mov [eax + FRAGMENT_entry.NextPtr], -1
mov [eax + FRAGMENT_entry.PrevPtr], edi
mov [eax + FRAGMENT_entry.Owner], ebx
 
add esp, 4
ret
 
 
 
.last_fragment:
DEBUGF 1,"Last fragmented packet received!\n"
call .find_fragment_slot
cmp esi, -1
je .dump
 
mov esi, [esi + FRAGMENT_slot.ptr] ; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer
push esi
xor eax, eax ;
or edi, -1
.count_bytes:
cmp [esi + FRAGMENT_entry.PrevPtr], edi
jne .destroy_slot_pop ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov cx, word [esi + FRAGMENT_entry.Data + IPv4_Packet.TotalLength] ; Add total length
xchg cl, ch
DEBUGF 1,"Packet size: %u\n", cx
add ax, cx
movzx cx, byte [esi + FRAGMENT_entry.Data + IPv4_Packet.VersionAndIHL] ; Sub Header length
and cx, 0x000F
shl cx, 2
DEBUGF 1,"Header size: %u\n", cx
sub ax, cx
mov edi, esi
mov esi, [esi + FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .count_bytes
 
mov esi, [esp+4] ;;;
mov [edi + FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code
mov [esi + FRAGMENT_entry.NextPtr], -1
mov [esi + FRAGMENT_entry.PrevPtr], edi
mov [esi + FRAGMENT_entry.Owner], ebx
 
mov cx, [edx + IPv4_Packet.TotalLength] ; Note: This time we dont substract Header length
xchg cl , ch
DEBUGF 1,"Packet size: %u\n", cx
add ax , cx
DEBUGF 1,"Total Received data size: %u\n", eax
 
push eax
mov ax , [edx + IPv4_Packet.FlagsAndFragmentOffset]
xchg al , ah
shl ax , 3
add cx , ax
pop eax
DEBUGF 1,"Total Fragment size: %u\n", ecx
 
cmp ax, cx
jne .destroy_slot_pop
 
push eax
push eax
call kernel_alloc
test eax, eax
je .destroy_slot_pop ; If we dont have enough space to allocate the buffer, discard all packets in slot
mov edx, [esp+4] ; Get pointer to first fragment entry back in edx
 
.rebuild_packet_loop:
movzx ecx, word [edx + FRAGMENT_entry.Data + IPv4_Packet.FlagsAndFragmentOffset] ; Calculate the fragment offset
xchg cl , ch ; intel byte order
shl cx , 3 ; multiply by 8 and clear first 3 bits
DEBUGF 1,"Fragment offset: %u\n", cx
 
lea edi, [eax + ecx] ; Notice that edi will be equal to eax for first fragment
movzx ebx, byte [edx + FRAGMENT_entry.Data + IPv4_Packet.VersionAndIHL] ; Find header size (in ebx) of fragment
and bx , 0x000F ;
shl bx , 2 ;
 
lea esi, [edx + FRAGMENT_entry.Data] ; Set esi to the correct begin of fragment
movzx ecx, word [edx + FRAGMENT_entry.Data + IPv4_Packet.TotalLength] ; Calculate total length of fragment
xchg cl, ch ; intel byte order
 
cmp edi, eax ; Is this packet the first fragment ?
je .first_fragment
sub cx, bx ; If not, dont copy the header
add esi, ebx ;
.first_fragment:
 
push cx ; First copy dword-wise, then byte-wise
shr cx, 2 ;
rep movsd ;
pop cx ;
and cx, 3 ;
rep movsb ;
 
push eax
push edx ; Push pointer to fragment onto stack
mov edx, [edx + FRAGMENT_entry.NextPtr] ; Set edx to the next pointer
call kernel_free ; free the previous fragment buffer (this uses the value from stack)
pop eax
cmp edx, -1 ; Check if it is last fragment in chain
jne .rebuild_packet_loop
 
pop ecx ;
xchg cl, ch
mov edx, eax
mov word [edx + IPv4_Packet.TotalLength], cx
add esp, 8
 
xchg cl, ch ; This prints the IP packet to the debug board (usefull when using serial output debug..)
push ecx ;;;;
push eax ;;;;
; mov esi, edx ;
; ;
; @@: ;
; lodsb ;
; DEBUGF 1,"%x ", eax:2 ;
; loop @r ;
 
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
and ax, 0x000F ;
shl ax, 2 ;
sub ecx, eax ;
add eax, edx
push eax
mov al , [edx + IPv4_Packet.Protocol]
pop edx ; Offset to data (tcp/udp/icmp/.. Packet)
 
; cmp al , PROTOCOL_TCP
; je TCP_Handler
 
; cmp al , PROTOCOL_UDP
; je UDP_Handler
 
cmp al , IP_PROTO_ICMP
je ICMP_Handler_fragments
 
DEBUGF 1,"IP_Handler - unknown protocol:%u\n",al
 
jmp .dump
 
 
.destroy_slot_pop:
add esp, 4
.destroy_slot:
DEBUGF 1,"Destroy fragment slot! MUaHaHAhAHA!\n"
; TODO!
jmp .dump
 
 
 
;-----------------------------------------------------------------
;
; find fragment slot
;
; IN: pointer to fragmented packet in edx ; TODO: the RFC says we should check protocol too
; OUT: pointer to slot in edi, -1 on error
;
;-----------------------------------------------------------------
 
.find_fragment_slot:
 
push eax ebx ecx edx
mov ax , word [edx + IPv4_Packet.Identification]
mov ecx, MAX_FRAGMENTS
mov esi, FRAGMENT_LIST
mov ebx, dword [edx + IPv4_Packet.SourceAddress]
mov edx, dword [edx + IPv4_Packet.DestinationAddress]
.find_slot:
cmp word [esi + FRAGMENT_slot.id], ax
jne .try_next
cmp dword [esi + FRAGMENT_slot.SrcIP], ebx
jne .try_next
cmp dword [esi + FRAGMENT_slot.DstIP], edx
je .found_slot
.try_next:
add esi, FRAGMENT_slot.size
loop .find_slot
; pop edx ebx
or esi, -1
; ret
 
.found_slot:
pop edx ecx ebx eax
ret
 
 
;-----------------------------------------------------------------
;
; Decrease TimeToLive of all fragment slots
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
IPv4_decrease_fragment_ttls:
 
mov esi, FRAGMENT_LIST
mov ecx, MAX_FRAGMENTS
.loop:
cmp [esi + FRAGMENT_slot.ttl], 0
je .try_next
dec [esi + FRAGMENT_slot.ttl]
jnz .try_next
DEBUGF 1,"Fragment slot timed-out!\n"
; TODO: clear all entry's of timed-out slot
.try_next:
add esi, 4
loop .loop
ret
 
 
 
 
 
;-----------------------------------------------------------------
;
; Create_IPv4_Packet
;
; IN: eax = dest ip
; ebx = source ip
; ecx = data length
; dx = fragment id
; di = protocol
;
; OUT: eax points to buffer start
; ebx is size of complete buffer
; edi = pointer to start of data (-1 on error)
; ecx = unchanged (packet size of embedded data)
; edx = pointer to device struct (needed for sending procedure)
; esi = pointer to sending procedure
;
;-----------------------------------------------------------------
 
;;; TODO: create fragmented packets
 
align 4
IPv4_create_Packet:
 
DEBUGF 1,"Create IPv4 Packet\n"
 
cmp ecx, 1514
jg .exit_
 
cmp eax, -1
je .broadcast ; If it is broadcast, just send
 
push eax
stdcall arp_table_manager, ARP_TABLE_IP_TO_MAC, eax, temp_dstmac ;opcode,IP,MAC_ptr - Get the MAC address.
cmp eax, ARP_NO_ENTRY
pop eax
jne .send
 
DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n"
 
; TODO: QUEUE!
or edi, -1
 
ret
 
.broadcast:
mov dword [temp_dstmac], -1
mov word [temp_dstmac+4], -1
 
 
.send:
push ecx eax ebx dx di
call IPv4_dest_to_dev
mov edi, [ETH_DRV_LIST + 4*edi]
lea eax, [edi + ETH_DEVICE.mac]
mov ebx, temp_dstmac
mov ecx, [esp+12]
add ecx, IPv4_Packet.DataOrOptional
mov di , ETHER_IPv4
call ETH_create_Packet ; TODO: figure out a way to make this work with other protocols too
cmp edi, -1
je .exit
 
mov [edi + IPv4_Packet.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_Packet.TypeOfService], 0
xchg ch, cl
mov [edi + IPv4_Packet.TotalLength], cx
mov [edi + IPv4_Packet.FlagsAndFragmentOffset], 0x0000
mov [edi + IPv4_Packet.TimeToLive], 128
mov [edi + IPv4_Packet.HeaderChecksum], 0
pop cx
mov [edi + IPv4_Packet.Protocol], cl
pop cx
mov [edi + IPv4_Packet.Identification], cx
pop ecx
mov [edi + IPv4_Packet.SourceAddress], ecx
pop ecx
mov [edi + IPv4_Packet.DestinationAddress], ecx
 
push eax
stdcall checksum_jb, edi, IPv4_Packet.DataOrOptional ; buf_ptr, buf_size
xchg al, ah
mov [edi + IPv4_Packet.HeaderChecksum], ax
pop eax ecx
add edi, IPv4_Packet.DataOrOptional
 
DEBUGF 1,"IPv4 Packet for device %x created successfully\n", edx
 
ret
 
.exit:
add esp, 16
.exit_:
or edi, -1
ret
 
 
uglobal
temp_dstmac dp ?
endg
 
 
;---------------------------------------------------------------------------
;
; IPv4_dest_to_dev
;
; IN: Destination IP in eax
; OUT: device id in edi
;
;---------------------------------------------------------------------------
 
align 4
IPv4_dest_to_dev:
 
DEBUGF 1,"IPv4 destination to device: "
 
xor edi, edi
mov ecx, MAX_IP
 
.loop:
mov ebx, [IP_LIST+edi] ; we dont need to worry about non exisiting ip interfaces
and ebx, [SUBNET_LIST+edi] ; they have IP and SUBNET set to all one's, so they will have no match except 255.255.255.255
; (only a moron would insert that ip into this function..)
mov edx, eax
and edx, [SUBNET_LIST+edi]
 
cmp ebx, edx
je .found_it
 
add edi, 4
loop .loop
 
xor edi, edi ; if none found, use device 0 as default device
 
.found_it:
shr edi, 2
 
DEBUGF 1,"%u\n",edi
 
ret
 
 
 
;---------------------------------------------------------------------------
;
; IPv4_get_frgmnt_num
;
; IN: /
; OUT: fragment number in ax
;
;---------------------------------------------------------------------------
 
align 4
IPv4_get_frgmnt_num:
xor ax, ax ;;; TODO: replace this with real code
 
ret
 
 
;---------------------------------------------------------------------------
;
; IPv4_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
 
align 4
IPv4_API:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
dec bl
jz .read_ip ; 2
dec bl
jz .write_ip ; 3
dec bl
jz .read_dns ; 4
dec bl
jz .write_dns ; 5
dec bl
jz .read_subnet ; 6
dec bl
jz .write_subnet ; 7
dec bl
jz .read_gateway ; 8
dec bl
jz .write_gateway ; 9
 
.error:
mov eax, -1
ret
 
.packets_tx:
add eax, IP_PACKETS_TX
mov eax, [eax]
ret
 
.packets_rx:
add eax, IP_PACKETS_RX
mov eax, [eax]
ret
 
.read_ip:
add eax, IP_LIST
mov eax, [eax]
ret
 
.write_ip:
add eax, IP_LIST
mov [eax], ecx
xor eax, eax
ret
 
.read_dns:
add eax, DNS_LIST
mov eax, [eax]
ret
 
.write_dns:
add eax, DNS_LIST
mov [eax], ecx
xor eax, eax
ret
 
.read_subnet:
add eax, SUBNET_LIST
mov eax, [eax]
ret
 
.write_subnet:
add eax, SUBNET_LIST
mov [eax], ecx
xor eax, eax
ret
 
.read_gateway:
add eax, GATEWAY_LIST
mov eax, [eax]
ret
 
.write_gateway:
add eax, GATEWAY_LIST
mov [eax], ecx
xor eax, eax
ret
 
 
 
 
/kernel/branches/net/network/IPv6.inc
--- ethernet.inc (nonexistent)
+++ ethernet.inc (revision 1161)
@@ -0,0 +1,509 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ;;
+;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
+;; Distributed under terms of the GNU General Public License ;;
+;; ;;
+;; ETHERNET.INC ;;
+;; ;;
+;; Ethernet network layer for KolibriOS ;;
+;; ;;
+;; Written by hidnplayr@kolibrios.org ;;
+;; ;;
+;; GNU GENERAL PUBLIC LICENSE ;;
+;; Version 2, June 1991 ;;
+;; ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+$Revision: 983 $
+
+MAX_ETH_DEVICES equ MAX_NET_DEVICES
+ETH_QUEUE_SIZE equ 16
+
+struct ETH_FRAME
+ .DstMAC dp ? ; destination MAC-address [6 bytes]
+ .SrcMAC dp ? ; source MAC-address [6 bytes]
+ .Type dw ? ; type of the upper-layer protocol [2 bytes]
+ .Data: ; data [46-1500 bytes]
+ends
+
+struct ETH_DEVICE
+ .unload dd ?
+ .reset dd ?
+ .transmit dd ?
+ .set_MAC dd ?
+ .get_MAC dd ?
+ .set_mode dd ?
+ .get_mode dd ?
+
+ .bytes_tx dq ?
+ .bytes_rx dq ?
+ .packets_tx dd ?
+ .packets_rx dd ?
+ .mode dd ? ; This dword contains cable status (10mbit/100mbit, full/half duplex, auto negotiation or not,..)
+ .name dd ?
+ .mac dp ?
+ends ; the rest of the device struct depends on the type of device
+
+
+align 4
+uglobal
+
+ ETH_RUNNING dd ?
+ ETH_DRV_LIST rd MAX_ETH_DEVICES
+ ETH_IN_QUEUE rd 3*ETH_QUEUE_SIZE+3
+ ETH_OUT_QUEUE rd 3*ETH_QUEUE_SIZE+3
+endg
+
+
+;-----------------------------------------------
+;
+; ETH_init
+;
+; This function resets all ethernet variables
+;
+; IN: /
+; OUT: /
+;
+;-----------------------------------------------
+
+align 4
+ETH_init:
+
+ xor eax, eax
+ mov edi, ETH_RUNNING
+ mov ecx, (1+MAX_ETH_DEVICES)
+ rep stosd
+
+ mov dword [ETH_IN_QUEUE], eax
+ mov dword [ETH_IN_QUEUE+4], ETH_IN_QUEUE + queue.data
+ mov dword [ETH_IN_QUEUE+8], ETH_IN_QUEUE + queue.data
+
+ mov dword [ETH_OUT_QUEUE], eax
+ mov dword [ETH_OUT_QUEUE+4], ETH_OUT_QUEUE + queue.data
+ mov dword [ETH_OUT_QUEUE+8], ETH_OUT_QUEUE + queue.data
+
+ ret
+
+
+
+;---------------------------------------------------------
+;
+; ETH_Add_Device:
+;
+; This function is called by ethernet drivers,
+; to register each running ethernet device to the kernel
+;
+; IN: Pointer to device structure in ebx
+; OUT: Device num in eax, -1 on error
+;
+;---------------------------------------------------------
+
+align 4
+ETH_Add_Device:
+
+ DEBUGF 1,"ETH_Add_Device: %x\n", ebx
+ cmp [ETH_RUNNING], MAX_ETH_DEVICES
+ jge .error
+
+ mov eax, ebx
+ mov ecx, MAX_ETH_DEVICES ; We need to check whole list because a device may be removed without re-organizing list
+ mov edi, ETH_DRV_LIST
+
+ cld
+ repne scasd ; See if device is already in the list
+ jz .error
+
+ xor eax, eax
+ mov ecx, MAX_ETH_DEVICES
+ mov edi, ETH_DRV_LIST
+
+ repne scasd ; Find empty spot in the list
+ jnz .error
+
+ sub edi, 4
+ mov [edi], ebx ; add device to list
+
+ sub edi, ETH_DRV_LIST ; edi = 4*device num Calculate device number in eax
+ mov eax, edi ; edx = 4*device num
+ shr eax, 2
+
+; shr eax, 1 ; edx = 2*device num
+; add edi, eax ; edi = 6*device_num Meanwhile, calculate MAC offset in edi
+; shr eax, 1 ; edx = device num
+; add edi, MAC_LIST ; edi = MAC_LIST+6*device_num
+; push eax
+
+; push edi
+; call [ebx+ETH_DEVICE.get_MAC] ; Get MAC address from driver
+; pop edi
+;
+; stosd ; Write MAC address to the MAC list
+; mov ax, bx ;
+; stosw ;
+
+ inc [ETH_RUNNING] ; Indicate that one more ethernet device is up and running
+; pop eax ; Output device num in eax
+ DEBUGF 1,"- succes: %u\n",eax
+ ret
+
+ .error:
+ or eax, -1
+ DEBUGF 1,"- fail\n"
+
+ ret
+
+
+
+
+;--------------------------------
+;
+; ETH_Remove_Device:
+;
+; This function is called by ethernet drivers,
+; to unregister ethernet devices from the kernel
+;
+; IN: Pointer to device structure in ebx
+; OUT: eax: -1 on error
+;
+;--------------------------------
+
+align 4
+ETH_Remove_Device:
+
+ cmp [ETH_RUNNING], 0
+ je .error
+
+ mov eax, ebx
+ mov ecx, MAX_ETH_DEVICES
+ mov edi, ETH_DRV_LIST
+
+ repne scasd
+ jnz .error
+
+ xor eax, eax
+ mov dword [edi-4], eax
+
+ dec [ETH_RUNNING]
+
+ ret
+
+ .error:
+ or eax, -1
+
+ ret
+
+
+
+;-------------------------------------------------------------
+;
+; ETH_Receiver:
+;
+; This function is called by ethernet drivers,
+; It pushes the received ethernet packets onto the eth_in_queue
+;
+; IN: Pointer to buffer in [esp], size of buffer in [esp-4], pointer to eth_device in ebx
+; OUT: /
+;
+;-------------------------------------------------------------
+
+align 4
+ETH_Receiver:
+ DEBUGF 1,"ETH_Receiver \n"
+
+ add_to_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, .gohome
+
+ .gohome:
+ ret
+
+
+
+
+
+;-------------------------------------------------------------
+;
+; ETH_Handler:
+;
+; Handles all queued eth packets (called from kernel's main_loop)
+;
+; IN: /
+; OUT: /
+;
+;-------------------------------------------------------------
+
+align 4
+ETH_Handler:
+
+ get_from_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, .gohome
+
+ DEBUGF 1,"ETH_Handler - size: %u\n", ecx
+ cmp ecx, 60 ; check packet length
+ jl .dump
+ sub ecx, ETH_FRAME.Data
+
+ lea edx, [eax + ETH_FRAME.Data]
+ mov ax , [eax + ETH_FRAME.Type]
+
+ cmp ax, ETHER_IPv4
+ je IPv4_Handler
+
+ cmp ax, ETHER_ARP
+ je ARP_Handler
+
+ DEBUGF 1,"Unknown ethernet packet type %x\n", ax
+
+ .dump:
+ DEBUGF 1,"Dumping packet\n"
+ call kernel_free
+ add esp, 4
+
+ .gohome:
+ ret ; return 1. to get more from queue / 2. to caller
+
+
+
+;-----------------------------------------------------------------
+;
+; ETH_Sender:
+;
+; This function sends an ethernet packet to the correct driver.
+;
+; IN: Pointer to buffer in [esp]
+; size of buffer in [esp+4]
+; pointer to device struct in ebx
+; OUT: /
+;
+;-----------------------------------------------------------------
+
+align 4
+ETH_Sender:
+ DEBUGF 1,"ETH_Sender \n"
+
+ add_to_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, .gohome
+
+ .gohome:
+ ret
+
+
+align 4
+ETH_send_queued:
+
+ get_from_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, .gohome
+
+ call ETH_struc2dev ; convert struct ptr to device num (this way we know if driver is still mounted)
+ cmp edi, -1
+ je .fail
+
+ DEBUGF 1,"ETH_Sender - device: %u\n", edi
+
+ jmp [ebx+ETH_DEVICE.transmit]
+
+ .fail:
+ call kernel_free
+ add esp, 4 ; pop (balance stack)
+ DEBUGF 1,"ETH_Sender - fail\n"
+ .gohome:
+ ret
+
+;---------------------------------------------------------------------------
+;
+; ETH_struc2dev
+;
+; IN: pointer to device struct in ebx
+;
+; OUT: edi is -1 on error, device number otherwise
+;
+;---------------------------------------------------------------------------
+
+align 4
+ETH_struc2dev:
+ push eax ecx
+
+ mov eax, ebx
+ mov ecx, MAX_ETH_DEVICES
+ mov edi, ETH_DRV_LIST
+
+ repne scasd
+ jnz .error
+
+ sub edi, ETH_DRV_LIST+4
+ shr edi, 2
+
+ pop ecx eax
+ ret
+ .error:
+ or edi, -1
+ pop ecx eax
+
+ ret
+
+
+;---------------------------------------------------------------------------
+;
+; ETH_create_Packet
+;
+; IN: pointer to source mac in eax
+; pointer to destination mac in ebx
+; packet size in ecx
+; device number in edx
+; protocol in di
+;
+; OUT: edi is -1 on error, pointer to buffer otherwise ;; TODO: XCHG EDX AND EBX output parameters
+; eax points to buffer start
+; ebx is size of complete buffer
+; ecx is unchanged (packet size of embedded data)
+; edx is pointer to device structure
+; esi points to procedure wich needs to be called to send packet
+;
+;---------------------------------------------------------------------------
+
+align 4
+ETH_create_Packet:
+
+ DEBUGF 1,"Creating Ethernet Packet:\n"
+
+ cmp ecx, 60-ETH_FRAME.Data
+ jl .exit
+ cmp ecx, 1514-ETH_FRAME.Data
+ jg .exit
+
+ push ecx di eax ebx edx
+
+ add ecx, ETH_FRAME.Data
+ push ecx
+ push ecx
+ call kernel_alloc
+ test eax, eax
+ jz .pop_exit
+
+ pop ecx
+ pop edx
+
+ DEBUGF 1,"1"
+ mov edi, eax
+ pop esi
+ movsd
+ movsw
+ DEBUGF 1,"2"
+ pop esi
+ movsd
+ movsw
+ DEBUGF 1,"3"
+ pop ax
+ stosw
+ DEBUGF 1,"4"
+
+ lea eax, [edi - ETH_FRAME.Data] ; Set eax to buffer start
+ mov ebx, ecx ; Set ebx to complete buffer size
+ pop ecx
+ mov esi, ETH_Sender
+
+ xor edx, edx ;;;; TODO: Fixme
+ mov edx, [ETH_DRV_LIST + edx]
+
+ DEBUGF 1,"done: %x size:%u device:%x\n", eax, ebx, edx
+ ret
+
+ .pop_exit:
+ add esp, 18
+ .exit:
+ or edi, -1
+ ret
+
+
+
+
+;---------------------------------------------------------------------------
+;
+; ETH_API
+;
+; This function is called by system function 75
+;
+; IN: subfunction number in bl
+; device number in bh
+; ecx, edx, .. depends on subfunction
+;
+; OUT:
+;
+;---------------------------------------------------------------------------
+
+align 4
+ETH_API:
+
+ movzx eax, bh
+ shl eax, 2
+
+ test bl, bl
+ jz .packets_tx ; 0
+ dec bl
+ jz .packets_rx ; 1
+ dec bl
+ jz .bytes_tx ; 2
+ dec bl
+ jz .bytes_rx ; 3
+ dec bl
+ jz .read_mac ; 4
+ dec bl
+ jz .write_mac ; 5
+ dec bl
+ jz .in_queue ; 6
+ dec bl
+ jz .out_queue ; 7
+
+
+.error:
+ mov eax, -1
+ ret
+
+.packets_tx:
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+ mov eax, [eax + ETH_DEVICE.packets_tx]
+ ret
+
+.packets_rx:
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+ mov eax, [eax + ETH_DEVICE.packets_rx]
+ ret
+
+.bytes_tx:
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+ mov eax, dword [eax + ETH_DEVICE.bytes_tx + 4]
+ ret
+
+.bytes_rx:
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+ mov eax, dword [eax + ETH_DEVICE.bytes_rx + 4]
+ ret
+
+.read_mac:
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+; push eax
+; call dword [eax + ETH_DEVICE.get_MAC]
+; pop eax
+ movzx ebx, word [eax + ETH_DEVICE.mac]
+ mov eax, dword [eax + ETH_DEVICE.mac + 2]
+ mov [esp+20+4], ebx ; TODO: fix this ugly code
+ ret
+
+.write_mac:
+ push ecx
+ push dx
+ add eax, ETH_DRV_LIST
+ mov eax, [eax]
+ mov eax, dword [eax + ETH_DEVICE.set_MAC]
+ call eax
+ ret
+
+.in_queue:
+ add eax, ETH_IN_QUEUE
+ mov eax, [eax + queue.size]
+ ret
+
+.out_queue:
+ add eax, ETH_OUT_QUEUE
+ mov eax, [eax + queue.size]
+ ret
\ No newline at end of file
/kernel/branches/net/network/icmp.inc
0,0 → 1,447
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ICMP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
$Revision: 593 $
 
; ICMP types & codes
 
ICMP_ECHOREPLY equ 0 ; echo reply message
 
ICMP_UNREACH equ 3
ICMP_UNREACH_NET equ 0 ; bad net
ICMP_UNREACH_HOST equ 1 ; bad host
ICMP_UNREACH_PROTOCOL equ 2 ; bad protocol
ICMP_UNREACH_PORT equ 3 ; bad port
ICMP_UNREACH_NEEDFRAG equ 4 ; IP_DF caused drop
ICMP_UNREACH_SRCFAIL equ 5 ; src route failed
ICMP_UNREACH_NET_UNKNOWN equ 6 ; unknown net
ICMP_UNREACH_HOST_UNKNOWN equ 7 ; unknown host
ICMP_UNREACH_ISOLATED equ 8 ; src host isolated
ICMP_UNREACH_NET_PROHIB equ 9 ; prohibited access
ICMP_UNREACH_HOST_PROHIB equ 10 ; ditto
ICMP_UNREACH_TOSNET equ 11 ; bad tos for net
ICMP_UNREACH_TOSHOST equ 12 ; bad tos for host
ICMP_UNREACH_FILTER_PROHIB equ 13 ; admin prohib
ICMP_UNREACH_HOST_PRECEDENCE equ 14 ; host prec vio.
ICMP_UNREACH_PRECEDENCE_CUTOFF equ 15 ; prec cutoff
 
ICMP_SOURCEQUENCH equ 4 ; Packet lost, slow down
 
ICMP_REDIRECT equ 5 ; shorter route, codes:
ICMP_REDIRECT_NET equ 0 ; for network
ICMP_REDIRECT_HOST equ 1 ; for host
ICMP_REDIRECT_TOSNET equ 2 ; for tos and net
ICMP_REDIRECT_TOSHOST equ 3 ; for tos and host
 
ICMP_ALTHOSTADDR equ 6 ; alternate host address
ICMP_ECHO equ 8 ; echo service
ICMP_ROUTERADVERT equ 9 ; router advertisement
ICMP_ROUTERADVERT_NORMAL equ 0 ; normal advertisement
ICMP_ROUTERADVERT_NOROUTE_COMMON equ 16 ; selective routing
 
ICMP_ROUTERSOLICIT equ 10 ; router solicitation
ICMP_TIMXCEED equ 11 ; time exceeded, code:
ICMP_TIMXCEED_INTRANS equ 0 ; ttl==0 in transit
ICMP_TIMXCEED_REASS equ 1 ; ttl==0 in reass
 
ICMP_PARAMPROB equ 12 ; ip header bad
ICMP_PARAMPROB_ERRATPTR equ 0 ; error at param ptr
ICMP_PARAMPROB_OPTABSENT equ 1 ; req. opt. absent
ICMP_PARAMPROB_LENGTH equ 2 ; bad length
 
ICMP_TSTAMP equ 13 ; timestamp request
ICMP_TSTAMPREPLY equ 14 ; timestamp reply
ICMP_IREQ equ 15 ; information request
ICMP_IREQREPLY equ 16 ; information reply
ICMP_MASKREQ equ 17 ; address mask request
ICMP_MASKREPLY equ 18 ; address mask reply
ICMP_TRACEROUTE equ 30 ; traceroute
ICMP_DATACONVERR equ 31 ; data conversion error
ICMP_MOBILE_REDIRECT equ 32 ; mobile host redirect
ICMP_IPV6_WHEREAREYOU equ 33 ; IPv6 where-are-you
ICMP_IPV6_IAMHERE equ 34 ; IPv6 i-am-here
ICMP_MOBILE_REGREQUEST equ 35 ; mobile registration req
ICMP_MOBILE_REGREPLY equ 36 ; mobile registreation reply
ICMP_SKIP equ 39 ; SKIP
 
ICMP_PHOTURIS equ 40 ; Photuris
ICMP_PHOTURIS_UNKNOWN_INDEX equ 1 ; unknown sec index
ICMP_PHOTURIS_AUTH_FAILED equ 2 ; auth failed
ICMP_PHOTURIS_DECRYPT_FAILED equ 3 ; decrypt failed
 
 
 
struct ICMP_Packet
.Type db ?
.Code db ?
.Checksum dw ?
.Identifier dw ?
.SequenceNumber dw ?
.Data:
ends
 
 
align 4
uglobal
ICMP_PACKETS_TX rd MAX_IP
ICMP_PACKETS_RX rd MAX_IP
endg
 
;-----------------------------------------------------------------
;
; ICMP_init
;
; This function resets all ICMP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
ICMP_init:
 
xor eax, eax
mov edi, ICMP_PACKETS_TX
mov ecx, 2*MAX_IP
rep stosd
 
ret
 
 
;--------------------------------
;
; ICMP_Handler:
;
; Called by IP_handler,
; this procedure will send reply's to ICMP echo's etc ;;; TODO: update this to work with fragmented packets too!
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; ICMP Packet size in ecx
; pointer to ICMP Packet data in edx
; OUT: /
;
;--------------------------------
 
align 4
ICMP_Handler: ;TODO: works only on pure ethernet right now !
 
DEBUGF 1,"ICMP_Handler - start\n"
cmp byte [edx + ICMP_Packet.Type], ICMP_ECHO ; Is this an echo request? discard if not
jne .check_sockets
 
mov byte [edx + ICMP_Packet.Type], ICMP_ECHOREPLY ; Change Packet type to reply
mov word [edx + ICMP_Packet.Checksum], 0 ; Set checksum to 0, needed to calculate new checksum
 
; exchange dest and source address in IP header
; exchange dest and source MAC in ETH header
mov esi, [esp]
 
mov eax, dword [esi + ETH_FRAME.DstMAC]
mov ecx, dword [esi + ETH_FRAME.SrcMAC]
mov dword [esi + ETH_FRAME.SrcMAC], eax
mov dword [esi + ETH_FRAME.DstMAC], ecx
 
mov ax, word [esi + ETH_FRAME.DstMAC + 4]
mov cx, word [esi + ETH_FRAME.SrcMAC + 4]
mov word [esi + ETH_FRAME.SrcMAC + 4], ax
mov word [esi + ETH_FRAME.DstMAC + 4], cx
 
mov eax, dword [esi + ETH_FRAME.Data + IPv4_Packet.SourceAddress]
mov ecx, dword [esi + ETH_FRAME.Data + IPv4_Packet.DestinationAddress]
mov dword [esi + ETH_FRAME.Data + IPv4_Packet.DestinationAddress], eax
mov dword [esi + ETH_FRAME.Data + IPv4_Packet.SourceAddress], ecx
 
; Recalculate ip header checksum
; mov esi, [esp]
add esi, ETH_FRAME.Data ; Point esi to start of IP Packet
movzx eax, byte [esi + IPv4_Packet.VersionAndIHL] ; Calculate IP Header length by using IHL field
and eax, 0x0000000F ;
shl eax, 2 ;
push ebx edx esi
stdcall checksum_jb, esi, eax ; calculate the checksum
pop esi edx ebx
xchg al, ah ; convert to intel byte order
; mov esi, [esp]
mov word [esi + IPv4_Packet.HeaderChecksum], ax ; Store it in the IP Packet header
 
; Recalculate ICMP CheckSum
; mov esi, [esp] ; Find length of IP Packet
movzx eax, word[esi + IPv4_Packet.TotalLength] ;
xchg ah , al ;
movzx edi, byte [esi + IPv4_Packet.VersionAndIHL] ; Calculate IP Header length by using IHL field
and edi, 0x0000000F ;
shl edi, 2 ;
sub ax , di ; Now we know the length of ICMP data in eax
push ebx edx
stdcall checksum_jb,edx,eax ; Calculate the checksum of icmp data
pop edx ebx
xchg al, ah ; Convert to intel byte order
mov word [edx + ICMP_Packet.Checksum], ax
 
jmp ETH_Sender
 
.check_sockets:
; TODO: validate the header & checksum. Discard buffer if error
 
; Look for an open ICMP socket
 
mov esi, net_sockets
.try_more:
mov ax , [edx + ICMP_Packet.Identifier]
.next_socket:
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .dump
cmp [esi + SOCKET.Type], IP_PROTO_ICMP
jne .next_socket
cmp [esi + SOCKET.LocalPort], ax
jne .next_socket
 
cmp [esi + SOCKET.rxDataCount],0 ; get # of bytes already in buffer
jnz .dump ; only one packet at a time may be in the buffer!
 
cmp ecx, SOCKETBUFFSIZE - SOCKETHEADERSIZE; TODO: fix this problem !
jg .dump
 
DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi
 
lea ebx, [esi + SOCKET.lock]
call wait_mutex
 
; Now, copy data to socket. We have socket address in esi.
; We have ICMP Packet in edx
; number of bytes in ecx
 
; note: we do not strip the header!
 
DEBUGF 1,"bytes: %u\n", ecx
 
mov [esi + SOCKET.rxDataCount], ecx
 
lea edi, [esi + SOCKETHEADERSIZE]
push esi
push ecx
mov esi, edx
shr ecx, 2
rep movsd ; copy the data across
pop ecx
and ecx, 3
rep movsb
pop esi
 
DEBUGF 1,"ICMP socket updated\n"
 
mov [esi + SOCKET.lock], 0
 
; flag an event to the application
mov eax, [esi + SOCKET.PID] ; get socket owner PID
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
 
jmp .dump
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
 
mov [check_idle_semaphore], 200
 
.dump:
DEBUGF 1,"ICMP_Handler - dumping\n"
 
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
 
;--------------------------------
;
; ICMP_Handler_fragments:
;
; Called by IP_handler,
; this procedure will send reply's to ICMP echo's etc
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; ICMP Packet size in ecx
; pointer to ICMP Packet data in edx
; OUT: /
;
;--------------------------------
 
align 4
ICMP_Handler_fragments: ; works only on pure ethernet right now !
 
DEBUGF 1,"ICMP_Handler_fragments - start\n"
 
cmp ecx, 65500
jg .dump
 
cmp byte [edx + ICMP_Packet.Type], ICMP_ECHO ; Is this an echo request? discard if not
jne .dump
 
mov esi, [esp]
 
sub ecx, ICMP_Packet.Data
mov eax, [esi + IPv4_Packet.SourceAddress]
mov ebx, [esi + IPv4_Packet.DestinationAddress]
push word [esi + IPv4_Packet.Identification]
 
mov di , [edx + ICMP_Packet.Identifier]
shl edi, 16
mov di , [edx + ICMP_Packet.SequenceNumber]
 
mov esi, edx
add esi, ICMP_Packet.Data
pop dx
shl edx, 16
mov dx , ICMP_ECHOREPLY shl 8 + 0 ; Type + Code
 
call ICMP_create_Packet
 
.dump:
DEBUGF 1,"ICMP_Handler_fragments - end\n"
 
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
;-----------------------------------------------------------------
;
; Note: ICMP only works on top of IP protocol :)
;
; inputs:
;
; eax = dest ip
; ebx = source ip
; ecx = data length
; dh = type
; dl = code
; high 16 bits of edx = fragment id (for IP header)
; esi = data offset
; edi = identifier shl 16 + sequence number
;
;-----------------------------------------------------------------
 
align 4
ICMP_create_Packet:
 
DEBUGF 1,"Create ICMP Packet\n"
 
push esi edi edx
 
add ecx, ICMP_Packet.Data
mov di , IP_PROTO_ICMP
shr edx, 16
 
call IPv4_create_Packet
 
DEBUGF 1,"full icmp packet size: %u\n", ebx
 
cmp edi, -1
je .exit
 
pop eax
mov word [edi + ICMP_Packet.Type], ax ; Write both type and code bytes at once
pop eax
mov [edi + ICMP_Packet.SequenceNumber], ax
shr eax, 16
mov [edi + ICMP_Packet.Identifier], ax
mov [edi + ICMP_Packet.Checksum], 0
 
stdcall checksum_jb, edi , ecx
xchg al, ah
mov [edi + ICMP_Packet.Checksum], ax
 
pop esi
sub ecx, ICMP_Packet.Data
add edi, ICMP_Packet.Data
push cx
shr cx , 2
rep movsd
pop cx
and cx , 3
rep movsb
 
sub edi, ebx ;; TODO: find a better way to remember start of packet
xchg ebx, edx
mov ecx, [ebx + ETH_DEVICE.transmit]
push edx edi ecx
DEBUGF 1,"Sending ICMP Packet\n"
ret ; Send the packet (create_packet routine outputs pointer to routine to send packet in eax)
 
.exit:
add esp, 8
ret
 
 
 
 
;---------------------------------------------------------------------------
;
; ICMP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
 
align 4
ICMP_API:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
add eax, ICMP_PACKETS_TX
mov eax, [eax]
ret
 
.packets_rx:
add eax, ICMP_PACKETS_RX
mov eax, [eax]
ret
/kernel/branches/net/network/queue.inc
0,0 → 1,102
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; queue.inc ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 983 $
 
struct queue
.size dd ?
.w_ptr dd ?
.r_ptr dd ?
.data:
ends
 
struct queue_entry
.owner dd ?
.data_ptr dd ?
.data_size dd ?
.size:
ends
 
 
macro add_to_queue ptr, size, returnaddr {
 
cmp dword [ptr + queue.size], size ; Check if queue isnt full
jge .fail
 
DEBUGF 1,"Queuing packet for device %x\n",ebx
 
inc dword [ptr + queue.size]
 
mov edi, dword [ptr + queue.w_ptr] ; Current write pointer (FIFO!)
 
mov eax, ebx
stosd
pop eax
stosd
pop eax
stosd
 
cmp edi, size*queue_entry.size+ptr+queue.data ; entry size
jl .no_wrap
 
sub edi, size*queue_entry.size
 
.no_wrap:
mov dword [ptr + queue.w_ptr], edi
jmp returnaddr
 
.fail:
DEBUGF 1,"queuing failed\n"
 
call kernel_free
add esp, 4
ret
 
}
 
 
macro get_from_queue ptr, size, returnaddr {
 
.start_of_code:
cmp dword [ptr + queue.size], 0 ; any packets queued?
je returnaddr
 
DEBUGF 1,"Dequeuing packet"
 
dec dword [ptr + queue.size]
 
push dword .start_of_code ; return address for call's
 
mov esi, [ptr + queue.r_ptr]
lodsd
mov ebx, eax
lodsd
mov ecx, eax
lodsd
push eax
push ecx
xchg eax, ecx
 
DEBUGF 1," for device %x\n", ebx
 
cmp esi, size*queue_entry.size+ptr+queue.data ; entry size
jl .no_wrap
 
sub esi, size*queue_entry.size
 
.no_wrap:
mov dword [ptr + queue.r_ptr], esi
 
}
 
/kernel/branches/net/network/socket.inc
0,0 → 1,958
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; SOCKET.INC ;;
;; ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; based on code by mike.dld ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 1019 $
 
align 4
struct SOCKET
.PrevPtr dd ? ; pointer to previous socket in list
.NextPtr dd ? ; pointer to next socket in list
.Number dd ? ; socket number (unique within single process)
.PID dd ? ; application process id
.Domain dd ? ; INET/UNIX/..
.Type dd ? ; RAW/UDP/TCP/...
.LocalIP dd ? ; local IP address
.RemoteIP dd ? ; remote IP address
.LocalPort dw ? ; local port
.RemotePort dw ? ; remote port
; .OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state)
; .OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
.rxDataCount dd ? ; rx data count
; .TCBState dd ? ; TCB state
; .TCBTimer dd ? ; TCB timer (seconds)
; .ISS dd ? ; initial send sequence
; .IRS dd ? ; initial receive sequence
; .SND_UNA dd ? ; sequence number of unack'ed sent Packets
; .SND_NXT dd ? ; bext send sequence number to use
; .SND_WND dd ? ; send window
; .RCV_NXT dd ? ; next receive sequence number to use
; .RCV_WND dd ? ; receive window
; .SEG_LEN dd ? ; segment length
; .SEG_WND dd ? ; segment window
.wndsizeTimer dd ? ; window size timer
.lock dd ? ; lock mutex
.backlog dw ? ; Backlog
.rxData: ; receive data buffer here
ends
 
MAX_backlog equ 20
 
; socket buffers
SOCKETBUFFSIZE equ 4096 ; state + config + buffer.
SOCKETHEADERSIZE equ SOCKET.rxData ; thus 4096 - SOCKETHEADERSIZE bytes data
 
uglobal
net_sockets rd 2
last_UDP_port dw ? ; These values give the number of the last used ephemeral port
last_TCP_port dw ? ;
endg
 
 
;-----------------------------------------------
;
; SOCKET_init
;
; -
;
; IN: /
; OUT: /
;
;-----------------------------------------------
 
align 4
socket_init:
 
mov [net_sockets], 0
mov [net_sockets + 4], 0
 
mov [last_UDP_port], MIN_EPHEMERAL_PORT
mov [last_TCP_port], MIN_EPHEMERAL_PORT
 
ret
 
 
;-----------------------------------------------------------------------------
;
; Socket API (function 74)
;
;-----------------------------------------------------------------------------
 
align 4
sys_socket:
 
test bl, bl
jz socket_open ; 0
dec bl
jz socket_close ; 1
dec bl
jz socket_bind ; 2
dec bl
jz socket_listen ; 3
dec bl
jz socket_connect ; 4
dec bl
jz socket_accept ; 5
dec bl
jz socket_send ; 6
dec bl
jz socket_recv ; 7
 
error:
mov dword [esp+32],-1
 
ret
 
 
 
 
;-----------------------------------------------
;
; SOCKET_open
;
;
; IN: domain in ecx
; type in edx
; set esi to zero, it is reserved for future use
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------
socket_open:
 
DEBUGF 1,"socket_open: domain: %u, type: %u",ecx, edx
 
call net_socket_alloc
or eax, eax
jz error
 
mov [eax + SOCKET.Domain], ecx
mov [eax + SOCKET.Type], edx
 
stdcall net_socket_addr_to_num, eax
DEBUGF 1,", socketnumber: %u\n", eax
 
mov [esp+32], eax
 
ret
 
 
 
 
;-----------------------------------------------
;
; SOCKET_bind
;
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------
 
socket_bind:
 
DEBUGF 1,"Socket_bind: socknum: %u sockaddr: %x, length: %u, ",ecx,edx,esi
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
jz error
 
cmp esi, 2
jl error
 
cmp word [edx], AF_INET4
je .af_inet4
 
jmp error
 
.af_inet4:
 
cmp esi, 6
jl error
 
mov bx, word [edx + 2]
DEBUGF 1,"local port: %u ",bx
test bx, bx
jnz .check_only
 
mov bx , [last_UDP_port]
 
.find_port_loop:
inc bx
inc [last_UDP_port]
 
.check_only:
mov esi, net_sockets
 
.next_udp_socket:
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .udp_port_ok
 
cmp [esi + SOCKET.Type], IP_PROTO_UDP
jne .next_udp_socket
 
cmp [esi + SOCKET.LocalPort], bx
jne .next_udp_socket
 
cmp word [edx + 2], 0
jne error
 
cmp bx, MAX_EPHEMERAL_PORT
jle .find_port_loop
 
mov [last_UDP_port], MIN_EPHEMERAL_PORT
jmp error
 
.udp_port_ok:
mov word [eax + SOCKET.LocalPort], bx
 
mov ebx, dword [edx + 4]
mov dword [eax + SOCKET.LocalIP], ebx
 
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\
[eax + SOCKET.LocalIP]:1,[eax + SOCKET.LocalIP + 1]:1,[eax + SOCKET.LocalIP + 2]:1,[eax + SOCKET.LocalIP + 3]:1
 
mov dword [esp+32],0
ret
 
 
 
 
;-----------------------------------------------
;
; SOCKET_connect
;
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------
align 4
 
socket_connect:
 
DEBUGF 1,"Socket_connect: socknum: %u sockaddr: %x, length: %u,",ecx,edx,esi
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
jz error
 
cmp esi, 2
jl error
 
cmp word [edx], AF_INET4
je .af_inet4
 
jmp error
 
.af_inet4:
 
cmp esi, 8
jl error
 
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Type], IP_PROTO_ICMP
je .icmp
 
; cmp [eax + SOCKET.Type], IP_PROTO_TCP
; je .tcp
 
jmp error
 
.udp:
 
mov bx , word [edx + 2]
mov word [eax + SOCKET.RemotePort], bx
 
DEBUGF 1,"remote port: %u ",bx
 
mov ebx, dword [edx + 4]
mov dword [eax + SOCKET.RemoteIP], ebx
 
DEBUGF 1,"remote ip: %u.%u.%u.%u\n",[edx+4]:1,[edx+5]:1,[edx+6]:1,[edx+7]:1
 
mov dword [esp+32],0
ret
 
.icmp:
 
 
ret
 
 
 
.tcp:
 
;local sockAddr dd ?
 
; cmp esi, SOCKET_PASSIVE
; jne .skip_port_check
;
; push ebx
; mov eax, ebx
; xchg al, ah
; mov ebx, net_sockets
;
; .next_socket:
; mov ebx, [ebx + SOCKET.NextPtr]
; or ebx, ebx
; jz .last_socket
; cmp [ebx + SOCKET.TCBState], TCB_LISTEN
; jne .next_socket
; cmp [ebx + SOCKET.LocalPort], ax
; jne .next_socket
;
; xchg al, ah
; DEBUGF 1, "K : error: port %u is listened by 0x%x\n", ax, ebx
; pop ebx
; jmp .error
;
; .last_socket:
; pop ebx
;
; .skip_port_check:
 
; mov [eax + SOCKET.wndsizeTimer], 0 ; Reset the window timer.
;
; xchg bh, bl
; mov [eax + SOCKET.LocalPort], bx
; xchg ch, cl
; mov [eax + SOCKET.RemotePort], cx
; mov [eax + SOCKET.OrigRemotePort], cx
; mov ebx, [IP_LIST]
; mov [eax + SOCKET.LocalIP], ebx
; mov [eax + SOCKET.RemoteIP], edx
; mov [eax + SOCKET.OrigRemoteIP], edx
 
; mov ebx, TCB_LISTEN
; cmp esi, SOCKET_PASSIVE
; je @f
; mov ebx, TCB_SYN_SENT
; @@: mov [eax + SOCKET.TCBState], ebx ; Indicate the state of the TCB
 
; cmp ebx, TCB_LISTEN
; je .exit
 
; Now, if we are in active mode, then we have to send a SYN to the specified remote port
; mov eax, EMPTY_QUEUE
; call dequeue
; cmp ax, NO_BUFFER
; je .exit
 
; push eax
 
; mov bl, TH_SYN
; xor ecx, ecx
; stdcall build_tcp_Packet, [sockAddr]
 
; mov eax, NET1OUT_QUEUE
; mov edx, [IP_LIST]
; mov ecx, [sockAddr]
; cmp edx, [ecx + SOCKET.RemoteIP]
; jne .not_local
; mov eax, IPIN_QUEUE
 
; .not_local:
; Send it.
; pop ebx
; call queue
 
.exit:
xor eax, eax
ret
 
 
 
 
;-----------------------------------------------
;
; SOCKET_listen
;
;
; IN: socket number in ecx
; backlog in edx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------
 
socket_listen:
 
DEBUGF 1,"Socket_listen: socknum: %u backlog: %u\n",ecx,edx
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
jz error
 
cmp edx, MAX_backlog
jl .ok
mov dx , 20
.ok:
 
mov [eax + SOCKET.backlog], dx
 
; TODO: insert code for active connections like TCP
 
mov dword [esp+32], 0
ret
 
 
 
 
 
 
;-----------------------------------------------
;
; SOCKET_accept
;
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------
 
 
socket_accept:
 
DEBUGF 1,"Socket_accept: socknum: %u sockaddr: %x, length: %u\n",ecx,edx,esi
 
stdcall net_socket_num_to_addr, ecx
or eax, eax
jz error
mov esi, eax
 
cmp [esi + SOCKET.backlog], 0
jz error
 
call net_socket_alloc
or eax, eax
jz error
mov edi, eax
 
dec [esi + SOCKET.backlog]
 
mov ecx, (SOCKET.rxData+3)/4
rep movsd
 
mov [edi + SOCKET.backlog], 0
 
; TODO: fill in structure in ecx
 
mov [esi + SOCKET.RemoteIP], 0
mov [esi + SOCKET.RemotePort], 0
 
stdcall net_socket_addr_to_num, eax
mov [esp+32], eax
 
ret
 
 
 
;-----------------------------------------------
;
; SOCKET_close
;
;
; IN: socket number in ecx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------
 
socket_close:
 
DEBUGF 1,"Socket_close: socknum: %u\n",ecx
 
stdcall net_socket_num_to_addr, ecx
or eax, eax
jz error
 
 
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Type], IP_PROTO_ICMP
je .icmp
 
; cmp [eax + SOCKET.Type], IP_PROTO_TCP
; je .tcp
 
jmp error
 
.udp:
 
lea ebx, [eax + SOCKET.lock]
call wait_mutex
; TODO: mark the socket for deletion, using the mutex
 
stdcall net_socket_free, eax
 
mov dword [esp+32],0
ret
 
 
.icmp:
 
 
 
ret
 
.tcp:
 
if 1 = 0
;local sockAddr dd ?
 
; DEBUGF 1, "K : socket_close_tcp (0x%x)\n", ebx
; first, remove any resend entries
pusha
 
mov esi, resendQ
mov ecx, 0
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .last_resendq ; None left
cmp [esi + 4], ebx
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:
popa
 
mov ebx, eax
; mov [sockAddr], eax
 
cmp [eax + SOCKET.TCBState], TCB_LISTEN
je .destroy_tcb
cmp [eax + SOCKET.TCBState], TCB_SYN_SENT
je .destroy_tcb
 
; Now construct the response, and queue for sending by IP
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .error
 
push eax
 
mov bl, TH_FIN
xor ecx, ecx
xor esi, esi
stdcall build_tcp_Packet, [sockAddr]
 
mov ebx, [sockAddr]
; increament SND.NXT in socket
lea esi, [ebx + SOCKET.SND_NXT]
call inc_inet_esi
 
; Get the socket state
mov eax, [ebx + SOCKET.TCBState]
cmp eax, TCB_SYN_RECEIVED
je .fin_wait_1
cmp eax, TCB_ESTABLISHED
je .fin_wait_1
 
; assume CLOSE WAIT
; Send a fin, then enter last-ack state
mov [ebx + SOCKET.TCBState], TCB_LAST_ACK
jmp .send
 
.fin_wait_1:
; Send a fin, then enter finwait2 state
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
 
.send:
mov eax, NET1OUT_QUEUE
mov edx, [IP_LIST]
; mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
jmp .exit
 
 
.destroy_tcb:
 
stdcall net_socket_free, eax
 
end if
 
.exit:
mov dword [esp+32],0
ret
 
 
 
 
;-----------------------------------------------
;
; SOCKET_receive
;
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
; flags in edi
; OUT: eax is number of bytes copied, -1 on error
;
;-----------------------------------------------
 
socket_recv:
 
DEBUGF 1,"Socket_receive: socknum: %u sockaddr: %x, length: %u, flags: %x\n",ecx,edx,esi,edi
 
stdcall net_socket_num_to_addr, ecx ; get real socket address
or eax, eax
jz error
 
DEBUGF 1,"real socket address:%x\n", eax
 
mov dword[esp+32], -1
 
mov edi, edx
 
lea ebx, [eax + SOCKET.lock]
call wait_mutex
 
mov ecx, [eax + SOCKET.rxDataCount] ; get count of bytes
DEBUGF 1,"bytes in socket:%u\n", ecx
test ecx, ecx ; if count of bytes is zero..
jz .exit ; exit function (eax will be zero)
 
cmp ecx, esi ; if buffer size is larger then the bytes of data, copy all data
jle .copy_all_bytes
 
sub ecx, esi ; store new count (data bytes in buffer - bytes we're about to copy)
mov [eax + SOCKET.rxDataCount], ecx ;
push ecx
mov edx, esi
 
call .start_copy ; copy to the application
 
mov dword[esp+32], edx
 
lea edi, [eax + SOCKET.rxData] ; Now shift the remaining bytes to start of buffer
lea esi, [edi + edx]
mov ecx, [esp]
shr ecx, 2 ; divide eax by 4
rep movsd ; copy all full dwords
pop ecx
and ecx, 3
rep movsb ; copy remaining bytes
 
.exit:
mov [eax + SOCKET.lock], 0
ret
 
.copy_all_bytes:
mov dword[esp+32], ecx
mov [eax + SOCKET.rxDataCount], 0 ; store new count (zero)
push dword .exit ; this code results in same as commented out code
 
.start_copy:
DEBUGF 1,"copying %u bytes\n",ecx
 
lea esi, [eax + SOCKET.rxData]
push ecx
shr ecx, 2 ; divide eax by 4
rep movsd
pop ecx
and ecx, 3
rep movsb ; copy the rest bytes
 
ret ; exit, or go back to shift remaining bytes if any
 
 
 
;-----------------------------------------------
;
; SOCKET_send
;
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
; flags in edi
; OUT: -1 on error
;
;-----------------------------------------------
 
socket_send:
 
DEBUGF 1,"Socket_send: socknum: %u sockaddr: %x, length: %u, flags: %x, ",ecx,edx,esi,edi
 
stdcall net_socket_num_to_addr, ecx ; get real socket address
or eax, eax
jz error
 
DEBUGF 1,"Socket type:%u\n", [eax + SOCKET.Type]:4
 
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Type], IP_PROTO_ICMP
je .icmp
 
; cmp [eax + SOCKET.Type], IP_PROTO_TCP
; je .tcp
 
jmp error
 
.udp:
 
DEBUGF 1,"type: UDP\n"
 
mov ecx, esi
mov esi, edx
mov edx, dword [eax + SOCKET.LocalPort] ; load local port and remote port at once
 
DEBUGF 1,"local port: %u, remote port:%u\n",[eax + SOCKET.LocalPort]:2, [eax + SOCKET.RemotePort]:2
mov ebx, [eax + SOCKET.LocalIP]
mov eax, [eax + SOCKET.RemoteIP]
 
call UDP_create_Packet
 
mov [esp+32], eax
ret
 
.icmp:
; note: for ICMP sockets the SOCKET.LocalPort is used as the 'Identifier' value for ICMP packets
; the application must add the header to the data, the kernel will fill in 'identifier' and 'checksum'
 
sub ecx, ICMP_Packet.Data
mov esi, edx
push ax
call IPv4_get_frgmnt_num
mov dx, ax
pop ax
shl edx, 16
mov dh , [esi + ICMP_Packet.Type]
mov dl , [esi + ICMP_Packet.Code]
mov di , [esi + ICMP_Packet.Identifier]
; mov [eax + SOCKET.LocalPort], di ; Set localport to the identifier number, so we can receive reply's
shl edi, 16
mov di , [esi + ICMP_Packet.SequenceNumber]
add esi, ICMP_Packet.Data
mov ebx, [eax + SOCKET.LocalIP]
mov eax, [eax + SOCKET.RemoteIP]
call ICMP_create_Packet
 
mov [esp+32], eax
ret
 
.tcp:
 
ret
 
 
 
 
 
 
 
 
; Allocate memory for socket data and put new socket into the list
; Newly created socket is initialized with calling PID and number and
; put into beginning of list (which is a fastest way).
;
; @return socket structure address in EAX
;
proc net_socket_alloc stdcall uses ebx ecx edx edi
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "K : net_socket_alloc (0x%x)\n", eax
; check if we can allocate needed amount of memory
or eax, eax
jz .exit
 
; zero-initialize allocated memory
push eax
mov edi, eax
mov ecx, SOCKETBUFFSIZE / 4
; cld
xor eax, eax
rep stosd
pop eax
 
; add socket to the list by changing pointers
mov ebx, net_sockets
push [ebx + SOCKET.NextPtr]
mov [ebx + SOCKET.NextPtr], eax
mov [eax + SOCKET.PrevPtr], ebx
pop ebx
mov [eax + SOCKET.NextPtr], ebx
or ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
 
@@: ; set socket owner PID to the one of calling process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
 
; find first free socket number and use it
;mov edx, ebx
mov ebx, net_sockets
xor ecx, ecx
.next_socket_number:
inc ecx
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .last_socket_number
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
;cmp [ebx + SOCKET.PID], edx
;jne .next_socket
mov ebx, net_sockets
jmp .next_socket_number
 
.last_socket_number:
mov [eax + SOCKET.Number], ecx
 
.exit:
ret
endp
 
; Free socket data memory and pop socket off the list
;
; @param sockAddr is a socket structure address
;
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
mov eax, [sockAddr]
DEBUGF 1, "K : net_socket_free (0x%x)\n", eax
; check if we got something similar to socket structure address
or eax, eax
jz .error
 
; make sure sockAddr is one of the socket addresses in the list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
 
; okay, we found the correct one
; remove it from the list first, changing pointers
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
mov [eax + SOCKET.NextPtr], ebx
or ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
 
@@: ; and finally free the memory structure used
stdcall kernel_free, [sockAddr]
ret
 
.error:
DEBUGF 1, "K : failed\n"
ret
endp
 
; Get socket structure address by its number
; Scan through sockets list to find the socket with specified number.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
;
; @param sockNum is a socket number
; @return socket structure address or 0 (not found) in EAX
;
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
mov eax, [sockNum]
; check if we got something similar to socket number
or eax, eax
jz .error
 
; scan through sockets list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
cmp [ebx + SOCKET.Number], eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
 
; okay, we found the correct one
mov eax, ebx
ret
 
.error:
xor eax, eax
ret
endp
 
; Get socket number by its structure address
; Scan through sockets list to find the socket with specified address.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
;
; @param sockAddr is a socket structure address
; @return socket number (SOCKET.Number) or 0 (not found) in EAX
;
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
mov eax, [sockAddr]
; check if we got something similar to socket structure address
or eax, eax
jz .error
 
; scan through sockets list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
 
; okay, we found the correct one
mov eax, [ebx + SOCKET.Number]
ret
 
.error:
xor eax, eax
ret
endp
/kernel/branches/net/network/stack.inc
0,0 → 1,293
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; STACK.INC ;;
;; ;;
;; TCP/IP stack for Menuet OS ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; ;;
;; See file COPYING for details ;;
;; ;;
;; Version 0.7 ;;
;; Added a timer per socket to allow delays when rx window ;;
;; gets below 1KB ;;
;; ;;
;;10.01.2007 Bugfix for checksum function from Paolo Franchetti ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 983 $
 
 
;*******************************************************************
; Interface
; The interfaces defined in ETHERNET.INC plus:
; stack_init
; stack_handler
; app_stack_handler
; app_socket_handler
; checksum
;
;*******************************************************************
 
uglobal
last_1sTick db ?
last_1hsTick dd ?
endg
 
MAX_NET_DEVICES equ 16
 
; TCP opening modes
SOCKET_PASSIVE equ 0
SOCKET_ACTIVE equ 1
 
;AF_UNSPEC equ 0
;AF_UNIX equ 1
AF_INET4 equ 2
;AF_AX25 equ 3
;AF_IPX equ 4
;AF_APPLETALK equ 5
;AF_NETROM equ 6
;AF_BRIDGE equ 7
;AF_AAL5 equ 8
;AF_X25 equ 9
;AF_INET6 equ 10
;AF_MAX equ 12
 
IP_PROTO_IP equ 0
IP_PROTO_ICMP equ 1
IP_PROTO_TCP equ 6
IP_PROTO_UDP equ 17
 
MIN_EPHEMERAL_PORT equ 49152
MAX_EPHEMERAL_PORT equ 61000
 
include "queue.inc"
include "ARP.inc"
include "IPv4.inc"
include "ethernet.inc"
include "socket.inc"
;include "TCP.inc"
include "UDP.inc"
include "ICMP.inc"
 
;-----------------------------------------------
;
; stack_init
;
; This function calls all network init procedures
;
; IN: /
; OUT: /
;
;-----------------------------------------------
 
align 4
stack_init:
 
call ETH_init
call IPv4_init
call ARP_init
call UDP_init
call ICMP_init
call socket_init
 
mov al, 0x0 ; set up 1s timer
out 0x70, al
in al, 0x71
mov [last_1sTick], al
 
ret
 
 
 
;-----------------------------------------------
;
; stack_handler
;
; This function calls all network init procedures
;
; IN: /
; OUT: /
;
;-----------------------------------------------
 
align 4
stack_handler:
 
cmp [ETH_RUNNING], 0
je .exit
 
call ETH_Handler ; handle all queued ethernet packets
call ETH_send_queued
 
; Test for 10ms tick, call tcp timer
mov eax, [timer_ticks]
cmp eax, [last_1hsTick]
je .sec_tick
 
mov [last_1hsTick], eax
; call tcp_tx_handler
 
.sec_tick:
 
; Test for 1 second event, call 1s timer functions
mov al, 0x0 ;second
out 0x70, al
in al, 0x71
cmp al, [last_1sTick]
je .exit
 
mov [last_1sTick], al
 
stdcall arp_table_manager, ARP_TABLE_TIMER, 0, 0
call IPv4_decrease_fragment_ttls
; call tcp_tcb_handler
 
.exit:
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Checksum [by Johnny_B]
;; IN:
;; buf_ptr=POINTER to buffer
;; buf_size=SIZE of buffer
;; OUT:
;; AX=16-bit checksum
;; Saves all used registers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc checksum_jb stdcall uses ebx esi ecx,\
buf_ptr:DWORD, buf_size:DWORD
 
xor eax, eax
xor ebx, ebx ;accumulator
mov esi, dword[buf_ptr]
mov ecx, dword[buf_size]
shr ecx, 1 ; ecx=ecx/2
jnc @f ; if CF==0 then size is even number
mov bh, byte[esi + ecx*2]
@@:
cld
 
.loop:
lodsw ;eax=word[esi],esi=esi+2
xchg ah,al ;cause must be a net byte-order
add ebx, eax
loop .loop
 
mov eax, ebx
shr eax, 16
add ax, bx
not ax
 
ret
endp
 
 
 
;----------------------------------------------------------------
;
;
;
;----------------------------------------------------------------
 
align 4
sys_network:
 
cmp bh, MAX_NET_DEVICES ; Check if device number exists
jge .doesnt_exist
 
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6
 
cmp dword [esi + ETH_DRV_LIST], 0 ; check if driver is running
je .doesnt_exist
 
test bl, bl ; 0 = Get device type (ethernet/token ring/...)
jnz @f
 
;TODO: write code here
 
 
@@:
dec bl ; 1 = Get device name
jnz @f
 
mov esi, [esi + ETH_DRV_LIST]
mov esi, [esi + ETH_DEVICE.name]
mov edi, ecx
 
mov ecx, 64 ; max length
repnz movsb
 
ret
 
; TODO: create function wich outputs number of active network devices
 
@@:
.doesnt_exist:
DEBUGF 1,"sys_network: invalid device/function specified!\n"
mov eax, -1
 
ret
 
 
 
 
;----------------------------------------------------------------
;
;
;
;----------------------------------------------------------------
 
align 4
sys_protocols:
 
cmp bh, MAX_NET_DEVICES ; Check if device number exists
jge .doesnt_exist
 
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6
 
cmp dword [esi + ETH_DRV_LIST], 0 ; check if driver is running TODO: check otehr lists too
je .doesnt_exist
 
push .return ; return address (we will be using jumps instead of calls)
 
mov eax, ebx ; set ax to protocol number
shr eax, 16 ;
 
cmp ax , IP_PROTO_IP
je IPv4_API
 
cmp ax , IP_PROTO_ICMP
je ICMP_API
 
cmp ax , IP_PROTO_UDP
je UDP_API
 
; cmp ax , IP_PROTO_TCP
; je TCP_API
 
cmp ax, ETHER_ARP
je ARP_API
 
cmp ax, 1337
je ETH_API
 
add esp,4 ; if we reached here, no function was called, so we need to balance stack
 
.doesnt_exist:
DEBUGF 1,"sys_protocols: invalid device specified!\n"
mov eax, -1
 
.return:
mov [esp+32], eax
ret
/kernel/branches/net/network/tcp.inc
0,0 → 1,1346
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; TCP.INC ;;
;; ;;
;; TCP Processes for Menuet OS TCP/IP stack ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; ;;
;; See file COPYING for details ;;
;; v0.6 : Added reset handling in the established state ;;
;; Added a timer per socket to allow delays when ;;
;; rx window gets below 1KB ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$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
 
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
 
 
;***************************************************************************
; Function
; tcp_tcb_handler
;
; Description
; Handles sockets in the timewait state, closing them
; when the TCB timer expires
;
;***************************************************************************
 
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
;
;***************************************************************************
 
proc tcp_tx_handler stdcall
; 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
endp
 
 
;***************************************************************************
; Function
; tcp_rx
;
; Description
; TCP protocol handler
; This is a kernel function, called by ip_rx
; IP buffer address given in edx
; IP buffer number in eax
; Free up (or re-use) IP buffer when finished
;
;***************************************************************************
 
proc tcp_rx stdcall uses ebx
; The process is as follows.
; Look for a socket with matching remote IP, remote port, local port
; if not found, then
; look for remote IP + local port match ( where sockets remote port = 0)
; if not found, then
; look for a socket where local socket port == IP Packets remote port
; where sockets remote port, remote IP = 0
; discard if not found
; Call sockets tcbStateMachine, with pointer to Packet.
; the state machine will not delete the Packet, so do that here.
 
push eax
 
; 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 + 20 + 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, [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 + 20 + 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 + 20 + 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, [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.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 + 20 + 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:
pop eax
call freeBuff
ret
endp
 
 
;***************************************************************************
; 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
;
;***************************************************************************
 
proc build_tcp_Packet stdcall, sockAddr:DWORD
push ecx ; Save data length
 
; convert buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
 
mov edx, eax
 
mov [edx + 20 + TCP_Packet.Flags], bl ; TCP flags
 
mov ebx, [sockAddr]
 
; 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
 
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
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
 
 
 
 
 
TCP_HANDLER:
;;; TODO: write code here
 
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
 
 
/kernel/branches/net/network/udp.inc
0,0 → 1,294
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; UDP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 983 $
 
 
struct UDP_Packet
.SourcePort dw ?
.DestinationPort dw ?
.Length dw ? ; Length of (UDP Header + Data)
.Checksum dw ?
.Data:
 
ends
 
 
align 4
uglobal
UDP_PACKETS_TX rd MAX_IP
UDP_PACKETS_RX rd MAX_IP
endg
 
 
;-----------------------------------------------------------------
;
; UDP_init
;
; This function resets all UDP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
UDP_init:
 
xor eax, eax
mov edi, UDP_PACKETS_TX
mov ecx, 2*MAX_IP
rep stosd
 
ret
 
 
 
;-----------------------------------------------------------------
;
; UDP_Handler:
;
; Called by IPv4_handler,
; this procedure will inject the udp data diagrams in the application sockets.
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; UDP Packet size in ecx
; pointer to UDP Packet data in edx
; OUT: /
;
;-----------------------------------------------------------------
 
UDP_Handler:
 
DEBUGF 1,"UDP_Handler\n"
; TODO: First validate the header & checksum. Discard buffer if error
 
; Look for a socket where
; IP Packet UDP Destination Port = local Port
; IP Packet SA = Remote IP
 
mov esi, net_sockets
.try_more:
mov ax , [edx + UDP_Packet.DestinationPort] ; get the local port from the IP Packet's UDP header
rol ax , 8
.next_socket:
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .dump
cmp [esi + SOCKET.Type], IP_PROTO_UDP
jne .next_socket
cmp [esi + SOCKET.LocalPort], ax
jne .next_socket
 
; For dhcp, we must allow any remote server to respond.
; I will accept the first incoming response to be the one
; I bind to, if the socket is opened with a destination IP address of
; 255.255.255.255
cmp [esi + SOCKET.RemoteIP], 0xffffffff
je @f
 
mov eax, [esp]
mov eax, [eax + ETH_FRAME.Data + IPv4_Packet.SourceAddress] ; get the Source address from the IP Packet
cmp [esi + SOCKET.RemoteIP], eax
jne .try_more ; Quit if the source IP is not valid, check for more sockets with this IP/PORT combination
 
@@:
DEBUGF 1,"Found valid UDP packet for socket %x\n", esi
 
 
; sub ecx, UDP_Packet.Data ; get # of bytes in ecx
; mov eax, ecx
 
movzx ecx, [edx + UDP_Packet.Length]
xchg cl , ch
 
; cmp ecx, eax ; If UDP packet size is bigger then IP packet told us,
; jg .error ; Something must went wrong!
 
lea ebx, [esi + SOCKET.lock]
call wait_mutex
 
; OK - we have a valid UDP Packet for this socket.
; First, update the sockets remote port number with the incoming msg
; - it will have changed
; from the original ( 69 normally ) to allow further connects
mov ax, [edx + UDP_Packet.SourcePort] ; get the UDP source port
xchg al, ah
mov [esi + SOCKET.RemotePort], ax
 
; Now, copy data to socket. We have socket address as [eax + sockets].
; We have IP Packet in edx
 
add edx, UDP_Packet.Data
mov eax, [esi + SOCKET.rxDataCount] ; get # of bytes already in buffer
DEBUGF 1,"bytes in socket: %u ", eax
lea edi, [ecx + eax] ; check for buffer overflow
cmp edi, SOCKETBUFFSIZE - SOCKETHEADERSIZE ;
jg .dump ;
add [esi + SOCKET.rxDataCount], ecx ; increment the count of bytes in buffer
DEBUGF 1,"adding %u bytes\n", ecx
 
; ecx has count, edx points to data
 
lea edi, [esi + eax + SOCKETHEADERSIZE]
push esi
push ecx
mov esi, edx
shr ecx, 2
rep movsd ; copy the data across
pop ecx
and ecx, 3
rep movsb
pop esi
 
DEBUGF 1,"UDP socket updated\n"
 
mov [esi + SOCKET.lock], 0
 
; flag an event to the application
mov eax, [esi + SOCKET.PID] ; get socket owner PID
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
 
jmp .dump
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
 
mov [check_idle_semaphore], 200
 
.dump:
DEBUGF 1,"UDP_handler - dumping\n"
 
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
 
 
 
;-----------------------------------------------------------------
;
; Note: UDP works only on top of IP protocol :)
;
; IN: eax = dest ip
; ebx = source ip
; ecx = data length
; edx = remote port shl 16 + local port
; esi = data offset
;
;-----------------------------------------------------------------
 
UDP_create_Packet:
 
DEBUGF 1,"Create UDP Packet\n"
 
push edx esi
 
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
 
sub ecx , UDP_Packet.Data
mov byte[edi + UDP_Packet.Length], ch
mov byte[edi + UDP_Packet.Length+1], cl
 
pop esi
push edi
add edi, UDP_Packet.Data
push cx
shr ecx, 2
rep movsd
pop cx
and cx , 3
rep movsb
pop edi
 
pop ecx
bswap ecx ; convert little endian - big endian
rol ecx, 16 ;
mov dword [edi + UDP_Packet.SourcePort], ecx ; notice: we write both port's at once
 
 
mov [edi + UDP_Packet.Checksum], 0
 
; TODO: calculate checksum using Pseudo-header (However, using a 0 as checksum shouldnt generate any errors :)
 
push ebx eax ; TODO: make this work on other protocols besides ethernet
mov ebx,edx ;
DEBUGF 1,"Sending UDP Packet to device %x\n", ebx ;
jmp ETH_Sender ;
 
.exit:
ret
 
 
 
;---------------------------------------------------------------------------
;
; UDP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
 
align 4
UDP_API:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
add eax, UDP_PACKETS_TX
mov eax, [eax]
ret
 
.packets_rx:
add eax, UDP_PACKETS_RX
mov eax, [eax]
ret
/kernel/branches/net/network/.
Property changes:
Added: svn:mergeinfo