;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; ARP.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$ ARP_NO_ENTRY equ 0 ARP_VALID_MAPPING equ 1 ARP_AWAITING_RESPONSE equ 2 ARP_RESPONSE_TIMEOUT equ 3 ARP_REQUEST_TTL = 20 ; in seconds ARP_ENTRY_TTL = 600 ; in seconds 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 ; ; This function resets all ARP variables ; ; IN: / ; OUT: / ; ;----------------------------------------------------------------- align 4 ARP_init: xor eax, eax mov [NumARP], eax mov edi, ARP_PACKETS_TX mov ecx, 2*MAX_NET_DEVICES rep stosd ret ;----------------------------------------------------------------- ; ; ARP_IP_to_MAC ; ; This function resets all ARP variables ; ; IN: eax = IPv4 address ; OUT: eax = -1 on error, else eax = first two bytes of mac ; ( high 16 bits are zero) ; ebx = last four bytes of mac ; TODO: special eax value for 'request send' ; ;----------------------------------------------------------------- align 4 ARP_IP_to_MAC: DEBUGF 1,"ARP_IP_to_MAC\n" ; 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 xor edx, edx ;;; TODO: find device num in edx mov ebx, [IP_LIST + edx] and ebx, [SUBNET_LIST + edx] mov ecx, eax and ecx, [SUBNET_LIST + edx] cmp ecx, ebx je .local mov eax, [GATEWAY_LIST + edx] DEBUGF 1,"requested IP is not on subnet, using gateway\n" .local: ; try to find it on the list mov ecx, [NumARP] test ecx, ecx jz .not_in_list mov esi, ARPTable + ARP_ENTRY.IP .scan_loop: cmp [esi], eax je .found_it add esi, ARP_ENTRY.size loop .scan_loop .not_in_list: DEBUGF 1,"IP not found on list, preparing for ARP request\n" ; if not, reserve an entry in list and send an ARP request packet push eax pushw ARP_REQUEST_TTL pushw ARP_AWAITING_RESPONSE pushd 0 pushw 0 pushd eax call ARP_add_entry cmp eax, -1 je .full ; ; This piece of code waits for an ARP reply mov ebx, eax pop eax push ebx call ARP_create_request push [timer_ticks] add dword[esp], 100*ARP_REQUEST_TTL DEBUGF 1,"Waiting for ARP reply, time: %x, entry:%u\n",[timer_ticks], [esp + 4] .dirty_loop: call change_task ; The ARP reply hasnt been received yet, tell the processor to do some other stuff first mov eax, [esp + 4] imul eax, ARP_ENTRY.size add eax, ARPTable cmp [eax + ARP_ENTRY.Status], ARP_VALID_MAPPING je .gogogo mov eax, [esp] ; Check if the reply hasnt timed-out yet cmp [timer_ticks], eax jl .dirty_loop ; or eax, -1 add esp, 8 ret .found_it: DEBUGF 1,"found MAC in ARPTable\n" movzx eax, word [esi+ARP_ENTRY.MAC] mov ebx, dword[esi+ARP_ENTRY.MAC+2] ret .full: add esp, 4 mov eax, -1 ret .gogogo: DEBUGF 1,"got ARP reply, time: %x\n",[timer_ticks] mov ebx, dword[eax+ARP_ENTRY.MAC+2] movzx eax, word [eax+ARP_ENTRY.MAC] add esp, 8 ret ;--------------------------------------------------------------------------- ; ; ARP_create_request ; ; IN: ip in eax ; ; OUT: / ; ;--------------------------------------------------------------------------- align 4 ARP_create_request: DEBUGF 1,"Create ARP Packet\n" call IPv4_dest_to_dev push eax ; DestIP mov eax, [IP_LIST+4*edi] ; senderIP push eax mov edi, [NET_DRV_LIST + 4*edi] lea eax, [edi + ETH_DEVICE.mac] mov ebx, ETH_BROADCAST mov ecx, 60 ; minimum packet size mov edx, edi ;;; mov di , ETHER_ARP call ETH_create_packet jz .exit mov ecx, eax mov [edi + ARP_Packet.HardwareType], 0x0100 ;Ethernet mov [edi + ARP_Packet.ProtocolType], 0x0008 ;IP mov [edi + ARP_Packet.HardwareSize], 6 ;MAC-addr length mov [edi + ARP_Packet.ProtocolSize], 4 ;IP-addr length mov [edi + ARP_Packet.Opcode], ARP_REQ_OPCODE ;Request add edi, ARP_Packet.SenderMAC ; sendermac lea esi, [ebx + ETH_DEVICE.mac] ; movsw ; movsd ; pop eax ; stosd ; mov eax, -1 ; destmac stosd ; stosw ; pop eax stosd ; DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx push edx ecx call [ebx + NET_DEVICE.transmit] ret .exit: add esp, 8 DEBUGF 1,"Create ARP Packet - failed\n" mov eax, -1 ret ;--------------------------------------------------------------------------- ; ; ARP_decrease_entry_ttls ; ; IN: / ; OUT: / ; ;--------------------------------------------------------------------------- align 4 ARP_decrease_entry_ttls: mov ecx, [NumARP] test ecx, ecx jz .exit mov ebx, ARPTable .timer_loop: cmp [ebx + ARP_ENTRY.TTL], 0xFFFF je .timer_loop_end ;if TTL==0xFFFF then it's static entry cmp [ebx + ARP_ENTRY.TTL], 0 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 [ebx + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE jne @f mov [ebx + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT mov [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, [NumARP] sub esi, ecx ;esi=index of entry, will be deleted push ebx ecx call ARP_del_entry pop ecx ebx jmp .timer_loop_end .timer_loop_end_with_dec: dec [ebx + ARP_ENTRY.TTL] ;decrease TTL .timer_loop_end: add ebx, ARP_ENTRY.size loop .timer_loop .exit: ret ;----------------------------------------------------------------- ; ; ARP_add_entry (or update) ; ; IN: arp entry in stack: esp .IP ; esp+4 .MAC ; esp+10 .Status ; esp+12 .TTL ; esp+14 ; ; OUT: eax = entry #, -1 on error ; ;----------------------------------------------------------------- ; TODO: use a mutex align 4 ARP_add_entry: DEBUGF 1,"ARP add entry: " mov ecx, [NumARP] test ecx, ecx jz .add mov eax, dword[esp + 4 + ARP_ENTRY.MAC] mov bx , word[esp + 4 + ARP_ENTRY.MAC + 4] mov esi, ARPTable .loop: cmp dword [esi + ARP_ENTRY.MAC], eax jne .maybe_next cmp word [esi + ARP_ENTRY.MAC + 4], bx jne .maybe_next cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF ; static entry jne .notstatic cmp dword[esp + 4 + ARP_ENTRY.TTL], 0xFFFF jne .error .notstatic: mov ebx, [NumARP] xchg ebx, ecx sub ecx, ebx jmp .add .maybe_next: add esi, ARP_ENTRY.size loop .loop mov ecx, [NumARP] cmp ecx, ARP_TABLE_SIZE jge .error .add: push ecx imul ecx, ARP_ENTRY.size lea edi, [ecx + ARPTable] lea esi, [esp + 8] mov ecx, ARP_ENTRY.size/2 repz movsw inc [NumARP] pop eax DEBUGF 1,"New entry created: %u\n", eax .exit: DEBUGF 1,"Exiting\n" ret ARP_ENTRY.size .error: DEBUGF 1,"error! \n" mov eax, -1 jmp .exit ;----------------------------------------------------------------- ; ; ARP_del_entry ; ; IN: entry # in esi ; OUT: / ; ;----------------------------------------------------------------- align 4 ARP_del_entry: DEBUGF 1,"ARP del entry %u, total entrys: %u\n", esi, [NumARP] cmp esi, [NumARP] jge .error imul esi, ARP_ENTRY.size mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size sub ecx, esi lea edi, [ARPTable + 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! rep movsw dec [NumARP] ;decrease arp-entries counter DEBUGF 1,"ARP entry deleted\n" .error: ret ;----------------------------------------------------------------- ; ; 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 cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE ; Is this a reply packet? jne .maybe_request DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\ [edx + ARP_Packet.SenderIP]:1,[edx + ARP_Packet.SenderIP+1]:1,[edx + ARP_Packet.SenderIP+2]:1,[edx + ARP_Packet.SenderIP+3]:1, mov ecx, [NumARP] test ecx, ecx jz .exit mov eax, [edx + ARP_Packet.SenderIP] mov esi, ARPTable+ARP_ENTRY.IP .loop: cmp [esi], eax je .gotit add esi, ARP_ENTRY.size loop .loop jmp .exit .gotit: DEBUGF 1,"ARP_Handler - found matching entry\n" cmp [esi+ARP_ENTRY.Status], 0x0300 ;if it is a static entry, dont touch it je .exit DEBUGF 1,"ARP_Handler - updating entry\n" mov [esi+ARP_ENTRY.Status], ARP_VALID_MAPPING mov [esi+ARP_ENTRY.TTL], ARP_ENTRY_TTL mov eax, dword [edx + ARP_Packet.SenderMAC] mov dword [esi+ARP_ENTRY.MAC], eax mov ax , word [edx + ARP_Packet.SenderMAC + 4] mov word [esi+ARP_ENTRY.MAC+4], ax jmp .exit ;------ .maybe_request: cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet? jne .exit call NET_ptr_to_num DEBUGF 1,"ARP Request packet through device: %u\n", edi inc [ARP_PACKETS_RX+4*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 ; OK, it is a request for one of our MAC addresses. Build the frame and send it ; We can reuse the buffer. (faster then using ARP_create_packet) 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, [NET_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 DEBUGF 1,"ARP_Handler - Sending reply \n" call [ebx + NET_DEVICE.transmit] ret .exit: call kernel_free add esp, 4 ; pop (balance stack) DEBUGF 1,"ARP_Handler - exiting\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: cmp ecx, [NumARP] jge .error ; edi = pointer to buffer ; ecx = # entry imul ecx, ARP_ENTRY.size add ecx, ARPTable mov esi, ecx mov ecx, ARP_ENTRY.size/2 rep movsw xor eax, eax ret .write: ; esi = pointer to buffer sub esp, ARP_ENTRY.size mov edi, esp mov ecx, ARP_ENTRY.size/2 rep movsw call ARP_add_entry ;out: eax = entry number, -1 on error ret .remove: ; ecx = # entry mov esi, ecx call ARP_del_entry ret