Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/ipv4/arp.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file arp.c
 * @brief ARP (Address Resolution Protocol)
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @section Description
 *
 * Address Resolution Protocol is used to determine the hardware address of
 * a specific host when only its IPv4 address is known. Refer to RFC 826
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL ARP_TRACE_LEVEL

//Dependencies
#include <string.h>
#include "core/net.h"
#include "core/ethernet.h"
#include "ipv4/arp.h"
#include "debug.h"

//Check TCP/IP stack configuration
#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)

//Tick counter to handle periodic operations
systime_t arpTickCounter;


/**
 * @brief ARP cache initialization
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t arpInit(NetInterface *interface)
{
   //Initialize the ARP cache
   memset(interface->arpCache, 0, sizeof(interface->arpCache));

   //Successful initialization
   return NO_ERROR;
}


/**
 * @brief Flush ARP cache
 * @param[in] interface Underlying network interface
 **/

void arpFlushCache(NetInterface *interface)
{
   uint_t i;
   ArpCacheEntry *entry;

   //Loop through ARP cache entries
   for(i = 0; i < ARP_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->arpCache[i];

      //Drop packets that are waiting for address resolution
      arpFlushQueuedPackets(interface, entry);
      //Release ARP entry
      entry->state = ARP_STATE_NONE;
   }
}


/**
 * @brief Create a new entry in the ARP cache
 * @param[in] interface Underlying network interface
 * @return Pointer to the newly created entry
 **/

ArpCacheEntry *arpCreateEntry(NetInterface *interface)
{
   uint_t i;
   ArpCacheEntry *entry;
   ArpCacheEntry *oldestEntry;

   //Keep track of the oldest entry
   oldestEntry = &interface->arpCache[0];

   //Loop through ARP cache entries
   for(i = 0; i < ARP_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->arpCache[i];

      //Check whether the entry is currently in used or not
      if(entry->state == ARP_STATE_NONE)
      {
         //Erase contents
         memset(entry, 0, sizeof(ArpCacheEntry));
         //Return a pointer to the ARP entry
         return entry;
      }

      //Keep track of the oldest entry in the table
      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
         oldestEntry = entry;
   }

   //Drop any pending packets
   arpFlushQueuedPackets(interface, oldestEntry);
   //The oldest entry is removed whenever the table runs out of space
   memset(oldestEntry, 0, sizeof(ArpCacheEntry));
   //Return a pointer to the ARP entry
   return oldestEntry;
}


/**
 * @brief Search the ARP cache for a given IPv4 address
 * @param[in] interface Underlying network interface
 * @param[in] ipAddr IPv4 address
 * @return A pointer to the matching ARP entry is returned. NULL is returned
 *   if the specified IPv4 address could not be found in ARP cache
 **/

ArpCacheEntry *arpFindEntry(NetInterface *interface, Ipv4Addr ipAddr)
{
   uint_t i;
   ArpCacheEntry *entry;

   //Loop through ARP cache entries
   for(i = 0; i < ARP_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->arpCache[i];

      //Check whether the entry is currently in used
      if(entry->state != ARP_STATE_NONE)
      {
         //Current entry matches the specified address?
         if(entry->ipAddr == ipAddr)
            return entry;
      }
   }

   //No matching entry in ARP cache...
   return NULL;
}


/**
 * @brief Send packets that are waiting for address resolution
 * @param[in] interface Underlying network interface
 * @param[in] entry Pointer to a ARP cache entry
 **/

void arpSendQueuedPackets(NetInterface *interface, ArpCacheEntry *entry)
{
   uint_t i;
   ArpQueueItem *item;

   //Check current state
   if(entry->state == ARP_STATE_INCOMPLETE)
   {
      //Loop through the queued packets
      for(i = 0; i < entry->queueSize; i++)
      {
         //Point to the current queue item
         item = &entry->queue[i];

         //Send the IPv4 packet
         ethSendFrame(interface, &entry->macAddr,
            item->buffer, item->offset, ETH_TYPE_IPV4);

         //Release memory buffer
         netBufferFree(item->buffer);
      }
   }

   //The queue is now empty
   entry->queueSize = 0;
}


/**
 * @brief Flush packet queue
 * @param[in] interface Underlying network interface
 * @param[in] entry Pointer to a ARP cache entry
 **/

