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 */
|
---|