Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ping.c Source File

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