Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/core/udp.c

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

File content as of revision 0:8918a71cdbe9:

/**
 * @file udp.c
 * @brief UDP (User Datagram 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.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

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

//Dependencies
#include <string.h>
#include "core/net.h"
#include "core/ip.h"
#include "core/udp.h"
#include "core/socket.h"
#include "ipv4/ipv4.h"
#include "ipv6/ipv6.h"
#include "ipv6/ipv6_misc.h"
#include "mibs/mib2_module.h"
#include "debug.h"

//Check TCP/IP stack configuration
#if (UDP_SUPPORT == ENABLED)

//Ephemeral ports are used for dynamic port assignment
static uint16_t udpDynamicPort;
//Mutex to prevent simultaneous access to the callback table
OsMutex udpCallbackMutex;
//Table that holds the registered user callbacks
UdpRxCallbackDesc udpCallbackTable[UDP_CALLBACK_TABLE_SIZE];


/**
 * @brief UDP related initialization
 * @return Error code
 **/

error_t udpInit(void)
{
   //Reset ephemeral port number
   udpDynamicPort = 0;

   //Create a mutex to prevent simultaneous access to the callback table
   if(!osCreateMutex(&udpCallbackMutex))
   {
      //Failed to create mutex
      return ERROR_OUT_OF_RESOURCES;
   }

   //Initialize callback table
   memset(udpCallbackTable, 0, sizeof(udpCallbackTable));

   //Successful initialization
   return NO_ERROR;
}


/**
 * @brief Get an ephemeral port number
 * @return Ephemeral port
 **/

uint16_t udpGetDynamicPort(void)
{
   uint_t port;

   //Retrieve current port number
   port = udpDynamicPort;

   //Invalid port number?
   if(port < SOCKET_EPHEMERAL_PORT_MIN || port > SOCKET_EPHEMERAL_PORT_MAX)
   {
      //Generate a random port number
      port = SOCKET_EPHEMERAL_PORT_MIN + netGetRand() %
         (SOCKET_EPHEMERAL_PORT_MAX - SOCKET_EPHEMERAL_PORT_MIN + 1);
   }

   //Next dynamic port to use
   if(port < SOCKET_EPHEMERAL_PORT_MAX)
   {
      //Increment port number
      udpDynamicPort = port + 1;
   }
   else
   {
      //Wrap around if necessary
      udpDynamicPort = SOCKET_EPHEMERAL_PORT_MIN;
   }

   //Return an ephemeral port number
   return port;
}


/**
 * @brief Incoming UDP datagram processing
 * @param[in] interface Underlying network interface
 * @param[in] pseudoHeader UDP pseudo header
 * @param[in] buffer Multi-part buffer containing the incoming UDP datagram
 * @param[in] offset Offset to the first byte of the UDP header
 * @return Error code
 **/

error_t udpProcessDatagram(NetInterface *interface,
   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
{
   error_t error;
   uint_t i;
   size_t length;
   UdpHeader *header;
   Socket *socket;
   SocketQueueItem *queueItem;
   NetBuffer *p;

   //Retrieve the length of the UDP datagram
   length = netBufferGetLength(buffer) - offset;

   //Ensure the UDP header is valid
   if(length < sizeof(UdpHeader))
   {
      //Number of received UDP datagrams that could not be delivered for
      //reasons other than the lack of an application at the destination port
      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInErrors, 1);

      //Report an error
      return ERROR_INVALID_HEADER;
   }

   //Point to the UDP header
   header = netBufferAt(buffer, offset);
   //Sanity check
   if(header == NULL)
      return ERROR_FAILURE;

   //Debug message
   TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump UDP header contents for debugging purpose
   udpDumpHeader(header);

   //When UDP runs over IPv6, the checksum is mandatory
   if(header->checksum != 0x0000 || pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Verify UDP checksum
      if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
         pseudoHeader->length, buffer, offset, length) != 0x0000)
      {
         //Debug message
         TRACE_WARNING("Wrong UDP header checksum!\r\n");

         //Number of received UDP datagrams that could not be delivered for
         //reasons other than the lack of an application at the destination port
         MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInErrors, 1);

         //Report an error
         return ERROR_WRONG_CHECKSUM;
      }
   }

   //Loop through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket
      socket = socketTable + i;

      //UDP socket found?
      if(socket->type != SOCKET_TYPE_DGRAM)
         continue;
      //Check whether the socket is bound to a particular interface
      if(socket->interface && socket->interface != interface)
         continue;
      //Check destination port number
      if(socket->localPort != ntohs(header->destPort))
         continue;
      //Source port number filtering
      if(socket->remotePort && socket->remotePort != ntohs(header->srcPort))
         continue;