void arpFlushQueuedPackets(NetInterface *interface, ArpCacheEntry *entry)
{
   uint_t i;

   //Check current state
   if(entry->state == ARP_STATE_INCOMPLETE)
   {
      //Drop packets that are waiting for address resolution
      for(i = 0; i < entry->queueSize; i++)
      {
         //Release memory buffer
         netBufferFree(entry->queue[i].buffer);
      }
   }

   //The queue is now empty
   entry->queueSize = 0;
}


/**
 * @brief Address resolution using ARP protocol
 * @param[in] interface Underlying network interface
 * @param[in] ipAddr IPv4 address
 * @param[in] macAddr Physical address matching the specified IPv4 address
 * @return Error code
 **/

error_t arpResolve(NetInterface *interface, Ipv4Addr ipAddr, MacAddr *macAddr)
{
   error_t error;
   ArpCacheEntry *entry;

   //Search the ARP cache for the specified IPv4 address
   entry = arpFindEntry(interface, ipAddr);

   //Check whether a matching entry has been found
   if(entry != NULL)
   {
      //Check the state of the ARP entry
      if(entry->state == ARP_STATE_INCOMPLETE)
      {
         //The address resolution is already in progress
         error = ERROR_IN_PROGRESS;
      }
      else if(entry->state == ARP_STATE_STALE)
      {
         //Copy the MAC address associated with the specified IPv4 address
         *macAddr = entry->macAddr;

         //Start delay timer
         entry->timestamp = osGetSystemTime();
         //Delay before sending the first probe
         entry->timeout = ARP_DELAY_FIRST_PROBE_TIME;
         //Switch to the DELAY state
         entry->state = ARP_STATE_DELAY;

         //Successful address resolution
         error = NO_ERROR;
      }
      else
      {
         //Copy the MAC address associated with the specified IPv4 address
         *macAddr = entry->macAddr;

         //Successful address resolution
         error = NO_ERROR;
      }
   }
   else
   {
      //If no entry exists, then create a new one
      entry = arpCreateEntry(interface);

      //ARP cache entry successfully created?
      if(entry != NULL)
      {
         //Record the IPv4 address whose MAC address is unknown
         entry->ipAddr = ipAddr;

         //Reset retransmission counter
         entry->retransmitCount = 0;
         //No packet are pending in the transmit queue
         entry->queueSize = 0;

         //Send an ARP request
         arpSendRequest(interface, entry->ipAddr, &MAC_BROADCAST_ADDR);

         //Save the time at which the packet was sent
         entry->timestamp = osGetSystemTime();
         //Set timeout value
         entry->timeout = ARP_REQUEST_TIMEOUT;
         //Enter INCOMPLETE state
         entry->state = ARP_STATE_INCOMPLETE;

         //The address resolution is in progress
         error = ERROR_IN_PROGRESS;
      }
      else
      {
         //Failed to create ARP cache entry...
         error = ERROR_OUT_OF_RESOURCES;
      }
   }

   //Return status code
   return error;
}


/**
 * @brief Enqueue an IPv4 packet waiting for address resolution
 * @param[in] interface Underlying network interface
 * @param[in] ipAddr IPv4 address of the destination host
 * @param[in] buffer Multi-part buffer containing the packet to be enqueued
 * @param[in] offset Offset to the first byte of the packet
 * @return Error code
 **/

