Webserver+3d print

Dependents:   Nucleo

Revision:
0:8918a71cdbe9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/arp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,908 @@
+/**
+ * @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
+