#if (IPV4_SUPPORT == ENABLED)
      //An IPv4 packet was received?
      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
               continue;
         }

         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
               continue;
         }
      }
      else
#endif
#if (IPV6_SUPPORT == ENABLED)
      //An IPv6 packet was received?
      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
               continue;
         }

         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
               continue;
         }
      }
      else
#endif
      //An invalid packet was received?
      {
         //This should never occur...
         continue;
      }

      //The current socket meets all the criteria
      break;
   }

   //Point to the payload
   offset += sizeof(UdpHeader);
   length -= sizeof(UdpHeader);

   //No matching socket found?
   if(i >= SOCKET_MAX_COUNT)
   {
      //Invoke user callback, if any
      error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset);
      //Return status code
      return error;
   }

   //Empty receive queue?
   if(!socket->receiveQueue)
   {
      //Allocate a memory buffer to hold the data and the associated descriptor
      p = netBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Point to the newly created item
         queueItem = netBufferAt(p, 0);
         queueItem->buffer = p;
         //Add the newly created item to the queue
         socket->receiveQueue = queueItem;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }
   else
   {
      //Point to the very first item
      queueItem = socket->receiveQueue;
      //Reach the last item in the receive queue
      for(i = 1; queueItem->next; i++)
         queueItem = queueItem->next;

      //Make sure the receive queue is not full
      if(i >= UDP_RX_QUEUE_SIZE)
         return ERROR_RECEIVE_QUEUE_FULL;

      //Allocate a memory buffer to hold the data and the associated descriptor
      p = netBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Add the newly created item to the queue
         queueItem->next = netBufferAt(p, 0);
         //Point to the newly created item
         queueItem = queueItem->next;
         queueItem->buffer = p;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }

   //Failed to allocate memory?
   if(queueItem == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Initialize next field
   queueItem->next = NULL;
   //Record the source port number
   queueItem->srcPort = ntohs(header->srcPort);

#if (IPV4_SUPPORT == ENABLED)
   //IPv4 remote address?
   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
   {
      //Save the source IPv4 address
      queueItem->srcIpAddr.length = sizeof(Ipv4Addr);
      queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
      //Save the destination IPv4 address
      queueItem->destIpAddr.length = sizeof(Ipv4Addr);
      queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //IPv6 remote address?
   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Save the source IPv6 address
      queueItem->srcIpAddr.length = sizeof(Ipv6Addr);
      queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
      //Save the destination IPv6 address
      queueItem->destIpAddr.length = sizeof(Ipv6Addr);
      queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
   }
#endif

   //Offset to the payload
   queueItem->offset = sizeof(SocketQueueItem);
   //Copy the payload
   netBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length);

   //Notify user that data is available
   udpUpdateEvents(socket);

   //Total number of UDP datagrams delivered to UDP users
   MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInDatagrams, 1);
   MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCInDatagrams, 1);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Send a UDP datagram
 * @param[in] socket Handle referencing the socket
 * @param[in] destIpAddr IP address of the target host
 * @param[in] destPort Target port number
 * @param[in] data Pointer to data payload
 * @param[in] length Length of the payload data
 * @param[out] written Actual number of bytes written (optional parameter)
 * @return Error code
 **/