error_t arpEnqueuePacket(NetInterface *interface,
   Ipv4Addr ipAddr, NetBuffer *buffer, size_t offset)
{
   error_t error;
   uint_t i;
   size_t length;
   ArpCacheEntry *entry;

   //Retrieve the length of the multi-part buffer
   length = netBufferGetLength(buffer);

   //Search the ARP cache for the specified IPv4 address
   entry = arpFindEntry(interface, ipAddr);

   //Check whether a matching entry exists
   if(entry != NULL)
   {
      //Check current state
      if(entry->state == ARP_STATE_INCOMPLETE)
      {
         //Check whether the packet queue is full
         if(entry->queueSize >= ARP_MAX_PENDING_PACKETS)
         {
            //When the queue overflows, the new arrival should replace the oldest entry
            netBufferFree(entry->queue[0].buffer);

            //Make room for the new packet
            for(i = 1; i < ARP_MAX_PENDING_PACKETS; i++)
               entry->queue[i - 1] = entry->queue[i];

            //Adjust the number of pending packets
            entry->queueSize--;
         }

         //Index of the entry to be filled in
         i = entry->queueSize;
         //Allocate a memory buffer to store the packet
         entry->queue[i].buffer = netBufferAlloc(length);

         //Successful memory allocation?
         if(entry->queue[i].buffer != NULL)
         {
            //Copy the contents of the IPv4 packet
            netBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length);
            //Offset to the first byte of the IPv4 header
            entry->queue[i].offset = offset;

            //Increment the number of queued packets
            entry->queueSize++;
            //The packet was successfully enqueued
            error = NO_ERROR;
         }
         else
         {
            //Failed to allocate memory
            error = ERROR_OUT_OF_MEMORY;
         }
      }
      else
      {
         //The address is already resolved
         error = ERROR_UNEXPECTED_STATE;
      }
   }
   else
   {
      //No matching entry in ARP cache
      error = ERROR_NOT_FOUND;
   }

   //Return status code
   return error;
}


/**
 * @brief ARP timer handler
 *
 * This routine must be periodically called by the TCP/IP stack to
 * manage ARP cache
 *
 * @param[in] interface Underlying network interface
 **/

void arpTick(NetInterface *interface)
{
   uint_t i;
   systime_t time;
   ArpCacheEntry *entry;

   //Get current time
   time = osGetSystemTime();

   //Go through ARP cache
   for(i = 0; i < ARP_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->arpCache[i];

      //INCOMPLETE state?
      if(entry->state == ARP_STATE_INCOMPLETE)
      {
         //The request timed out?
         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
         {
            //Increment retransmission counter
            entry->retransmitCount++;

            //Check whether the maximum number of retransmissions has been exceeded
            if(entry->retransmitCount < ARP_MAX_REQUESTS)
            {
               //Retransmit ARP request
               arpSendRequest(interface, entry->ipAddr, &MAC_BROADCAST_ADDR);

               //Save the time at which the packet was sent
               entry->timestamp = time;
               //Set timeout value
               entry->timeout = ARP_REQUEST_TIMEOUT;
            }
            else
            {
               //Drop packets that are waiting for address resolution
               arpFlushQueuedPackets(interface, entry);
               //The entry should be deleted since address resolution has failed
               entry->state = ARP_STATE_NONE;
            }
         }
      }
      //REACHABLE state?
      else if(entry->state == ARP_STATE_REACHABLE)
      {
         //Periodically time out ARP cache entries
         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
         {
            //Save current time
            entry->timestamp = osGetSystemTime();
            //Enter STALE state
            entry->state = ARP_STATE_STALE;
         }
      }
      //DELAY state?
      else if(entry->state == ARP_STATE_DELAY)
      {
         //Wait for the specified delay before sending the first probe
         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
         {
            //Send a point-to-point ARP request to the host
            arpSendRequest(interface, entry->ipAddr, &entry->macAddr);

            //Save the time at which the packet was sent
            entry->timestamp = time;
            //Set timeout value
            entry->timeout = ARP_PROBE_TIMEOUT;
            //Switch to the PROBE state
            entry->state = ARP_STATE_PROBE;
         }
      }
      //PROBE state?
      else if(entry->state == ARP_STATE_PROBE)
      {
         //The request timed out?
         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
         {
            //Increment retransmission counter
            entry->retransmitCount++;

            //Check whether the maximum number of retransmissions has been exceeded
            if(entry->retransmitCount < ARP_MAX_PROBES)
            {
               //Send a point-to-point ARP request to the host
               arpSendRequest(interface, entry->ipAddr, &entry->macAddr);

               //Save the time at which the packet was sent
               entry->timestamp = time;
               //Set timeout value
               entry->timeout = ARP_PROBE_TIMEOUT;
            }
            else
            {
               //The entry should be deleted since the host is not reachable anymore
               entry->state = ARP_STATE_NONE;
            }
         }
      }
   }
}


/**
 * @brief Incoming ARP packet processing
 * @param[in] interface Underlying network interface
 * @param[in] arpPacket Incoming ARP packet
 * @param[in] length Packet length
 **/

