/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 |