Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers sntp_client.c Source File

sntp_client.c

Go to the documentation of this file.
00001 /**
00002  * @file sntp_client.c
00003  * @brief SNTP client (Simple Network Time Protocol)
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  * @section Description
00026  *
00027  * The Simple Network Time Protocol is used to synchronize computer clocks
00028  * in the Internet. Refer to RFC 4330 for more details
00029  *
00030  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00031  * @version 1.7.6
00032  **/
00033 
00034 //Switch to the appropriate trace level
00035 #define TRACE_LEVEL SNTP_TRACE_LEVEL
00036 
00037 //Dependencies
00038 #include "core/net.h"
00039 #include "sntp/sntp_client.h"
00040 #include "error.h"
00041 #include "debug.h"
00042 
00043 //Check TCP/IP stack configuration
00044 #if (SNTP_CLIENT_SUPPORT == ENABLED)
00045 
00046 
00047 /**
00048  * @brief Retrieve current time from NTP server using SNTP protocol
00049  * @param[in] interface Underlying network interface (optional parameter)
00050  * @param[in] serverIpAddr IP address of the NTP server
00051  * @param[out] timestamp Current time
00052  * @return Error code
00053  **/
00054 
00055  error_t sntpClientGetTimestamp(NetInterface *interface,
00056    const IpAddr *serverIpAddr, NtpTimestamp *timestamp)
00057 {
00058    error_t error;
00059    uint_t i;
00060    systime_t timeout;
00061    SntpClientContext context;
00062 
00063    //Check parameters
00064    if(serverIpAddr == NULL || timestamp == NULL)
00065       return ERROR_INVALID_PARAMETER;
00066 
00067    //Use default network interface?
00068    if(interface == NULL)
00069       interface = netGetDefaultInterface();
00070 
00071    //Open a UDP socket
00072    context.socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
00073    //Failed to open socket?
00074    if(!context.socket)
00075       return ERROR_OPEN_FAILED;
00076 
00077    //Associate the socket with the relevant interface
00078    error = socketBindToInterface(context.socket, interface);
00079    //Any error to report?
00080    if(error)
00081    {
00082       //Close socket
00083       socketClose(context.socket);
00084       //Return status code
00085       return error;
00086    }
00087 
00088    //Only accept datagrams from the specified NTP server
00089    error = socketConnect(context.socket, serverIpAddr, NTP_PORT);
00090    //Any error to report?
00091    if(error)
00092    {
00093       //Close socket
00094       socketClose(context.socket);
00095       //Return status code
00096       return error;
00097    }
00098 
00099    //Initial timeout value
00100    timeout = SNTP_CLIENT_INIT_TIMEOUT;
00101 
00102    //Retransmission loop
00103    for(i = 0; i < SNTP_CLIENT_MAX_RETRIES; i++)
00104    {
00105       //Send NTP request message
00106       error = sntpSendRequest(&context);
00107       //Failed to send message ?
00108       if(error)
00109          break;
00110 
00111       //Wait for a valid NTP response message
00112       error = sntpWaitForResponse(&context, timeout);
00113       //Valid NTP response received?
00114       if(!error)
00115          break;
00116 
00117       //The timeout value is doubled for each subsequent retransmission
00118       timeout = MIN(timeout * 2, SNTP_CLIENT_MAX_TIMEOUT);
00119    }
00120 
00121    //Successful processing?
00122    if(!error)
00123    {
00124       //Save server timestamp
00125       timestamp->seconds = ntohl(context.message.transmitTimestamp.seconds);
00126       timestamp->fraction = ntohl(context.message.transmitTimestamp.fraction);
00127    }
00128 
00129    //Close socket
00130    socketClose(context.socket);
00131    //Return status code
00132    return error;
00133 }
00134 
00135 
00136 /**
00137  * @brief Send NTP request using SNTP protocol
00138  * @param[in] context SNTP client context
00139  * @return Error code
00140  **/
00141 
00142 error_t sntpSendRequest(SntpClientContext *context)
00143 {
00144    size_t length;
00145    NtpHeader *message;
00146 
00147    //Point to the buffer where to format the NTP message
00148    message = &context->message;
00149 
00150    //Clear NTP message
00151    memset(message, 0, sizeof(NtpHeader));
00152 
00153    //Format NTP request
00154    message->vn = NTP_VERSION_3;
00155    message->mode = NTP_MODE_CLIENT;
00156 
00157    //Time at which the NTP request was sent
00158    context->t1 = osGetSystemTime();
00159 
00160    //The Transmit Timestamp allows a simple calculation to determine the
00161    //propagation delay between the server and client and to align the system
00162    //clock generally within a few tens of milliseconds relative to the server
00163    message->transmitTimestamp.seconds = 0;
00164    message->transmitTimestamp.fraction = htonl(context->t1);
00165 
00166    //Length of the NTP request
00167    length = sizeof(NtpHeader);
00168 
00169    //Debug message
00170    TRACE_INFO("Sending NTP request message (%" PRIuSIZE " bytes)...\r\n", sizeof(NtpHeader));
00171    //Dump NTP message
00172    sntpDumpMessage(message, length);
00173 
00174    //Send NTP request
00175    return socketSend(context->socket, message, length, NULL, 0);
00176 }
00177 
00178 
00179 /**
00180  * @brief Wait for a valid response from the NTP server
00181  * @param[in] context Pointer to the SNTP client context
00182  * @param[in] timeout Maximum time period to wait
00183  * @return Error code
00184  **/
00185 
00186 error_t sntpWaitForResponse(SntpClientContext *context, systime_t timeout)
00187 {
00188    error_t error;
00189    size_t length;
00190    systime_t elapsedTime;
00191 
00192    //Time elapsed since the NTP request was sent
00193    elapsedTime = 0;
00194 
00195    //Keep listening as long as the retransmission timeout has not been reached
00196    while(elapsedTime < timeout)
00197    {
00198       //Adjust receive timeout
00199       error = socketSetTimeout(context->socket, timeout - elapsedTime);
00200       //Any error to report?
00201       if(error)
00202          break;
00203 
00204       //Wait for a response from the NTP server
00205       error = socketReceive(context->socket, &context->message,
00206          sizeof(NtpHeader), &length, 0);
00207 
00208       //Any datagram received?
00209       if(!error)
00210       {
00211          //Time at which the response was received
00212          context->t4 = osGetSystemTime();
00213 
00214          //Parse incoming datagram
00215          error = sntpParseResponse(context, &context->message, length);
00216          //Valid NTP response message?
00217          if(!error)
00218             return NO_ERROR;
00219       }
00220 
00221       //Compute the time elapsed since the NTP request was sent
00222       elapsedTime = osGetSystemTime() - context->t1;
00223    }
00224 
00225    //The timeout period elapsed
00226    return ERROR_TIMEOUT;
00227 }
00228 
00229 
00230 /**
00231  * @brief Parse NTP server response
00232  * @param[in] context Pointer to the SNTP client context
00233  * @param[in] message NTP response message to parse
00234  * @param[in] length Length of the incoming NTP message
00235  * @return Error code
00236  **/
00237 
00238 error_t sntpParseResponse(SntpClientContext *context,
00239    const NtpHeader *message, size_t length)
00240 {
00241    //Ensure the NTP message is valid
00242    if(length < sizeof(NtpHeader))
00243       return ERROR_INVALID_MESSAGE;
00244 
00245    //Debug message
00246    TRACE_INFO("NTP response message received (%" PRIuSIZE " bytes)...\r\n", length);
00247    //Dump NTP message
00248    sntpDumpMessage(message, length);
00249 
00250    //The server reply should be discarded if any of the VN, Stratum,
00251    //or Transmit Timestamp fields is 0
00252    if(!message->vn || !message->stratum)
00253       return ERROR_INVALID_MESSAGE;
00254    if(!message->transmitTimestamp.seconds || !message->transmitTimestamp.fraction)
00255       return ERROR_INVALID_MESSAGE;
00256 
00257    //The server reply should be discarded if the Mode field is
00258    //not 4 (unicast) or 5 (broadcast)
00259    if(message->mode != NTP_MODE_SERVER && message->mode != NTP_MODE_BROADCAST)
00260       return ERROR_INVALID_MESSAGE;
00261 
00262    //The Originate Timestamp in the server reply should match the
00263    //Transmit Timestamp used in the client request
00264    if(message->originateTimestamp.seconds != 0)
00265       return ERROR_INVALID_TIMESTAMP;
00266    if(message->originateTimestamp.fraction != htonl(context->t1))
00267       return ERROR_INVALID_TIMESTAMP;
00268 
00269    //The NTP response message is acceptable
00270    return NO_ERROR;
00271 }
00272 
00273 
00274 /**
00275  * @brief Dump NTP message for debugging purpose
00276  * @param[in] message Pointer to the NTP message
00277  * @param[in] length Length of the NTP message
00278  **/
00279 
00280 void sntpDumpMessage(const NtpHeader *message, size_t length)
00281 {
00282 #if (SNTP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
00283    uint8_t *p;
00284    NtpAuthData* authData;
00285 
00286    //Point to the beginning of the message
00287    p = (uint8_t *) message;
00288 
00289    //Check message length
00290    if(length >= sizeof(NtpHeader))
00291    {
00292       //Dump NTP message
00293       TRACE_DEBUG("  Mode = %" PRIu8 "\r\n", message->mode);
00294       TRACE_DEBUG("  Version = %" PRIu8 "\r\n", message->vn);
00295       TRACE_DEBUG("  Leap indicator = %" PRIu8 "\r\n", message->li);
00296       TRACE_DEBUG("  Stratum = %" PRIu8 "\r\n", message->stratum);
00297       TRACE_DEBUG("  Poll = %" PRIu8 "\r\n", message->poll);
00298       TRACE_DEBUG("  Precision = %" PRId8 "\r\n", message->precision);
00299       TRACE_DEBUG("  Root Delay = %" PRIu32 "\r\n", ntohl(message->rootDelay));
00300       TRACE_DEBUG("  Root Dispersion = %" PRIu32 "\r\n", ntohl(message->rootDispersion));
00301       TRACE_DEBUG("  Reference Identifier = %" PRIu32 "\r\n", ntohl(message->referenceIdentifier));
00302 
00303       //Dump reference timestamp
00304       TRACE_DEBUG("  ReferenceTimestamp\r\n");
00305       sntpDumpTimestamp(&message->referenceTimestamp);
00306 
00307       //Dump originate timestamp
00308       TRACE_DEBUG("  Originate Timestamp\r\n");
00309       sntpDumpTimestamp(&message->originateTimestamp);
00310 
00311       //Dump receive timestamp
00312       TRACE_DEBUG("  Receive Timestamp\r\n");
00313       sntpDumpTimestamp(&message->receiveTimestamp);
00314 
00315       //Dump transmit timestamp
00316       TRACE_DEBUG("  Transmit Timestamp\r\n");
00317       sntpDumpTimestamp(&message->transmitTimestamp);
00318 
00319       //Advance data pointer
00320       p += sizeof(NtpHeader);
00321       length -= sizeof(NtpHeader);
00322 
00323       //Any authentication data?
00324       if(length >= sizeof(NtpAuthData))
00325       {
00326          //Point to the beginning of the authentication data
00327          authData = (NtpAuthData *) p;
00328 
00329          //Dump transmit timestamp
00330          TRACE_DEBUG("  Key Identifier = %" PRIu32 "\r\n", ntohl(authData->keyIdentifier));
00331          //Dump message digest
00332          TRACE_DEBUG("  Message Digest\r\n");
00333          TRACE_DEBUG_ARRAY("    ", authData->messageDigest, 16);
00334       }
00335    }
00336 #endif
00337 }
00338 
00339 
00340 /**
00341  * @brief Dump NTP timestamp
00342  * @param[in] timestamp Pointer to the NTP timestamp
00343  **/
00344 
00345 void sntpDumpTimestamp(const NtpTimestamp *timestamp)
00346 {
00347    //Dump seconds
00348    TRACE_DEBUG("    Seconds = %" PRIu32 "\r\n", ntohl(timestamp->seconds));
00349    //Dump fraction field
00350    TRACE_DEBUG("    Fraction = %" PRIu32 "\r\n", ntohl(timestamp->fraction));
00351 }
00352 
00353 #endif
00354