Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 17:10:16 by
