Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/ipv4/icmp.c

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

File content as of revision 0:8918a71cdbe9:

/**
 * @file icmp.c
 * @brief ICMP (Internet Control Message 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 ICMP_TRACE_LEVEL

//Dependencies
#include <string.h>
#include "core/net.h"
#include "core/ip.h"
#include "ipv4/ipv4.h"
#include "ipv4/icmp.h"
#include "mibs/mib2_module.h"
#include "debug.h"

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


/**
 * @brief Incoming ICMP message processing
 * @param[in] interface Underlying network interface
 * @param[in] srcIpAddr Source IPv4 address
 * @param[in] buffer Multi-part buffer containing the incoming ICMP message
 * @param[in] offset Offset to the first byte of the ICMP message
 **/

void icmpProcessMessage(NetInterface *interface,
   Ipv4Addr srcIpAddr, const NetBuffer *buffer, size_t offset)
{
   size_t length;
   IcmpHeader *header;

   //Total number of ICMP messages which the entity received
   MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInMsgs, 1);

   //Retrieve the length of the ICMP message
   length = netBufferGetLength(buffer) - offset;

   //Ensure the message length is correct
   if(length < sizeof(IcmpHeader))
   {
      //Number of ICMP messages which the entity received but determined
      //as having ICMP-specific errors
      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInErrors, 1);
      //Silently discard incoming message
      return;
   }

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

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

   //Verify checksum value
   if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
   {
      //Debug message
      TRACE_WARNING("Wrong ICMP header checksum!\r\n");
      //Number of ICMP messages which the entity received but determined
      //as having ICMP-specific errors
      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInErrors, 1);
      //Drop incoming message
      return;
   }

   //Check the type of ICMP message
   switch(header->type)
   {
   //Echo Request?
   case ICMP_TYPE_ECHO_REQUEST:
      //Process Echo Request message
      icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset);
      break;
   //Unknown type?
   default:
      //Debug message
      TRACE_WARNING("Unknown ICMP message type!\r\n");
      //Discard incoming ICMP message
      break;
   }
}


/**
 * @brief Echo Request message processing
 * @param[in] interface Underlying network interface
 * @param[in] srcIpAddr Source IPv4 address
 * @param[in] request Multi-part buffer containing the incoming Echo Request message
 * @param[in] requestOffset Offset to the first byte of the Echo Request message
 **/

void icmpProcessEchoRequest(NetInterface *interface,
   Ipv4Addr srcIpAddr, const NetBuffer *request, size_t requestOffset)
{
   error_t error;
   size_t requestLength;
   size_t replyOffset;
   size_t replyLength;
   NetBuffer *reply;
   IcmpEchoMessage *requestHeader;
   IcmpEchoMessage *replyHeader;
   Ipv4PseudoHeader pseudoHeader;

   //Number of ICMP Echo Request messages received
   MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInEchos, 1);

   //Retrieve the length of the Echo Request message
   requestLength = netBufferGetLength(request) - requestOffset;

   //Ensure the packet length is correct
   if(requestLength < sizeof(IcmpEchoMessage))
      return;

   //Point to the Echo Request header
   requestHeader = netBufferAt(request, requestOffset);
   //Sanity check
   if(requestHeader == NULL)
      return;

   //Debug message
   TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength);
   //Dump message contents for debugging purpose
   icmpDumpEchoMessage(requestHeader);

   //Allocate memory to hold the Echo Reply message
   reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset);
   //Failed to allocate memory?
   if(reply == NULL)
      return;

   //Point to the Echo Reply header
   replyHeader = netBufferAt(reply, replyOffset);

   //Format Echo Reply header
   replyHeader->type = ICMP_TYPE_ECHO_REPLY;
   replyHeader->code = 0;
   replyHeader->checksum = 0;
   replyHeader->identifier = requestHeader->identifier;
   replyHeader->sequenceNumber = requestHeader->sequenceNumber;

   //Point to the first data byte
   requestOffset += sizeof(IcmpEchoMessage);
   requestLength -= sizeof(IcmpEchoMessage);

   //Copy data
   error = netBufferConcat(reply, request, requestOffset, requestLength);

   //Check status code
   if(!error)
   {
      //Get the length of the resulting message
      replyLength = netBufferGetLength(reply) - replyOffset;
      //Calculate ICMP header checksum
      replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength);

      //Format IPv4 pseudo header
      pseudoHeader.srcAddr = interface->ipv4Context.addr;
      pseudoHeader.destAddr = srcIpAddr;
      pseudoHeader.reserved = 0;
      pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
      pseudoHeader.length = htons(replyLength);

      //Total number of ICMP messages which this entity attempted to send
      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutMsgs, 1);
      //Number of ICMP Echo Reply messages sent
      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutEchoReps, 1);

      //Debug message
      TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength);
      //Dump message contents for debugging purpose
      icmpDumpEchoMessage(replyHeader);

      //Send Echo Reply message
      ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL);
   }

   //Free previously allocated memory block
   netBufferFree(reply);
}