error_t udpSendDatagram(Socket *socket, const IpAddr *destIpAddr,
   uint16_t destPort, const void *data, size_t length, size_t *written)
{
   error_t error;
   size_t offset;
   NetBuffer *buffer;

   //Allocate a memory buffer to hold the UDP datagram
   buffer = udpAllocBuffer(0, &offset);
   //Failed to allocate buffer?
   if(buffer == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Copy data payload
   error = netBufferAppend(buffer, data, length);

   //Successful processing?
   if(!error)
   {
      //Send UDP datagram
      error = udpSendDatagramEx(socket->interface, socket->localPort,
         destIpAddr, destPort, buffer, offset, socket->ttl);
   }

   //Successful processing?
   if(!error)
   {
      //Total number of data bytes successfully transmitted
      if(written != NULL)
         *written = length;
   }

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


/**
 * @brief Send a UDP datagram (raw interface)
 * @param[in] interface Underlying network interface
 * @param[in] srcPort Source port
 * @param[in] destIpAddr IP address of the target host
 * @param[in] destPort Target port number
 * @param[in] buffer Multi-part buffer containing the payload
 * @param[in] offset Offset to the first payload byte
 * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero
 * @return Error code
 **/

error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr,
   uint16_t destPort, NetBuffer *buffer, size_t offset, uint8_t ttl)
{
   error_t error;
   size_t length;
   UdpHeader *header;
   IpPseudoHeader pseudoHeader;

   //Make room for the UDP header
   offset -= sizeof(UdpHeader);
   //Retrieve the length of the datagram
   length = netBufferGetLength(buffer) - offset;

   //Point to the UDP header
   header = netBufferAt(buffer, offset);
   //Sanity check
   if(header == NULL)
      return ERROR_FAILURE;

   //Format UDP header
   header->srcPort = htons(srcPort);
   header->destPort = htons(destPort);
   header->length = htons(length);
   header->checksum = 0;

#if (IPV4_SUPPORT == ENABLED)
   //Destination address is an IPv4 address?
   if(destIpAddr->length == sizeof(Ipv4Addr))
   {
      Ipv4Addr srcIpAddr;

      //Select the source IPv4 address and the relevant network interface
      //to use when sending data to the specified destination host
      error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr);

      //Check status code
      if(error)
      {
         //Handle the special case where the destination address is the
         //broadcast address
         if(destIpAddr->ipv4Addr == IPV4_BROADCAST_ADDR)
         {
            //Use the unspecified address as source address
            srcIpAddr = IPV4_UNSPECIFIED_ADDR;
         }
         else
         {
            //Source address selection failed
            return error;
         }
      }

      //Format IPv4 pseudo header
      pseudoHeader.length = sizeof(Ipv4PseudoHeader);
      pseudoHeader.ipv4Data.srcAddr = srcIpAddr;
      pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr;
      pseudoHeader.ipv4Data.reserved = 0;
      pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_UDP;
      pseudoHeader.ipv4Data.length = htons(length);

      //Calculate UDP header checksum
      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data,
         sizeof(Ipv4PseudoHeader), buffer, offset, length);
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   //Destination address is an IPv6 address?
   if(destIpAddr->length == sizeof(Ipv6Addr))
   {
      //Select the source IPv6 address and the relevant network interface
      //to use when sending data to the specified destination host
      error = ipv6SelectSourceAddr(&interface,
         &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr);
      //Any error to report?
      if(error)
         return error;

      //Format IPv6 pseudo header
      pseudoHeader.length = sizeof(Ipv6PseudoHeader);
      pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr;
      pseudoHeader.ipv6Data.length = htonl(length);
      pseudoHeader.ipv6Data.reserved = 0;
      pseudoHeader.ipv6Data.nextHeader = IPV6_UDP_HEADER;

      //Calculate UDP header checksum
      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data,
         sizeof(Ipv6PseudoHeader), buffer, offset, length);
   }
   else
#endif
   //Invalid destination address?
   {
      //An internal error has occurred
      return ERROR_FAILURE;
   }

   //If the computed checksum is zero, it is transmitted as all ones. An all
   //zero transmitted checksum value means that the transmitter generated no
   //checksum
   if(header->checksum == 0x0000)
      header->checksum = 0xFFFF;

   //Total number of UDP datagrams sent from this entity
   MIB2_INC_COUNTER32(mib2Base.udpGroup.udpOutDatagrams, 1);
   MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCOutDatagrams, 1);

   //Debug message
   TRACE_INFO("Sending UDP datagram (%" PRIuSIZE " bytes)\r\n", length);
   //Dump UDP header contents for debugging purpose
   udpDumpHeader(header);

   //Send UDP datagram
   error = ipSendDatagram(interface, &pseudoHeader, buffer, offset, ttl);
   //Return status code
   return error;
}


