[164] | 1 | /**
|
---|
| 2 | * @file
|
---|
| 3 | * SNTP client module
|
---|
| 4 | *
|
---|
| 5 | * This is simple "SNTP" client for the lwIP raw API.
|
---|
| 6 | * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
|
---|
| 7 | *
|
---|
| 8 | * For a list of some public NTP servers, see this link :
|
---|
| 9 | * http://support.ntp.org/bin/view/Servers/NTPPoolServers
|
---|
| 10 | *
|
---|
| 11 | * @todo:
|
---|
| 12 | * - set/change servers at runtime
|
---|
| 13 | * - complete SNTP_CHECK_RESPONSE checks 3 and 4
|
---|
| 14 | * - support broadcast/multicast mode?
|
---|
| 15 | */
|
---|
| 16 |
|
---|
| 17 | /*
|
---|
| 18 | * Redistribution and use in source and binary forms, with or without modification,
|
---|
| 19 | * are permitted provided that the following conditions are met:
|
---|
| 20 | *
|
---|
| 21 | * 1. Redistributions of source code must retain the above copyright notice,
|
---|
| 22 | * this list of conditions and the following disclaimer.
|
---|
| 23 | * 2. Redistributions in binary form must reproduce the above copyright notice,
|
---|
| 24 | * this list of conditions and the following disclaimer in the documentation
|
---|
| 25 | * and/or other materials provided with the distribution.
|
---|
| 26 | * 3. The name of the author may not be used to endorse or promote products
|
---|
| 27 | * derived from this software without specific prior written permission.
|
---|
| 28 | *
|
---|
| 29 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
---|
| 30 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
---|
| 31 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
---|
| 32 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
---|
| 33 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
---|
| 34 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
---|
| 35 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
---|
| 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
---|
| 37 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
---|
| 38 | * OF SUCH DAMAGE.
|
---|
| 39 | *
|
---|
| 40 | * This file is part of the lwIP TCP/IP stack.
|
---|
| 41 | *
|
---|
| 42 | * Author: Simon Goldschmidt (lwIP raw API part)
|
---|
| 43 | */
|
---|
| 44 |
|
---|
| 45 | #include "lwip/opt.h"
|
---|
| 46 |
|
---|
| 47 | #include "sntp.h"
|
---|
| 48 |
|
---|
| 49 | #include "lwip/timers.h"
|
---|
| 50 | #include "lwip/udp.h"
|
---|
| 51 | #include "lwip/dns.h"
|
---|
| 52 | #include "lwip/ip_addr.h"
|
---|
| 53 | #include "lwip/pbuf.h"
|
---|
| 54 |
|
---|
| 55 | #include <string.h>
|
---|
| 56 | #include <time.h>
|
---|
| 57 |
|
---|
| 58 | #if LWIP_UDP
|
---|
| 59 |
|
---|
| 60 | /**
|
---|
| 61 | * SNTP_DEBUG: Enable debugging for SNTP.
|
---|
| 62 | */
|
---|
| 63 | #ifndef SNTP_DEBUG
|
---|
| 64 | #define SNTP_DEBUG LWIP_DBG_OFF
|
---|
| 65 | #endif
|
---|
| 66 |
|
---|
| 67 | /** SNTP server port */
|
---|
| 68 | #ifndef SNTP_PORT
|
---|
| 69 | #define SNTP_PORT 123
|
---|
| 70 | #endif
|
---|
| 71 |
|
---|
| 72 | /** Set this to 1 to allow SNTP_SERVER_ADDRESS to be a DNS name */
|
---|
| 73 | #ifndef SNTP_SERVER_DNS
|
---|
| 74 | #define SNTP_SERVER_DNS 0
|
---|
| 75 | #endif
|
---|
| 76 |
|
---|
| 77 | /** Set this to 1 to support more than one server */
|
---|
| 78 | #ifndef SNTP_SUPPORT_MULTIPLE_SERVERS
|
---|
| 79 | #define SNTP_SUPPORT_MULTIPLE_SERVERS 0
|
---|
| 80 | #endif
|
---|
| 81 |
|
---|
| 82 | /** \def SNTP_SERVER_ADDRESS
|
---|
| 83 | * \brief SNTP server address:
|
---|
| 84 | * - as IPv4 address in "u32_t" format
|
---|
| 85 | * - as a DNS name if SNTP_SERVER_DNS is set to 1
|
---|
| 86 | * May contain multiple server names (e.g. "pool.ntp.org","second.time.server")
|
---|
| 87 | */
|
---|
| 88 | #ifndef SNTP_SERVER_ADDRESS
|
---|
| 89 | #if SNTP_SERVER_DNS
|
---|
| 90 | #define SNTP_SERVER_ADDRESS "pool.ntp.org"
|
---|
| 91 | #else
|
---|
| 92 | #define SNTP_SERVER_ADDRESS "213.161.194.93" /* pool.ntp.org */
|
---|
| 93 | #endif
|
---|
| 94 | #endif
|
---|
| 95 |
|
---|
| 96 | /** Sanity check:
|
---|
| 97 | * Define this to
|
---|
| 98 | * - 0 to turn off sanity checks (default; smaller code)
|
---|
| 99 | * - >= 1 to check address and port of the response packet to ensure the
|
---|
| 100 | * response comes from the server we sent the request to.
|
---|
| 101 | * - >= 2 to check returned Originate Timestamp against Transmit Timestamp
|
---|
| 102 | * sent to the server (to ensure response to older request).
|
---|
| 103 | * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp
|
---|
| 104 | * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast).
|
---|
| 105 | * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each
|
---|
| 106 | * greater than or equal to 0 and less than infinity, where infinity is
|
---|
| 107 | * currently a cozy number like one second. This check avoids using a
|
---|
| 108 | * server whose synchronization source has expired for a very long time.
|
---|
| 109 | */
|
---|
| 110 | #ifndef SNTP_CHECK_RESPONSE
|
---|
| 111 | #define SNTP_CHECK_RESPONSE 0
|
---|
| 112 | #endif
|
---|
| 113 |
|
---|
| 114 | /** According to the RFC, this shall be a random delay
|
---|
| 115 | * between 1 and 5 minutes (in milliseconds) to prevent load peaks.
|
---|
| 116 | * This can be defined to a random generation function,
|
---|
| 117 | * which must return the delay in milliseconds as u32_t.
|
---|
| 118 | * Turned off by default.
|
---|
| 119 | */
|
---|
| 120 | #ifndef SNTP_STARTUP_DELAY
|
---|
| 121 | #define SNTP_STARTUP_DELAY 0
|
---|
| 122 | #endif
|
---|
| 123 |
|
---|
| 124 | /** SNTP receive timeout - in milliseconds
|
---|
| 125 | * Also used as retry timeout - this shouldn't be too low.
|
---|
| 126 | * Default is 3 seconds.
|
---|
| 127 | */
|
---|
| 128 | #ifndef SNTP_RECV_TIMEOUT
|
---|
| 129 | #define SNTP_RECV_TIMEOUT 3000
|
---|
| 130 | #endif
|
---|
| 131 |
|
---|
| 132 | /** SNTP update delay - in milliseconds
|
---|
| 133 | * Default is 1 hour.
|
---|
| 134 | */
|
---|
| 135 | #ifndef SNTP_UPDATE_DELAY
|
---|
| 136 | #define SNTP_UPDATE_DELAY 3600000
|
---|
| 137 | #endif
|
---|
| 138 | #if (SNTP_UPDATE_DELAY < 15000) && !SNTP_SUPPRESS_DELAY_CHECK
|
---|
| 139 | #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds!"
|
---|
| 140 | #endif
|
---|
| 141 |
|
---|
| 142 | /** SNTP macro to change system time and/or the update the RTC clock */
|
---|
| 143 | #ifndef SNTP_SET_SYSTEM_TIME
|
---|
| 144 | #define SNTP_SET_SYSTEM_TIME(sec) ((void)sec)
|
---|
| 145 | #endif
|
---|
| 146 |
|
---|
| 147 | /** SNTP macro to change system time including microseconds */
|
---|
| 148 | #ifdef SNTP_SET_SYSTEM_TIME_US
|
---|
| 149 | #define SNTP_CALC_TIME_US 1
|
---|
| 150 | #define SNTP_RECEIVE_TIME_SIZE 2
|
---|
| 151 | #else
|
---|
| 152 | #define SNTP_SET_SYSTEM_TIME_US(sec, us)
|
---|
| 153 | #define SNTP_CALC_TIME_US 0
|
---|
| 154 | #define SNTP_RECEIVE_TIME_SIZE 1
|
---|
| 155 | #endif
|
---|
| 156 |
|
---|
| 157 | /** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2
|
---|
| 158 | * to send in request and compare in response.
|
---|
| 159 | */
|
---|
| 160 | #ifndef SNTP_GET_SYSTEM_TIME
|
---|
| 161 | #define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0)
|
---|
| 162 | #endif
|
---|
| 163 |
|
---|
| 164 | /** Default retry timeout (in milliseconds) if the response
|
---|
| 165 | * received is invalid.
|
---|
| 166 | * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached.
|
---|
| 167 | */
|
---|
| 168 | #ifndef SNTP_RETRY_TIMEOUT
|
---|
| 169 | #define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT
|
---|
| 170 | #endif
|
---|
| 171 |
|
---|
| 172 | /** Maximum retry timeout (in milliseconds). */
|
---|
| 173 | #ifndef SNTP_RETRY_TIMEOUT_MAX
|
---|
| 174 | #define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10)
|
---|
| 175 | #endif
|
---|
| 176 |
|
---|
| 177 | /** Increase retry timeout with every retry sent
|
---|
| 178 | * Default is on to conform to RFC.
|
---|
| 179 | */
|
---|
| 180 | #ifndef SNTP_RETRY_TIMEOUT_EXP
|
---|
| 181 | #define SNTP_RETRY_TIMEOUT_EXP 1
|
---|
| 182 | #endif
|
---|
| 183 |
|
---|
| 184 | /* the various debug levels for this file */
|
---|
| 185 | #define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
|
---|
| 186 | #define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
|
---|
| 187 | #define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
|
---|
| 188 | #define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
|
---|
| 189 | #define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
|
---|
| 190 |
|
---|
| 191 | #define SNTP_ERR_KOD 1
|
---|
| 192 |
|
---|
| 193 | /* SNTP protocol defines */
|
---|
| 194 | #define SNTP_MSG_LEN 48
|
---|
| 195 |
|
---|
| 196 | #define SNTP_OFFSET_LI_VN_MODE 0
|
---|
| 197 | #define SNTP_LI_MASK 0xC0
|
---|
| 198 | #define SNTP_LI_NO_WARNING 0x00
|
---|
| 199 | #define SNTP_LI_LAST_MINUTE_61_SEC 0x01
|
---|
| 200 | #define SNTP_LI_LAST_MINUTE_59_SEC 0x02
|
---|
| 201 | #define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
|
---|
| 202 |
|
---|
| 203 | #define SNTP_VERSION_MASK 0x38
|
---|
| 204 | #define SNTP_VERSION (4/* NTP Version 4*/<<3)
|
---|
| 205 |
|
---|
| 206 | #define SNTP_MODE_MASK 0x07
|
---|
| 207 | #define SNTP_MODE_CLIENT 0x03
|
---|
| 208 | #define SNTP_MODE_SERVER 0x04
|
---|
| 209 | #define SNTP_MODE_BROADCAST 0x05
|
---|
| 210 |
|
---|
| 211 | #define SNTP_OFFSET_STRATUM 1
|
---|
| 212 | #define SNTP_STRATUM_KOD 0x00
|
---|
| 213 |
|
---|
| 214 | #define SNTP_OFFSET_ORIGINATE_TIME 24
|
---|
| 215 | #define SNTP_OFFSET_RECEIVE_TIME 32
|
---|
| 216 | #define SNTP_OFFSET_TRANSMIT_TIME 40
|
---|
| 217 |
|
---|
| 218 | /* number of seconds between 1900 and 1970 */
|
---|
| 219 | #define DIFF_SEC_1900_1970 (2208988800UL)
|
---|
| 220 |
|
---|
| 221 | /**
|
---|
| 222 | * SNTP packet format (without optional fields)
|
---|
| 223 | * Timestamps are coded as 64 bits:
|
---|
| 224 | * - 32 bits seconds since Jan 01, 1970, 00:00
|
---|
| 225 | * - 32 bits seconds fraction (0-padded)
|
---|
| 226 | * For future use, if the MSB in the seconds part is set, seconds are based
|
---|
| 227 | * on Feb 07, 2036, 06:28:16.
|
---|
| 228 | */
|
---|
| 229 | #ifdef PACK_STRUCT_USE_INCLUDES
|
---|
| 230 | # include "arch/bpstruct.h"
|
---|
| 231 | #endif
|
---|
| 232 | PACK_STRUCT_BEGIN
|
---|
| 233 | struct sntp_msg {
|
---|
| 234 | PACK_STRUCT_FIELD(u8_t li_vn_mode);
|
---|
| 235 | PACK_STRUCT_FIELD(u8_t stratum);
|
---|
| 236 | PACK_STRUCT_FIELD(u8_t poll);
|
---|
| 237 | PACK_STRUCT_FIELD(u8_t precision);
|
---|
| 238 | PACK_STRUCT_FIELD(u32_t root_delay);
|
---|
| 239 | PACK_STRUCT_FIELD(u32_t root_dispersion);
|
---|
| 240 | PACK_STRUCT_FIELD(u32_t reference_identifier);
|
---|
| 241 | PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
|
---|
| 242 | PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
|
---|
| 243 | PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
|
---|
| 244 | PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
|
---|
| 245 | } PACK_STRUCT_STRUCT;
|
---|
| 246 | PACK_STRUCT_END
|
---|
| 247 | #ifdef PACK_STRUCT_USE_INCLUDES
|
---|
| 248 | # include "arch/epstruct.h"
|
---|
| 249 | #endif
|
---|
| 250 |
|
---|
| 251 | /* function prototypes */
|
---|
| 252 | static void sntp_request(void *arg);
|
---|
| 253 |
|
---|
| 254 | /** The UDP pcb used by the SNTP client */
|
---|
| 255 | static struct udp_pcb* sntp_pcb;
|
---|
| 256 | /** Addresses of servers */
|
---|
| 257 | static char* sntp_server_addresses[] = {SNTP_SERVER_ADDRESS};
|
---|
| 258 | #if SNTP_SUPPORT_MULTIPLE_SERVERS
|
---|
| 259 | /** The currently used server (initialized to 0) */
|
---|
| 260 | static u8_t sntp_current_server;
|
---|
| 261 | static u8_t sntp_num_servers = sizeof(sntp_server_addresses)/sizeof(char*);
|
---|
| 262 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
---|
| 263 | #define sntp_current_server 0
|
---|
| 264 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
---|
| 265 |
|
---|
| 266 | #if SNTP_RETRY_TIMEOUT_EXP
|
---|
| 267 | #define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
|
---|
| 268 | /** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
|
---|
| 269 | static u32_t sntp_retry_timeout;
|
---|
| 270 | #else /* SNTP_RETRY_TIMEOUT_EXP */
|
---|
| 271 | #define SNTP_RESET_RETRY_TIMEOUT()
|
---|
| 272 | #define sntp_retry_timeout SNTP_RETRY_TIMEOUT
|
---|
| 273 | #endif /* SNTP_RETRY_TIMEOUT_EXP */
|
---|
| 274 |
|
---|
| 275 | #if SNTP_CHECK_RESPONSE >= 1
|
---|
| 276 | /** Saves the last server address to compare with response */
|
---|
| 277 | static ip_addr_t sntp_last_server_address;
|
---|
| 278 | #endif /* SNTP_CHECK_RESPONSE >= 1 */
|
---|
| 279 |
|
---|
| 280 | #if SNTP_CHECK_RESPONSE >= 2
|
---|
| 281 | /** Saves the last timestamp sent (which is sent back by the server)
|
---|
| 282 | * to compare against in response */
|
---|
| 283 | static u32_t sntp_last_timestamp_sent[2];
|
---|
| 284 | #endif /* SNTP_CHECK_RESPONSE >= 2 */
|
---|
| 285 |
|
---|
| 286 | /**
|
---|
| 287 | * SNTP processing of received timestamp
|
---|
| 288 | */
|
---|
| 289 | static void
|
---|
| 290 | sntp_process(u32_t *receive_timestamp)
|
---|
| 291 | {
|
---|
| 292 | /* convert SNTP time (1900-based) to unix GMT time (1970-based)
|
---|
| 293 | * @todo: if MSB is 1, SNTP time is 2036-based!
|
---|
| 294 | */
|
---|
| 295 | time_t t = (ntohl(receive_timestamp[0]) - DIFF_SEC_1900_1970);
|
---|
| 296 |
|
---|
| 297 | #if SNTP_CALC_TIME_US
|
---|
| 298 | u32_t us = ntohl(receive_timestamp[1]) / 4295;
|
---|
| 299 | SNTP_SET_SYSTEM_TIME_US(t, us);
|
---|
| 300 | /* display local time from GMT time */
|
---|
| 301 | LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&t), us));
|
---|
| 302 |
|
---|
| 303 | #else /* SNTP_CALC_TIME_US */
|
---|
| 304 |
|
---|
| 305 | /* change system time and/or the update the RTC clock */
|
---|
| 306 | SNTP_SET_SYSTEM_TIME(t);
|
---|
| 307 | /* display local time from GMT time */
|
---|
| 308 | LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&t)));
|
---|
| 309 | #endif /* SNTP_CALC_TIME_US */
|
---|
| 310 | }
|
---|
| 311 |
|
---|
| 312 | /**
|
---|
| 313 | * Initialize request struct to be sent to server.
|
---|
| 314 | */
|
---|
| 315 | static void
|
---|
| 316 | sntp_initialize_request(struct sntp_msg *req)
|
---|
| 317 | {
|
---|
| 318 | memset(req, 0, SNTP_MSG_LEN);
|
---|
| 319 | req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
|
---|
| 320 |
|
---|
| 321 | #if SNTP_CHECK_RESPONSE >= 2
|
---|
| 322 | {
|
---|
| 323 | u32_t sntp_time_sec, sntp_time_us;
|
---|
| 324 | /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
|
---|
| 325 | SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
|
---|
| 326 | sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970);
|
---|
| 327 | req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
|
---|
| 328 | /* we send/save us instead of fraction to be faster... */
|
---|
| 329 | sntp_last_timestamp_sent[1] = htonl(sntp_time_us);
|
---|
| 330 | req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
|
---|
| 331 | }
|
---|
| 332 | #endif /* SNTP_CHECK_RESPONSE >= 2 */
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | /**
|
---|
| 336 | * Retry: send a new request (and increase retry timeout).
|
---|
| 337 | *
|
---|
| 338 | * @param arg is unused (only necessary to conform to sys_timeout)
|
---|
| 339 | */
|
---|
| 340 | static void
|
---|
| 341 | sntp_retry(void* arg)
|
---|
| 342 | {
|
---|
| 343 | LWIP_UNUSED_ARG(arg);
|
---|
| 344 |
|
---|
| 345 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
|
---|
| 346 | sntp_retry_timeout));
|
---|
| 347 |
|
---|
| 348 | /* set up a timer to send a retry and increase the retry delay */
|
---|
| 349 | sys_timeout(sntp_retry_timeout, sntp_request, NULL);
|
---|
| 350 |
|
---|
| 351 | #if SNTP_RETRY_TIMEOUT_EXP
|
---|
| 352 | {
|
---|
| 353 | u32_t new_retry_timeout;
|
---|
| 354 | /* increase the timeout for next retry */
|
---|
| 355 | new_retry_timeout = sntp_retry_timeout << 1;
|
---|
| 356 | /* limit to maximum timeout and prevent overflow */
|
---|
| 357 | if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
|
---|
| 358 | (new_retry_timeout > sntp_retry_timeout)) {
|
---|
| 359 | sntp_retry_timeout = new_retry_timeout;
|
---|
| 360 | }
|
---|
| 361 | }
|
---|
| 362 | #endif /* SNTP_RETRY_TIMEOUT_EXP */
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | #if SNTP_SUPPORT_MULTIPLE_SERVERS
|
---|
| 366 | /**
|
---|
| 367 | * If Kiss-of-Death is received (or another packet parsing error),
|
---|
| 368 | * try the next server or retry the current server and increase the retry
|
---|
| 369 | * timeout if only one server is available.
|
---|
| 370 | *
|
---|
| 371 | * @param arg is unused (only necessary to conform to sys_timeout)
|
---|
| 372 | */
|
---|
| 373 | static void
|
---|
| 374 | sntp_try_next_server(void* arg)
|
---|
| 375 | {
|
---|
| 376 | LWIP_UNUSED_ARG(arg);
|
---|
| 377 |
|
---|
| 378 | if (sntp_num_servers > 1) {
|
---|
| 379 | /* new server: reset retry timeout */
|
---|
| 380 | SNTP_RESET_RETRY_TIMEOUT();
|
---|
| 381 | sntp_current_server++;
|
---|
| 382 | if (sntp_current_server >= sntp_num_servers) {
|
---|
| 383 | sntp_current_server = 0;
|
---|
| 384 | }
|
---|
| 385 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
|
---|
| 386 | (u16_t)sntp_current_server));
|
---|
| 387 | /* instantly send a request to the next server */
|
---|
| 388 | sntp_request(NULL);
|
---|
| 389 | } else {
|
---|
| 390 | sntp_retry(NULL);
|
---|
| 391 | }
|
---|
| 392 | }
|
---|
| 393 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
---|
| 394 | /* Always retry on error if only one server is supported */
|
---|
| 395 | #define sntp_try_next_server sntp_retry
|
---|
| 396 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
---|
| 397 |
|
---|
| 398 | /** UDP recv callback for the sntp pcb */
|
---|
| 399 | static void
|
---|
| 400 | sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
|
---|
| 401 | {
|
---|
| 402 | u8_t mode;
|
---|
| 403 | u8_t stratum;
|
---|
| 404 | u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
|
---|
| 405 | err_t err;
|
---|
| 406 |
|
---|
| 407 | LWIP_UNUSED_ARG(arg);
|
---|
| 408 | LWIP_UNUSED_ARG(pcb);
|
---|
| 409 |
|
---|
| 410 | /* packet received: stop retry timeout */
|
---|
| 411 | sys_untimeout(sntp_try_next_server, NULL);
|
---|
| 412 | sys_untimeout(sntp_request, NULL);
|
---|
| 413 |
|
---|
| 414 | err = ERR_ARG;
|
---|
| 415 | #if SNTP_CHECK_RESPONSE >= 1
|
---|
| 416 | /* check server address and port */
|
---|
| 417 | if (ip_addr_cmp(addr, &sntp_last_server_address) &&
|
---|
| 418 | (port == SNTP_PORT))
|
---|
| 419 | #else /* SNTP_CHECK_RESPONSE >= 1 */
|
---|
| 420 | LWIP_UNUSED_ARG(addr);
|
---|
| 421 | LWIP_UNUSED_ARG(port);
|
---|
| 422 | #endif /* SNTP_CHECK_RESPONSE >= 1 */
|
---|
| 423 | {
|
---|
| 424 | /* process the response */
|
---|
| 425 | if (p->tot_len == SNTP_MSG_LEN) {
|
---|
| 426 | pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
|
---|
| 427 | mode &= SNTP_MODE_MASK;
|
---|
| 428 | /* if this is a SNTP response... */
|
---|
| 429 | if ((mode == SNTP_MODE_SERVER) ||
|
---|
| 430 | (mode == SNTP_MODE_BROADCAST)) {
|
---|
| 431 | pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
|
---|
| 432 | if (stratum == SNTP_STRATUM_KOD) {
|
---|
| 433 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
|
---|
| 434 | err = SNTP_ERR_KOD;
|
---|
| 435 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
|
---|
| 436 | } else {
|
---|
| 437 | #if SNTP_CHECK_RESPONSE >= 2
|
---|
| 438 | /* check originate_timetamp against sntp_last_timestamp_sent */
|
---|
| 439 | u32_t originate_timestamp[2];
|
---|
| 440 | pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
|
---|
| 441 | if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
|
---|
| 442 | (originate_timestamp[1] != sntp_last_timestamp_sent[1]))
|
---|
| 443 | {
|
---|
| 444 | LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
|
---|
| 445 | } else
|
---|
| 446 | #endif /* SNTP_CHECK_RESPONSE >= 2 */
|
---|
| 447 | /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
|
---|
| 448 | {
|
---|
| 449 | /* correct answer */
|
---|
| 450 | err = ERR_OK;
|
---|
| 451 | pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_RECEIVE_TIME);
|
---|
| 452 | }
|
---|
| 453 | }
|
---|
| 454 | } else {
|
---|
| 455 | LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
|
---|
| 456 | }
|
---|
| 457 | } else {
|
---|
| 458 | LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
|
---|
| 459 | }
|
---|
| 460 | }
|
---|
| 461 | pbuf_free(p);
|
---|
| 462 | if (err == ERR_OK) {
|
---|
| 463 | /* Correct response, reset retry timeout */
|
---|
| 464 | SNTP_RESET_RETRY_TIMEOUT();
|
---|
| 465 |
|
---|
| 466 | sntp_process(receive_timestamp);
|
---|
| 467 |
|
---|
| 468 | /* Set up timeout for next request */
|
---|
| 469 | sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
|
---|
| 470 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
|
---|
| 471 | (u32_t)SNTP_UPDATE_DELAY));
|
---|
| 472 | } else if (err == SNTP_ERR_KOD) {
|
---|
| 473 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
|
---|
| 474 | sntp_try_next_server(NULL);
|
---|
| 475 | } else {
|
---|
| 476 | /* another error, try the same server again */
|
---|
| 477 | sntp_retry(NULL);
|
---|
| 478 | }
|
---|
| 479 | }
|
---|
| 480 |
|
---|
| 481 | /** Actually send an sntp request to a server.
|
---|
| 482 | *
|
---|
| 483 | * @param server_addr resolved IP address of the SNTP server
|
---|
| 484 | */
|
---|
| 485 | static void
|
---|
| 486 | sntp_send_request(ip_addr_t *server_addr)
|
---|
| 487 | {
|
---|
| 488 | struct pbuf* p;
|
---|
| 489 | p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
|
---|
| 490 | if (p != NULL) {
|
---|
| 491 | struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
|
---|
| 492 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
|
---|
| 493 | /* initialize request message */
|
---|
| 494 | sntp_initialize_request(sntpmsg);
|
---|
| 495 | /* send request */
|
---|
| 496 | udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
|
---|
| 497 | /* free the pbuf after sending it */
|
---|
| 498 | pbuf_free(p);
|
---|
| 499 | /* set up receive timeout: try next server or retry on timeout */
|
---|
| 500 | sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
|
---|
| 501 | #if SNTP_CHECK_RESPONSE >= 1
|
---|
| 502 | /* save server address to verify it in sntp_recv */
|
---|
| 503 | ip_addr_set(&sntp_last_server_address, server_addr);
|
---|
| 504 | #endif /* SNTP_CHECK_RESPONSE >= 1 */
|
---|
| 505 | } else {
|
---|
| 506 | LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
|
---|
| 507 | (u32_t)SNTP_RETRY_TIMEOUT));
|
---|
| 508 | /* out of memory: set up a timer to send a retry */
|
---|
| 509 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
|
---|
| 510 | }
|
---|
| 511 | }
|
---|
| 512 |
|
---|
| 513 | #if SNTP_SERVER_DNS
|
---|
| 514 | /**
|
---|
| 515 | * DNS found callback when using DNS names as server address.
|
---|
| 516 | */
|
---|
| 517 | static void
|
---|
| 518 | sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg)
|
---|
| 519 | {
|
---|
| 520 | LWIP_UNUSED_ARG(hostname);
|
---|
| 521 | LWIP_UNUSED_ARG(arg);
|
---|
| 522 |
|
---|
| 523 | if (ipaddr != NULL) {
|
---|
| 524 | /* Address resolved, send request */
|
---|
| 525 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
|
---|
| 526 | sntp_send_request(ipaddr);
|
---|
| 527 | } else {
|
---|
| 528 | /* DNS resolving failed -> try another server */
|
---|
| 529 | LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
|
---|
| 530 | sntp_try_next_server(NULL);
|
---|
| 531 | }
|
---|
| 532 | }
|
---|
| 533 | #endif /* SNTP_SERVER_DNS */
|
---|
| 534 |
|
---|
| 535 | /**
|
---|
| 536 | * Send out an sntp request.
|
---|
| 537 | *
|
---|
| 538 | * @param arg is unused (only necessary to conform to sys_timeout)
|
---|
| 539 | */
|
---|
| 540 | static void
|
---|
| 541 | sntp_request(void *arg)
|
---|
| 542 | {
|
---|
| 543 | ip_addr_t sntp_server_address;
|
---|
| 544 | err_t err;
|
---|
| 545 |
|
---|
| 546 | LWIP_UNUSED_ARG(arg);
|
---|
| 547 |
|
---|
| 548 | /* initialize SNTP server address */
|
---|
| 549 | #if SNTP_SERVER_DNS
|
---|
| 550 | err = dns_gethostbyname(sntp_server_addresses[sntp_current_server], &sntp_server_address,
|
---|
| 551 | sntp_dns_found, NULL);
|
---|
| 552 | if (err == ERR_INPROGRESS) {
|
---|
| 553 | /* DNS request sent, wait for sntp_dns_found being called */
|
---|
| 554 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
|
---|
| 555 | return;
|
---|
| 556 | }
|
---|
| 557 | #else /* SNTP_SERVER_DNS */
|
---|
| 558 | err = ipaddr_aton(sntp_server_addresses[sntp_current_server], &sntp_server_address)
|
---|
| 559 | ? ERR_OK : ERR_ARG;
|
---|
| 560 |
|
---|
| 561 | #endif /* SNTP_SERVER_DNS */
|
---|
| 562 |
|
---|
| 563 | if (err == ERR_OK) {
|
---|
| 564 | sntp_send_request(&sntp_server_address);
|
---|
| 565 | } else {
|
---|
| 566 | /* address conversion failed, try another server */
|
---|
| 567 | LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
|
---|
| 568 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
|
---|
| 569 | }
|
---|
| 570 | }
|
---|
| 571 |
|
---|
| 572 | /**
|
---|
| 573 | * Initialize this module.
|
---|
| 574 | * Send out request instantly or after SNTP_STARTUP_DELAY.
|
---|
| 575 | */
|
---|
| 576 | void
|
---|
| 577 | sntp_init(void)
|
---|
| 578 | {
|
---|
| 579 | if (sntp_pcb == NULL) {
|
---|
| 580 | SNTP_RESET_RETRY_TIMEOUT();
|
---|
| 581 | sntp_pcb = udp_new();
|
---|
| 582 | LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
|
---|
| 583 | if (sntp_pcb != NULL) {
|
---|
| 584 | udp_recv(sntp_pcb, sntp_recv, NULL);
|
---|
| 585 | #if SNTP_STARTUP_DELAY
|
---|
| 586 | sys_timeout((u32_t)SNTP_STARTUP_DELAY, sntp_request, NULL);
|
---|
| 587 | #else
|
---|
| 588 | sntp_request(NULL);
|
---|
| 589 | #endif
|
---|
| 590 | }
|
---|
| 591 | }
|
---|
| 592 | }
|
---|
| 593 |
|
---|
| 594 | /**
|
---|
| 595 | * Stop this module.
|
---|
| 596 | */
|
---|
| 597 | void
|
---|
| 598 | sntp_stop(void)
|
---|
| 599 | {
|
---|
| 600 | if (sntp_pcb != NULL) {
|
---|
| 601 | sys_untimeout(sntp_request, NULL);
|
---|
| 602 | udp_remove(sntp_pcb);
|
---|
| 603 | sntp_pcb = NULL;
|
---|
| 604 | }
|
---|
| 605 | }
|
---|
| 606 | #endif /* LWIP_UDP */
|
---|