Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ping.c
00001 /** 00002 * @file ping.c 00003 * @brief Ping utility 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL PING_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include "core/net.h" 00034 #include "core/ping.h" 00035 #include "core/ip.h" 00036 #include "ipv4/ipv4.h" 00037 #include "ipv4/icmp.h" 00038 #include "ipv6/ipv6.h" 00039 #include "ipv6/ipv6_misc.h" 00040 #include "ipv6/icmpv6.h" 00041 #include "core/socket.h" 00042 #include "debug.h" 00043 00044 //Check TCP/IP stack configuration 00045 #if (PING_SUPPORT == ENABLED) 00046 00047 //Sequence number field 00048 static uint16_t pingSequenceNumber = 0; 00049 00050 00051 /** 00052 * @brief Test the reachability of a host 00053 * 00054 * Ping operates by sending an ICMP Echo Request message to the 00055 * target host and waiting for an ICMP Echo Reply message 00056 * 00057 * @param[in] interface Underlying network interface (optional parameter) 00058 * @param[in] targetIpAddr IP address of the host to reach 00059 * @param[in] size Size of the data payload in bytes 00060 * @param[in] ttl Time-To-Live value to be used 00061 * @param[in] timeout Maximum time to wait before giving up 00062 * @param[out] rtt Round-trip time (optional parameter) 00063 * @return Error code 00064 **/ 00065 00066 error_t ping(NetInterface *interface, const IpAddr *targetIpAddr, 00067 size_t size, uint8_t ttl, systime_t timeout, systime_t *rtt) 00068 { 00069 error_t error; 00070 PingContext context; 00071 00072 //Check parameters 00073 if(targetIpAddr == NULL) 00074 return ERROR_INVALID_PARAMETER; 00075 00076 //Initialize context 00077 pingInit(&context); 00078 00079 //Start of exception handling block 00080 do 00081 { 00082 //Select the specified network interface 00083 error = pingBindToInterface(&context, interface); 00084 //Any error to report? 00085 if(error) 00086 break; 00087 00088 //Set timeout value 00089 error = pingSetTimeout(&context, timeout); 00090 //Any error to report? 00091 if(error) 00092 break; 00093 00094 //Send an ICMP Echo Request message 00095 error = pingSendRequest(&context, targetIpAddr, size, ttl); 00096 //Any error to report? 00097 if(error) 00098 break; 00099 00100 //Wait for a matching Echo Reply message 00101 error = pingWaitForReply(&context, NULL, rtt); 00102 //Any error to report? 00103 if(error) 00104 break; 00105 00106 //End of exception handling block 00107 } while(0); 00108 00109 //Release resources 00110 pingRelease(&context); 00111 00112 //Return status code 00113 return error; 00114 } 00115 00116 00117 /** 00118 * @brief Initialize ping context 00119 * @param[in] context Pointer to the ping context 00120 **/ 00121 00122 void pingInit(PingContext *context) 00123 { 00124 //Make sure the context is valid 00125 if(context != NULL) 00126 { 00127 //Initialize context 00128 memset(context, 0, sizeof(PingContext)); 00129 00130 //Set the default timeout to be used 00131 context->timeout = PING_DEFAULT_TIMEOUT; 00132 } 00133 } 00134 00135 00136 /** 00137 * @brief Set timeout value 00138 * @param[in] context Pointer to the ping context 00139 * @param[in] timeout Maximum time to wait 00140 * @return Error code 00141 **/ 00142 00143 error_t pingSetTimeout(PingContext *context, systime_t timeout) 00144 { 00145 //Invalid context? 00146 if(context == NULL) 00147 return ERROR_INVALID_PARAMETER; 00148 00149 //Save timeout value 00150 context->timeout = timeout; 00151 00152 //Successful processing 00153 return NO_ERROR; 00154 } 00155 00156 00157 /** 00158 * @brief Select a particular network interface 00159 * @param[in] context Pointer to the ping context 00160 * @param[in] interface Network interface to be used 00161 * @return Error code 00162 **/ 00163 00164 error_t pingBindToInterface(PingContext *context, NetInterface *interface) 00165 { 00166 //Invalid context? 00167 if(context == NULL) 00168 return ERROR_INVALID_PARAMETER; 00169 00170 //Select the specified network interface 00171 context->interface = interface; 00172 00173 //Successful processing 00174 return NO_ERROR; 00175 } 00176 00177 00178 /** 00179 * @brief Send an ICMP Echo Request message 00180 * @param[in] context Pointer to the ping context 00181 * @param[in] targetIpAddr IP address of the host to reach 00182 * @param[in] size Size of the data payload, in bytes 00183 * @param[in] ttl Time-To-Live value to be used 00184 * @return Error code 00185 **/ 00186 00187 error_t pingSendRequest(PingContext *context, 00188 const IpAddr *targetIpAddr, size_t size, uint8_t ttl) 00189 { 00190 error_t error; 00191 size_t i; 00192 size_t length; 00193 NetInterface *interface; 00194 IcmpEchoMessage *message; 00195 00196 //Invalid context? 00197 if(context == NULL) 00198 return ERROR_INVALID_PARAMETER; 00199 00200 //Limit the size of the data payload 00201 context->dataPayloadSize = MIN (size, PING_MAX_DATA_SIZE); 00202 00203 //Close existing socket, if necessary 00204 if(context->socket != NULL) 00205 { 00206 socketClose(context->socket); 00207 context->socket = NULL; 00208 } 00209 00210 //Identifier field is used to help matching requests and replies 00211 context->identifier = netGetRand(); 00212 00213 //Get exclusive access 00214 osAcquireMutex(&netMutex); 00215 //Sequence Number field is increment each time an Echo Request is sent 00216 context->sequenceNumber = pingSequenceNumber++; 00217 //Release exclusive access 00218 osReleaseMutex(&netMutex); 00219 00220 //Point to the buffer where to format the ICMP message 00221 message = (IcmpEchoMessage *) context->buffer; 00222 00223 //Format ICMP Echo Request message 00224 message->type = ICMP_TYPE_ECHO_REQUEST; 00225 message->code = 0; 00226 message->checksum = 0; 00227 message->identifier = context->identifier; 00228 message->sequenceNumber = context->sequenceNumber; 00229 00230 //Initialize data payload 00231 for(i = 0; i < context->dataPayloadSize; i++) 00232 message->data[i] = i & 0xFF; 00233 00234 //Length of the complete ICMP message including header and data 00235 length = sizeof(IcmpEchoMessage) + context->dataPayloadSize; 00236 00237 //Select the relevant network interface 00238 interface = context->interface; 00239 00240 #if (IPV4_SUPPORT == ENABLED) 00241 //Is target address an IPv4 address? 00242 if(targetIpAddr->length == sizeof(Ipv4Addr)) 00243 { 00244 Ipv4Addr srcIpAddr; 00245 00246 //Select the source IPv4 address and the relevant network 00247 //interface to use when pinging the specified host 00248 error = ipv4SelectSourceAddr(&interface, targetIpAddr->ipv4Addr, 00249 &srcIpAddr); 00250 00251 //Any error to report? 00252 if(error) 00253 return error; 00254 00255 //ICMP Echo Request message 00256 message->type = ICMP_TYPE_ECHO_REQUEST; 00257 //Message checksum calculation 00258 message->checksum = ipCalcChecksum(message, length); 00259 00260 //Open a raw socket 00261 context->socket = socketOpen(SOCKET_TYPE_RAW_IP, SOCKET_IP_PROTO_ICMP); 00262 } 00263 else 00264 #endif 00265 #if (IPV6_SUPPORT == ENABLED) 00266 //Is target address an IPv6 address? 00267 if(targetIpAddr->length == sizeof(Ipv6Addr)) 00268 { 00269 Ipv6PseudoHeader pseudoHeader; 00270 00271 //Select the source IPv6 address and the relevant network 00272 //interface to use when pinging the specified host 00273 error = ipv6SelectSourceAddr(&interface, &targetIpAddr->ipv6Addr, 00274 &pseudoHeader.srcAddr); 00275 00276 //Any error to report? 00277 if(error) 00278 return error; 00279 00280 //ICMPv6 Echo Request message 00281 message->type = ICMPV6_TYPE_ECHO_REQUEST; 00282 00283 //Format IPv6 pseudo header 00284 pseudoHeader.destAddr = targetIpAddr->ipv6Addr; 00285 pseudoHeader.length = htonl(length); 00286 pseudoHeader.reserved = 0; 00287 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 00288 00289 //Message checksum calculation 00290 message->checksum = ipCalcUpperLayerChecksum(&pseudoHeader, 00291 sizeof(Ipv6PseudoHeader), message, length); 00292 00293 //Open a raw socket 00294 context->socket = socketOpen(SOCKET_TYPE_RAW_IP, SOCKET_IP_PROTO_ICMPV6); 00295 } 00296 else 00297 #endif 00298 //Invalid target address? 00299 { 00300 //Report an error 00301 return ERROR_INVALID_ADDRESS; 00302 } 00303 00304 //Failed to open socket? 00305 if(context->socket == NULL) 00306 return ERROR_OPEN_FAILED; 00307 00308 //Set the TTL value to be used 00309 context->socket->ttl = ttl; 00310 00311 //Start of exception handling block 00312 do 00313 { 00314 //Associate the newly created socket with the relevant interface 00315 error = socketBindToInterface(context->socket, interface); 00316 //Unable to bind the socket to the desired interface? 00317 if(error) 00318 break; 00319 00320 //Debug message 00321 TRACE_INFO("Sending ICMP echo request to %s (%" PRIuSIZE " bytes)...\r\n", 00322 ipAddrToString(targetIpAddr, NULL), length); 00323 00324 //Send Echo Request message 00325 error = socketSendTo(context->socket, targetIpAddr, 0, 00326 message, length, NULL, 0); 00327 //Failed to send message ? 00328 if(error) 00329 break; 00330 00331 //Save the time at which the request was sent 00332 context->timestamp = osGetSystemTime(); 00333 00334 //End of exception handling block 00335 } while(0); 00336 00337 //Any error to report? 00338 if(error) 00339 { 00340 //Clean up side effects 00341 socketClose(context->socket); 00342 context->socket = NULL; 00343 } 00344 00345 //Return status code 00346 return error; 00347 } 00348 00349 00350 /** 00351 * @brief Check whether an incoming ICMP message is acceptable 00352 * @param[in] context Pointer to the ping context 00353 * @param[in] srcIpAddr Source IP address 00354 * @param[in] destIpAddr Destination IP address 00355 * @param[in] message Pointer to the incoming ICMP message 00356 * @param[in] length Length of the message, in bytes 00357 * @return Error code 00358 **/ 00359 00360 error_t pingCheckReply(PingContext *context, const IpAddr *srcIpAddr, 00361 const IpAddr *destIpAddr, const IcmpEchoMessage *message, size_t length) 00362 { 00363 size_t i; 00364 00365 //Check message length 00366 if(length != (sizeof(IcmpEchoMessage) + context->dataPayloadSize)) 00367 return ERROR_INVALID_MESSAGE; 00368 00369 #if (IPV4_SUPPORT == ENABLED) 00370 //Is target address an IPv4 address? 00371 if(context->socket->protocol == SOCKET_IP_PROTO_ICMP) 00372 { 00373 //Check address type 00374 if(destIpAddr->length != sizeof(Ipv4Addr)) 00375 return ERROR_INVALID_MESSAGE; 00376 00377 //Check message type 00378 if(message->type != ICMP_TYPE_ECHO_REPLY) 00379 return ERROR_INVALID_MESSAGE; 00380 00381 //Verify checksum value 00382 if(ipCalcChecksum(message, length) != 0x0000) 00383 return ERROR_INVALID_MESSAGE; 00384 } 00385 else 00386 #endif 00387 #if (IPV6_SUPPORT == ENABLED) 00388 //Is target address an IPv6 address? 00389 if(context->socket->protocol == SOCKET_IP_PROTO_ICMPV6) 00390 { 00391 Ipv6PseudoHeader pseudoHeader; 00392 00393 //Check address type 00394 if(destIpAddr->length != sizeof(Ipv6Addr)) 00395 return ERROR_INVALID_MESSAGE; 00396 00397 //Check message type 00398 if(message->type != ICMPV6_TYPE_ECHO_REPLY) 00399 return ERROR_INVALID_MESSAGE; 00400 00401 //Format IPv6 pseudo header 00402 pseudoHeader.srcAddr = srcIpAddr->ipv6Addr; 00403 pseudoHeader.destAddr = destIpAddr->ipv6Addr; 00404 pseudoHeader.length = htonl(length); 00405 pseudoHeader.reserved = 0; 00406 pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; 00407 00408 //Verify checksum value 00409 if(ipCalcUpperLayerChecksum(&pseudoHeader, 00410 sizeof(Ipv6PseudoHeader), message, length) != 0x0000) 00411 { 00412 //The checksum is not valid 00413 return ERROR_INVALID_MESSAGE; 00414 } 00415 } 00416 else 00417 #endif 00418 //Invalid target address? 00419 { 00420 //Report an error 00421 return ERROR_INVALID_ADDRESS; 00422 } 00423 00424 //Make sure the response identifier matches the request identifier 00425 if(message->identifier != context->identifier) 00426 return ERROR_INVALID_MESSAGE; 00427 //Make sure the sequence number is correct 00428 if(message->sequenceNumber != context->sequenceNumber) 00429 return ERROR_INVALID_MESSAGE; 00430 00431 //Verify data payload 00432 for(i = 0; i < context->dataPayloadSize; i++) 00433 { 00434 //Compare received data against expected data pattern 00435 if(message->data[i] != (i & 0xFF)) 00436 return ERROR_INVALID_MESSAGE; 00437 } 00438 00439 //The ICMP Echo Reply message is acceptable 00440 return NO_ERROR; 00441 } 00442 00443 00444 /** 00445 * @brief Wait for a matching ICMP Echo Reply message 00446 * @param[in] context Pointer to the ping context 00447 * @param[out] targetIpAddr IP address of the remote host (optional parameter) 00448 * @param[out] rtt Round-trip time (optional parameter) 00449 * @return Error code 00450 **/ 00451 00452 error_t pingWaitForReply(PingContext *context, 00453 IpAddr *targetIpAddr, systime_t *rtt) 00454 { 00455 error_t error; 00456 size_t length; 00457 systime_t time; 00458 systime_t timeout; 00459 IpAddr srcIpAddr; 00460 IpAddr destIpAddr; 00461 00462 //Invalid context? 00463 if(context == NULL) 00464 return ERROR_INVALID_PARAMETER; 00465 00466 //Wait for an ICMP Echo Reply message 00467 do 00468 { 00469 //Get current time 00470 time = osGetSystemTime(); 00471 00472 //Compute the timeout to be used 00473 if(timeCompare(time, context->timestamp + context->timeout) < 0) 00474 timeout = context->timestamp + context->timeout - time; 00475 else 00476 timeout = 0; 00477 00478 //Adjust receive timeout 00479 error = socketSetTimeout(context->socket, timeout); 00480 //Any error to report? 00481 if(error) 00482 break; 00483 00484 //Wait for an incoming ICMP message 00485 error = socketReceiveEx(context->socket, &srcIpAddr, NULL, 00486 &destIpAddr, context->buffer, PING_BUFFER_SIZE, &length, 0); 00487 00488 #if (NET_RTOS_SUPPORT == DISABLED) 00489 //Catch timeout exception 00490 if(error == ERROR_TIMEOUT) 00491 error = ERROR_WOULD_BLOCK; 00492 #endif 00493 00494 //Get current time 00495 time = osGetSystemTime(); 00496 00497 //Check status code 00498 if(!error) 00499 { 00500 //Check whether the incoming ICMP message is acceptable 00501 error = pingCheckReply(context, &srcIpAddr, &destIpAddr, 00502 (IcmpEchoMessage *) context->buffer, length); 00503 } 00504 00505 //Check status code 00506 if(!error) 00507 { 00508 //Calculate round-trip time 00509 context->rtt = time - context->timestamp; 00510 00511 //Debug message 00512 TRACE_INFO("ICMP echo reply received from %s (%" PRIu32 " ms)...\r\n", 00513 ipAddrToString(&srcIpAddr, NULL), context->rtt); 00514 00515 //Return the IP address of the host 00516 if(targetIpAddr != NULL) 00517 *targetIpAddr = srcIpAddr; 00518 00519 //Return the round-trip time 00520 if(rtt != NULL) 00521 *rtt = context->rtt; 00522 } 00523 else 00524 { 00525 //Timeout value exceeded? 00526 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00527 { 00528 //Report an error 00529 error = ERROR_TIMEOUT; 00530 } 00531 } 00532 00533 //Wait for the next incoming ICMP message 00534 } while(error == ERROR_INVALID_MESSAGE); 00535 00536 //Return status code 00537 return error; 00538 } 00539 00540 00541 /** 00542 * @brief Release ping context 00543 * @param[in] context Pointer to the ping context 00544 **/ 00545 00546 void pingRelease(PingContext *context) 00547 { 00548 //Make sure the context is valid 00549 if(context != NULL) 00550 { 00551 //Close underlying socket 00552 if(context->socket != NULL) 00553 { 00554 socketClose(context->socket); 00555 context->socket = NULL; 00556 } 00557 } 00558 } 00559 00560 #endif 00561
Generated on Tue Jul 12 2022 17:10:15 by
1.7.2