Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
ping.c
Go to the documentation of this file.
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