void arpProcessPacket(NetInterface *interface, ArpPacket *arpPacket, size_t length)
{
   //Discard invalid ARP packets
   if(length < sizeof(ArpPacket))
      return;

   //Debug message
   TRACE_INFO("ARP packet received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump ARP packet contents for debugging purpose
   arpDumpPacket(arpPacket);

   //Make sure the hardware type is valid
   if(arpPacket->hrd != HTONS(ARP_HARDWARE_TYPE_ETH))
      return;
   //Make sure the protocol type is valid
   if(arpPacket->pro != HTONS(ARP_PROTOCOL_TYPE_IPV4))
      return;
   //Check the length of the hardware address
   if(arpPacket->hln != sizeof(MacAddr))
      return;
   //Check the length of the protocol address
   if(arpPacket->pln != sizeof(Ipv4Addr))
      return;

   //Check the state of the host address
   if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_TENTATIVE)
   {
      //If the host receives any ARP packet where the sender IP address is
      //the address being probed for, then this is a conflicting ARP packet
      if(arpPacket->spa == interface->ipv4Context.addr)
      {
         //An address conflict has been detected...
         interface->ipv4Context.addrConflict = TRUE;
         //Exit immediately
         return;
      }
   }
   else if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
   {
      //Check whether the sender protocol address matches the IP
      //address assigned to the interface
      if(arpPacket->spa == interface->ipv4Context.addr)
      {
         //If the sender hardware address does not match the hardware
         //address of that interface, then this is a conflicting ARP packet
         if(!macCompAddr(&arpPacket->sha, &interface->macAddr))
         {
            //An address conflict has been detected...
            interface->ipv4Context.addrConflict = TRUE;
            //Exit immediately
            return;
         }
      }
   }

   //Check whether the target protocol address matches the IP
   //address assigned to the interface
   if(arpPacket->tpa != interface->ipv4Context.addr)
      return;

   //Check operation code
   switch(ntohs(arpPacket->op))
   {
   //ARP request?
   case ARP_OPCODE_ARP_REQUEST:
      //Process incoming ARP request
      arpProcessRequest(interface, arpPacket);
      break;
   //ARP reply?
   case ARP_OPCODE_ARP_REPLY:
      //Process incoming ARP reply
      arpProcessReply(interface, arpPacket);
      break;
   //Unknown operation code?
   default:
      //Debug message
      TRACE_INFO("Unknown operation code!\r\n");
      //Discard incoming packet
      break;
   }
}


/**
 * @brief Incoming ARP request processing
 * @param[in] interface Underlying network interface
 * @param[in] arpRequest Incoming ARP request
 **/

void arpProcessRequest(NetInterface *interface, ArpPacket *arpRequest)
{
   //Debug message
   TRACE_INFO("ARP Request received...\r\n");

   //Check sender protocol address
   if(ipv4IsBroadcastAddr(interface, arpRequest->spa))
      return;
   if(ipv4IsMulticastAddr(arpRequest->spa))
      return;

   //Check whether the target IP address is an address being probed for
   if(ipv4IsTentativeAddr(interface, arpRequest->tpa))
   {
      //ARP probe received?
      if(arpRequest->spa == IPV4_UNSPECIFIED_ADDR)
      {
         //If the sender hardware address does not match the hardware
         //address of that interface, then this is a conflicting ARP packet
         if(!macCompAddr(&arpRequest->sha, &interface->macAddr))
         {
            //An address conflict has been detected...
            interface->ipv4Context.addrConflict = TRUE;
         }
      }

      //In all cases, the host must not respond to an ARP request for an
      //address being probed for
      return;
   }

   //Send ARP reply
   arpSendReply(interface, arpRequest->spa, &arpRequest->sha, &arpRequest->sha);
}


/**
 * @brief Incoming ARP reply processing
 * @param[in] interface Underlying network interface
 * @param[in] arpReply Incoming ARP reply
 **/

