/********************************************************************************* File: arp.c Date: Version: 0.1 Author: Jari Lahti (jari.lahti@violasystems.com) Description: arp (address resolution protocol) functions Version Info: *********************************************************************************/ #include "opentcp.h" //extern UINT8 RemoteEthernet[]; //Host Ethernet Address //extern UINT32 IP_RemoteAdd; //Remote IP Address //extern UINT32 IP_LocalAdd; //My IP address struct arpentry arptable[ARP_TSIZE]; UINT8 ArpTimer; UINT8 Process_ARP (struct otcp_ethframe* frame) { /* Checks received Ethernet Frame to see if it's an ARP */ /* If valid ARP response, updates ARP cache */ /* If ARP request, sends response */ /* Return values: FALSE = No ARP, TRUE = Was ARP */ /* if ARP detected, upper layer protocols must skip it */ UINT8 temp; /* Check if ARP packet*/ if( frame->protocol == ARP_ETHCODE ) { /* Yep, ARP */ NETWORK_RECEIVE_INITIALIZE(frame->bufindex); /* Is it long enough? */ if( frame->framesize < (2*MAXHWALEN + 2*MAXPRALEN + 2 + 6) ) { /* Somehow corrupted ARP packet */ DEBUGOUT("Corrupted ARP packet\n\r"); NETWORK_RECEIVE_END(); return(TRUE); } /* Ignore next 6 bytes: , */ /* and */ for(temp=0; temp<6; temp++) RECEIVE_NETWORK_B(); DEBUGOUT("Incoming ARP..\n\r"); /* Check if request or response */ if( RECEIVE_NETWORK_B() == 0x00) { temp = RECEIVE_NETWORK_B(); //get opcode //debugout(temp); if( temp == ARP_REQUEST ) { DEBUGOUT(" ARP REQUEST Received..\n\r"); ARPSendResponse(); } else if( temp == ARP_REPLY ) { DEBUGOUT("ARP Response Received..\n\r"); ARPGetResponse(); } /* Wasn't request or response or all done, dump it */ } //NETWORK_RECEIVE_END(); return(TRUE); } /* Wasn't ARP, don't touch the packet */ return(FALSE); } void ARPSendResponse(void) { UINT8 rem_hwadr[MAXHWALEN]; UINT32 rem_ip; UINT32 ltemp; UINT8 i; /* Record Sender's HW address */ for( i=0; i < MAXHWALEN; i++ ) rem_hwadr[i] = RECEIVE_NETWORK_B(); /* Read Sender's IP Address */ rem_ip = 0; for( i=0; i>8) ); /* Hardware Type */ SEND_NETWORK_B( (BYTE)AR_HARDWARE ); SEND_NETWORK_B(0x08); /* Protocol Type */ SEND_NETWORK_B(0x00); SEND_NETWORK_B(MAXHWALEN); /* HW Adr Len */ SEND_NETWORK_B(MAXPRALEN); /* Protocol Adr. Len*/ SEND_NETWORK_B( 0x00 ); /* ARP Opcode */ SEND_NETWORK_B( 0x02 ); SEND_NETWORK_B((UINT8)(localmachine.localHW[0])); /* Address fields */ SEND_NETWORK_B((UINT8)(localmachine.localHW[1])); SEND_NETWORK_B((UINT8)(localmachine.localHW[2])); SEND_NETWORK_B((UINT8)(localmachine.localHW[3])); SEND_NETWORK_B((UINT8)(localmachine.localHW[4])); SEND_NETWORK_B((UINT8)(localmachine.localHW[5])); SEND_NETWORK_B((UINT8)(localmachine.localip>>24)); SEND_NETWORK_B((UINT8)(localmachine.localip>>16)); SEND_NETWORK_B((UINT8)(localmachine.localip>>8)); SEND_NETWORK_B((UINT8)(localmachine.localip)); SEND_NETWORK_B((UINT8)rem_hwadr[0]); SEND_NETWORK_B((UINT8)rem_hwadr[1]); SEND_NETWORK_B((UINT8)rem_hwadr[2]); SEND_NETWORK_B((UINT8)rem_hwadr[3]); SEND_NETWORK_B((UINT8)rem_hwadr[4]); SEND_NETWORK_B((UINT8)rem_hwadr[5]); SEND_NETWORK_B((UINT8)(rem_ip>>24)); SEND_NETWORK_B((UINT8)(rem_ip>>16)); SEND_NETWORK_B((UINT8)(rem_ip>>8)); SEND_NETWORK_B((UINT8)rem_ip); NETWORK_COMPLETE_SEND(0x0040); /* Send the packet */ DEBUGOUT("ARP Reply Sent..\n\r"); /* Add the Sender's info to cache because we can */ arpadd(rem_ip, &otcp_txframe.destination[0], ARP_TEMP_IP); return; } void ARPGetResponse(void) { struct arpentry *qstruct; UINT8 rem_hwadr[MAXHWALEN]; UINT32 rem_ip; UINT32 ltemp; UINT8 i; UINT8 j; /* Read Sender's HW address */ for( i=0; i < MAXHWALEN; i++ ) rem_hwadr[i-1] = RECEIVE_NETWORK_B(); /* Read Sender's IP Address */ rem_ip = 0; for( i=0; istate == ARP_FREE ) continue; if( qstruct->state == ARP_RESERVED ) continue; if( rem_ip == qstruct->pradr ) { /* We are caching that IP, refresh it */ DEBUGOUT("Refreshing ARP cache from Reply..\n\r"); for( j=0; jhwadr[j] = rem_hwadr[j]; qstruct->ttl = ARP_TIMEOUT; qstruct->retries = ARP_MAXRETRY; qstruct->state = ARP_RESOLVED; /* Done */ break; } } } /****************************************************************************** Function: arpsendreq Parameters: UINT8 entry - Entry to ARP table Return val: none Date: 01.11.2001 Desc: Send ARP request of given entry *******************************************************************************/ void arpsendreq (UINT8 entry) { struct arpentry *qstruct; UINT8 i; qstruct = &arptable[entry]; NETWORK_SEND_INITIALIZE(TXBUF_ENET); /* Add datalink (Ethernet addresses) information */ for( i=0; i>8) ); /* Hardware Type */ SEND_NETWORK_B( (BYTE) AR_HARDWARE ); SEND_NETWORK_B(0x08); /* Protocol Type */ SEND_NETWORK_B(0x00); SEND_NETWORK_B(MAXHWALEN); /* HW Adr Len */ SEND_NETWORK_B(MAXPRALEN); /* Protocol Adr. Len*/ SEND_NETWORK_B( (BYTE)(ARP_REQUEST>>8)); /* ARP Opcode */ SEND_NETWORK_B( (BYTE) ARP_REQUEST ); SEND_NETWORK_B((UINT8)localmachine.localHW[0]); /* Address fields */ SEND_NETWORK_B((UINT8)localmachine.localHW[1]); SEND_NETWORK_B((UINT8)localmachine.localHW[2]); SEND_NETWORK_B((UINT8)localmachine.localHW[3]); SEND_NETWORK_B((UINT8)localmachine.localHW[4]); SEND_NETWORK_B((UINT8)localmachine.localHW[5]); SEND_NETWORK_B((UINT8)(localmachine.localip>>24)); SEND_NETWORK_B((UINT8)(localmachine.localip>>16)); SEND_NETWORK_B((UINT8)(localmachine.localip>>8)); SEND_NETWORK_B((UINT8)localmachine.localip); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)0xFF); SEND_NETWORK_B((UINT8)(qstruct->pradr>>24)); SEND_NETWORK_B((UINT8)(qstruct->pradr>>16)); SEND_NETWORK_B((UINT8)(qstruct->pradr>>8)); SEND_NETWORK_B((UINT8)qstruct->pradr); /* Packet assembled now, just send it ... */ NETWORK_COMPLETE_SEND(0x0040); /* Min packet size */ DEBUGOUT("ARP Request Sent\n\r"); } /****************************************************************************** Function: arpalloc Parameters: UINT8 type - type of ARP entry Return val: positive - pointer to allocated arp entry (-1) - entry not found (entries used up) Date: 01.11.2001 Desc: Allocate arp entry for given type. Chooses the unused entry if one exists. Otherwice deletes entries in round-robin fashion. Static entries are not deleted/reallocated. *******************************************************************************/ INT8 arpalloc (UINT8 type) { struct arpentry *qstruct; INT8 i; static UINT8 aenext = 1; /* Cache Manager */ INT16 found; /* Try to find free entry */ found = -1; for( i=0; istate = ARP_RESERVED; qstruct->type = type; return( (UINT8)found ); } /* if no success, try ro find first temporary entry */ /* on round-robin fashion */ for( i=0; i= ARP_TSIZE ) aenext = 1; } /* Was there free or temporary entries? */ if( found == (-1) ) return(-1); /* Next time start from next entry */ aenext = (aenext + 1); if( aenext >= ARP_TSIZE ) aenext = 1; qstruct = &arptable[found]; /* Set ARP initial parameters */ qstruct->state = ARP_RESERVED; qstruct->type = type; /* Was return(i)!!! <-wrong!! */ return((UINT8)found); } /****************************************************************************** Function: arpadd Parameters: UINT32 pra - protocol address UINT8* hwadr - Ethernet MAC address (6 bytes) UINT8 type - type of address allocated if not found Return val: INT8 (-1) - Not succesful (no ARP entries) 0 - Address already in cache, refreshed 1 - New entry created Date: 10.7.2002 Desc: Add given IP address and MAC address to ARP cache *******************************************************************************/ INT8 arpadd (UINT32 pra, UINT8* hwadr, UINT8 type) { struct arpentry *qstruct; INT8 i; INT8 j; for( i=0; istate == ARP_FREE ) continue; if( (qstruct->pradr == pra) && (pra != IP_BROADCAST_ADDRESS)) { /* The address is in cache, refresh it */ DEBUGOUT(" Refreshing Existing ARP Entry..\n\r"); for( j=0; jhwadr[j] = *hwadr++; qstruct->ttl = ARP_TIMEOUT; qstruct->retries = ARP_MAXRETRY; qstruct->state = ARP_RESOLVED; /* All OK */ return (0); } } /* Does this address belong to outer world? */ if( IsSub(pra, &localmachine) == 0) return(-1); if( localmachine.defgw == pra ) { if(localmachine.defgw != 0) { type = ARP_FIXED_IP; } } /* Address was'nt on cache. Need to allocate new one */ DEBUGOUT("Allocating New ARP Entry..\n\r"); i = arpalloc(type); if( i < 0 ) /* No Entries Left? */ return(-1); /* Fill the fields */ qstruct = &arptable[i]; qstruct->pradr = pra; /* Fill IP */ for(i=0; ihwadr[i] = *hwadr++; /* Fill HW address */ qstruct->retries = ARP_MAXRETRY; qstruct->ttl = ARP_TIMEOUT; qstruct->state = ARP_RESOLVED; DEBUGOUT("ARP Entry Created!..\n\r"); return(1); } /****************************************************************************** Function: arpfind Parameters: UINT32 pra - protocol address struct netif *localmachine - pointer to config. of network interface used UINT8 type - type of address allocated if not found Return val: struct arpentry* - pointer to solved entry of arp table 0 - entry not found or not ready yet Date: 01.11.2001 Desc: Find an ARP entry given a protocol address. If no entry is found, new one is allocated and ARPREQ sent. *******************************************************************************/ struct arpentry* arpfind (LWORD pra, struct netif *machine, UINT8 type) { struct arpentry *qstruct; INT8 i; DEBUGOUT("Trying to find MAC address from ARP Cache\n\r"); /* Is the address in the cache */ for( i=0; istate == ARP_FREE ) continue; if( qstruct->pradr == pra) { /* The address is in cache, is it valid? */ DEBUGOUT("Address In Cache\n\r"); if( qstruct->state < ARP_RESOLVED ) { DEBUGOUT("Address in cache but unresolved :(\n\r"); return(0); } /* All OK */ return(qstruct); } } /* The address wasn't on the cache. Is it in our Subnet? */ if( IsSub(pra, machine) ) { /* Yep, we need to send ARP REQUEST */ DEBUGOUT("Need to send ARP Request to local network..\n\r"); if( machine->defgw == pra ) { if(machine->defgw != 0) { type = ARP_FIXED_IP; } } i = arpalloc(type); if( i < 0 ) /* No Entries Left? */ return(0); /* Send Request after filling the fields */ qstruct = &arptable[i]; qstruct->pradr = pra; /* Fill IP */ qstruct->hwadr[0] = 0xFF; /* Fill Broadcast IP */ qstruct->hwadr[1] = 0xFF; qstruct->hwadr[2] = 0xFF; qstruct->hwadr[3] = 0xFF; qstruct->hwadr[4] = 0xFF; qstruct->hwadr[5] = 0xFF; qstruct->retries = ARP_MAXRETRY; qstruct->ttl = ARP_RESEND; arpsendreq( i ); qstruct->state = ARP_PENDING; /* Waiting for Reply */ return(0); } /* The Address belongst to the outern world, need to use MAC of */ /* Default Gateway */ DEBUGOUT("Need to use MAC of Default GW\n\r"); if(machine->defgw == 0) /* It's not specified */ return(0); for( i=0; istate == ARP_FREE ) continue; if( qstruct->pradr == machine->defgw ) { /* The address is in cache, is it valid? */ if( qstruct->state < ARP_RESOLVED ) { DEBUGOUT("The Address of Def. GW is not Solved!\n\r"); return(0); } /* All OK */ DEBUGOUT(" >> Default Gateway MAC found!\n\r"); return(qstruct); } } /* No default GW address on cache, allocate entry for it? */ i = arpalloc(ARP_FIXED_IP); if( i < 0 ) /* No Entries Left? */ return(0); /* Send Request after filling the fields */ qstruct = &arptable[i]; qstruct->pradr = machine->defgw; /* Fill IP */ qstruct->hwadr[0] = 0xFF; /* Fill Broadcast IP */ qstruct->hwadr[1] = 0xFF; qstruct->hwadr[2] = 0xFF; qstruct->hwadr[3] = 0xFF; qstruct->hwadr[4] = 0xFF; qstruct->hwadr[5] = 0xFF; qstruct->retries = ARP_MAXRETRY; qstruct->ttl = ARP_RESEND; arpsendreq( i ); qstruct->state = ARP_PENDING; /* Waiting for Reply */ return 0; } /****************************************************************************** Function: arp_keepcache Parameters: UINT32 pra - ip address to be held on cache Return val: INT16 0 - OK (-1) - Not OK Date: 02.4.2003 Desc: Tries to keep given dynamic IP on cache. Notice that there is no guarantee for that because round robin cache replacement may override the entry. This function is best suitable for keeping ARP for TCP connections by calling it periodically. *******************************************************************************/ INT16 arp_keepcache (UINT32 pra) { struct arpentry *qstruct; INT8 i; DEBUGOUT("Trying to keep IP on cache\r\n"); /* Is the address in the cache */ for( i=0; istate == ARP_FREE ) continue; if( qstruct->state < ARP_RESOLVED ) continue; if( qstruct->pradr == pra) { /* The address is in cache, is it valid? */ if( qstruct->type != ARP_TEMP_IP ) return(-1); /* Is it time to start refrsh the entry? */ if( qstruct->ttl <= (ARP_RESEND * 2) ) qstruct->state = ARP_REFRESHING; /* All OK */ DEBUGOUT("Keeping address on cache\r\n"); return(0); } } return(-1); } /****************************************************************************** Function: arpmanage Parameters: none Return val: void Date: 04.11.2001 Desc: Iterate through ARP cache aging entries. If timed-out entry is found, remove it (dynamic address) or update it (static address). This function MUST be called periodically by system. *******************************************************************************/ void arpmanage (void) { struct arpentry *qstruct; UINT8 i,j; static UINT8 aenext=0; /* Check Timer before entering */ if( check_timer(ArpTimer) ) return; init_timer( ArpTimer, ARP_MANG_TOUT*TIMERTIC); //DEBUGOUT("Managing ARP Cache\n\r"); for( i=0; i= ARP_TSIZE) aenext = 0; if( qstruct->state == ARP_FREE ) continue; /* TODO: How about ARP_RESERVED? */ if( qstruct->ttl > 0 ) /* Aging */ qstruct->ttl --; if( qstruct->ttl == 0 ) /* Timed Out? */ { /* Do it for temporay entries */ DEBUGOUT("Found Timed out Entry..\n\r"); if( qstruct->type == ARP_TEMP_IP ) { /* Release it? */ if( qstruct->state == ARP_RESOLVED ) { DEBUGOUT("Releasing ARP Entry..\n\r"); qstruct->state = ARP_FREE; continue; } /* Decrease retries left */ if( qstruct->retries > 0 ) qstruct->retries--; if( qstruct->retries == 0 ) { DEBUGOUT("ARP Replies Used up, releasing entry..\n\r"); qstruct->state = ARP_FREE; continue; } /* So we need to resend ARP request */ DEBUGOUT("Trying to Resolve dynamic ARP Entry..\n\r"); qstruct->ttl = ARP_RESEND; arpsendreq( j ); if(qstruct->state != ARP_REFRESHING) qstruct->state = ARP_PENDING; /* Waiting for Reply */ return; } /* Do it for Static Entries */ if( qstruct->type == ARP_FIXED_IP ) { /* So we need to resend ARP request */ /* Do not try to refresh broadcast */ if(qstruct->pradr == IP_BROADCAST_ADDRESS) { qstruct->ttl = ARP_TIMEOUT; continue; } DEBUGOUT("Refreshing Static ARP Entry..\n\r"); if( qstruct->retries > 0 ) qstruct->retries--; if( qstruct->retries == 0 ) qstruct->state = ARP_PENDING; else qstruct->state = ARP_REFRESHING; qstruct->ttl = ARP_RESEND; arpsendreq( j ); return; } } } } /****************************************************************************** Function: arpinit Parameters: none Return val: none Date: 01.11.2001 Desc: Initialize data structures for ARP processing *******************************************************************************/ void arpinit (void) { struct arpentry *qstruct; INT8 i; DEBUGOUT("Initializing ARP"); for( i=0; istate = ARP_FREE; qstruct->type = ARP_TEMP_IP; DEBUGOUT("."); } ArpTimer = get_timer(); init_timer(ArpTimer, ARP_MANG_TOUT*TIMERTIC); /* set broadcast entry */ qstruct = &arptable[0]; qstruct->pradr = IP_BROADCAST_ADDRESS; qstruct->state = ARP_RESOLVED; qstruct->type = ARP_FIXED_IP; qstruct->ttl = ARP_TIMEOUT; qstruct->retries = ARP_MAXRETRY; for(i=0; ihwadr[i] = 0xFF; DEBUGOUT("\n\r"); } /****************************************************************************** Function: IsSub Parameters: UINT32 ipadr - IP address under check struct netif *machine - pointer to config. of network interface used Return val: 1 - ipadr belongs to subnet of given machine 0 - ipadr is NOT a part of subnet on given machine Date: 05.11.2001 Desc: Checks if the given IP address belongst to subnet of given machine. *******************************************************************************/ UINT8 IsSub (LWORD ipadr, struct netif* machine) { UINT32 ltemp; ltemp = ipadr & machine->netmask; /* Get Subnet part */ ltemp ^= (machine->localip & machine->netmask); /* Compare to my IP */ if( ltemp ) return(0); return(1); }