icmp.c
Go to the documentation of this file.
1 /**
2  * @file icmp.c
3  * @brief ICMP (Internet Control Message Protocol)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL ICMP_TRACE_LEVEL
31 
32 //Dependencies
33 #include <string.h>
34 #include "core/net.h"
35 #include "core/ip.h"
36 #include "ipv4/ipv4.h"
37 #include "ipv4/icmp.h"
38 #include "mibs/mib2_module.h"
39 #include "mibs/ip_mib_module.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (IPV4_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Incoming ICMP message processing
48  * @param[in] interface Underlying network interface
49  * @param[in] srcIpAddr Source IPv4 address
50  * @param[in] buffer Multi-part buffer containing the incoming ICMP message
51  * @param[in] offset Offset to the first byte of the ICMP message
52  **/
53 
55  Ipv4Addr srcIpAddr, const NetBuffer *buffer, size_t offset)
56 {
57  size_t length;
58  IcmpHeader *header;
59 
60  //Total number of ICMP messages which the entity received
61  MIB2_INC_COUNTER32(icmpGroup.icmpInMsgs, 1);
62  IP_MIB_INC_COUNTER32(icmpStats.icmpStatsInMsgs, 1);
63 
64  //Retrieve the length of the ICMP message
65  length = netBufferGetLength(buffer) - offset;
66 
67  //Ensure the message length is correct
68  if(length < sizeof(IcmpHeader))
69  {
70  //Number of ICMP messages which the entity received but determined
71  //as having ICMP-specific errors
72  MIB2_INC_COUNTER32(icmpGroup.icmpInErrors, 1);
73  IP_MIB_INC_COUNTER32(icmpStats.icmpStatsInErrors, 1);
74 
75  //Silently discard incoming message
76  return;
77  }
78 
79  //Point to the ICMP message header
80  header = netBufferAt(buffer, offset);
81  //Sanity check
82  if(header == NULL)
83  return;
84 
85  //Debug message
86  TRACE_INFO("ICMP message received (%" PRIuSIZE " bytes)...\r\n", length);
87  //Dump message contents for debugging purpose
88  icmpDumpMessage(header);
89 
90  //Verify checksum value
91  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
92  {
93  //Debug message
94  TRACE_WARNING("Wrong ICMP header checksum!\r\n");
95 
96  //Number of ICMP messages which the entity received but determined
97  //as having ICMP-specific errors
98  MIB2_INC_COUNTER32(icmpGroup.icmpInErrors, 1);
99  IP_MIB_INC_COUNTER32(icmpStats.icmpStatsInErrors, 1);
100 
101  //Drop incoming message
102  return;
103  }
104 
105  //Update ICMP statistics
106  icmpUpdateInStats(header->type);
107 
108  //Check the type of ICMP message
109  switch(header->type)
110  {
111  //Echo Request?
113  //Process Echo Request message
114  icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset);
115  break;
116  //Unknown type?
117  default:
118  //Debug message
119  TRACE_WARNING("Unknown ICMP message type!\r\n");
120  //Discard incoming ICMP message
121  break;
122  }
123 }
124 
125 
126 /**
127  * @brief Echo Request message processing
128  * @param[in] interface Underlying network interface
129  * @param[in] srcIpAddr Source IPv4 address
130  * @param[in] request Multi-part buffer containing the incoming Echo Request message
131  * @param[in] requestOffset Offset to the first byte of the Echo Request message
132  **/
133 
135  Ipv4Addr srcIpAddr, const NetBuffer *request, size_t requestOffset)
136 {
137  error_t error;
138  size_t requestLength;
139  size_t replyOffset;
140  size_t replyLength;
141  NetBuffer *reply;
142  IcmpEchoMessage *requestHeader;
143  IcmpEchoMessage *replyHeader;
144  Ipv4PseudoHeader pseudoHeader;
145 
146  //Retrieve the length of the Echo Request message
147  requestLength = netBufferGetLength(request) - requestOffset;
148 
149  //Ensure the packet length is correct
150  if(requestLength < sizeof(IcmpEchoMessage))
151  return;
152 
153  //Point to the Echo Request header
154  requestHeader = netBufferAt(request, requestOffset);
155  //Sanity check
156  if(requestHeader == NULL)
157  return;
158 
159  //Debug message
160  TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength);
161  //Dump message contents for debugging purpose
162  icmpDumpEchoMessage(requestHeader);
163 
164  //Allocate memory to hold the Echo Reply message
165  reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset);
166  //Failed to allocate memory?
167  if(reply == NULL)
168  return;
169 
170  //Point to the Echo Reply header
171  replyHeader = netBufferAt(reply, replyOffset);
172 
173  //Format Echo Reply header
174  replyHeader->type = ICMP_TYPE_ECHO_REPLY;
175  replyHeader->code = 0;
176  replyHeader->checksum = 0;
177  replyHeader->identifier = requestHeader->identifier;
178  replyHeader->sequenceNumber = requestHeader->sequenceNumber;
179 
180  //Point to the first data byte
181  requestOffset += sizeof(IcmpEchoMessage);
182  requestLength -= sizeof(IcmpEchoMessage);
183 
184  //Copy data
185  error = netBufferConcat(reply, request, requestOffset, requestLength);
186 
187  //Check status code
188  if(!error)
189  {
190  //Get the length of the resulting message
191  replyLength = netBufferGetLength(reply) - replyOffset;
192  //Calculate ICMP header checksum
193  replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength);
194 
195  //Format IPv4 pseudo header
196  pseudoHeader.srcAddr = interface->ipv4Context.addr;
197  pseudoHeader.destAddr = srcIpAddr;
198  pseudoHeader.reserved = 0;
199  pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
200  pseudoHeader.length = htons(replyLength);
201 
202  //Update ICMP statistics
204 
205  //Debug message
206  TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength);
207  //Dump message contents for debugging purpose
208  icmpDumpEchoMessage(replyHeader);
209 
210  //Send Echo Reply message
211  ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL);
212  }
213 
214  //Free previously allocated memory block
215  netBufferFree(reply);
216 }
217 
218 
219 /**
220  * @brief Send an ICMP Error message
221  * @param[in] interface Underlying network interface
222  * @param[in] type Message type
223  * @param[in] code Specific message code
224  * @param[in] parameter Specific message parameter
225  * @param[in] ipPacket Multi-part buffer that holds the invoking IPv4 packet
226  * @param[in] ipPacketOffset Offset to the first byte of the IPv4 packet
227  * @return Error code
228  **/
229 
230 error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
231  uint8_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
232 {
233  error_t error;
234  size_t offset;
235  size_t length;
236  Ipv4Header *ipHeader;
237  NetBuffer *icmpMessage;
238  IcmpErrorMessage *icmpHeader;
239  Ipv4PseudoHeader pseudoHeader;
240 
241  //Retrieve the length of the invoking IPv4 packet
242  length = netBufferGetLength(ipPacket) - ipPacketOffset;
243 
244  //Check the length of the IPv4 packet
245  if(length < sizeof(Ipv4Header))
246  return ERROR_INVALID_LENGTH;
247 
248  //Point to the header of the invoking packet
249  ipHeader = netBufferAt(ipPacket, ipPacketOffset);
250  //Sanity check
251  if(ipHeader == NULL)
252  return ERROR_FAILURE;
253 
254  //Never respond to a packet destined to a broadcast or a multicast address
255  if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) ||
256  ipv4IsMulticastAddr(ipHeader->destAddr))
257  {
258  //Report an error
259  return ERROR_INVALID_ADDRESS;
260  }
261 
262  //Length of the data that will be returned along with the ICMP header
263  length = MIN(length, (size_t) ipHeader->headerLength * 4 + 8);
264 
265  //Allocate a memory buffer to hold the ICMP message
266  icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset);
267  //Failed to allocate memory?
268  if(icmpMessage == NULL)
269  return ERROR_OUT_OF_MEMORY;
270 
271  //Point to the ICMP header
272  icmpHeader = netBufferAt(icmpMessage, offset);
273 
274  //Format ICMP message
275  icmpHeader->type = type;
276  icmpHeader->code = code;
277  icmpHeader->checksum = 0;
278  icmpHeader->parameter = parameter;
279  icmpHeader->unused = 0;
280 
281  //Copy the IP header and the first 8 bytes of the original datagram data
282  error = netBufferConcat(icmpMessage, ipPacket, ipPacketOffset, length);
283 
284  //Check status code
285  if(!error)
286  {
287  //Get the length of the resulting message
288  length = netBufferGetLength(icmpMessage) - offset;
289  //Message checksum calculation
290  icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length);
291 
292  //Format IPv4 pseudo header
293  pseudoHeader.srcAddr = ipHeader->destAddr;
294  pseudoHeader.destAddr = ipHeader->srcAddr;
295  pseudoHeader.reserved = 0;
296  pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
297  pseudoHeader.length = htons(length);
298 
299  //Update ICMP statistics
301 
302  //Debug message
303  TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length);
304  //Dump message contents for debugging purpose
305  icmpDumpErrorMessage(icmpHeader);
306 
307  //Send ICMP Error message
308  error = ipv4SendDatagram(interface, &pseudoHeader,
309  icmpMessage, offset, IPV4_DEFAULT_TTL);
310  }
311 
312  //Free previously allocated memory
313  netBufferFree(icmpMessage);
314 
315  //Return status code
316  return error;
317 }
318 
319 
320 /**
321  * @brief Update ICMP input statistics
322  * @param[in] type ICMP message type
323  **/
324 
325 void icmpUpdateInStats(uint8_t type)
326 {
327  //Check ICMP message type
328  switch(type)
329  {
331  //Number of ICMP Destination Unreachable messages received
332  MIB2_INC_COUNTER32(icmpGroup.icmpInDestUnreachs, 1);
333  break;
335  //Number of ICMP Time Exceeded messages received
336  MIB2_INC_COUNTER32(icmpGroup.icmpInTimeExcds, 1);
337  break;
339  //Number of ICMP Parameter Problem messages received
340  MIB2_INC_COUNTER32(icmpGroup.icmpInParmProbs, 1);
341  break;
343  //Number of ICMP Source Quench messages received
344  MIB2_INC_COUNTER32(icmpGroup.icmpInSrcQuenchs, 1);
345  break;
346  case ICMP_TYPE_REDIRECT:
347  //Number of ICMP Redirect messages received
348  MIB2_INC_COUNTER32(icmpGroup.icmpInRedirects, 1);
349  break;
351  //Number of ICMP Echo Request messages received
352  MIB2_INC_COUNTER32(icmpGroup.icmpInEchos, 1);
353  break;
355  //Number of ICMP Echo Reply messages received
356  MIB2_INC_COUNTER32(icmpGroup.icmpInEchoReps, 1);
357  break;
359  //Number of ICMP Timestamp Request messages received
360  MIB2_INC_COUNTER32(icmpGroup.icmpInTimestamps, 1);
361  break;
363  //Number of ICMP Timestamp Reply messages received
364  MIB2_INC_COUNTER32(icmpGroup.icmpInTimestampReps, 1);
365  break;
367  //Number of ICMP Address Mask Request messages received
368  MIB2_INC_COUNTER32(icmpGroup.icmpInAddrMasks, 1);
369  break;
371  //Number of ICMP Address Mask Reply messages received
372  MIB2_INC_COUNTER32(icmpGroup.icmpInAddrMaskReps, 1);
373  break;
374  default:
375  //Just for sanity
376  break;
377  }
378 
379  //Increment per-message type ICMP counter
380  IP_MIB_INC_COUNTER32(icmpMsgStatsTable.icmpMsgStatsInPkts[type], 1);
381 }
382 
383 
384 /**
385  * @brief Update ICMP output statistics
386  * @param[in] type ICMPv6 message type
387  **/
388 
390 {
391  //Total number of ICMP messages which this entity attempted to send
392  MIB2_INC_COUNTER32(icmpGroup.icmpOutMsgs, 1);
393  IP_MIB_INC_COUNTER32(icmpStats.icmpStatsOutMsgs, 1);
394 
395  //Check ICMP message type
396  switch(type)
397  {
399  //Number of ICMP Destination Unreachable messages sent
400  MIB2_INC_COUNTER32(icmpGroup.icmpOutDestUnreachs, 1);
401  break;
403  //Number of ICMP Time Exceeded messages sent
404  MIB2_INC_COUNTER32(icmpGroup.icmpOutTimeExcds, 1);
405  break;
407  //Number of ICMP Parameter Problem messages sent
408  MIB2_INC_COUNTER32(icmpGroup.icmpOutParmProbs, 1);
409  break;
411  //Number of ICMP Source Quench messages sent
412  MIB2_INC_COUNTER32(icmpGroup.icmpOutSrcQuenchs, 1);
413  break;
414  case ICMP_TYPE_REDIRECT:
415  //Number of ICMP Redirect messages sent
416  MIB2_INC_COUNTER32(icmpGroup.icmpOutRedirects, 1);
417  break;
419  //Number of ICMP Echo Request messages sent
420  MIB2_INC_COUNTER32(icmpGroup.icmpOutEchos, 1);
421  break;
423  //Number of ICMP Echo Reply messages sent
424  MIB2_INC_COUNTER32(icmpGroup.icmpOutEchoReps, 1);
425  break;
427  //Number of ICMP Timestamp Request messages sent
428  MIB2_INC_COUNTER32(icmpGroup.icmpOutTimestamps, 1);
429  break;
431  //Number of ICMP Timestamp Reply messages sent
432  MIB2_INC_COUNTER32(icmpGroup.icmpOutTimestampReps, 1);
433  break;
435  //Number of ICMP Address Mask Request messages sent
436  MIB2_INC_COUNTER32(icmpGroup.icmpOutAddrMasks, 1);
437  break;
439  //Number of ICMP Address Mask Reply messages sent
440  MIB2_INC_COUNTER32(icmpGroup.icmpOutAddrMaskReps, 1);
441  break;
442  default:
443  //Just for sanity
444  break;
445  }
446 
447  //Increment per-message type ICMP counter
448  IP_MIB_INC_COUNTER32(icmpMsgStatsTable.icmpMsgStatsOutPkts[type], 1);
449 }
450 
451 
452 /**
453  * @brief Dump ICMP message for debugging purpose
454  * @param[in] message Pointer to the ICMP message
455  **/
456 
458 {
459  //Dump ICMP message
460  TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type);
461  TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code);
462  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
463 }
464 
465 
466 /**
467  * @brief Dump ICMP Echo Request or Echo Reply message
468  * @param[in] message Pointer to the ICMP message
469  **/
470 
472 {
473  //Dump ICMP message
474  TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type);
475  TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code);
476  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
477  TRACE_DEBUG(" Identifier = 0x%04" PRIX16 "\r\n", ntohs(message->identifier));
478  TRACE_DEBUG(" Sequence Number = 0x%04" PRIX16 "\r\n", ntohs(message->sequenceNumber));
479 }
480 
481 
482 /**
483  * @brief Dump generic ICMP Error message
484  * @param[in] message Pointer to the ICMP message
485  **/
486 
488 {
489  //Dump ICMP message
490  TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type);
491  TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code);
492  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
493  TRACE_DEBUG(" Parameter = %" PRIu8 "\r\n", message->parameter);
494 }
495 
496 #endif
bool_t ipv4IsBroadcastAddr(NetInterface *interface, Ipv4Addr ipAddr)
Check whether an IPv4 address is a broadcast address.
Definition: ipv4.c:1275
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:232
void icmpUpdateInStats(uint8_t type)
Update ICMP input statistics.
Definition: icmp.c:325
__start_packed struct @172 IcmpEchoMessage
ICMP Echo Request and Echo Reply messages.
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint8_t ttl)
Send an IPv4 datagram.
Definition: ipv4.c:774
void icmpProcessEchoRequest(NetInterface *interface, Ipv4Addr srcIpAddr, const NetBuffer *request, size_t requestOffset)
Echo Request message processing.
Definition: icmp.c:134
TCP/IP stack core.
void icmpDumpEchoMessage(const IcmpEchoMessage *message)
Dump ICMP Echo Request or Echo Reply message.
Definition: icmp.c:471
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:280
Debugging facilities.
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:295
Generic error code.
Definition: error.h:43
uint8_t message[]
Definition: chap.h:150
MIB-II module.
void icmpUpdateOutStats(uint8_t type)
Update ICMP output statistics.
Definition: icmp.c:389
IPv4 and IPv6 common routines.
char_t type
#define htons(value)
Definition: cpu_endian.h:390
void icmpDumpMessage(const IcmpHeader *message)
Dump ICMP message for debugging purpose.
Definition: icmp.c:457
#define MIB2_INC_COUNTER32(name, value)
Definition: mib2_module.h:154
#define Ipv4PseudoHeader
Definition: ipv4.h:37
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint8_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
Send an ICMP Error message.
Definition: icmp.c:230
error_t netBufferConcat(NetBuffer *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Concatenate two multi-part buffers.
Definition: net_mem.c:440
#define IPV4_DEFAULT_TTL
Definition: ipv4.h:54
void icmpProcessMessage(NetInterface *interface, Ipv4Addr srcIpAddr, const NetBuffer *buffer, size_t offset)
Incoming ICMP message processing.
Definition: icmp.c:54
#define ntohs(value)
Definition: cpu_endian.h:396
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:596
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:471
IPv4 (Internet Protocol Version 4)
uint8_t ipPacket[]
Definition: ndp.h:427
uint32_t parameter
Definition: icmp.h:121
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
#define MIN(a, b)
Definition: os_port.h:60
__start_packed struct @167 IcmpHeader
ICMP header.
void icmpDumpErrorMessage(const IcmpErrorMessage *message)
Dump generic ICMP Error message.
Definition: icmp.c:487
IP MIB module.
Ipv4Addr srcIpAddr
Definition: ipcp.h:75
#define TRACE_INFO(...)
Definition: debug.h:86
error_t
Error codes.
Definition: error.h:40
#define TRACE_WARNING(...)
Definition: debug.h:78
#define IP_MIB_INC_COUNTER32(name, value)
Definition: ip_mib_module.h:44
uint8_t code
Definition: coap_common.h:179
#define PRIuSIZE
Definition: compiler_port.h:72
#define NetInterface
Definition: net.h:34
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:150
#define Ipv4Header
Definition: ipv4.h:34
ICMP (Internet Control Message Protocol)
uint8_t length
Definition: dtls_misc.h:140
#define TRACE_DEBUG(...)
Definition: debug.h:98
__start_packed struct @168 IcmpErrorMessage
ICMP Error message.