void arpProcessReply(NetInterface *interface, ArpPacket *arpReply)
{
   ArpCacheEntry *entry;

   //Debug message
   TRACE_INFO("ARP Reply received...\r\n");

   //Check sender protocol address
   if(arpReply->spa == IPV4_UNSPECIFIED_ADDR)
      return;
   if(ipv4IsBroadcastAddr(interface, arpReply->spa))
      return;
   if(ipv4IsMulticastAddr(arpReply->spa))
      return;

   //Check sender hardware address
   if(macCompAddr(&arpReply->sha, &MAC_UNSPECIFIED_ADDR))
      return;
   if(macCompAddr(&arpReply->sha, &MAC_BROADCAST_ADDR))
      return;
   if(macIsMulticastAddr(&arpReply->sha))
      return;

   //Check whether the target IP address is an address being probed for
   if(ipv4IsTentativeAddr(interface, arpReply->tpa))
      return;

   //Search the ARP cache for the specified IPv4 address
   entry = arpFindEntry(interface, arpReply->spa);

   //Check whether a matching entry has been found
   if(entry != NULL)
   {
      //Check current state
      if(entry->state == ARP_STATE_INCOMPLETE)
      {
         //Record the corresponding MAC address
         entry->macAddr = arpReply->sha;

         //Send all the packets that are pending for transmission
         arpSendQueuedPackets(interface, entry);

         //Save current time
         entry->timestamp = osGetSystemTime();
         //The validity of the ARP entry is limited in time
         entry->timeout = ARP_REACHABLE_TIME;
         //Switch to the REACHABLE state
         entry->state = ARP_STATE_REACHABLE;
      }
      else if(entry->state == ARP_STATE_REACHABLE)
      {
         //Different link-layer address than cached?
         if(!macCompAddr(&arpReply->sha, &entry->macAddr))
         {
            //Enter STALE state
            entry->state = ARP_STATE_STALE;
         }
      }
      else if(entry->state == ARP_STATE_PROBE)
      {
         //Record IPv4/MAC address pair
         entry->ipAddr = arpReply->spa;
         entry->macAddr = arpReply->sha;

         //Save current time
         entry->timestamp = osGetSystemTime();
         //The validity of the ARP entry is limited in time
         entry->timeout = ARP_REACHABLE_TIME;
         //Switch to the REACHABLE state
         entry->state = ARP_STATE_REACHABLE;
      }
   }
}


/**
 * @brief Send ARP probe
 * @param[in] interface Underlying network interface
 * @param[in] targetIpAddr Target IPv4 address
 * @return Error code
 **/

