1 /* 2 * FreeRTOS+TCP <DEVELOPMENT BRANCH> 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_IP_Private.h" 47 #include "FreeRTOS_ICMP.h" 48 #include "FreeRTOS_Sockets.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] pxNetworkBuffer The pointer to the network buffer descriptor 78 * that contains the ICMP message. 79 * 80 * @return eReleaseBuffer when the message buffer should be released, or eReturnEthernetFrame 81 * when the packet should be returned. 82 */ ProcessICMPPacket(const NetworkBufferDescriptor_t * const pxNetworkBuffer)83 eFrameProcessingResult_t ProcessICMPPacket( const NetworkBufferDescriptor_t * const pxNetworkBuffer ) 84 { 85 eFrameProcessingResult_t eReturn = eReleaseBuffer; 86 87 iptraceICMP_PACKET_RECEIVED(); 88 89 configASSERT( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ); 90 91 if( pxNetworkBuffer->xDataLength >= sizeof( ICMPPacket_t ) ) 92 { 93 /* Map the buffer onto a ICMP-Packet struct to easily access the 94 * fields of ICMP packet. */ 95 96 /* MISRA Ref 11.3.1 [Misaligned access] */ 97 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 98 /* coverity[misra_c_2012_rule_11_3_violation] */ 99 ICMPPacket_t * pxICMPPacket = ( ( ICMPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer ); 100 101 switch( pxICMPPacket->xICMPHeader.ucTypeOfMessage ) 102 { 103 case ipICMP_ECHO_REQUEST: 104 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) 105 { 106 eReturn = prvProcessICMPEchoRequest( pxICMPPacket, pxNetworkBuffer ); 107 } 108 #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) */ 109 break; 110 111 case ipICMP_ECHO_REPLY: 112 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 113 { 114 prvProcessICMPEchoReply( pxICMPPacket ); 115 } 116 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */ 117 break; 118 119 default: 120 /* Only ICMP echo packets are handled. */ 121 break; 122 } 123 } 124 125 return eReturn; 126 } 127 128 #endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ 129 /*-----------------------------------------------------------*/ 130 131 #if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) 132 133 /** 134 * @brief Process an ICMP echo request. 135 * 136 * @param[in,out] pxICMPPacket The IP packet that contains the ICMP message. 137 * @param pxNetworkBuffer Pointer to the network buffer containing the ICMP packet. 138 * @returns Function returns eReturnEthernetFrame. 139 */ prvProcessICMPEchoRequest(ICMPPacket_t * const pxICMPPacket,const NetworkBufferDescriptor_t * const pxNetworkBuffer)140 static eFrameProcessingResult_t prvProcessICMPEchoRequest( ICMPPacket_t * const pxICMPPacket, 141 const NetworkBufferDescriptor_t * const pxNetworkBuffer ) 142 { 143 ICMPHeader_t * pxICMPHeader; 144 IPHeader_t * pxIPHeader; 145 uint32_t ulIPAddress; 146 147 pxICMPHeader = &( pxICMPPacket->xICMPHeader ); 148 pxIPHeader = &( pxICMPPacket->xIPHeader ); 149 150 /* HT:endian: changed back */ 151 iptraceSENDING_PING_REPLY( pxIPHeader->ulSourceIPAddress ); 152 153 /* The checksum can be checked here - but a ping reply should be 154 * returned even if the checksum is incorrect so the other end can 155 * tell that the ping was received - even if the ping reply contains 156 * invalid data. */ 157 pxICMPHeader->ucTypeOfMessage = ( uint8_t ) ipICMP_ECHO_REPLY; 158 ulIPAddress = pxIPHeader->ulDestinationIPAddress; 159 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; 160 pxIPHeader->ulSourceIPAddress = ulIPAddress; 161 /* Update the TTL field. */ 162 pxIPHeader->ucTimeToLive = ipconfigICMP_TIME_TO_LIVE; 163 164 /* The stack doesn't support fragments, so the fragment offset field must always be zero. 165 * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. 166 */ 167 #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) 168 pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; 169 #else 170 pxIPHeader->usFragmentOffset = 0U; 171 #endif 172 173 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) 174 { 175 /* calculate the IP header checksum, in case the driver won't do that. */ 176 pxIPHeader->usHeaderChecksum = 0x00U; 177 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), uxIPHeaderSizePacket( pxNetworkBuffer ) ); 178 pxIPHeader->usHeaderChecksum = ( uint16_t ) ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); 179 180 /* calculate the ICMP checksum for an outgoing packet. */ 181 ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxICMPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); 182 } 183 #else 184 { 185 /* Just to prevent compiler warnings about unused parameters. */ 186 ( void ) pxNetworkBuffer; 187 188 /* Many EMAC peripherals will only calculate the ICMP checksum 189 * correctly if the field is nulled beforehand. */ 190 pxICMPHeader->usChecksum = 0U; 191 } 192 #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */ 193 194 return eReturnEthernetFrame; 195 } 196 197 #endif /* ipconfigREPLY_TO_INCOMING_PINGS == 1 */ 198 /*-----------------------------------------------------------*/ 199 200 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) 201 202 /** 203 * @brief Process an ICMP echo reply. 204 * 205 * @param[in] pxICMPPacket The IP packet that contains the ICMP message. 206 */ prvProcessICMPEchoReply(ICMPPacket_t * const pxICMPPacket)207 static void prvProcessICMPEchoReply( ICMPPacket_t * const pxICMPPacket ) 208 { 209 ePingReplyStatus_t eStatus = eSuccess; 210 uint16_t usDataLength, usCount; 211 uint8_t * pucByte; 212 213 /* Find the total length of the IP packet. */ 214 usDataLength = pxICMPPacket->xIPHeader.usLength; 215 usDataLength = FreeRTOS_ntohs( usDataLength ); 216 217 /* Remove the length of the IP headers to obtain the length of the ICMP 218 * message itself. */ 219 usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_IPv4_HEADER ); 220 221 /* Remove the length of the ICMP header, to obtain the length of 222 * data contained in the ping. */ 223 usDataLength = ( uint16_t ) ( ( ( uint32_t ) usDataLength ) - ipSIZE_OF_ICMPv4_HEADER ); 224 225 /* Checksum has already been checked before in prvProcessIPPacket */ 226 227 /* Find the first byte of the data within the ICMP packet. */ 228 pucByte = ( uint8_t * ) pxICMPPacket; 229 pucByte = &( pucByte[ sizeof( ICMPPacket_t ) ] ); 230 231 /* Check each byte. */ 232 for( usCount = 0; usCount < usDataLength; usCount++ ) 233 { 234 if( *pucByte != ( uint8_t ) ipECHO_DATA_FILL_BYTE ) 235 { 236 eStatus = eInvalidData; 237 break; 238 } 239 240 pucByte++; 241 } 242 243 /* Call back into the application to pass it the result. */ 244 vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier ); 245 } 246 247 #endif /* if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ 248 /*-----------------------------------------------------------*/ 249