/**
 * @brief Send an ICMP Error message
 * @param[in] interface Underlying network interface
 * @param[in] type Message type
 * @param[in] code Specific message code
 * @param[in] parameter Specific message parameter
 * @param[in] ipPacket Multi-part buffer that holds the invoking IPv4 packet
 * @param[in] ipPacketOffset Offset to the first byte of the IPv4 packet
 * @return Error code
 **/

error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
   uint8_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
{
   error_t error;
   size_t offset;
   size_t length;
   Ipv4Header *ipHeader;
   NetBuffer *icmpMessage;
   IcmpErrorMessage *icmpHeader;
   Ipv4PseudoHeader pseudoHeader;

   //Retrieve the length of the invoking IPv4 packet
   length = netBufferGetLength(ipPacket) - ipPacketOffset;

   //Check the length of the IPv4 packet
   if(length < sizeof(Ipv4Header))
      return ERROR_INVALID_LENGTH;

   //Point to the header of the invoking packet
   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
   //Sanity check
   if(ipHeader == NULL)
      return ERROR_FAILURE;

   //Never respond to a packet destined to a broadcast or a multicast address
   if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) ||
      ipv4IsMulticastAddr(ipHeader->destAddr))
   {
      //Report an error
      return ERROR_INVALID_ADDRESS;
   }

   //Length of the data that will be returned along with the ICMP header
   length = MIN(length, (size_t) ipHeader->headerLength * 4 + 8);

   //Allocate a memory buffer to hold the ICMP message
   icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset);
   //Failed to allocate memory?
   if(icmpMessage == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Point to the ICMP header
   icmpHeader = netBufferAt(icmpMessage, offset);

   //Format ICMP message
   icmpHeader->type = type;
   icmpHeader->code = code;
   icmpHeader->checksum = 0;
   icmpHeader->parameter = parameter;
   icmpHeader->unused = 0;

   //Copy the IP header and the first 8 bytes of the original datagram data
   error = netBufferConcat(icmpMessage, ipPacket, ipPacketOffset, length);

   //Check status code
   if(!error)
   {
      //Get the length of the resulting message
      length = netBufferGetLength(icmpMessage) - offset;
      //Message checksum calculation
      icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length);

      //Format IPv4 pseudo header
      pseudoHeader.srcAddr = ipHeader->destAddr;
      pseudoHeader.destAddr = ipHeader->srcAddr;
      pseudoHeader.reserved = 0;
      pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
      pseudoHeader.length = htons(length);

      //Total number of ICMP messages which this entity attempted to send
      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutMsgs, 1);

      //Check ICMP message type
      if(code == ICMP_TYPE_DEST_UNREACHABLE)
      {
         //Number of ICMP Destination Unreachable messages sent
         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutDestUnreachs, 1);
      }
      else if(code == ICMP_TYPE_SOURCE_QUENCH)
      {
         //Number of ICMP Source Quench messages sent
         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutSrcQuenchs, 1);
      }
      else if(code == ICMP_TYPE_REDIRECT)
      {
         //Number of ICMP Redirect messages sent
         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutRedirects, 1);
      }
      else if(code == ICMP_TYPE_TIME_EXCEEDED)
      {
         //Number of ICMP Time Exceeded messages sent
         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutTimeExcds, 1);
      }
      else if(code == ICMP_TYPE_PARAM_PROBLEM)
      {
         //Number of ICMP Parameter Problem messages sent
         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutParmProbs, 1);
      }

      //Debug message
      TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length);
      //Dump message contents for debugging purpose
      icmpDumpErrorMessage(icmpHeader);

      //Send ICMP Error message
      error = ipv4SendDatagram(interface, &pseudoHeader,
         icmpMessage, offset, IPV4_DEFAULT_TTL);
   }

   //Free previously allocated memory
   netBufferFree(icmpMessage);

   //Return status code
   return error;
}


/**
 * @brief Dump ICMP message for debugging purpose
 * @param[in] message Pointer to the ICMP message
 **/

void icmpDumpMessage(const IcmpHeader *message)
{
   //Dump ICMP message
   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
}


/**
 * @brief Dump ICMP Echo Request or Echo Reply message
 * @param[in] message Pointer to the ICMP message
 **/

void icmpDumpEchoMessage(const IcmpEchoMessage *message)
{
   //Dump ICMP message
   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
   TRACE_DEBUG("  Identifier = 0x%04" PRIX16 "\r\n", ntohs(message->identifier));
   TRACE_DEBUG("  Sequence Number = 0x%04" PRIX16 "\r\n", ntohs(message->sequenceNumber));
}


/**
 * @brief Dump generic ICMP Error message
 * @param[in] message Pointer to the ICMP message
 **/

void icmpDumpErrorMessage(const IcmpErrorMessage *message)
{
   //Dump ICMP message
   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
   TRACE_DEBUG("  Parameter = %" PRIu8 "\r\n", message->parameter);
}

#endif