/**
 * @brief Receive data from a UDP socket
 * @param[in] socket Handle referencing the socket
 * @param[out] srcIpAddr Source IP address (optional)
 * @param[out] srcPort Source port number (optional)
 * @param[out] destIpAddr Destination IP address (optional)
 * @param[out] data Buffer where to store the incoming data
 * @param[in] size Maximum number of bytes that can be received
 * @param[out] received Number of bytes that have been received
 * @param[in] flags Set of flags that influences the behavior of this function
 * @return Error code
 **/

error_t udpReceiveDatagram(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags)
{
   SocketQueueItem *queueItem;

   //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
   if(!(flags & SOCKET_FLAG_DONT_WAIT))
   {
      //The receive queue is empty?
      if(!socket->receiveQueue)
      {
         //Set the events the application is interested in
         socket->eventMask = SOCKET_EVENT_RX_READY;
         //Reset the event object
         osResetEvent(&socket->event);

         //Release exclusive access
         osReleaseMutex(&netMutex);
         //Wait until an event is triggered
         osWaitForEvent(&socket->event, socket->timeout);
         //Get exclusive access
         osAcquireMutex(&netMutex);
      }
   }

   //Check whether the read operation timed out
   if(!socket->receiveQueue)
   {
      //No data can be read
      *received = 0;
      //Report a timeout error
      return ERROR_TIMEOUT;
   }

   //Point to the first item in the receive queue
   queueItem = socket->receiveQueue;
   //Copy data to user buffer
   *received = netBufferRead(data, queueItem->buffer, queueItem->offset, size);

   //Save the source IP address
   if(srcIpAddr)
      *srcIpAddr = queueItem->srcIpAddr;
   //Save the source port number
   if(srcPort)
      *srcPort = queueItem->srcPort;
   //Save the destination IP address
   if(destIpAddr)
      *destIpAddr = queueItem->destIpAddr;

   //If the SOCKET_FLAG_PEEK flag is set, the data is copied
   //into the buffer but is not removed from the input queue
   if(!(flags & SOCKET_FLAG_PEEK))
   {
      //Remove the item from the receive queue
      socket->receiveQueue = queueItem->next;
      //Deallocate memory buffer
      netBufferFree(queueItem->buffer);
   }

   //Update the state of events
   udpUpdateEvents(socket);

   //Successful read operation
   return NO_ERROR;
}


/**
 * @brief Allocate a buffer to hold a UDP packet
 * @param[in] length Desired payload length
 * @param[out] offset Offset to the first byte of the payload
 * @return The function returns a pointer to the newly allocated
 *   buffer. If the system is out of resources, NULL is returned
 **/

NetBuffer *udpAllocBuffer(size_t length, size_t *offset)
{
   NetBuffer *buffer;

   //Allocate a buffer to hold the UDP header and the payload
   buffer = ipAllocBuffer(length + sizeof(UdpHeader), offset);
   //Failed to allocate buffer?
   if(buffer == NULL)
      return NULL;

   //Offset to the first byte of the payload
   *offset += sizeof(UdpHeader);

   //Return a pointer to the freshly allocated buffer
   return buffer;
}


/**
 * @brief Update UDP related events
 * @param[in] socket Handle referencing the socket
 **/

void udpUpdateEvents(Socket *socket)
{
   //Clear event flags
   socket->eventFlags = 0;

   //The socket is marked as readable if a datagram is pending in the queue
   if(socket->receiveQueue)
      socket->eventFlags |= SOCKET_EVENT_RX_READY;

   //Check whether the socket is bound to a particular network interface
   if(socket->interface != NULL)
   {
      //Handle link up and link down events
      if(socket->interface->linkState)
         socket->eventFlags |= SOCKET_EVENT_LINK_UP;
      else
         socket->eventFlags |= SOCKET_EVENT_LINK_DOWN;
   }

   //Mask unused events
   socket->eventFlags &= socket->eventMask;

   //Any event to signal?
   if(socket->eventFlags)
   {
      //Unblock I/O operations currently in waiting state
      osSetEvent(&socket->event);

      //Set user event to signaled state if necessary
      if(socket->userEvent != NULL)
         osSetEvent(socket->userEvent);
   }
}


/**
 * @brief Register user callback
 * @param[in] interface Underlying network interface
 * @param[in] port UDP port number
 * @param[in] callback Callback function to be called when a datagram is received
 * @param[in] params Callback function parameter (optional)
 * @return Error code
 **/