error_t arpSendProbe(NetInterface *interface, Ipv4Addr targetIpAddr)
{
   error_t error;
   size_t offset;
   NetBuffer *buffer;
   ArpPacket *arpRequest;

   //Allocate a memory buffer to hold an ARP packet
   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
   //Failed to allocate buffer?
   if(buffer == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the ARP packet
   arpRequest = netBufferAt(buffer, offset);

   //Format ARP request
   arpRequest->hrd = htons(ARP_HARDWARE_TYPE_ETH);
   arpRequest->pro = htons(ARP_PROTOCOL_TYPE_IPV4);
   arpRequest->hln = sizeof(MacAddr);
   arpRequest->pln = sizeof(Ipv4Addr);
   arpRequest->op = htons(ARP_OPCODE_ARP_REQUEST);
   arpRequest->sha = interface->macAddr;
   arpRequest->spa = IPV4_UNSPECIFIED_ADDR;
   arpRequest->tha = MAC_UNSPECIFIED_ADDR;
   arpRequest->tpa = targetIpAddr;

   //Debug message
   TRACE_INFO("Sending ARP Probe (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
   //Dump ARP packet contents for debugging purpose
   arpDumpPacket(arpRequest);

   //Send ARP request
   error = ethSendFrame(interface, &MAC_BROADCAST_ADDR,
      buffer, offset, ETH_TYPE_ARP);

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}


/**
 * @brief Send ARP request
 * @param[in] interface Underlying network interface
 * @param[in] targetIpAddr Target IPv4 address
 * @param[in] destMacAddr Destination MAC address
 * @return Error code
 **/

error_t arpSendRequest(NetInterface *interface,
   Ipv4Addr targetIpAddr, const MacAddr *destMacAddr)
{
   error_t error;
   size_t offset;
   NetBuffer *buffer;
   ArpPacket *arpRequest;
   Ipv4Addr senderIpAddr;

   //Select the most appropriate sender IP address to be used
   error = ipv4SelectSourceAddr(&interface, targetIpAddr, &senderIpAddr);
   //No address assigned to the interface?
   if(error)
      return error;

   //Allocate a memory buffer to hold an ARP packet
   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
   //Failed to allocate buffer?
   if(buffer == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the ARP packet
   arpRequest = netBufferAt(buffer, offset);

   //Format ARP request
   arpRequest->hrd = htons(ARP_HARDWARE_TYPE_ETH);
   arpRequest->pro = htons(ARP_PROTOCOL_TYPE_IPV4);
   arpRequest->hln = sizeof(MacAddr);
   arpRequest->pln = sizeof(Ipv4Addr);
   arpRequest->op = htons(ARP_OPCODE_ARP_REQUEST);
   arpRequest->sha = interface->macAddr;
   arpRequest->spa = senderIpAddr;
   arpRequest->tha = MAC_UNSPECIFIED_ADDR;
   arpRequest->tpa = targetIpAddr;

   //Debug message
   TRACE_INFO("Sending ARP Request (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
   //Dump ARP packet contents for debugging purpose
   arpDumpPacket(arpRequest);

   //Send ARP request
   error = ethSendFrame(interface, destMacAddr, buffer, offset, ETH_TYPE_ARP);

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}


/**
 * @brief Send ARP reply
 * @param[in] interface Underlying network interface
 * @param[in] targetIpAddr Target IPv4 address
 * @param[in] targetMacAddr Target MAC address
 * @param[in] destMacAddr Destination MAC address
 * @return Error code
 **/

error_t arpSendReply(NetInterface *interface, Ipv4Addr targetIpAddr,
   const MacAddr *targetMacAddr, const MacAddr *destMacAddr)
{
   error_t error;
   size_t offset;
   NetBuffer *buffer;
   ArpPacket *arpReply;

   //Allocate a memory buffer to hold an ARP packet
   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
   //Failed to allocate buffer?
   if(buffer == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the ARP packet
   arpReply = netBufferAt(buffer, offset);

   //Format ARP reply
   arpReply->hrd = htons(ARP_HARDWARE_TYPE_ETH);
   arpReply->pro = htons(ETH_TYPE_IPV4);
   arpReply->hln = sizeof(MacAddr);
   arpReply->pln = sizeof(Ipv4Addr);
   arpReply->op = htons(ARP_OPCODE_ARP_REPLY);
   arpReply->sha = interface->macAddr;
   arpReply->spa = interface->ipv4Context.addr;
   arpReply->tha = *targetMacAddr;
   arpReply->tpa = targetIpAddr;

   //Debug message
   TRACE_INFO("Sending ARP Reply (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
   //Dump ARP packet contents for debugging purpose
   arpDumpPacket(arpReply);

   //Send ARP reply
   error = ethSendFrame(interface, destMacAddr, buffer, offset, ETH_TYPE_ARP);

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}


/**
 * @brief Dump ARP packet for debugging purpose
 * @param[in] arpPacket ARP header
 **/

void arpDumpPacket(const ArpPacket *arpPacket)
{
   //Dump ARP packet contents
   TRACE_DEBUG("  Hardware Type (hrd) = 0x%04" PRIX16 "\r\n", ntohs(arpPacket->hrd));
   TRACE_DEBUG("  Protocol Type (pro) = 0x%04" PRIX16 "\r\n", ntohs(arpPacket->pro));
   TRACE_DEBUG("  Hardware Address Length (hln) = %" PRIu8 "\r\n", arpPacket->hln);
   TRACE_DEBUG("  Protocol Address Length (pln) = %" PRIu8 "\r\n", arpPacket->pln);
   TRACE_DEBUG("  Opcode (op) = %" PRIu16 "\r\n", ntohs(arpPacket->op));
   TRACE_DEBUG("  Sender Hardware Address (sha)= %s\r\n", macAddrToString(&arpPacket->sha, NULL));
   TRACE_DEBUG("  Sender Protocol Address (spa) = %s\r\n", ipv4AddrToString(arpPacket->spa, NULL));
   TRACE_DEBUG("  Target Hardware Address (tha)= %s\r\n", macAddrToString(&arpPacket->tha, NULL));
   TRACE_DEBUG("  Target Protocol Address (tpa) = %s\r\n", ipv4AddrToString(arpPacket->tpa, NULL));
}

#endif