1 /* 2 * FreeRTOS+TCP V3.1.0 3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 * 5 * SPDX-License-Identifier: MIT 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 * this software and associated documentation files (the "Software"), to deal in 9 * the Software without restriction, including without limitation the rights to 10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 * the Software, and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * http://aws.amazon.com/freertos 25 * http://www.FreeRTOS.org 26 */ 27 28 /** 29 * @file FreeRTOS_ICMP.c 30 * @brief Implements the Internet Control Message Protocol for the FreeRTOS+TCP network stack. 31 */ 32 33 /* Standard includes. */ 34 #include <stdint.h> 35 #include <stdio.h> 36 #include <string.h> 37 38 /* FreeRTOS includes. */ 39 #include "FreeRTOS.h" 40 #include "task.h" 41 #include "queue.h" 42 #include "semphr.h" 43 44 /* FreeRTOS+TCP includes. */ 45 #include "FreeRTOS_IP.h" 46 #include "FreeRTOS_ICMP.h" 47 #include "FreeRTOS_Sockets.h" 48 #include "FreeRTOS_IP_Private.h" 49 #include "FreeRTOS_ARP.h" 50 #include "FreeRTOS_UDP_IP.h" 51 #include "FreeRTOS_DHCP.h" 52 #include "NetworkInterface.h" 53 #include "NetworkBufferManagement.h" 54 #include "FreeRTOS_DNS.h" 55 56 /* 57 * Turns around an incoming ping request to convert it into a ping reply. 58 */ 59 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) 60 static eFrameProcessingResult_t prvProcessICMPEchoRequest( ICMPPacket_t * const pxICMPPacket, 61 const NetworkBufferDescriptor_t * const pxNetworkBuffer ); 62 #endif /* ipconfigREPLY_TO_INCOMING_PINGS */ 63 64 /* 65 * Processes incoming ping replies. The application callback function 66 * vApplicationPingReplyHook() is called with the results. 67 */ 68 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 69 static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket ); 70 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ 71 72 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 73 74 /** 75 * @brief Process an ICMP packet. Only echo requests and echo replies are recognised and handled. 76 * 77 * @param[in,out] pxICMPPacket: The IP packet that contains the ICMP message. 78 * 79 * @return eReleaseBuffer when the message buffer should be released, or eReturnEthernetFrame 80 * when the packet should be returned. 81 */ ProcessICMPPacket(const NetworkBufferDescriptor_t * const pxNetworkBuffer)82 eFrameProcessingResult_t ProcessICMPPacket( const NetworkBufferDescriptor_t * const pxNetworkBuffer ) 83 { 84 eFrameProcessingResult_t eReturn = eReleaseBuffer; 85 86 iptraceICMP_PACKET_RECEIVED(); 87 88 configASSERT( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ); 89 90 if( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ) 91 { 92 /* Map the buffer onto a ICMP-Packet struct to easily access the 93 * fields of ICMP packet. */ 94 95 /* MISRA Ref 11.3.1 [Misaligned access] */ 96 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 97 /* coverity[misra_c_2012_rule_11_3_violation] */ 98 ICMPPacket_t * pxICMPPacket = ( ( ICMPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); 99 100 switch( pxICMPPacket->xICMPHeader.ucTypeOfMessage ) 101 { 102 case ipICMP_ECHO_REQUEST: 103 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) 104 { 105 eReturn = prvProcessICMPEchoRequest( pxICMPPacket, pxNetworkBuffer ); 106 } 107 #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) */ 108 break; 109 110 case ipICMP_ECHO_REPLY: 111 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 112 { 113 prvProcessICMPEchoReply( pxICMPPacket ); 114 } 115 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ 116 break; 117 118 default: 119 /* Only ICMP echo packets are handled. */ 120 break; 121 } 122 } 123 124 return eReturn; 125 } 126 127 #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ 128 /*-----------------------------------------------------------*/ 129 130 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) 131 132 /** 133 * @brief Process an ICMP echo request. 134 * 135 * @param[in,out] pxICMPPacket: The IP packet that contains the ICMP message. 136 */ prvProcessICMPEchoRequest(ICMPPacket_t * const pxICMPPacket,const NetworkBufferDescriptor_t * const pxNetworkBuffer)137 static eFrameProcessingResult_t prvProcessICMPEchoRequest( ICMPPacket_t * const pxICMPPacket, 138 const NetworkBufferDescriptor_t * const pxNetworkBuffer ) 139 { 140 ICMPHeader_t * pxICMPHeader; 141 IPHeader_t * pxIPHeader; 142 143 pxICMPHeader = &( pxICMPPacket->xICMPHeader ); 144 pxIPHeader = &( pxICMPPacket->xIPHeader ); 145 146 /* HT:endian: changed back */ 147 iptraceSENDING_PING_REPLY( pxIPHeader->ulSourceIPAddress ); 148 149 /* The checksum can be checked here - but a ping reply should be 150 * returned even if the checksum is incorrect so the other end can 151 * tell that the ping was received - even if the ping reply contains 152 * invalid data. */ 153 pxICMPHeader->ucTypeOfMessage = ( uint8_t ) ipICMP_ECHO_REPLY; 154 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; 155 pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; 156 /* Update the TTL field. */ 157 pxIPHeader->ucTimeToLive = ipconfigICMP_TIME_TO_LIVE; 158 159 /* The stack doesn't support fragments, so the fragment offset field must always be zero. 160 * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. 161 */ 162 #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) 163 pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; 164 #else 165 pxIPHeader->usFragmentOffset = 0U; 166 #endif 167 168 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) 169 { 170 /* calculate the IP header checksum, in case the driver won't do that. */ 171 pxIPHeader->usHeaderChecksum = 0x00U; 172 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); 173 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); 174 175 /* calculate the ICMP checksum for an outgoing packet. */ 176 ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxICMPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); 177 } 178 #else 179 { 180 /* Many EMAC peripherals will only calculate the ICMP checksum 181 * correctly if the field is nulled beforehand. */ 182 pxICMPHeader->usChecksum = 0U; 183 } 184 #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */ 185 186 return eReturnEthernetFrame; 187 } 188 189 #endif /* ipconfigREPLY_TO_INCOMING_PINGS == 1 */ 190 /*-----------------------------------------------------------*/ 191 192 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 193 194 /** 195 * @brief Process an ICMP echo reply. 196 * 197 * @param[in] pxICMPPacket: The IP packet that contains the ICMP message. 198 */ prvProcessICMPEchoReply(ICMPPacket_t * const pxICMPPacket)199 static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket ) 200 { 201 ePingReplyStatus_t eStatus = eSuccess; 202 uint16_t usDataLength, usCount; 203 uint8_t * pucByte; 204 205 /* Find the total length of the IP packet. */ 206 usDataLength = pxICMPPacket->xIPHeader.usLength; 207 usDataLength = FreeRTOS_ntohs( usDataLength ); 208 209 /* Remove the length of the IP headers to obtain the length of the ICMP 210 * message itself. */ 211 usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_IPv4_HEADER ); 212 213 /* Remove the length of the ICMP header, to obtain the length of 214 * data contained in the ping. */ 215 usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_ICMP_HEADER ); 216 217 /* Checksum has already been checked before in prvProcessIPPacket */ 218 219 /* Find the first byte of the data within the ICMP packet. */ 220 pucByte = ( uint8_t * ) pxICMPPacket; 221 pucByte = &( pucByte[ sizeof( ICMPPacket_t ) ] ); 222 223 /* Check each byte. */ 224 for( usCount = 0; usCount < usDataLength; usCount++ ) 225 { 226 if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE ) 227 { 228 eStatus = eInvalidData; 229 break; 230 } 231 232 pucByte++; 233 } 234 235 /* Call back into the application to pass it the result. */ 236 vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier ); 237 } 238 239 #endif /* if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ 240 /*-----------------------------------------------------------*/ 241