error_t udpAttachRxCallback(NetInterface *interface,
   uint16_t port, UdpRxCallback callback, void *params)
{
   uint_t i;
   UdpRxCallbackDesc *entry;

   //Acquire exclusive access to the callback table
   osAcquireMutex(&udpCallbackMutex);

   //Loop through the table
   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
   {
      //Point to the current entry
      entry = &udpCallbackTable[i];

      //Check whether the entry is currently in used
      if(entry->callback == NULL)
      {
         //Create a new entry
         entry->interface = interface;
         entry->port = port;
         entry->callback = callback;
         entry->params = params;
         //We are done
         break;
      }
   }

   //Release exclusive access to the callback table
   osReleaseMutex(&udpCallbackMutex);

   //Failed to attach the specified user callback?
   if(i >= UDP_CALLBACK_TABLE_SIZE)
      return ERROR_OUT_OF_RESOURCES;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Unregister user callback
 * @param[in] interface Underlying network interface
 * @param[in] port UDP port number
 * @return Error code
 **/

error_t udpDetachRxCallback(NetInterface *interface, uint16_t port)
{
   error_t error;
   uint_t i;
   UdpRxCallbackDesc *entry;

   //Initialize status code
   error = ERROR_FAILURE;

   //Acquire exclusive access to the callback table
   osAcquireMutex(&udpCallbackMutex);

   //Loop through the table
   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
   {
      //Point to the current entry
      entry = &udpCallbackTable[i];

      //Check whether the entry is currently in used
      if(entry->callback != NULL)
      {
         //Does the specified port number match the current entry?
         if(entry->port == port && entry->interface == interface)
         {
            //Unregister user callback
            entry->callback = NULL;
            //A matching entry has been found
            error = NO_ERROR;
         }
      }
   }

   //Release exclusive access to the callback table
   osReleaseMutex(&udpCallbackMutex);

   //Return status code
   return error;
}


/**
 * @brief Invoke user callback
 * @param[in] interface Underlying network interface
 * @param[in] pseudoHeader UDP pseudo header
 * @param[in] header UDP header
 * @param[in] buffer Multi-part buffer containing the payload
 * @param[in] offset Offset to the first byte of the payload
 * @return Error code
 **/

error_t udpInvokeRxCallback(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *header, const NetBuffer *buffer, size_t offset)
{
   error_t error;
   uint_t i;
   void *params;
   UdpRxCallbackDesc *entry;

   //Initialize status code
   error = ERROR_PORT_UNREACHABLE;

   //Acquire exclusive access to the callback table
   osAcquireMutex(&udpCallbackMutex);

   //Loop through the table
   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
   {
      //Point to the current entry
      entry = &udpCallbackTable[i];

      //Check whether the entry is currently in used
      if(entry->callback != NULL)
      {
         //Bound to a particular interface?
         if(entry->interface == NULL || entry->interface == interface)
         {
            //Does the specified port number match the current entry?
            if(entry->port == ntohs(header->destPort))
            {
               //Retrieve callback parameters
               params = entry->params;

               //Release mutex to prevent any deadlock
               if(params == NULL)
                  osReleaseMutex(&udpCallbackMutex);

               //Invoke user callback function
               entry->callback(interface, pseudoHeader,
                  header, buffer, offset, params);

               //Acquire mutex
               if(params == NULL)
                  osAcquireMutex(&udpCallbackMutex);

               //A matching entry has been found
               error = NO_ERROR;
            }
         }
      }
   }

   //Release exclusive access to the callback table
   osReleaseMutex(&udpCallbackMutex);

   //Check status code
   if(error)
   {
      //Total number of received UDP datagrams for which there was
      //no application at the destination port
      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpNoPorts, 1);
   }
   else
   {
      //Total number of UDP datagrams delivered to UDP users
      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInDatagrams, 1);
      MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCInDatagrams, 1);
   }

   //Return status code
   return error;
}


/**
 * @brief Dump UDP header for debugging purpose
 * @param[in] datagram Pointer to the UDP header
 **/

void udpDumpHeader(const UdpHeader *datagram)
{
   //Dump UDP header contents
   TRACE_DEBUG("  Source Port = %" PRIu16 "\r\n", ntohs(datagram->srcPort));
   TRACE_DEBUG("  Destination Port = %" PRIu16 "\r\n", ntohs(datagram->destPort));
   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(datagram->length));
   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(datagram->checksum));
}

#endif